diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 416c5120..1d01065d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -34,7 +34,43 @@ env:
   CARGO_PROFILE_CHECK_ONLY_CODEGEN_UNITS: 4
 
 jobs:
-  test:
+  changed-files:
+    runs-on: ubuntu-latest
+    name: Test changed-files
+    permissions:
+      pull-requests: read
+
+    outputs:
+      crates_changed: ${{ steps.list-changed-files.outputs.crates_changed }}
+      has_detect_target_changed: ${{ steps.list-changed-files.outputs.has_detect_target_changed }}
+
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+
+      - name: Get changed files
+        id: changed-files
+        uses: tj-actions/changed-files@v44
+        with:
+          dir_names: true
+          dir_names_exclude_current_dir: true
+          dir_names_max_depth: 2
+          separator: "\n"
+
+      - name: List all changed files
+        id: list-changed-files
+        env:
+          ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
+        run: |
+          set -euxo pipefail
+          crates_changed="$(echo "$ALL_CHANGED_FILES" | grep crates | cut -d / -f 2)"
+          has_detect_target_changed="$(echo "$crates_changed" | grep -q detect-targets && echo true || echo false)"
+          echo "crates_changed=$crates_changed" | tee -a "$GITHUB_OUTPUT"
+          echo "has_detect_target_changed=$has_detect_target_changed" | tee -a "$GITHUB_OUTPUT"
+
+  unit-tests:
+    needs: changed-files
     strategy:
       fail-fast: false
       matrix:
@@ -60,11 +96,52 @@ jobs:
         with:
           tools: cargo-nextest
 
-      - run: just test
+      - name: Decide crates to test
+        shell: bash
+        env:
+          CRATES_CHANGED: ${{ needs.changed-files.outputs.crates_changed }}
+        run: |
+          ARGS=""
+          for crate in $CRATES_CHANGED; do
+              ARGS="$ARGS -p $crate"
+          done
+          echo "CARGO_NEXTEST_ADDITIONAL_ARGS=$ARGS" | tee -a "$GITHUB_ENV"
+
+      - run: just unit-tests
         env:
           GITHUB_TOKEN: ${{ secrets.CI_TEST_GITHUB_TOKEN }}
           CI_UNIT_TEST_GITHUB_TOKEN: ${{ secrets.CI_UNIT_TEST_GITHUB_TOKEN }}
 
+  e2e-tests:
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - target: x86_64-apple-darwin
+            os: macos-14
+          - target: x86_64-unknown-linux-gnu
+            os: ubuntu-latest
+          - target: x86_64-pc-windows-msvc
+            os: windows-latest
+
+    runs-on: ${{ matrix.os }}
+    env:
+      CARGO_BUILD_TARGET: ${{ matrix.target }}
+
+    steps:
+      - uses: actions/checkout@v4
+      - uses: ./.github/actions/just-setup
+        env:
+          # just-setup use binstall to install sccache,
+          # which works better when we provide it with GITHUB_TOKEN.
+          GITHUB_TOKEN: ${{ secrets.CI_RELEASE_TEST_GITHUB_TOKEN }}
+
+      - run: |
+          just build
+          just e2e-tests
+        env:
+          GITHUB_TOKEN: ${{ secrets.CI_TEST_GITHUB_TOKEN }}
+
   cross-check:
     strategy:
       fail-fast: false
@@ -161,6 +238,8 @@ jobs:
       CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 4
 
   detect-targets-build:
+    needs: changed-files
+    if: needs.changed-files.outputs.has_detect_target_changed == 'true'
     runs-on: ubuntu-latest
     env:
       CARGO_BUILD_TARGET: x86_64-unknown-linux-musl
@@ -186,7 +265,10 @@ jobs:
 
   detect-targets-alpine-test:
     runs-on: ubuntu-latest
-    needs: detect-targets-build
+    needs:
+      - detect-targets-build
+      - changed-files
+    if: needs.changed-files.outputs.has_detect_target_changed == 'true'
     steps:
       - uses: actions/checkout@v4
 
@@ -203,7 +285,10 @@ jobs:
             alpine /bin/ash -c "apk update && apk add bash && test.sh x86_64-unknown-linux-musl"
 
   detect-targets-ubuntu-test:
