From 4de9b01f87f96715ccae19427529a7e39638514e Mon Sep 17 00:00:00 2001 From: BD Himes Date: Mon, 1 Jun 2026 16:33:25 +0200 Subject: [PATCH 1/9] Runs the SDK E2E tests concurrently per test case (rather than per test file) --- .github/workflows/check-sdk-tests.yml | 87 +++++++-------------------- 1 file changed, 23 insertions(+), 64 deletions(-) diff --git a/.github/workflows/check-sdk-tests.yml b/.github/workflows/check-sdk-tests.yml index 632f86b..99fac81 100644 --- a/.github/workflows/check-sdk-tests.yml +++ b/.github/workflows/check-sdk-tests.yml @@ -91,7 +91,7 @@ jobs: if: always() && needs.check-labels.outputs.run-sdk-tests == 'true' runs-on: ubuntu-latest outputs: - test-files: ${{ steps.get-tests.outputs.test-files }} + test-cases: ${{ steps.get-tests.outputs.test-cases }} steps: - name: Research preparation working-directory: ${{ github.workspace }} @@ -110,11 +110,26 @@ jobs: - name: Install dependencies run: sudo apt-get install -y jq - - name: Find e2e test files + - name: Find e2e test cases id: get-tests run: | - test_files=$(find ${{ github.workspace }}/bittensor/tests/e2e_tests -name "test*.py" | jq -R -s -c 'split("\n") | map(select(. != ""))') - echo "test-files=$test_files" >> $GITHUB_OUTPUT + # Emit one matrix entry per individual test function (as a `path::test_name` + # node id) rather than per file, so each test gets its own concurrent job. + # The e2e suite has no test classes, so grepping top-level `def test_*` is + # sufficient and keeps this job dependency-free (no pytest collection needed). + # Node ids are relative to the bittensor repo root so they resolve inside the + # prebuilt image (where bittensor lives at /opt/bittensor), not the workspace. + cd "${{ github.workspace }}/bittensor" + test_cases=$( + while IFS= read -r f; do + grep -oE '^(async[[:space:]]+)?def[[:space:]]+test_[A-Za-z0-9_]+' "$f" \ + | grep -oE 'test_[A-Za-z0-9_]+' \ + | while IFS= read -r name; do printf '%s::%s\n' "$f" "$name"; done + done < <(find tests/e2e_tests -name "test*.py") \ + | jq -R -s -c 'split("\n") | map(select(. != ""))' + ) + echo "Collected $(echo "$test_cases" | jq 'length') test cases" + echo "test-cases=$test_cases" >> $GITHUB_OUTPUT shell: bash pull-docker-image: @@ -152,13 +167,7 @@ jobs: fail-fast: false max-parallel: 16 matrix: - rust-branch: - - stable - rust-target: - - x86_64-unknown-linux-gnu - os: - - ubuntu-latest - test-file: ${{ fromJson(needs.find-e2e-tests.outputs.test-files) }} + test-case: ${{ fromJson(needs.find-e2e-tests.outputs.test-cases) }} env: RELEASE_NAME: development @@ -168,59 +177,8 @@ jobs: TARGET: ${{ matrix.rust-target }} timeout-minutes: 60 - name: "e2e tests: ${{ matrix.test-file }}" + name: "e2e tests: ${{ matrix.test-case }}" steps: - - name: Check-out repository - uses: actions/checkout@v6 - - - name: Install system dependencies - run: | - sudo apt-get update && - sudo apt-get install -y clang curl libssl-dev llvm libudev-dev protobuf-compiler - - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: '3.13' - - - name: Install uv - uses: astral-sh/setup-uv@v4 - - - name: Clone Bittensor SDK repo - run: git clone https://github.com/latent-to/bittensor.git - - - name: Checkout Bittensor branch - working-directory: ${{ github.workspace }}/bittensor - run: | - if ! git fetch origin $BITTENSOR_BRANCH; then - echo "❌ Error: Branch '$BITTENSOR_BRANCH' does not exist in latent-to/bittensor." - exit 1 - fi - git checkout FETCH_HEAD - echo "✅ Using Bittensor branch: $BITTENSOR_BRANCH" - - - name: Install Bittensor SDK dependencies - working-directory: ${{ github.workspace }}/bittensor - run: uv pip install --system '.[dev]' - - - name: Clone async-substrate-interface repo - run: git clone https://github.com/latent-to/async-substrate-interface.git - - - name: Checkout PR branch in async-substrate-interface - working-directory: ${{ github.workspace }}/async-substrate-interface - run: | - BRANCH="${{ github.event.pull_request.head.ref || github.ref_name }}" - REPO_URL="${{ github.event.pull_request.head.repo.clone_url || 'https://github.com/latent-to/async-substrate-interface.git' }}" - git fetch $REPO_URL $BRANCH - git checkout FETCH_HEAD - echo "Current SHA: $(git rev-parse HEAD)" - - - name: Install async-substrate-interface with dev dependencies - working-directory: ${{ github.workspace }}/async-substrate-interface - run: | - uv pip uninstall --system async-substrate-interface || true - uv pip install --system '.[dev]' - - name: Download Cached Docker Image uses: actions/download-artifact@v8 with: @@ -230,7 +188,8 @@ jobs: run: docker load -i subtensor-localnet.tar - name: Run e2e tests - run: pytest ${{ matrix.test-file }} -s + working-directory: /opt/bittensor + run: pytest "${{ matrix.test-case }}" -s run-integration-and-unit-test: From 6ca701138cfb5d6a2016db98fb6feb879b5e02f1 Mon Sep 17 00:00:00 2001 From: BD Himes Date: Mon, 1 Jun 2026 16:33:50 +0200 Subject: [PATCH 2/9] Builds a reusable e2e tester docker image that is used to execute the test cases --- .github/workflows/check-sdk-tests.yml | 99 +++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/.github/workflows/check-sdk-tests.yml b/.github/workflows/check-sdk-tests.yml index 99fac81..c176cf0 100644 --- a/.github/workflows/check-sdk-tests.yml +++ b/.github/workflows/check-sdk-tests.yml @@ -3,6 +3,7 @@ name: Bittensor SDK Test permissions: pull-requests: write contents: read + packages: write concurrency: group: e2e-sdk-${{ github.ref }} @@ -155,14 +156,104 @@ jobs: name: subtensor-localnet path: subtensor-localnet.tar + build-e2e-image: + # Build the test environment (system deps + bittensor[dev] + this PR's + # async-substrate-interface) into a single image ONCE per run, so the e2e + # matrix jobs below just pull it and run pytest — no per-test setup. + needs: + - check-labels + if: always() && needs.check-labels.outputs.run-sdk-tests == 'true' + runs-on: ubuntu-latest + outputs: + image: ${{ steps.meta.outputs.image }} + steps: + - name: Clone Bittensor SDK repo + run: git clone https://github.com/latent-to/bittensor.git + + - name: Checkout Bittensor branch + working-directory: ${{ github.workspace }}/bittensor + run: | + if ! git fetch origin $BITTENSOR_BRANCH; then + echo "❌ Error: Branch '$BITTENSOR_BRANCH' does not exist in latent-to/bittensor." + exit 1 + fi + git checkout FETCH_HEAD + echo "✅ Using Bittensor branch: $BITTENSOR_BRANCH" + + - name: Clone async-substrate-interface repo + run: git clone https://github.com/latent-to/async-substrate-interface.git + + - name: Checkout PR branch in async-substrate-interface + working-directory: ${{ github.workspace }}/async-substrate-interface + run: | + BRANCH="${{ github.event.pull_request.head.ref || github.ref_name }}" + REPO_URL="${{ github.event.pull_request.head.repo.clone_url || 'https://github.com/latent-to/async-substrate-interface.git' }}" + git fetch $REPO_URL $BRANCH + git checkout FETCH_HEAD + echo "Current SHA: $(git rev-parse HEAD)" + + - name: Write Dockerfile + run: | + # Exclude the (large) .git dirs from the build context. + printf '**/.git\n' > .dockerignore + cat > Dockerfile <<'DOCKERFILE' + FROM python:3.13-slim + + # System deps: build toolchain for any source wheels, the docker CLI so the + # e2e fixtures can launch the subtensor-localnet container (docker-out-of-docker), + # and git for any VCS-based installs. + RUN apt-get update && apt-get install -y --no-install-recommends \ + clang curl libssl-dev llvm libudev-dev protobuf-compiler git docker.io \ + && rm -rf /var/lib/apt/lists/* + + RUN pip install --no-cache-dir uv + + COPY bittensor /opt/bittensor + COPY async-substrate-interface /opt/async-substrate-interface + + # Install the SDK with dev extras, then overlay this PR's build of + # async-substrate-interface so the tests exercise the PR code. + RUN uv pip install --system '/opt/bittensor[dev]' \ + && (uv pip uninstall --system async-substrate-interface || true) \ + && uv pip install --system /opt/async-substrate-interface + + # pytest is invoked from here so the relative `tests/e2e_tests/...` node ids resolve. + WORKDIR /opt/bittensor + DOCKERFILE + + - name: Set image ref + id: meta + run: echo "image=ghcr.io/${{ github.repository }}/e2e:${{ github.run_id }}-${{ github.run_attempt }}" >> $GITHUB_OUTPUT + + - name: Log in to GitHub Container Registry + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin + + - name: Build and push image + run: | + docker build -t "${{ steps.meta.outputs.image }}" . + docker push "${{ steps.meta.outputs.image }}" + run-e2e-tests: needs: - check-labels - find-e2e-tests - pull-docker-image + - build-e2e-image if: always() && needs.check-labels.outputs.run-sdk-tests == 'true' runs-on: ubuntu-latest + + # Run each test inside the prebuilt image: all deps are already installed, so + # there is no per-test apt/clone/install. Docker-out-of-docker (mounted host + # socket) + host networking let the e2e fixtures launch the localnet container + # and reach it at ws://localhost:9944, just as on a bare runner. + container: + image: ${{ needs.build-e2e-image.outputs.image }} + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + options: --network host -v /var/run/docker.sock:/var/run/docker.sock + strategy: fail-fast: false max-parallel: 16 @@ -170,11 +261,9 @@ jobs: test-case: ${{ fromJson(needs.find-e2e-tests.outputs.test-cases) }} env: - RELEASE_NAME: development - RUSTV: ${{ matrix.rust-branch }} - RUST_BACKTRACE: full - RUST_BIN_DIR: target/${{ matrix.rust-target }} - TARGET: ${{ matrix.rust-target }} + # The localnet image is preloaded from the cached tar below; skip the + # fixture's own `docker pull`. + SKIP_PULL: "1" timeout-minutes: 60 name: "e2e tests: ${{ matrix.test-case }}" From 580d7b2e18b36a48a4a802c01016e9d2e7ebe36d Mon Sep 17 00:00:00 2001 From: BD Himes Date: Mon, 1 Jun 2026 16:40:10 +0200 Subject: [PATCH 3/9] Adjust for github runner --- .github/workflows/check-sdk-tests.yml | 38 ++++++++++++++------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/.github/workflows/check-sdk-tests.yml b/.github/workflows/check-sdk-tests.yml index c176cf0..df66ba1 100644 --- a/.github/workflows/check-sdk-tests.yml +++ b/.github/workflows/check-sdk-tests.yml @@ -243,31 +243,21 @@ jobs: if: always() && needs.check-labels.outputs.run-sdk-tests == 'true' runs-on: ubuntu-latest - # Run each test inside the prebuilt image: all deps are already installed, so - # there is no per-test apt/clone/install. Docker-out-of-docker (mounted host - # socket) + host networking let the e2e fixtures launch the localnet container - # and reach it at ws://localhost:9944, just as on a bare runner. - container: - image: ${{ needs.build-e2e-image.outputs.image }} - credentials: - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - options: --network host -v /var/run/docker.sock:/var/run/docker.sock - strategy: fail-fast: false max-parallel: 16 matrix: test-case: ${{ fromJson(needs.find-e2e-tests.outputs.test-cases) }} - env: - # The localnet image is preloaded from the cached tar below; skip the - # fixture's own `docker pull`. - SKIP_PULL: "1" - timeout-minutes: 60 name: "e2e tests: ${{ matrix.test-case }}" steps: + - name: Log in to GitHub Container Registry + run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin + + - name: Pull prebuilt test image + run: docker pull "${{ needs.build-e2e-image.outputs.image }}" + - name: Download Cached Docker Image uses: actions/download-artifact@v8 with: @@ -276,9 +266,21 @@ jobs: - name: Load Docker Image run: docker load -i subtensor-localnet.tar + # The job stays on the bare runner (no `container:`), so we can launch the test + # container ourselves with `--network host` — GitHub injects its own network into + # job containers, which conflicts with host mode. Host networking + the mounted + # docker socket let the e2e fixtures start the localnet (published on the host's + # :9944) and reach it at ws://localhost:9944. All deps are baked into the image, + # so there is no per-test apt/clone/install. - name: Run e2e tests - working-directory: /opt/bittensor - run: pytest "${{ matrix.test-case }}" -s + run: | + docker run --rm \ + --network host \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -w /opt/bittensor \ + -e SKIP_PULL=1 \ + "${{ needs.build-e2e-image.outputs.image }}" \ + pytest "${{ matrix.test-case }}" -s run-integration-and-unit-test: From 395530ee9407b531d2149a6c2e5aa8431c1a4b1b Mon Sep 17 00:00:00 2001 From: BD Himes Date: Mon, 1 Jun 2026 16:51:13 +0200 Subject: [PATCH 4/9] Troubleshooting --- .github/workflows/check-sdk-tests.yml | 32 +++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-sdk-tests.yml b/.github/workflows/check-sdk-tests.yml index df66ba1..2a09ec2 100644 --- a/.github/workflows/check-sdk-tests.yml +++ b/.github/workflows/check-sdk-tests.yml @@ -199,13 +199,22 @@ jobs: cat > Dockerfile <<'DOCKERFILE' FROM python:3.13-slim - # System deps: build toolchain for any source wheels, the docker CLI so the - # e2e fixtures can launch the subtensor-localnet container (docker-out-of-docker), - # and git for any VCS-based installs. + # System deps: build toolchain for any source wheels, curl for the docker CLI + # download below, and git for any VCS-based installs. RUN apt-get update && apt-get install -y --no-install-recommends \ - clang curl libssl-dev llvm libudev-dev protobuf-compiler git docker.io \ + clang curl libssl-dev llvm libudev-dev protobuf-compiler git \ && rm -rf /var/lib/apt/lists/* + # Static Docker CLI (client only). The e2e fixtures shell out to `docker` to + # launch the localnet via the mounted host socket; installing the static binary + # to /usr/local/bin guarantees it is on PATH regardless of the base distro's + # packaging (the `docker.io` apt package did not reliably provide it). The final + # `docker --version` fails the build early if the CLI is ever missing. + ARG DOCKER_CLI_VERSION=27.5.1 + RUN curl -fsSL "https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_CLI_VERSION}.tgz" \ + | tar -xz -C /usr/local/bin --strip-components=1 docker/docker \ + && docker --version + RUN pip install --no-cache-dir uv COPY bittensor /opt/bittensor @@ -272,6 +281,21 @@ jobs: # docker socket let the e2e fixtures start the localnet (published on the host's # :9944) and reach it at ws://localhost:9944. All deps are baked into the image, # so there is no per-test apt/clone/install. + # Fail loudly (instead of letting the e2e fixture silently skip) if the docker + # CLI isn't on PATH, the host daemon isn't reachable through the mounted socket, + # or the localnet image didn't load — the three things the `local_chain` fixture + # needs before it will actually start a chain. + - name: Preflight - verify docker is reachable inside the test container + run: | + docker run --rm \ + -v /var/run/docker.sock:/var/run/docker.sock \ + "${{ needs.build-e2e-image.outputs.image }}" \ + sh -euxc ' + command -v docker + docker version + docker image inspect ghcr.io/opentensor/subtensor-localnet:devnet-ready >/dev/null + ' + - name: Run e2e tests run: | docker run --rm \ From 08ad1447366e6ebfd580b69da09e7a306adc6970 Mon Sep 17 00:00:00 2001 From: BD Himes Date: Mon, 1 Jun 2026 16:56:52 +0200 Subject: [PATCH 5/9] Remove debug --- .github/workflows/check-sdk-tests.yml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/.github/workflows/check-sdk-tests.yml b/.github/workflows/check-sdk-tests.yml index 2a09ec2..3376eea 100644 --- a/.github/workflows/check-sdk-tests.yml +++ b/.github/workflows/check-sdk-tests.yml @@ -254,7 +254,7 @@ jobs: strategy: fail-fast: false - max-parallel: 16 + max-parallel: 64 matrix: test-case: ${{ fromJson(needs.find-e2e-tests.outputs.test-cases) }} @@ -281,21 +281,6 @@ jobs: # docker socket let the e2e fixtures start the localnet (published on the host's # :9944) and reach it at ws://localhost:9944. All deps are baked into the image, # so there is no per-test apt/clone/install. - # Fail loudly (instead of letting the e2e fixture silently skip) if the docker - # CLI isn't on PATH, the host daemon isn't reachable through the mounted socket, - # or the localnet image didn't load — the three things the `local_chain` fixture - # needs before it will actually start a chain. - - name: Preflight - verify docker is reachable inside the test container - run: | - docker run --rm \ - -v /var/run/docker.sock:/var/run/docker.sock \ - "${{ needs.build-e2e-image.outputs.image }}" \ - sh -euxc ' - command -v docker - docker version - docker image inspect ghcr.io/opentensor/subtensor-localnet:devnet-ready >/dev/null - ' - - name: Run e2e tests run: | docker run --rm \ From f079bb5a7020f6575e7ce3a073d05126334b76c2 Mon Sep 17 00:00:00 2001 From: BD Himes Date: Mon, 1 Jun 2026 17:15:19 +0200 Subject: [PATCH 6/9] Change to GH artifact rather than registry --- .github/workflows/check-sdk-tests.yml | 34 +++++++++++++++++---------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/check-sdk-tests.yml b/.github/workflows/check-sdk-tests.yml index 3376eea..a978d05 100644 --- a/.github/workflows/check-sdk-tests.yml +++ b/.github/workflows/check-sdk-tests.yml @@ -3,7 +3,6 @@ name: Bittensor SDK Test permissions: pull-requests: write contents: read - packages: write concurrency: group: e2e-sdk-${{ github.ref }} @@ -232,15 +231,24 @@ jobs: - name: Set image ref id: meta - run: echo "image=ghcr.io/${{ github.repository }}/e2e:${{ github.run_id }}-${{ github.run_attempt }}" >> $GITHUB_OUTPUT + # A plain local tag (no registry): the image is shipped to the matrix jobs as a + # `docker save` tarball artifact, not via a registry push. + run: echo "image=async-substrate-interface-e2e:${{ github.run_id }}-${{ github.run_attempt }}" >> $GITHUB_OUTPUT - - name: Log in to GitHub Container Registry - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin + - name: Build image + run: docker build -t "${{ steps.meta.outputs.image }}" . - - name: Build and push image - run: | - docker build -t "${{ steps.meta.outputs.image }}" . - docker push "${{ steps.meta.outputs.image }}" + - name: Save image to tarball + # Distribute via a GitHub artifact (fast internal transport, same as the localnet + # image) instead of GHCR, which avoids slow registry pulls across all matrix jobs, + # registry rate-limits, and the fork-PR push-permission problem. + run: docker save -o e2e-image.tar "${{ steps.meta.outputs.image }}" + + - name: Upload image as artifact + uses: actions/upload-artifact@v7 + with: + name: e2e-image + path: e2e-image.tar run-e2e-tests: needs: @@ -261,11 +269,13 @@ jobs: timeout-minutes: 60 name: "e2e tests: ${{ matrix.test-case }}" steps: - - name: Log in to GitHub Container Registry - run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin + - name: Download prebuilt test image + uses: actions/download-artifact@v8 + with: + name: e2e-image - - name: Pull prebuilt test image - run: docker pull "${{ needs.build-e2e-image.outputs.image }}" + - name: Load prebuilt test image + run: docker load -i e2e-image.tar - name: Download Cached Docker Image uses: actions/download-artifact@v8 From f644145bfe4876b4e77e47cd1e0f696e51eddd7d Mon Sep 17 00:00:00 2001 From: BD Himes Date: Mon, 1 Jun 2026 17:19:10 +0200 Subject: [PATCH 7/9] Rerun failing tests to avoid flakiness --- .github/workflows/check-sdk-tests.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check-sdk-tests.yml b/.github/workflows/check-sdk-tests.yml index a978d05..c08cb6b 100644 --- a/.github/workflows/check-sdk-tests.yml +++ b/.github/workflows/check-sdk-tests.yml @@ -292,6 +292,10 @@ jobs: # :9944) and reach it at ws://localhost:9944. All deps are baked into the image, # so there is no per-test apt/clone/install. - name: Run e2e tests + # --reruns retries a flaky test (e.g. when the localnet is listening but its RPC + # isn't ready yet, surfacing as a websocket InvalidMessage during connect). The + # whole test reruns, re-spinning the localnet fixture, so by the retry the node + # is ready. pytest-rerunfailures ships with bittensor[dev]. run: | docker run --rm \ --network host \ @@ -299,7 +303,7 @@ jobs: -w /opt/bittensor \ -e SKIP_PULL=1 \ "${{ needs.build-e2e-image.outputs.image }}" \ - pytest "${{ matrix.test-case }}" -s + pytest "${{ matrix.test-case }}" -s --reruns 2 --reruns-delay 5 run-integration-and-unit-test: From 8e6d4ddd840372f090db591060a6127ad24e253d Mon Sep 17 00:00:00 2001 From: BD Himes Date: Mon, 1 Jun 2026 18:58:34 +0200 Subject: [PATCH 8/9] Replaces `always()` with `!cancelled()` as per PR suggestions --- .github/workflows/check-sdk-tests.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/check-sdk-tests.yml b/.github/workflows/check-sdk-tests.yml index c08cb6b..ce8caa8 100644 --- a/.github/workflows/check-sdk-tests.yml +++ b/.github/workflows/check-sdk-tests.yml @@ -55,7 +55,7 @@ jobs: check-labels: needs: apply-label-to-new-pr runs-on: ubuntu-latest - if: always() + if: ${{ !cancelled() }} outputs: run-sdk-tests: ${{ steps.check-manual.outputs.run-sdk-tests || steps.get-labels-pr.outputs.run-sdk-tests }} steps: @@ -88,7 +88,7 @@ jobs: find-e2e-tests: needs: check-labels - if: always() && needs.check-labels.outputs.run-sdk-tests == 'true' + if: ${{ !cancelled() && needs.check-labels.outputs.run-sdk-tests == 'true' }} runs-on: ubuntu-latest outputs: test-cases: ${{ steps.get-tests.outputs.test-cases }} @@ -138,7 +138,7 @@ jobs: - find-e2e-tests runs-on: ubuntu-latest - if: always() && needs.check-labels.outputs.run-sdk-tests == 'true' + if: ${{ !cancelled() && needs.check-labels.outputs.run-sdk-tests == 'true' }} steps: - name: Log in to GitHub Container Registry run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $GITHUB_ACTOR --password-stdin @@ -161,7 +161,7 @@ jobs: # matrix jobs below just pull it and run pytest — no per-test setup. needs: - check-labels - if: always() && needs.check-labels.outputs.run-sdk-tests == 'true' + if: ${{ !cancelled() && needs.check-labels.outputs.run-sdk-tests == 'true' }} runs-on: ubuntu-latest outputs: image: ${{ steps.meta.outputs.image }} @@ -257,7 +257,7 @@ jobs: - pull-docker-image - build-e2e-image - if: always() && needs.check-labels.outputs.run-sdk-tests == 'true' + if: ${{ !cancelled() && needs.check-labels.outputs.run-sdk-tests == 'true' }} runs-on: ubuntu-latest strategy: @@ -309,7 +309,7 @@ jobs: run-integration-and-unit-test: needs: - check-labels - if: always() && needs.check-labels.outputs.run-sdk-tests == 'true' + if: ${{ !cancelled() && needs.check-labels.outputs.run-sdk-tests == 'true' }} runs-on: ubuntu-latest steps: - name: Check-out repository From 8c643869a4ef588fd21f84d2aa051067b740f866 Mon Sep 17 00:00:00 2001 From: BD Himes Date: Mon, 1 Jun 2026 19:07:40 +0200 Subject: [PATCH 9/9] Add success check as per PR suggestion --- .github/workflows/check-sdk-tests.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check-sdk-tests.yml b/.github/workflows/check-sdk-tests.yml index ce8caa8..3016b3c 100644 --- a/.github/workflows/check-sdk-tests.yml +++ b/.github/workflows/check-sdk-tests.yml @@ -257,7 +257,15 @@ jobs: - pull-docker-image - build-e2e-image - if: ${{ !cancelled() && needs.check-labels.outputs.run-sdk-tests == 'true' }} + # Only fan out the matrix if every input-producing dependency succeeded — otherwise + # all 139 jobs would spawn only to fail (missing image/localnet artifact, or no + # test list). !cancelled() alone would let them run on upstream failure. + if: >- + ${{ !cancelled() + && needs.check-labels.outputs.run-sdk-tests == 'true' + && needs.find-e2e-tests.result == 'success' + && needs.pull-docker-image.result == 'success' + && needs.build-e2e-image.result == 'success' }} runs-on: ubuntu-latest strategy: