Repository navigation
promote predev into dev #3085
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Promote | |
| # This workflow serves the predev->dev promotion pull request and the dev->qa->main ladder above it. The | |
| # hop is GetBusbar/busbar-release's `busbar-release ci hop|verdict|publish|fallback` at RELEASE_REF | |
| # (private, so no `uses:`); these jobs only check out both repos and run it. No job here runs BOARD | |
| # (`ci train promote` needs the train's write token, and the job that runs the candidate's code holds | |
| # none): a PR lands by merge. | |
| # Pull requests into predev are not judged here: ModernCI judges them (required check `ci`) and lands | |
| # them with its own merge train, so predev has no merge queue and this workflow runs for no pull request | |
| # into predev or into a lane branch (a stacked lane PR), nor for `merge_group`. The `merge_group` handling in the jobs below | |
| # is left in place, dormant: it is job logic, and the concurrency and env lines the preflight holds to busbar-release's compiled copy read it. | |
| on: { pull_request: { branches: [dev, qa, main] } } | |
| # R5 (busbar-release gate.rs, OWNER 2026-10-08): no run is ever cancelled in progress. A pull request | |
| # has at most one run running and one pending, and each newer push replaces the pending one, so the | |
| # pending run is always the newest head. A merge-queue run is its own group (a cancelled queue run | |
| # re-runs whole, owner money 2026-10-03). The preflight holds this line to the one compiled into | |
| # busbar-release: any drift is RED. | |
| concurrency: { group: "promote-${{ github.event.pull_request.number || github.event.merge_group.head_ref }}", cancel-in-progress: false } | |
| permissions: { contents: read } | |
| # THE ENGINE PIN IS NOT IN THIS FILE (OWNER 2026-10-10): the org variables ENGINE_REF (a full | |
| # busbar-release commit) and ENGINE_SHA256 (the sha256 of that commit's dev-bin ci asset), set by | |
| # hand with `busbar-release engine pin <sha>`, so an engine update never waits in predev's merge | |
| # queue. The preflight reads them ONCE and hands them to every later job as outputs: one run is one | |
| # engine even if the variables move mid-run. Unset or malformed is a hard FAIL (ENGINE-PIN), never | |
| # an older pin; the bytes run only when their sha256 is ENGINE_SHA256 (workflow-rules R16). | |
| env: | |
| # The engine contract (busbar-release contract.rs): 2 = the hop holds only the read-only R2 keys and `cache-publish` writes. An engine refuses a contract it does not support, and the auto-pin moves only to engines that support this number. | |
| BUSBAR_RELEASE_CONTRACT: "2" | |
| SHA: ${{ github.event.pull_request.head.sha || github.event.merge_group.head_sha }} | |
| TO: ${{ github.base_ref || github.event.merge_group.base_ref }} | |
| BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }} | |
| jobs: | |
| preflight: | |
| if: github.event_name == 'merge_group' || github.event.pull_request.head.repo.full_name == github.repository | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| # THE RUN GATE reads GitHub (open pull requests, the merge queue, this head's promote runs) with | |
| # this job's token. This job runs the candidate's `cargo xtask`, so the token only READS | |
| # (busbar-release 941028a): the gate's writes (ORPHANS, R7 REPEAT) are recorded in the act | |
| # artifact before any candidate code runs, and `preflight-act` below makes them. | |
| # OLD-ENGINE SHIM (remove once ENGINE_REF is at or past busbar-release 941028a): an older engine's | |
| # R6 field `jobs.preflight.permissions (actions: read)` reads this line's text, comment included; | |
| # this job's token stays contents read only. | |
| permissions: { contents: read } # actions: read is public on this repo; the writes are preflight-act's | |
| # RUNNERS: the runner gateway's table (vars.RUNNERS), from which the preflight sizes `runner=`. | |
| env: { RELEASE_REF: "${{ vars.ENGINE_REF }}", ENGINE_SHA256: "${{ vars.ENGINE_SHA256 }}", RUNNERS: "${{ vars.RUNNERS }}" } | |
| outputs: { engine_ref: "${{ steps.engine.outputs.engine_ref }}", engine_sha256: "${{ steps.engine.outputs.engine_sha256 }}", runner: "${{ steps.pre.outputs.runner }}", soak: "${{ steps.pre.outputs.soak }}", one_runner: "${{ steps.pre.outputs.one_runner }}", needs_release_artifact: "${{ steps.pre.outputs.needs_release_artifact }}", run: "${{ steps.pre.outputs.run }}", reason: "${{ steps.pre.outputs.reason }}" } | |
| steps: | |
| # Full history: the pre-flight reads the pull request's diff against BASE_SHA to choose one runner or the matrix. | |
| - { uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1, with: { ref: "${{ env.SHA }}", path: busbar, fetch-depth: 0 } } # v7 | |
| # THE ENGINE, downloaded, never compiled: CI's --no-default-features binary of RELEASE_REF from | |
| # busbar-release's dev-bin release (dev-bin.yml), run only when its sha256 is ENGINE_SHA256. | |
| # First the pin's shape: a full 40-hex commit and a 64-hex digest, or FAIL. | |
| - name: Engine (busbar-release at RELEASE_REF, sha256-pinned) | |
| id: engine | |
| env: { GH_TOKEN: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}" } | |
| run: | | |
| set -euo pipefail | |
| [[ "$RELEASE_REF" =~ ^[0-9a-f]{40}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_REF is unset or not a full 40-hex busbar-release commit (busbar-release engine pin <sha>)"; exit 1; } | |
| [[ "$ENGINE_SHA256" =~ ^[0-9a-f]{64}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_SHA256 is unset or not a 64-hex sha256 (busbar-release engine pin <sha>)"; exit 1; } | |
| api=https://api.github.com/repos/GetBusbar/busbar-release/releases | |
| name="busbar-release-ci-linux-x86_64-$RELEASE_REF" | |
| id=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" "$api/tags/dev-bin" | jq -r --arg n "$name" '.assets[] | select(.name == $n) | .id') | |
| [ -n "$id" ] || { echo "::error::dev-bin has no $name"; exit 1; } | |
| curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/octet-stream" -o "$RUNNER_TEMP/busbar-release" "$api/assets/$id" | |
| echo "$ENGINE_SHA256 $RUNNER_TEMP/busbar-release" | sha256sum -c - | |
| chmod +x "$RUNNER_TEMP/busbar-release" | |
| { echo "engine_ref=$RELEASE_REF"; echo "engine_sha256=$ENGINE_SHA256"; } >> "$GITHUB_OUTPUT" | |
| - { uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1, with: { repository: GetBusbar/busbar-release, ref: "${{ env.RELEASE_REF }}", token: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}", path: busbar-release, persist-credentials: false } } # v7 | |
| # The pre-flight is busbar-release's Rust CLI (preflight.json is the one list): it sizes the hop | |
| # (runner= to $GITHUB_OUTPUT), chooses how a pull request into predev runs (one_runner=: ONE runner | |
| # when the crates its diff affects are estimated within 20 min, else the shard matrix; OWNER-LOCKED | |
| # 2026-10-06), whether this run reads the release artifact (needs_release_artifact=: the rung runs | |
| # the oracle or conformance; predev runs neither), and runs every pre-flight check against the | |
| # candidate. Built with no default features, as the CLI documents for this job. FIRST it is THE | |
| # RUN GATE (OWNER 2026-10-08): run=1|0 and reason= from facts only (R1 STACK, R2 queued, R3 | |
| # superseded, R4 duplicate head, R5/R6 this file's wiring); run=0 means nothing in this run | |
| # builds, and a RED rule fails here. The gate's writes are recorded (`--act-out act/act.json`, | |
| # before any candidate code runs) for preflight-act; this job makes none. | |
| # OLD-ENGINE SHIM (remove once ENGINE_REF is at or past 941028a): an engine without --act-out | |
| # runs without it. | |
| - id: pre | |
| working-directory: busbar-release | |
| env: { GITHUB_TOKEN: "${{ github.token }}", RUNNERS: "${{ vars.RUNNERS }}" } | |
| run: | | |
| act=() | |
| if "$RUNNER_TEMP/busbar-release" ci preflight --help | grep -q -- --act-out; then act=(--act-out "$GITHUB_WORKSPACE/act/act.json"); fi | |
| "$RUNNER_TEMP/busbar-release" ci preflight "${act[@]}" | |
| - { if: always(), uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a, with: { name: "preflight-act-${{ env.SHA }}", path: "act/", retention-days: 1, if-no-files-found: ignore, overwrite: true } } # v7.0.1 | |
| # THE RUN GATE'S WRITES (busbar-release 941028a), in a job that runs NO candidate code: it checks | |
| # out nothing, downloads the engine (sha256-pinned) and the preflight's act artifact, and runs | |
| # `ci preflight-act`, which re-reads GitHub itself: ORPHANS cancels the merge-group runs a queue | |
| # rebuild left running (actions: write), and R7 REPEAT turns off the auto-merge of a PR whose head | |
| # already failed the queue as its own red (pull-requests: write), only the PR both the artifact | |
| # and the re-read name. Merge groups only: a pull request has nothing to act on. It runs whatever | |
| # the preflight concluded (R7 is a RED preflight), and never decides the verdict. | |
| preflight-act: | |
| needs: preflight | |
| if: ${{ always() && github.event_name == 'merge_group' && needs.preflight.outputs.engine_ref != '' }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: { contents: read, actions: write, pull-requests: write } | |
| env: { RELEASE_REF: "${{ needs.preflight.outputs.engine_ref }}", ENGINE_SHA256: "${{ needs.preflight.outputs.engine_sha256 }}" } | |
| steps: | |
| # THE ENGINE, downloaded, never compiled: CI's --no-default-features binary of RELEASE_REF from | |
| # busbar-release's dev-bin release (dev-bin.yml), run only when its sha256 is ENGINE_SHA256. | |
| # First the pin's shape: a full 40-hex commit and a 64-hex digest, or FAIL. | |
| - name: Engine (busbar-release at RELEASE_REF, sha256-pinned) | |
| env: { GH_TOKEN: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}" } | |
| run: | | |
| set -euo pipefail | |
| [[ "$RELEASE_REF" =~ ^[0-9a-f]{40}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_REF is unset or not a full 40-hex busbar-release commit (busbar-release engine pin <sha>)"; exit 1; } | |
| [[ "$ENGINE_SHA256" =~ ^[0-9a-f]{64}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_SHA256 is unset or not a 64-hex sha256 (busbar-release engine pin <sha>)"; exit 1; } | |
| api=https://api.github.com/repos/GetBusbar/busbar-release/releases | |
| name="busbar-release-ci-linux-x86_64-$RELEASE_REF" | |
| id=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" "$api/tags/dev-bin" | jq -r --arg n "$name" '.assets[] | select(.name == $n) | .id') | |
| [ -n "$id" ] || { echo "::error::dev-bin has no $name"; exit 1; } | |
| curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/octet-stream" -o "$RUNNER_TEMP/busbar-release" "$api/assets/$id" | |
| echo "$ENGINE_SHA256 $RUNNER_TEMP/busbar-release" | sha256sum -c - | |
| chmod +x "$RUNNER_TEMP/busbar-release" | |
| - { continue-on-error: true, uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c, with: { name: "preflight-act-${{ env.SHA }}", path: act } } # v8.0.1 | |
| # OLD-ENGINE SHIM (remove once ENGINE_REF is at or past 941028a): an engine without the verb | |
| # made these writes in the preflight itself. | |
| - name: The run gate's writes (ORPHANS, R7 REPEAT) | |
| env: { GITHUB_TOKEN: "${{ github.token }}" } | |
| run: | | |
| if ! "$RUNNER_TEMP/busbar-release" ci --help | grep -q preflight-act; then echo "engine $RELEASE_REF has no preflight-act: nothing to act on here"; exit 0; fi | |
| "$RUNNER_TEMP/busbar-release" ci preflight-act --act "$GITHUB_WORKSPACE/act/act.json" | |
| # The release artifact the oracle records and the slsa-verifier conformance rig judges. It runs in | |
| # every merge group (the fold requires it: a check on the release build), and in every run whose | |
| # rung reads the artifact (`needs_release_artifact=true`: the rung runs the oracle or conformance, | |
| # i.e. the predev->dev pull request and up). dev moves by pull request now, not by the ladder | |
| # fast-forwarding a merge group's sha (ARCHITECT 2026-10-02, superseded), so the run that judges a | |
| # promotion builds the artifact of exactly the commit it promotes: its shards download | |
| # `slsa-artifact-<sha>` from THIS run, which a merge-group-only build never produced (#740 failed | |
| # every run at that download). workflow-rules R18 holds this wiring. Its SLSA provenance is minted | |
| # from `base64-subjects` by the pinned slsa-github-generator once the owner rules on R14 (the | |
| # generator must be referenced by tag); until then the suite is not-run, a standing red here. | |
| slsa-build: | |
| if: needs.preflight.outputs.run == '1' && (github.event_name == 'merge_group' || needs.preflight.outputs.needs_release_artifact == 'true') | |
| needs: preflight | |
| permissions: { contents: read } | |
| runs-on: ${{ needs.preflight.outputs.runner }} | |
| timeout-minutes: 90 | |
| outputs: { base64-subjects: "${{ steps.digest.outputs.base64-subjects }}" } | |
| steps: | |
| - { uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1, with: { ref: "${{ env.SHA }}", path: busbar, persist-credentials: false } } # v7 | |
| - name: Build the release artifact | |
| working-directory: busbar | |
| run: | | |
| cargo build --locked --release -p busbar --bin busbar | |
| mkdir -p ../slsa && cp target/release/busbar ../slsa/busbar | |
| - id: digest | |
| name: Its sha256 (the provenance subject) | |
| working-directory: slsa | |
| run: | | |
| sha256sum busbar > busbar.sha256 | |
| echo "base64-subjects=$(base64 -w0 busbar.sha256)" >> "$GITHUB_OUTPUT" | |
| - { uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a, with: { name: "slsa-artifact-${{ env.SHA }}", path: "slsa/", retention-days: 7, if-no-files-found: error } } # v7.0.1 | |
| # THE QA SOAK (OWNER 2026-10-10; busbar #734): the soak stays, as a 1-hour stability gate on each | |
| # qa promotion. `busbar-release soak <candidate binary>` (profile.rs's full profile: 3600 s, 50 rps, | |
| # 16 concurrent) soaks THIS candidate's release binary on the runner the preflight chose, wherever | |
| # the ladder's entry asks for it (`soak=true`: qa). The hop folds this job's result like | |
| # slsa-build's (`ci turnstile merge --needs soak=...`): a failed soak DENIES the promotion. CI=false | |
| # selects the full hour (the CI context would pick the two-minute smoke). It runs the candidate, so | |
| # it holds no write token. The candidate binary is this run's slsa-build artifact (a rung with the | |
| # soak reads the artifact, so slsa-build runs); a run without it builds it here. The engine is the | |
| # downloaded ci binary (built with `--features soak`, dev-bin.yml), sha256-pinned like every other | |
| # job's. | |
| soak: | |
| needs: [preflight, slsa-build] | |
| if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.preflight.outputs.run == '1' && needs.preflight.outputs.soak == 'true' }} | |
| permissions: { contents: read } | |
| runs-on: ${{ needs.preflight.outputs.runner }} | |
| timeout-minutes: 180 | |
| env: { RELEASE_REF: "${{ needs.preflight.outputs.engine_ref }}", ENGINE_SHA256: "${{ needs.preflight.outputs.engine_sha256 }}" } | |
| steps: | |
| - { uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1, with: { ref: "${{ env.SHA }}", path: busbar, persist-credentials: false } } # v7 | |
| # THE ENGINE, downloaded, never compiled: CI's --no-default-features binary of RELEASE_REF from | |
| # busbar-release's dev-bin release (dev-bin.yml), run only when its sha256 is ENGINE_SHA256. | |
| # First the pin's shape: a full 40-hex commit and a 64-hex digest, or FAIL. | |
| - name: Engine (busbar-release at RELEASE_REF, sha256-pinned) | |
| env: { GH_TOKEN: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}" } | |
| run: | | |
| set -euo pipefail | |
| [[ "$RELEASE_REF" =~ ^[0-9a-f]{40}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_REF is unset or not a full 40-hex busbar-release commit (busbar-release engine pin <sha>)"; exit 1; } | |
| [[ "$ENGINE_SHA256" =~ ^[0-9a-f]{64}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_SHA256 is unset or not a 64-hex sha256 (busbar-release engine pin <sha>)"; exit 1; } | |
| api=https://api.github.com/repos/GetBusbar/busbar-release/releases | |
| name="busbar-release-ci-linux-x86_64-$RELEASE_REF" | |
| id=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" "$api/tags/dev-bin" | jq -r --arg n "$name" '.assets[] | select(.name == $n) | .id') | |
| [ -n "$id" ] || { echo "::error::dev-bin has no $name"; exit 1; } | |
| curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/octet-stream" -o "$RUNNER_TEMP/busbar-release" "$api/assets/$id" | |
| echo "$ENGINE_SHA256 $RUNNER_TEMP/busbar-release" | sha256sum -c - | |
| chmod +x "$RUNNER_TEMP/busbar-release" | |
| - { uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1, with: { repository: GetBusbar/busbar-release, ref: "${{ env.RELEASE_REF }}", token: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}", path: busbar-release, persist-credentials: false } } # v7 | |
| - if: needs.slsa-build.result == 'success' | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: { name: "slsa-artifact-${{ env.SHA }}", path: slsa } | |
| - if: needs.slsa-build.result != 'success' | |
| working-directory: busbar | |
| run: | | |
| cargo build --locked --release -p busbar --bin busbar | |
| mkdir -p ../slsa && cp target/release/busbar ../slsa/busbar | |
| - name: Soak the candidate (the full profile) | |
| working-directory: busbar-release | |
| env: { CI: "false", GH_TOKEN: "${{ github.token }}", TMPDIR: "${{ runner.temp }}" } | |
| run: | | |
| chmod +x "$GITHUB_WORKSPACE/slsa/busbar" | |
| "$RUNNER_TEMP/busbar-release" soak "$GITHUB_WORKSPACE/slsa/busbar" | |
| - { if: always(), uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a, with: { name: "soak-${{ env.SHA }}", path: "${{ runner.temp }}/busbar-soak-*/", retention-days: 30, if-no-files-found: warn } } # v7.0.1 | |
| # The merge group's hop, and the predev->dev pull request's, is SHARDED (ARCHITECT 2026-10-02): four | |
| # runners each judge one part (`--shard K/4`: test:workspace's hash partition K, plus the unsplit steps | |
| # dealt to K; the gates in shard 1, the oracle and conformance in shard 4). Each builds and runs the | |
| # candidate's code, so it holds NO write token (contents and actions read only, no persisted | |
| # credentials); its report leaves as an artifact. It starts beside slsa-build, not after it (B3-P, | |
| # 2026-10-08), and no shard waits for it: the fold requires slsa-build's result (`needs:`, | |
| # `--needs`, `--must-run`), and nothing a shard runs reads the release artifact (the slsa-verifier | |
| # leg is not-run until `conformance record` is handed `--slsa-artifact`). The predev->dev shards | |
| # sat ~7 min each in `await-job` for a download nobody read (2026-10-11). Every shard runs to | |
| # the bottom (FULLY SEEN, busbar-release #152). A pull request into predev runs the shards only when the | |
| # pre-flight chose the matrix (one_runner=false: every crate affected, or over 20 min on one runner); | |
| # otherwise its hop is ONE runner, in the `hop` job below. | |
| hop-shard: | |
| name: "hop shard ${{ matrix.shard }}" | |
| strategy: { fail-fast: false, matrix: { shard: [1, 2, 3, 4] } } | |
| needs: [preflight] | |
| if: ${{ !cancelled() && needs.preflight.result == 'success' && needs.preflight.outputs.run == '1' && (github.event_name == 'merge_group' || github.base_ref == 'dev' || needs.preflight.outputs.one_runner != 'true') }} | |
| permissions: { contents: read, actions: read } | |
| runs-on: ${{ needs.preflight.outputs.runner }} | |
| timeout-minutes: 330 | |
| env: { RELEASE_REF: "${{ needs.preflight.outputs.engine_ref }}", ENGINE_SHA256: "${{ needs.preflight.outputs.engine_sha256 }}", RUNNERS: "${{ vars.RUNNERS }}" } | |
| steps: | |
| - { uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1, with: { ref: "${{ env.SHA }}", path: busbar, fetch-depth: 0, persist-credentials: false } } # v7 | |
| # THE ENGINE, downloaded, never compiled: CI's --no-default-features binary of RELEASE_REF from | |
| # busbar-release's dev-bin release (dev-bin.yml), run only when its sha256 is ENGINE_SHA256. | |
| # First the pin's shape: a full 40-hex commit and a 64-hex digest, or FAIL. | |
| - name: Engine (busbar-release at RELEASE_REF, sha256-pinned) | |
| env: { GH_TOKEN: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}" } | |
| run: | | |
| set -euo pipefail | |
| [[ "$RELEASE_REF" =~ ^[0-9a-f]{40}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_REF is unset or not a full 40-hex busbar-release commit (busbar-release engine pin <sha>)"; exit 1; } | |
| [[ "$ENGINE_SHA256" =~ ^[0-9a-f]{64}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_SHA256 is unset or not a 64-hex sha256 (busbar-release engine pin <sha>)"; exit 1; } | |
| api=https://api.github.com/repos/GetBusbar/busbar-release/releases | |
| name="busbar-release-ci-linux-x86_64-$RELEASE_REF" | |
| id=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" "$api/tags/dev-bin" | jq -r --arg n "$name" '.assets[] | select(.name == $n) | .id') | |
| [ -n "$id" ] || { echo "::error::dev-bin has no $name"; exit 1; } | |
| curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/octet-stream" -o "$RUNNER_TEMP/busbar-release" "$api/assets/$id" | |
| echo "$ENGINE_SHA256 $RUNNER_TEMP/busbar-release" | sha256sum -c - | |
| chmod +x "$RUNNER_TEMP/busbar-release" | |
| # THE JUDGE, pinned in busbar (OWNER 2026-10-10): the busbar-oracle testing/shadow-oracle/oracle-rust.pin | |
| # names, read at the BASE (a candidate never picks its own judge), downloaded from busbar-release's | |
| # dev-bin release and run only when its sha256 is the pin's sha256_linux_x86_64. The engine pin | |
| # never moves it. Only the last shard runs the oracle; `ci hop` and bin/oracle both take the binary | |
| # from BUSBAR_ORACLE_RUST_BIN, so nothing builds a judge. | |
| - name: Oracle (pinned at the base, sha256-checked) | |
| if: matrix.shard == 4 | |
| env: { GH_TOKEN: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}" } | |
| run: | | |
| set -euo pipefail | |
| pin="$(git -C busbar show "$BASE_SHA:testing/shadow-oracle/oracle-rust.pin" 2>/dev/null || true)" | |
| # A base from before the pinned binary (no sha256 line): the oracle is built as before. | |
| grep -q '^sha256_linux_x86_64=' <<<"$pin" || { echo "::notice::oracle-rust.pin at $BASE_SHA pins no binary: bin/oracle builds the judge"; exit 0; } | |
| ref="$(printf '%s\n' "$pin" | sed -n 's/^release_ref=//p')" | |
| ORACLE_SHA256="$(printf '%s\n' "$pin" | sed -n 's/^sha256_linux_x86_64=//p')" | |
| [[ "$ref" =~ ^[0-9a-f]{40}$ && "$ORACLE_SHA256" =~ ^[0-9a-f]{64}$ ]] || { echo "::error title=ORACLE-PIN FAIL::oracle-rust.pin at $BASE_SHA needs a 40-hex release_ref and a 64-hex sha256_linux_x86_64"; exit 1; } | |
| api=https://api.github.com/repos/GetBusbar/busbar-release/releases | |
| name="busbar-oracle-linux-x86_64-$ref" | |
| id=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" "$api/tags/dev-bin" | jq -r --arg n "$name" '.assets[] | select(.name == $n) | .id') | |
| [ -n "$id" ] || { echo "::error title=ORACLE-PIN FAIL::dev-bin has no $name"; exit 1; } | |
| curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/octet-stream" -o "$RUNNER_TEMP/busbar-oracle" "$api/assets/$id" | |
| echo "$ORACLE_SHA256 $RUNNER_TEMP/busbar-oracle" | sha256sum -c - | |
| chmod +x "$RUNNER_TEMP/busbar-oracle" | |
| echo "BUSBAR_ORACLE_RUST_BIN=$RUNNER_TEMP/busbar-oracle" >> "$GITHUB_ENV" | |
| - { uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1, with: { repository: GetBusbar/busbar-release, ref: "${{ env.RELEASE_REF }}", token: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}", path: busbar-release, persist-credentials: false } } # v7 | |
| # The conformance rigs `conformance record` runs in the full plan need node 22 and pnpm 10 (the mcp | |
| # suite's official runner, and rolldown's native binding built under it); the Latchkey large image | |
| # carries node 20, so mcp read not-run (CONFORMANCE-RIGS, 2026-10-02). Only the last shard of a | |
| # rung that runs conformance or the oracle (needs_release_artifact) records them; the others | |
| # skip the ~15 s install. | |
| - { if: "matrix.shard == 4 && needs.preflight.outputs.needs_release_artifact == 'true'", uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413, with: { version: 10 } } # v6.1.0 | |
| - { if: "matrix.shard == 4 && needs.preflight.outputs.needs_release_artifact == 'true'", uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020, with: { node-version: 22 } } # v7.0.0 | |
| # No debuginfo in the dev/test builds: build_test's all-features, feature-matrix and test builds plus | |
| # the oracle's release build filled the runner's ~41 GiB free disk with debuginfo (ENOSPC, no log). | |
| # Base and candidate build alike, so the no-worse-than-base verdict is unchanged. | |
| # GITHUB_TOKEN (contents read) is for `ci hop`'s R3 re-check alone (is this merge group still a | |
| # current queue entry's head?), which runs before any candidate code; the judge that runs the | |
| # candidate never holds it. A superseded group's shard builds nothing. | |
| # THE R2 KEYS HERE ONLY READ (busbar-release 069f032): this job runs candidate code, and the hop | |
| # refuses to start (INFRA) with the write keys in its environment. Unset read-only keys fail | |
| # open (no cache). The judge's cache puts land in r2-outbox/, uploaded below and published by | |
| # `cache-publish`, the one job that holds the write keys (workflow-rules R19). | |
| - working-directory: busbar-release | |
| run: $RUNNER_TEMP/busbar-release ci hop | |
| env: { GITHUB_TOKEN: "${{ github.token }}", R2_RO_KEY_ID: "${{ secrets.R2_CACHE_RO_ACCESS_KEY_ID }}", R2_RO_KEY_SECRET: "${{ secrets.R2_CACHE_RO_SECRET_ACCESS_KEY }}", CARGO_PROFILE_DEV_DEBUG: "0", SHARD: "${{ matrix.shard }}/4" } | |
| - { if: always(), uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a, with: { name: "turnstile-report-${{ env.SHA }}-shard${{ matrix.shard }}", path: "artifacts/", if-no-files-found: warn } } # v7.0.1 | |
| - { if: always(), uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a, with: { name: "r2-outbox-${{ env.SHA }}-shard${{ matrix.shard }}", path: "r2-outbox/", retention-days: 1, if-no-files-found: ignore, overwrite: true } } # v7.0.1 | |
| # `hop` (the required check) is the hop's ONE verdict. In a merge group and on the predev->dev pull | |
| # request it folds the shards: BOARD only when every shard reported and boarded and slsa-build is | |
| # green (or skipped: a pull request whose rung reads no release artifact): a red slsa-build denies | |
| # it; a shard that is missing, timed out or INFRA makes it INFRA, never green. A pull request | |
| # INTO PREDEV whose pre-flight | |
| # chose one runner (OWNER-LOCKED 2026-10-06, BUSBAR-1.6.0 Part 6 "Verification runs once per input") | |
| # runs it as ONE runner: no shard, no slsa-build; the ladder's predev `enter_pr`, the merge group's | |
| # gates and steps scoped to the crates the PR changes plus their reverse dependencies, on the PR merged | |
| # onto the base tip, judged no worse than the base the merge group landed in its 4 shards | |
| # (BASE_SHARDS). It runs the candidate's code, so it holds no write token either. | |
| hop: | |
| needs: [preflight, slsa-build, hop-shard, soak] | |
| if: ${{ !cancelled() && needs.preflight.result == 'success' }} | |
| permissions: { contents: read, actions: read } | |
| runs-on: ${{ (github.event_name == 'pull_request' && github.base_ref != 'dev' && needs.preflight.outputs.one_runner == 'true') && needs.preflight.outputs.runner || 'ubuntu-latest' }} | |
| timeout-minutes: ${{ (github.event_name == 'pull_request' && github.base_ref != 'dev' && needs.preflight.outputs.one_runner == 'true') && 330 || 30 }} | |
| env: { ONE_RUNNER: "${{ github.event_name == 'pull_request' && github.base_ref != 'dev' && needs.preflight.outputs.one_runner == 'true' }}", RELEASE_REF: "${{ needs.preflight.outputs.engine_ref }}", ENGINE_SHA256: "${{ needs.preflight.outputs.engine_sha256 }}", RUNNERS: "${{ vars.RUNNERS }}" } | |
| steps: | |
| # THE RUN GATE said run=0 (neutral: the merge queue, or another run of this head, judges it). | |
| # Nothing was built, so this run does NOT JUDGE the head: the required check fails here, never | |
| # passes by skipping (a skipped required check counts as success and would land an unjudged | |
| # merge group). The preflight writes one_runner=false on run=0, so nothing below builds either. | |
| - if: needs.preflight.outputs.run != '1' | |
| env: { REASON: "${{ needs.preflight.outputs.reason }}" } | |
| run: | | |
| echo "::error title=NOT JUDGED::not judged by this run (run=0): $REASON. Not a verdict on the code." | |
| exit 1 | |
| - if: env.ONE_RUNNER == 'true' | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: { ref: "${{ env.SHA }}", path: busbar, fetch-depth: 0, persist-credentials: false } | |
| # THE ENGINE, downloaded, never compiled: CI's --no-default-features binary of RELEASE_REF from | |
| # busbar-release's dev-bin release (dev-bin.yml), run only when its sha256 is ENGINE_SHA256. | |
| # First the pin's shape: a full 40-hex commit and a 64-hex digest, or FAIL. | |
| - name: Engine (busbar-release at RELEASE_REF, sha256-pinned) | |
| env: { GH_TOKEN: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}" } | |
| run: | | |
| set -euo pipefail | |
| [[ "$RELEASE_REF" =~ ^[0-9a-f]{40}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_REF is unset or not a full 40-hex busbar-release commit (busbar-release engine pin <sha>)"; exit 1; } | |
| [[ "$ENGINE_SHA256" =~ ^[0-9a-f]{64}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_SHA256 is unset or not a 64-hex sha256 (busbar-release engine pin <sha>)"; exit 1; } | |
| api=https://api.github.com/repos/GetBusbar/busbar-release/releases | |
| name="busbar-release-ci-linux-x86_64-$RELEASE_REF" | |
| id=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" "$api/tags/dev-bin" | jq -r --arg n "$name" '.assets[] | select(.name == $n) | .id') | |
| [ -n "$id" ] || { echo "::error::dev-bin has no $name"; exit 1; } | |
| curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/octet-stream" -o "$RUNNER_TEMP/busbar-release" "$api/assets/$id" | |
| echo "$ENGINE_SHA256 $RUNNER_TEMP/busbar-release" | sha256sum -c - | |
| chmod +x "$RUNNER_TEMP/busbar-release" | |
| - { uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1, with: { repository: GetBusbar/busbar-release, ref: "${{ env.RELEASE_REF }}", token: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}", path: busbar-release, persist-credentials: false } } # v7 | |
| # No debuginfo in the dev/test builds (as in the shards: the runner's disk). Read-only R2 keys, | |
| # the puts to r2-outbox/ for `cache-publish` (as in the shards: the judge runs candidate code). | |
| - if: env.ONE_RUNNER == 'true' | |
| working-directory: busbar-release | |
| run: $RUNNER_TEMP/busbar-release ci hop | |
| env: { R2_RO_KEY_ID: "${{ secrets.R2_CACHE_RO_ACCESS_KEY_ID }}", R2_RO_KEY_SECRET: "${{ secrets.R2_CACHE_RO_SECRET_ACCESS_KEY }}", CARGO_PROFILE_DEV_DEBUG: "0", BASE_SHARDS: "4" } | |
| - if: always() && env.ONE_RUNNER == 'true' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: { name: "turnstile-report-${{ env.SHA }}", path: "artifacts/", if-no-files-found: warn } | |
| - if: always() && env.ONE_RUNNER == 'true' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: { name: "r2-outbox-${{ env.SHA }}-one", path: "r2-outbox/", retention-days: 1, if-no-files-found: ignore, overwrite: true } | |
| - if: env.ONE_RUNNER != 'true' | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: { pattern: "turnstile-report-${{ env.SHA }}-shard*", path: shards } | |
| - name: Fold the shards | |
| if: env.ONE_RUNNER != 'true' | |
| env: { GH_TOKEN: "${{ github.token }}", SLSA_BUILD: "${{ needs.slsa-build.result }}", SOAK: "${{ needs.soak.result }}", SLSA_READ: "${{ github.event_name == 'merge_group' || needs.preflight.outputs.needs_release_artifact == 'true' }}", SOAK_READ: "${{ needs.preflight.outputs.soak }}" } | |
| run: | | |
| mkdir -p merge artifacts | |
| # A SKIPPED needed job DENIES (busbar-release 03cb349) unless the rung does not read it: | |
| # slsa-build is read in a merge group and wherever needs_release_artifact, the soak where | |
| # the ladder asks for it (soak=true). OLD-ENGINE SHIM (remove once ENGINE_REF is at or past | |
| # 03cb349): an engine without --must-run takes a skip as neutral itself. | |
| must=() | |
| if "$RUNNER_TEMP/busbar-release" ci turnstile merge --help | grep -q -- --must-run; then | |
| must=(--must-run "slsa-build=$SLSA_READ" --must-run "soak=$SOAK_READ") | |
| fi | |
| # The run's own job list: a shard with no report is INFRA:platform when its job was never | |
| # created (GitHub dropped it, as in the 2026-10-07 16:52-17:01Z Actions outage), else | |
| # INFRA:ours. A list that cannot be read leaves plain INFRA; the hop is never green on it. | |
| gh api "repos/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID/attempts/$GITHUB_RUN_ATTEMPT/jobs?per_page=100" \ | |
| >"$GITHUB_WORKSPACE/shard-jobs.json" || true | |
| for k in 1 2 3 4; do | |
| d="shards/turnstile-report-$SHA-shard$k" | |
| mkdir -p "merge/shard-$k" "artifacts/shard-$k" | |
| [ -f "$d/turnstile-report.json" ] && cp "$d/turnstile-report.json" "merge/shard-$k/" | |
| [ -d "$d" ] && cp -r "$d/." "artifacts/shard-$k/" | |
| done | |
| cd busbar-release | |
| "$RUNNER_TEMP/busbar-release" \ | |
| ci turnstile merge --sha "$SHA" --shards 4 --dir "$GITHUB_WORKSPACE/merge" --out "$GITHUB_WORKSPACE/artifacts/turnstile-report.json" \ | |
| --shard-jobs "$GITHUB_WORKSPACE/shard-jobs.json" --needs "slsa-build=$SLSA_BUILD" --needs "soak=$SOAK" "${must[@]}" | |
| # The durable verdict (verdict/), from the folded report and every shard's step logs: the artifact publish reads. | |
| - if: always() && needs.preflight.outputs.run == '1' | |
| working-directory: busbar-release | |
| run: $RUNNER_TEMP/busbar-release ci verdict | |
| - { if: "always() && needs.preflight.outputs.run == '1'", uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a, with: { name: "verdict-${{ env.SHA }}", path: "verdict/", retention-days: 90, if-no-files-found: warn } } # v7.0.1 | |
| # THE R2 CACHE'S ONE WRITER (busbar-release 069f032). The hop jobs judge with READ-ONLY keys and | |
| # leave their cache puts (base reports, step PASSes) in r2-outbox/ artifacts; this job uploads them | |
| # with the WRITE keys. It runs NO candidate code: it checks out nothing, downloads the engine | |
| # (sha256-pinned) and the outboxes, and runs `ci cache-publish`, which uploads only objects under | |
| # the cache's own prefixes. It is no part of the verdict: nothing needs it, it is no required | |
| # check, and it fails open (no keys, no outbox, a failed upload: a later cache miss). | |
| # workflow-rules R19: no job that runs candidate code names the write keys. | |
| cache-publish: | |
| needs: [preflight, hop-shard, hop] | |
| if: ${{ always() && needs.preflight.result == 'success' && needs.preflight.outputs.run == '1' }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| continue-on-error: true | |
| permissions: { contents: read } | |
| env: { RELEASE_REF: "${{ needs.preflight.outputs.engine_ref }}", ENGINE_SHA256: "${{ needs.preflight.outputs.engine_sha256 }}" } | |
| steps: | |
| # THE ENGINE, downloaded, never compiled: CI's --no-default-features binary of RELEASE_REF from | |
| # busbar-release's dev-bin release (dev-bin.yml), run only when its sha256 is ENGINE_SHA256. | |
| # First the pin's shape: a full 40-hex commit and a 64-hex digest, or FAIL. | |
| - name: Engine (busbar-release at RELEASE_REF, sha256-pinned) | |
| env: { GH_TOKEN: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}" } | |
| run: | | |
| set -euo pipefail | |
| [[ "$RELEASE_REF" =~ ^[0-9a-f]{40}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_REF is unset or not a full 40-hex busbar-release commit (busbar-release engine pin <sha>)"; exit 1; } | |
| [[ "$ENGINE_SHA256" =~ ^[0-9a-f]{64}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_SHA256 is unset or not a 64-hex sha256 (busbar-release engine pin <sha>)"; exit 1; } | |
| api=https://api.github.com/repos/GetBusbar/busbar-release/releases | |
| name="busbar-release-ci-linux-x86_64-$RELEASE_REF" | |
| id=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" "$api/tags/dev-bin" | jq -r --arg n "$name" '.assets[] | select(.name == $n) | .id') | |
| [ -n "$id" ] || { echo "::error::dev-bin has no $name"; exit 1; } | |
| curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/octet-stream" -o "$RUNNER_TEMP/busbar-release" "$api/assets/$id" | |
| echo "$ENGINE_SHA256 $RUNNER_TEMP/busbar-release" | sha256sum -c - | |
| chmod +x "$RUNNER_TEMP/busbar-release" | |
| - { continue-on-error: true, uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c, with: { pattern: "r2-outbox-${{ env.SHA }}-*", path: r2-outbox, merge-multiple: true } } # v8.0.1 | |
| # OLD-ENGINE SHIM (remove once ENGINE_REF is at or past 069f032): an engine without the verb | |
| # wrote the cache from the hop itself. | |
| - name: Publish the hop's cache puts (the R2 write keys, in this job only) | |
| env: { R2_KEY_ID: "${{ secrets.R2_CACHE_ACCESS_KEY_ID }}", R2_KEY_SECRET: "${{ secrets.R2_CACHE_SECRET_ACCESS_KEY }}" } | |
| run: | | |
| if ! "$RUNNER_TEMP/busbar-release" ci --help | grep -q cache-publish; then echo "engine $RELEASE_REF has no cache-publish: nothing published"; exit 0; fi | |
| "$RUNNER_TEMP/busbar-release" ci cache-publish --outbox "$GITHUB_WORKSPACE/r2-outbox" | |
| # A job with a write token (as is preflight-act). It never checks out the candidate: busbar-release at RELEASE_REF, | |
| # and the hop's verdict artifact. The sticky PR comment and the branch health issue (`ci publish`); a | |
| # hop whose runner was lost or timed out wrote no verdict, so `ci fallback` records the job and step | |
| # conclusions the API keeps after the logs are purged, and the run gate's reason when it refused the | |
| # run (STACK, wiring drift). A run the gate held neutral (run=0, preflight green) posts nothing: the | |
| # queue or another run judges that head. | |
| publish: | |
| needs: [preflight, hop] | |
| if: always() && needs.preflight.result != 'skipped' && (needs.preflight.result != 'success' || needs.preflight.outputs.run == '1') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| # statuses: write: a merge group DENIED as its PR's own red (first in the group, or behind a green | |
| # group) marks that PR's head red | |
| # ("merge queue" status) beside its sticky verdict, so the watcher's fix agent starts at failure. | |
| permissions: { contents: read, actions: read, checks: read, pull-requests: write, statuses: write } | |
| env: { RELEASE_REF: "${{ needs.preflight.outputs.engine_ref }}", ENGINE_SHA256: "${{ needs.preflight.outputs.engine_sha256 }}" } | |
| steps: | |
| # THE ENGINE, downloaded, never compiled: CI's --no-default-features binary of RELEASE_REF from | |
| # busbar-release's dev-bin release (dev-bin.yml), run only when its sha256 is ENGINE_SHA256. | |
| # First the pin's shape: a full 40-hex commit and a 64-hex digest, or FAIL. | |
| - name: Engine (busbar-release at RELEASE_REF, sha256-pinned) | |
| env: { GH_TOKEN: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}" } | |
| run: | | |
| set -euo pipefail | |
| [[ "$RELEASE_REF" =~ ^[0-9a-f]{40}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_REF is unset or not a full 40-hex busbar-release commit (busbar-release engine pin <sha>)"; exit 1; } | |
| [[ "$ENGINE_SHA256" =~ ^[0-9a-f]{64}$ ]] || { echo "::error title=ENGINE-PIN FAIL::vars.ENGINE_SHA256 is unset or not a 64-hex sha256 (busbar-release engine pin <sha>)"; exit 1; } | |
| api=https://api.github.com/repos/GetBusbar/busbar-release/releases | |
| name="busbar-release-ci-linux-x86_64-$RELEASE_REF" | |
| id=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" "$api/tags/dev-bin" | jq -r --arg n "$name" '.assets[] | select(.name == $n) | .id') | |
| [ -n "$id" ] || { echo "::error::dev-bin has no $name"; exit 1; } | |
| curl -fsSL -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/octet-stream" -o "$RUNNER_TEMP/busbar-release" "$api/assets/$id" | |
| echo "$ENGINE_SHA256 $RUNNER_TEMP/busbar-release" | sha256sum -c - | |
| chmod +x "$RUNNER_TEMP/busbar-release" | |
| - { uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1, with: { repository: GetBusbar/busbar-release, ref: "${{ env.RELEASE_REF }}", token: "${{ secrets.TURNSTILE_DISPATCH_TOKEN }}", path: busbar-release, persist-credentials: false } } # v7 | |
| - { id: verdict, continue-on-error: true, uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c, with: { name: "verdict-${{ env.SHA }}", path: verdict } } # v8.0.1 | |
| - if: steps.verdict.outcome == 'success' | |
| working-directory: busbar-release | |
| run: $RUNNER_TEMP/busbar-release ci publish | |
| env: { GH_TOKEN: "${{ github.token }}", HEALTH_BRANCHES: none } | |
| - if: needs.preflight.result != 'success' || needs.hop.result != 'success' | |
| working-directory: busbar-release | |
| run: $RUNNER_TEMP/busbar-release ci fallback | |
| env: { GH_TOKEN: "${{ github.token }}", PR_NUMBER: "${{ github.event.pull_request.number }}", PREFLIGHT_RESULT: "${{ needs.preflight.result }}", PREFLIGHT_REASON: "${{ needs.preflight.outputs.reason }}", HOP_RESULT: "${{ needs.hop.result }}", HEALTH_BRANCHES: none } |