-    needs: detect-targets-build
+    needs:
+      - detect-targets-build
+      - changed-files
+    if: needs.changed-files.outputs.has_detect_target_changed == 'true'
     strategy:
       fail-fast: false
       matrix:
@@ -223,7 +308,10 @@ jobs:
           [ "$(./detect-targets)" = "$(printf 'x86_64-unknown-linux-gnu\nx86_64-unknown-linux-musl')" ]
 
   detect-targets-more-glibc-test:
-    needs: detect-targets-build
+    needs:
+      - detect-targets-build
+      - changed-files
+    if: needs.changed-files.outputs.has_detect_target_changed == 'true'
     strategy:
       fail-fast: false
       matrix:
@@ -248,7 +336,10 @@ jobs:
             ${{ matrix.container }} detect-targets )" = "$(printf 'x86_64-unknown-linux-gnu\nx86_64-unknown-linux-musl')" ]
 
   detect-targets-nix-test:
-    needs: detect-targets-build
+    needs:
+      - detect-targets-build
+      - changed-files
+    if: needs.changed-files.outputs.has_detect_target_changed == 'true'
     runs-on: ubuntu-latest
     steps:
       - uses: actions/download-artifact@v4
@@ -264,6 +355,8 @@ jobs:
             nixos/nix /detect-targets )" = x86_64-unknown-linux-musl ]
 
   detect-targets-android-check:
+    needs: changed-files
+    if: needs.changed-files.outputs.has_detect_target_changed == 'true'
     strategy:
       fail-fast: false
       matrix:
@@ -294,7 +387,8 @@ jobs:
   tests-pass:
     name: Tests pass
     needs:
-      - test
+      - unit-tests
+      - e2e-tests
       - cross-check
       - lint
       - release-dry-run
diff --git a/crates/binstalk-git-repo-api/src/gh_api_client.rs b/crates/binstalk-git-repo-api/src/gh_api_client.rs
index ac7f1565..bc480f67 100644
--- a/crates/binstalk-git-repo-api/src/gh_api_client.rs
+++ b/crates/binstalk-git-repo-api/src/gh_api_client.rs
@@ -493,7 +493,7 @@ mod test {
         remote::Client::new(
             concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")),
             None,
-            NonZeroU16::new(10).unwrap(),
+            NonZeroU16::new(200).unwrap(),
             1.try_into().unwrap(),
             [],
         )
diff --git a/justfile b/justfile
index c813172c..01270a4f 100644
--- a/justfile
+++ b/justfile
@@ -12,6 +12,7 @@ use-auditable := env_var_or_default("JUST_USE_AUDITABLE", "")
 timings := env_var_or_default("JUST_TIMINGS", "")
 build-std := env_var_or_default("JUST_BUILD_STD", "")
 enable-h3 := env_var_or_default("JUST_ENABLE_H3", "")
+cargo-nextest-additional-args := env_var_or_default("CARGO_NEXTEST_ADDITIONAL_ARGS", "")
 
 export BINSTALL_LOG_LEVEL := if env_var_or_default("RUNNER_DEBUG", "0") == "1" { "debug" } else { "info" }
 export BINSTALL_RATE_LIMIT := "30/1"
@@ -260,8 +261,8 @@ e2e-test-tls: (e2e-test "tls" "1.2") (e2e-test "tls" "1.3")
 e2e-tests: e2e-test-live e2e-test-manifest-path e2e-test-git e2e-test-other-repos e2e-test-strategies e2e-test-version-syntax e2e-test-upgrade e2e-test-tls e2e-test-self-upgrade-no-symlink e2e-test-uninstall e2e-test-subcrate e2e-test-no-track e2e-test-registries e2e-test-signing e2e-test-continue-on-failure e2e-test-private-github-repo
 
 unit-tests: print-env
-    cargo nextest run {{cargo-build-args}}
-    cargo test --doc {{cargo-build-args}}
+    cargo nextest run --target {{target}} {{cargo-nextest-additional-args}}
+    cargo test --doc --target {{target}}
 
 test: unit-tests build e2e-tests