fix(release): publish APT indexes by hash #720
Workflow file for this run
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: CI | |
| on: | |
| push: | |
| branches: | |
| - main | |
| pull_request: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| inputs: | |
| windows_runtime_smoke: | |
| description: Run native Windows runtime smoke checks | |
| required: false | |
| default: false | |
| type: boolean | |
| windows_package_smoke: | |
| description: Run the Windows release package preflight | |
| required: false | |
| # Ordinary main pushes intentionally skip Windows package qualification; | |
| # release qualification and v* tags enable it. | |
| default: false | |
| type: boolean | |
| release_qualification: | |
| description: Run the release-only delta after proving an exact fast main run | |
| required: false | |
| default: false | |
| type: boolean | |
| fast_run_id: | |
| description: Exact successful main push CI run consumed by release qualification | |
| required: false | |
| default: "" | |
| type: string | |
| expected_source_sha: | |
| description: Exact lowercase main SHA shared by the fast run and candidate | |
| required: false | |
| default: "" | |
| type: string | |
| release_intent_id: | |
| description: Unique non-secret identifier for this candidate decision | |
| required: false | |
| default: "" | |
| type: string | |
| planned_release_ref: | |
| description: Planned signed annotated tag, without creating it | |
| required: false | |
| default: "" | |
| type: string | |
| release_kind: | |
| description: Non-authoritative candidate channel | |
| required: false | |
| default: shadow | |
| type: choice | |
| options: [shadow, rc, stable] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ${{ github.event_name == 'workflow_dispatch' && inputs.release_qualification && 'rmux-release-candidate' || format('rmux-ci-{0}', github.run_id) }} | |
| cancel-in-progress: false | |
| env: | |
| CARGO_INCREMENTAL: "0" | |
| CARGO_HTTP_MULTIPLEXING: "false" | |
| CARGO_TERM_COLOR: always | |
| RUSTUP_MAX_RETRIES: "10" | |
| RUSTDOCFLAGS: -D warnings | |
| TMPDIR: /tmp | |
| RMUX_TMPDIR: /tmp | |
| jobs: | |
| verify-fast-evidence: | |
| name: Verify exhaustive fast evidence | |
| if: github.event_name == 'workflow_dispatch' && inputs.release_qualification | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| actions: read | |
| contents: read | |
| env: | |
| CANDIDATE_EXPECTED_SOURCE_SHA: ${{ inputs.expected_source_sha }} | |
| CANDIDATE_FAST_RUN_ID: ${{ inputs.fast_run_id }} | |
| CANDIDATE_RELEASE_INTENT_ID: ${{ inputs.release_intent_id }} | |
| CANDIDATE_PLANNED_RELEASE_REF: ${{ inputs.planned_release_ref }} | |
| CANDIDATE_RELEASE_KIND: ${{ inputs.release_kind }} | |
| outputs: | |
| fast_nextest_artifact_digest: ${{ steps.fast-nextest.outputs.artifact_digest }} | |
| fast_nextest_artifact_id: ${{ steps.fast-nextest.outputs.artifact_id }} | |
| steps: | |
| - name: Reject untrusted candidate inputs before checkout | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| [[ "$CANDIDATE_EXPECTED_SOURCE_SHA" =~ ^[0-9a-f]{40}$ ]] | |
| [[ "$CANDIDATE_FAST_RUN_ID" =~ ^[1-9][0-9]*$ ]] | |
| [[ "$CANDIDATE_RELEASE_INTENT_ID" =~ ^[A-Za-z0-9._:-]{8,128}$ ]] | |
| [[ "$CANDIDATE_PLANNED_RELEASE_REF" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$ ]] | |
| [[ "$CANDIDATE_RELEASE_KIND" =~ ^(shadow|rc|stable)$ ]] | |
| test "$GITHUB_REF" = refs/heads/main | |
| test "$GITHUB_RUN_ATTEMPT" = 1 | |
| test "$GITHUB_SHA" = "$CANDIDATE_EXPECTED_SOURCE_SHA" | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| with: | |
| ref: ${{ github.sha }} | |
| persist-credentials: false | |
| - name: Validate immutable candidate intent | |
| run: >- | |
| scripts/release/validate-candidate-intent.py | |
| --expected-source-sha "$CANDIDATE_EXPECTED_SOURCE_SHA" | |
| --actual-source-sha "$(git rev-parse HEAD)" | |
| --fast-run-id "$CANDIDATE_FAST_RUN_ID" | |
| --release-intent-id "$CANDIDATE_RELEASE_INTENT_ID" | |
| --planned-release-ref "$CANDIDATE_PLANNED_RELEASE_REF" | |
| --release-kind "$CANDIDATE_RELEASE_KIND" | |
| --github-ref "$GITHUB_REF" | |
| --github-run-attempt "$GITHUB_RUN_ATTEMPT" | |
| --output candidate-intent.json | |
| - name: Verify the selected fast run and all of its jobs | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: >- | |
| scripts/release/verify-fast-run.py | |
| --repository "${{ github.repository }}" | |
| --run-id "$CANDIDATE_FAST_RUN_ID" | |
| --expected-source-sha "$CANDIDATE_EXPECTED_SOURCE_SHA" | |
| --kind fast | |
| > fast-proof.json | |
| - id: fast-nextest | |
| name: Resolve the exact fast Windows test archive | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: >- | |
| scripts/release/actions-artifact.py resolve | |
| --repository "$GITHUB_REPOSITORY" | |
| --run-id "$CANDIDATE_FAST_RUN_ID" | |
| --name "rmux-windows-nextest-$CANDIDATE_EXPECTED_SOURCE_SHA" | |
| --expected-source-sha "$CANDIDATE_EXPECTED_SOURCE_SHA" | |
| --github-output "$GITHUB_OUTPUT" | |
| > fast-nextest-artifact.json | |
| - name: Upload immutable input proofs | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: rmux-fast-proof-${{ inputs.expected_source_sha }} | |
| path: | | |
| candidate-intent.json | |
| fast-nextest-artifact.json | |
| fast-proof.json | |
| if-no-files-found: error | |
| retention-days: 7 | |
| release-candidate-delta: | |
| name: Run release-only candidate delta | |
| if: github.event_name == 'workflow_dispatch' && inputs.release_qualification | |
| needs: verify-fast-evidence | |
| uses: ./.github/workflows/release-candidate-delta.yml | |
| with: | |
| expected_source_sha: ${{ inputs.expected_source_sha }} | |
| fast_run_id: ${{ inputs.fast_run_id }} | |
| permissions: | |
| actions: read | |
| contents: read | |
| release-candidate-delta-gate: | |
| name: Release candidate delta gate | |
| if: >- | |
| always() && github.event_name == 'workflow_dispatch' && | |
| inputs.release_qualification | |
| needs: [verify-fast-evidence, release-candidate-delta] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Require proof and every delta | |
| env: | |
| DELTA_RESULT: ${{ needs.release-candidate-delta.result }} | |
| FAST_PROOF_RESULT: ${{ needs.verify-fast-evidence.result }} | |
| run: | | |
| test "$FAST_PROOF_RESULT" = success | |
| test "$DELTA_RESULT" = success | |
| release-canonical-native: | |
| name: Build canonical native artifacts | |
| if: github.event_name == 'workflow_dispatch' && inputs.release_qualification | |
| needs: verify-fast-evidence | |
| uses: ./.github/workflows/canonical-native-build.yml | |
| with: | |
| expected_source_sha: ${{ inputs.expected_source_sha }} | |
| fast_run_id: ${{ inputs.fast_run_id }} | |
| fast_nextest_artifact_id: ${{ needs.verify-fast-evidence.outputs.fast_nextest_artifact_id }} | |
| fast_nextest_artifact_digest: ${{ needs.verify-fast-evidence.outputs.fast_nextest_artifact_digest }} | |
| release_intent_id: ${{ inputs.release_intent_id }} | |
| planned_release_ref: ${{ inputs.planned_release_ref }} | |
| release_kind: ${{ inputs.release_kind }} | |
| permissions: | |
| actions: read | |
| attestations: write | |
| contents: read | |
| id-token: write | |
| release-candidate-gate: | |
| name: Release candidate gate | |
| if: >- | |
| always() && github.event_name == 'workflow_dispatch' && | |
| inputs.release_qualification | |
| needs: [release-candidate-delta-gate, release-canonical-native] | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 5 | |
| permissions: {} | |
| steps: | |
| - name: Require release delta and every canonical byte | |
| env: | |
| CANONICAL_RESULT: ${{ needs.release-canonical-native.result }} | |
| DELTA_RESULT: ${{ needs.release-candidate-delta-gate.result }} | |
| run: | | |
| test "$DELTA_RESULT" = success | |
| test "$CANONICAL_RESULT" = success | |
| linux-quality: | |
| name: Linux format, lint, and docs | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| env: | |
| RUST_MIN_STACK: "8388608" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| components: clippy, rustfmt | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}- | |
| cargo-${{ runner.os }}-${{ runner.arch }}- | |
| - name: Fetch locked dependencies | |
| run: cargo fetch --locked | |
| - name: cargo fmt | |
| run: cargo fmt --all -- --check | |
| - name: cargo clippy | |
| run: cargo clippy --workspace --all-targets --locked -- -D warnings | |
| - name: Await-under-lock audit | |
| run: scripts/audit-await-holding-lock.sh | |
| - name: rustdoc with warnings denied | |
| run: cargo doc --workspace --locked --no-deps | |
| linux-build: | |
| name: Linux workspace build | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| env: | |
| RUST_MIN_STACK: "8388608" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}- | |
| cargo-${{ runner.os }}-${{ runner.arch }}- | |
| - name: Fetch locked dependencies | |
| run: cargo fetch --locked | |
| - name: cargo build | |
| run: cargo build --workspace --locked | |
| wasm-crypto: | |
| name: WASM crypto build and supply-chain gate | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| # Pin the exact rustc that reproduces the shipped browser blob (mirrored | |
| # in rmux-web-share PROVENANCE.json toolchain.rustc). A floating "stable" | |
| # changes codegen and would break byte-for-byte reproducibility — the | |
| # property rmux-web-share's verify-wasm-from-source.mjs gate enforces. | |
| toolchain: "1.94.1" | |
| targets: wasm32-unknown-unknown | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}- | |
| cargo-${{ runner.os }}-${{ runner.arch }}- | |
| - name: Fetch locked dependencies | |
| run: cargo fetch --locked | |
| # The browser bundle must NOT carry x25519-dalek/curve25519-dalek; the | |
| # WebCrypto path replaces them. cargo tree -i exits non-zero when the | |
| # package is absent, so success here means it leaked — fail closed. | |
| - name: Assert x25519-dalek is excluded from the WASM build | |
| run: | | |
| if cargo tree -p rmux-web-crypto --no-default-features --features wasm --target wasm32-unknown-unknown -i x25519-dalek >/dev/null 2>&1; then | |
| echo "::error::x25519-dalek must not enter the WASM crypto build" | |
| exit 1 | |
| fi | |
| echo "x25519-dalek correctly excluded from the WASM crypto build" | |
| # Regression guard for the browser test gate: the wasm-test feature set must | |
| # compile its test targets (tests/handshake.rs is x25519-gated out). | |
| - name: Compile the wasm-test gate | |
| run: cargo test -p rmux-web-crypto --no-default-features --features wasm-test --no-run --locked | |
| - name: Install pinned wasm-pack | |
| run: cargo install wasm-pack --version 0.13.1 --locked | |
| # Rebuild the exact blobs rmux-web-share ships, via the canonical recipe and | |
| # pinned toolchain, and publish them as an artifact for manual auditors. The | |
| # authoritative byte-equality-to-shipped check is cross-repo, in | |
| # rmux-web-share's verify-wasm-from-source.mjs (which rebuilds from this | |
| # source at the pinned commit); here we prove the recipe still produces the | |
| # browser primitive and expose the canonical bytes + their hashes. | |
| - name: Build and publish the browser WASM blobs | |
| env: | |
| RUSTUP_TOOLCHAIN: "1.94.1" | |
| run: | | |
| set -euo pipefail | |
| scripts/build-web-crypto-wasm.sh wasm | |
| mkdir -p "$RUNNER_TEMP/wasm-pkg" | |
| cp crates/rmux-web-crypto/pkg/rmux_web_crypto_wasm_bg.wasm \ | |
| crates/rmux-web-crypto/pkg/rmux_web_crypto_wasm.js \ | |
| "$RUNNER_TEMP/wasm-pkg/" | |
| # The Playwright-only blob shares the recipe; build it too so a break in | |
| # the test feature set is caught here, not in rmux-web-share CI. | |
| scripts/build-web-crypto-wasm.sh wasm-test | |
| { | |
| echo "### Browser WASM crypto blob (production)" | |
| echo "" | |
| echo "rustc 1.94.1 · wasm-bindgen 0.2.123 · wasm-opt off" | |
| echo "" | |
| echo '```' | |
| ( cd "$RUNNER_TEMP/wasm-pkg" && sha256sum rmux_web_crypto_wasm_bg.wasm rmux_web_crypto_wasm.js ) | |
| echo '```' | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: rmux-web-crypto-wasm | |
| path: ${{ runner.temp }}/wasm-pkg | |
| if-no-files-found: error | |
| linux-tests: | |
| name: Linux tests (${{ matrix.group }}) | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - group: core | |
| packages: -p rmux-types -p rmux-os -p rmux-proto -p rmux-ipc -p rmux-core -p rmux-client -p rmux-render-core -p rmux-web-crypto | |
| - group: pty-sdk | |
| packages: -p rmux-pty -p rmux-sdk | |
| - group: server | |
| packages: -p rmux-server | |
| - group: app-ui | |
| packages: -p rmux -p ratatui-rmux -p xtask | |
| env: | |
| RUST_MIN_STACK: "8388608" | |
| RUST_TEST_THREADS: "1" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ matrix.group }}-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ matrix.group }}- | |
| cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}- | |
| cargo-${{ runner.os }}-${{ runner.arch }}- | |
| - name: Fetch locked dependencies | |
| run: cargo fetch --locked | |
| - name: cargo test | |
| run: cargo test --locked --no-fail-fast ${{ matrix.packages }} | |
| linux-source-gates: | |
| name: Linux source boundary gates | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - name: Install the release writer Python baseline | |
| uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 | |
| with: | |
| python-version: "3.10" | |
| - name: Validate release writers on Python 3.10 | |
| run: | | |
| set -euo pipefail | |
| for script in \ | |
| channel-execution.py \ | |
| prepare-channel-retry.py \ | |
| publish-crate-set.py \ | |
| publish-linux-repository.py \ | |
| publish-owned-repository.py \ | |
| snap-candidate-status.py; do | |
| python "scripts/release/$script" --help >/dev/null | |
| done | |
| - name: Unsafe policy gate | |
| run: scripts/unsafe-check.sh | |
| - name: No network scoped runtime source scan | |
| run: scripts/no-network-in-runtime.sh | |
| - name: Platform neutrality source scan | |
| run: scripts/check-platform-neutrality.sh | |
| - name: No side effects in debug assertions | |
| run: scripts/no-debug-assert-side-effects.sh | |
| tmux-oracle-linux: | |
| name: Linux tmux 3.7b oracle harness | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| env: | |
| RUST_MIN_STACK: "8388608" | |
| RMUX_REQUIRE_TMUX: "1" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| - name: Install tmux oracle build dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| bison \ | |
| build-essential \ | |
| ca-certificates \ | |
| curl \ | |
| libevent-dev \ | |
| libncurses-dev \ | |
| libutf8proc-dev \ | |
| pkg-config | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}- | |
| cargo-${{ runner.os }}-${{ runner.arch }}- | |
| - name: Cache tmux oracle source | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: ~/.cache/rmux-oracle | |
| key: tmux-oracle-3.7b-87f2e99e3b685973f2ca002ffd6ed7e51a5744f7009daae5a15670b6d532db96 | |
| - name: Build pinned tmux 3.7b oracle | |
| run: scripts/oracle/build-tmux37.sh --prefix "$RUNNER_TEMP/rmux-oracle" | |
| - name: Fetch locked dependencies | |
| run: cargo fetch --locked | |
| - name: Source-file oracle must not skip | |
| env: | |
| RMUX_FROZEN_TMUX: ${{ runner.temp }}/rmux-oracle/tmux | |
| run: cargo test --locked --test unix_source_file_tmux_oracle -- --test-threads=1 | |
| - name: Differential harness oracle self-tests | |
| env: | |
| RMUX_FROZEN_TMUX: ${{ runner.temp }}/rmux-oracle/tmux | |
| run: cargo test --locked --test tmux_compat_harness -- --test-threads=1 | |
| - name: Upload exact tmux oracle for release delta | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: rmux-tmux-oracle-${{ github.sha }} | |
| path: | | |
| ${{ runner.temp }}/rmux-oracle/tmux | |
| ${{ runner.temp }}/rmux-oracle/tmux.reference | |
| if-no-files-found: error | |
| retention-days: 7 | |
| compression-level: 0 | |
| release-review-perf: | |
| name: Release review (perf) | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: windows-tests-gate | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 90 | |
| env: | |
| RUST_MIN_STACK: "8388608" | |
| CARGO_BUILD_JOBS: "4" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}- | |
| cargo-${{ runner.os }}-${{ runner.arch }}- | |
| - name: Fetch locked dependencies | |
| run: cargo fetch --locked | |
| - name: Release review performance section | |
| env: | |
| RMUX_PERF_AUTO_CURRENT: "1" | |
| RMUX_PERF_GATE_MODE: portable-budget | |
| RMUX_PERF_EXPECTED_GIT_SHA: ${{ github.sha }} | |
| RMUX_PERF_EXPECTED_PLATFORM: linux | |
| RMUX_PERF_EXPECTED_PROVENANCE: github-actions:${{ github.repository }}:${{ github.workflow }}:${{ github.job }}:perf:${{ github.run_id }}:${{ github.run_attempt }} | |
| RMUX_PERF_MAX_CURRENT_AGE_SECONDS: "21600" | |
| run: >- | |
| scripts/release-review-gate.sh | |
| --skip-package | |
| --section perf | |
| --target-dir target/release-review-ci-perf | |
| release-review-sections: | |
| name: Release review (${{ matrix.section }}) | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: [linux-source-gates, windows-tests-gate] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 90 | |
| strategy: | |
| fail-fast: false | |
| max-parallel: 6 | |
| matrix: | |
| section: [static, lint, server, cli, tmux, runtime-sdk] | |
| env: | |
| RUST_MIN_STACK: "8388608" | |
| CARGO_BUILD_JOBS: "4" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| components: clippy, rustfmt | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ matrix.section }}-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ matrix.section }}- | |
| cargo-${{ runner.os }}-${{ runner.arch }}- | |
| - name: Fetch locked dependencies | |
| run: cargo fetch --locked | |
| - name: Install tmux oracle build dependencies | |
| if: matrix.section == 'tmux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| bison \ | |
| build-essential \ | |
| ca-certificates \ | |
| curl \ | |
| libevent-dev \ | |
| libncurses-dev \ | |
| libutf8proc-dev \ | |
| pkg-config | |
| - name: Build pinned tmux 3.7b oracle | |
| if: matrix.section == 'tmux' | |
| run: scripts/oracle/build-tmux37.sh --prefix "$RUNNER_TEMP/rmux-oracle" | |
| - name: Release review section | |
| env: | |
| RMUX_FROZEN_TMUX: ${{ runner.temp }}/rmux-oracle/tmux | |
| RMUX_REQUIRE_TMUX: "1" | |
| RMUX_PERF_AUTO_CURRENT: "1" | |
| RMUX_PERF_GATE_MODE: portable-budget | |
| RMUX_PERF_EXPECTED_GIT_SHA: ${{ github.sha }} | |
| RMUX_PERF_EXPECTED_PLATFORM: linux | |
| RMUX_PERF_EXPECTED_PROVENANCE: github-actions:${{ github.repository }}:${{ github.workflow }}:${{ github.job }}:${{ matrix.section }}:${{ github.run_id }}:${{ github.run_attempt }} | |
| RMUX_PERF_MAX_CURRENT_AGE_SECONDS: "21600" | |
| run: >- | |
| scripts/release-review-gate.sh | |
| --skip-package | |
| --section ${{ matrix.section }} | |
| --target-dir target/release-review-ci-${{ matrix.section }} | |
| release-review-gate: | |
| name: Release review gate (no package) | |
| if: >- | |
| always() && | |
| startsWith(github.ref, 'refs/tags/v') | |
| needs: [release-review-perf, release-review-sections] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Require every release review section | |
| env: | |
| RELEASE_REVIEW_PERF_RESULT: ${{ needs.release-review-perf.result }} | |
| RELEASE_REVIEW_RESULT: ${{ needs.release-review-sections.result }} | |
| run: | | |
| test "$RELEASE_REVIEW_PERF_RESULT" = success | |
| test "$RELEASE_REVIEW_RESULT" = success | |
| snap-package: | |
| name: Snap package smoke | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: windows-tests-gate | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - name: Build snap | |
| id: snapcraft | |
| uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 | |
| with: | |
| path: . | |
| - name: Smoke snap package | |
| run: scripts/smoke-snap-package.sh "${{ steps.snapcraft.outputs.snap }}" | |
| - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: rmux-snap-${{ github.sha }} | |
| path: ${{ steps.snapcraft.outputs.snap }} | |
| if-no-files-found: error | |
| nix-flake: | |
| name: Nix flake gate | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: windows-tests-gate | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - name: Install Nix | |
| uses: cachix/install-nix-action@8aa03977d8d733052d78f4e008a241fd1dbf36b3 | |
| with: | |
| extra_nix_config: | | |
| experimental-features = nix-command flakes | |
| - name: nix flake check | |
| run: nix flake check | |
| - name: nix build | |
| run: nix build .#packages.x86_64-linux.default --no-link | |
| - name: nix run version matches Cargo.toml | |
| run: | | |
| set -euo pipefail | |
| expected="$(python3 - <<'PY' | |
| import pathlib | |
| import tomllib | |
| manifest = tomllib.loads(pathlib.Path("Cargo.toml").read_text()) | |
| print(manifest["workspace"]["package"]["version"]) | |
| PY | |
| )" | |
| actual="$(nix run . -- -V)" | |
| if [ "$actual" != "rmux $expected" ]; then | |
| echo "::error::expected 'rmux $expected', got '$actual'" | |
| exit 1 | |
| fi | |
| linux-sdk-smoke: | |
| name: Linux SDK v1 daemon smoke | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| env: | |
| RUST_MIN_STACK: "8388608" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}- | |
| cargo-${{ runner.os }}-${{ runner.arch }}- | |
| - name: SDK v1 daemon smoke | |
| run: scripts/smoke-sdk-v1.sh | |
| linux-perf-smoke: | |
| name: Linux non-final performance smoke | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| env: | |
| RUST_MIN_STACK: "8388608" | |
| RMUX_PERF_EXPECTED_GIT_SHA: ${{ github.sha }} | |
| RMUX_PERF_EXPECTED_PLATFORM: linux | |
| RMUX_PERF_EXPECTED_PROVENANCE: github-actions:${{ github.repository }}:${{ github.workflow }}:${{ github.job }}:${{ github.run_id }}:${{ github.run_attempt }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| # Intentionally uncached: historical multi-gigabyte restore/save costs | |
| # exceeded the compile-time benefit, so this required budget stays cold. | |
| - name: Non-final local performance budget smoke | |
| run: scripts/perf-bench.sh --iterations 3 --line-count 10000 --output-dir target/perf/non-final-ci --fail-on-budget | |
| - name: Issue 34 responsiveness smoke | |
| env: | |
| RMUX_BIN: target/release/rmux | |
| run: scripts/smoke-issue34-responsiveness-unix.sh | |
| - name: RSS and FD drift smoke | |
| env: | |
| RMUX_BIN: target/release/rmux | |
| run: scripts/smoke-rss-fd-drift-unix.sh | |
| linux-dependency-audit: | |
| name: Linux dependency audit | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/bin/cargo-deny | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| key: cargo-deny-${{ runner.os }}-${{ runner.arch }}-0.19.5-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-deny-${{ runner.os }}-${{ runner.arch }}-0.19.5- | |
| - name: Install cargo-deny | |
| if: hashFiles('deny.toml') != '' | |
| run: | | |
| if ! command -v cargo-deny >/dev/null 2>&1; then | |
| cargo install cargo-deny --version 0.19.5 --locked | |
| fi | |
| - name: cargo deny check | |
| if: hashFiles('deny.toml') != '' | |
| run: cargo deny check | |
| platform-runtime: | |
| name: Platform build and smoke on ${{ matrix.os }} | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 180 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: | |
| - macos-15-intel | |
| - macos-15 | |
| env: | |
| RUST_MIN_STACK: "8388608" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-${{ matrix.os }}-platform-runtime-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-${{ matrix.os }}-platform-runtime- | |
| - name: Configure temporary paths | |
| shell: bash | |
| run: | | |
| echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" | |
| echo "RMUX_TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" | |
| - name: Fetch locked dependencies | |
| run: cargo fetch --locked | |
| - name: cargo build | |
| run: cargo build --workspace --locked | |
| # These crates passed repeated parallel-safe runs; keep rmux-client and | |
| # suites with shared socket or process state serial below. | |
| - name: cargo test parallel-safe portable crates | |
| run: cargo test --locked --no-fail-fast -p rmux-types -p rmux-os -p rmux-proto -p rmux-ipc -p rmux-core | |
| - name: cargo test rmux-client serially | |
| run: cargo test --locked --no-fail-fast -p rmux-client -- --test-threads=1 | |
| - name: cargo test macOS direct /tmp socket path | |
| if: runner.os == 'macOS' | |
| run: cargo test --locked -p rmux --test cli_surface socket_path_flag_can_start_directly_under_tmp -- --exact --test-threads=1 | |
| - name: rmux binary smoke | |
| run: cargo run --package rmux --bin rmux --locked -- -V | |
| windows-test-archive: | |
| name: Windows workspace test archive | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: windows-latest | |
| timeout-minutes: 30 | |
| outputs: | |
| cache_hit: ${{ steps.archive-cache.outputs.cache-hit }} | |
| env: | |
| CARGO_PROFILE_TEST_DEBUG: "0" | |
| RUST_MIN_STACK: "8388608" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - name: Restore reusable Windows test archive | |
| id: archive-cache | |
| continue-on-error: true | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: target/windows-nextest.tar.zst | |
| key: windows-nextest-${{ runner.os }}-${{ github.sha }} | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| if: steps.archive-cache.outputs.cache-hit != 'true' | |
| with: | |
| toolchain: "1.96.1" | |
| - name: Tolerate unavailable Windows revocation servers | |
| if: steps.archive-cache.outputs.cache-hit != 'true' | |
| shell: pwsh | |
| run: | | |
| $curlHome = Join-Path $env:RUNNER_TEMP "rmux-curl" | |
| New-Item -ItemType Directory -Force -Path $curlHome | Out-Null | |
| "ssl-revoke-best-effort" | | |
| Set-Content -LiteralPath (Join-Path $curlHome ".curlrc") -Encoding ascii | |
| "CURL_HOME=$curlHome" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| - name: Install cargo-nextest | |
| if: steps.archive-cache.outputs.cache-hit != 'true' | |
| uses: taiki-e/install-action@c7eb1735f09259a5035e8e5d44b1406b1cddc0fb # v2.83.0 | |
| with: | |
| tool: nextest@0.9.138 | |
| fallback: none | |
| - name: Restore cargo cache | |
| if: steps.archive-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-windows-latest-nextest-archive-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-windows-latest-nextest-archive- | |
| cargo-windows-latest-platform-runtime- | |
| - name: Configure temporary paths | |
| if: steps.archive-cache.outputs.cache-hit != 'true' | |
| shell: bash | |
| run: | | |
| echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" | |
| echo "RMUX_TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" | |
| - name: Fetch locked dependencies | |
| if: steps.archive-cache.outputs.cache-hit != 'true' | |
| run: cargo fetch --locked | |
| - name: Build Windows test archive | |
| if: steps.archive-cache.outputs.cache-hit != 'true' | |
| run: cargo nextest archive --workspace --locked --archive-file target/windows-nextest.tar.zst | |
| - name: Bind the Windows test archive bytes | |
| shell: pwsh | |
| run: | | |
| $archive = "target/windows-nextest.tar.zst" | |
| if (-not (Test-Path -LiteralPath $archive -PathType Leaf)) { | |
| throw "Windows Nextest archive is missing" | |
| } | |
| $digest = (Get-FileHash -Algorithm SHA256 -LiteralPath $archive).Hash.ToLowerInvariant() | |
| [System.IO.File]::WriteAllText( | |
| "$archive.sha256", | |
| "$digest windows-nextest.tar.zst`n", | |
| [System.Text.Encoding]::ASCII | |
| ) | |
| - name: Upload Windows test archive | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: rmux-windows-nextest-${{ github.sha }} | |
| path: | | |
| target/windows-nextest.tar.zst | |
| target/windows-nextest.tar.zst.sha256 | |
| if-no-files-found: error | |
| retention-days: 7 | |
| compression-level: 0 | |
| windows-test-build: | |
| name: Windows build, lint, docs, and smoke | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: windows-latest | |
| timeout-minutes: 60 | |
| env: | |
| RUST_MIN_STACK: "8388608" | |
| RUST_TEST_THREADS: "1" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| components: clippy | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-windows-latest-platform-runtime-${{ hashFiles('Cargo.lock') }}-${{ github.sha }} | |
| restore-keys: | | |
| cargo-windows-latest-platform-runtime-${{ hashFiles('Cargo.lock') }}- | |
| cargo-windows-latest-platform-runtime- | |
| - name: Configure temporary paths | |
| shell: bash | |
| run: | | |
| echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" | |
| echo "RMUX_TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" | |
| - name: Fetch locked dependencies | |
| run: cargo fetch --locked | |
| - name: cargo build | |
| run: cargo build --workspace --locked | |
| - name: cargo clippy on Windows MSVC | |
| run: cargo clippy --workspace --all-targets --locked -- -D warnings | |
| - name: cargo test Windows doctests | |
| run: cargo test --workspace --doc --locked -- --test-threads=1 | |
| - name: rmux binary smoke | |
| run: cargo run --package rmux --bin rmux --locked -- -V | |
| windows-tests: | |
| name: Windows workspace tests (${{ matrix.shard }}/18) | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| needs: windows-test-archive | |
| runs-on: windows-latest | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| max-parallel: 18 | |
| matrix: | |
| shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] | |
| env: | |
| RUST_MIN_STACK: "8388608" | |
| RMUX_ALLOW_INTERNAL_DAEMON_IN_CALLER_JOB: "1" | |
| RMUX_WINDOWS_SMOKE_RMUX_BIN: ${{ github.workspace }}/target/debug/rmux.exe | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| - name: Tolerate unavailable Windows revocation servers | |
| shell: pwsh | |
| run: | | |
| $curlHome = Join-Path $env:RUNNER_TEMP "rmux-curl" | |
| New-Item -ItemType Directory -Force -Path $curlHome | Out-Null | |
| "ssl-revoke-best-effort" | | |
| Set-Content -LiteralPath (Join-Path $curlHome ".curlrc") -Encoding ascii | |
| "CURL_HOME=$curlHome" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| - name: Install cargo-nextest | |
| uses: taiki-e/install-action@c7eb1735f09259a5035e8e5d44b1406b1cddc0fb # v2.83.0 | |
| with: | |
| tool: nextest@0.9.138 | |
| fallback: none | |
| - name: Configure temporary paths | |
| shell: bash | |
| run: | | |
| echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" | |
| echo "RMUX_TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" | |
| - name: Download Windows test archive | |
| continue-on-error: true | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: rmux-windows-nextest-${{ github.sha }} | |
| path: target | |
| - name: Run Windows test shard | |
| shell: pwsh | |
| run: | | |
| cargo nextest run ` | |
| --archive-file target/windows-nextest.tar.zst ` | |
| --extract-to "$env:GITHUB_WORKSPACE" ` | |
| --extract-overwrite ` | |
| --workspace-remap "$env:GITHUB_WORKSPACE" ` | |
| --partition "slice:${{ matrix.shard }}/18" ` | |
| --no-fail-fast ` | |
| --retries 0 ` | |
| --test-threads num-cpus | |
| windows-tests-gate: | |
| name: Platform build and smoke on windows-latest | |
| if: >- | |
| always() && | |
| !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) | |
| needs: [windows-test-build, windows-tests] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Require every Windows test shard | |
| env: | |
| WINDOWS_BUILD_RESULT: ${{ needs.windows-test-build.result }} | |
| WINDOWS_TESTS_RESULT: ${{ needs.windows-tests.result }} | |
| run: | | |
| test "$WINDOWS_BUILD_RESULT" = success | |
| test "$WINDOWS_TESTS_RESULT" = success | |
| windows-test-archive-cache: | |
| name: Cache reusable Windows test archive | |
| if: >- | |
| always() && | |
| !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) && | |
| needs.windows-test-archive.result == 'success' && | |
| needs.windows-tests-gate.result == 'success' && | |
| needs.windows-test-archive.outputs.cache_hit != 'true' | |
| needs: [windows-test-archive, windows-tests-gate] | |
| runs-on: windows-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Download Windows test archive | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: rmux-windows-nextest-${{ github.sha }} | |
| path: target | |
| - name: Cache Windows test archive | |
| continue-on-error: true | |
| uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: target/windows-nextest.tar.zst | |
| key: windows-nextest-${{ runner.os }}-${{ github.sha }} | |
| windows-package-build: | |
| name: Windows release package build | |
| # Ordinary main pushes stay on the protected fast lane; release | |
| # qualification, targeted dispatches, and v* tags run this package chain. | |
| if: >- | |
| startsWith(github.ref, 'refs/tags/v') || | |
| (github.event_name == 'workflow_dispatch' && | |
| inputs.windows_package_smoke) | |
| needs: windows-tests-gate | |
| runs-on: windows-latest | |
| timeout-minutes: 60 | |
| env: | |
| CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS: "-C target-feature=+crt-static" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: windows-package-${{ runner.os }}-${{ runner.arch }}-rust-1.96.1-${{ hashFiles('Cargo.lock', 'scripts/package-windows.ps1', 'scripts/verify-package-windows.ps1') }} | |
| restore-keys: | | |
| windows-package-${{ runner.os }}-${{ runner.arch }}-rust-1.96.1- | |
| - name: Fetch locked dependencies | |
| run: cargo fetch --locked | |
| - name: Build Windows release package | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| $outputDir = "target/windows-package-preflight" | |
| if (Test-Path -LiteralPath $outputDir) { | |
| Remove-Item -LiteralPath $outputDir -Recurse -Force | |
| } | |
| ./scripts/package-windows.ps1 ` | |
| -Configuration release ` | |
| -Target x86_64-pc-windows-msvc ` | |
| -OutputDir $outputDir ` | |
| -PlatformLabel windows-x86_64 | |
| $archives = @(Get-ChildItem -Path $outputDir -Filter "rmux-*.zip") | |
| if ($archives.Count -ne 1) { | |
| throw "Expected exactly one Windows package archive, found $($archives.Count)" | |
| } | |
| $archive = $archives[0] | |
| ./scripts/verify-package-windows.ps1 ` | |
| -Archive $archive.FullName ` | |
| -Checksums "$outputDir/SHA256SUMS.txt" ` | |
| -RequireReleaseArtifact ` | |
| -ExpectedGitSha "${{ github.sha }}" | |
| - name: Upload Windows release package | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
| with: | |
| name: rmux-windows-package-${{ github.sha }} | |
| path: target/windows-package-preflight | |
| if-no-files-found: error | |
| retention-days: 3 | |
| windows-package-runtime-smoke: | |
| name: Windows package runtime smoke | |
| needs: windows-package-build | |
| runs-on: windows-latest | |
| timeout-minutes: 15 | |
| env: | |
| CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS: "-C target-feature=+crt-static" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - name: Configure temporary paths | |
| shell: pwsh | |
| run: | | |
| "TMPDIR=$env:RUNNER_TEMP" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| "RMUX_TMPDIR=$env:RUNNER_TEMP" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| - name: Download Windows release package | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 | |
| with: | |
| name: rmux-windows-package-${{ github.sha }} | |
| path: target/windows-package-preflight | |
| - name: Run packaged binary and daemon smokes | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| $outputDir = "target/windows-package-preflight" | |
| $archives = @(Get-ChildItem -Path $outputDir -Filter "rmux-*.zip") | |
| if ($archives.Count -ne 1) { | |
| throw "Expected exactly one downloaded Windows package archive, found $($archives.Count)" | |
| } | |
| $archive = $archives[0] | |
| ./scripts/verify-package-windows.ps1 ` | |
| -Archive $archive.FullName ` | |
| -Checksums "$outputDir/SHA256SUMS.txt" ` | |
| -RunBinary ` | |
| -RunDaemonSmoke ` | |
| -RequireReleaseArtifact ` | |
| -ExpectedGitSha "${{ github.sha }}" | |
| windows-package-harness-smoke: | |
| name: Windows package harness smoke (${{ matrix.smoke }}) | |
| needs: [windows-package-build, windows-test-archive] | |
| runs-on: windows-latest | |
| timeout-minutes: 15 | |
| strategy: | |
| fail-fast: false | |
| max-parallel: 2 | |
| matrix: | |
| smoke: [sdk, mouse] | |
| # The downloaded test harness uses the default dynamic CRT; packaged | |
| # binaries are independently verified CRT-static before it runs. | |
| env: | |
| CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS: "-C target-feature=+crt-static" | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - name: Tolerate unavailable Windows revocation servers | |
| shell: pwsh | |
| run: | | |
| $curlHome = Join-Path $env:RUNNER_TEMP "rmux-curl" | |
| New-Item -ItemType Directory -Force -Path $curlHome | Out-Null | |
| "ssl-revoke-best-effort" | | |
| Set-Content -LiteralPath (Join-Path $curlHome ".curlrc") -Encoding ascii | |
| "CURL_HOME=$curlHome" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| - name: Install cargo-nextest | |
| uses: taiki-e/install-action@c7eb1735f09259a5035e8e5d44b1406b1cddc0fb # v2.83.0 | |
| with: | |
| tool: nextest@0.9.138 | |
| fallback: none | |
| - name: Configure temporary paths | |
| shell: pwsh | |
| run: | | |
| "TMPDIR=$env:RUNNER_TEMP" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| "RMUX_TMPDIR=$env:RUNNER_TEMP" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append | |
| - name: Download Windows release package | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 | |
| with: | |
| name: rmux-windows-package-${{ github.sha }} | |
| path: target/windows-package-preflight | |
| - name: Download Windows test archive | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 | |
| with: | |
| name: rmux-windows-nextest-${{ github.sha }} | |
| path: target | |
| - name: Run package harness smoke | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = "Stop" | |
| $outputDir = "target/windows-package-preflight" | |
| $archives = @(Get-ChildItem -Path $outputDir -Filter "rmux-*.zip") | |
| if ($archives.Count -ne 1) { | |
| throw "Expected exactly one downloaded Windows package archive, found $($archives.Count)" | |
| } | |
| $archive = $archives[0] | |
| $arguments = @{ | |
| Archive = $archive.FullName | |
| Checksums = "$outputDir/SHA256SUMS.txt" | |
| NextestArchive = "target/windows-nextest.tar.zst" | |
| RequireReleaseArtifact = $true | |
| ExpectedGitSha = "${{ github.sha }}" | |
| } | |
| if ("${{ matrix.smoke }}" -eq "sdk") { | |
| $arguments.RunSdkSmoke = $true | |
| } else { | |
| $arguments.RunMouseBorderSmoke = $true | |
| } | |
| ./scripts/verify-package-windows.ps1 @arguments | |
| windows-package-preflight: | |
| name: Windows release package preflight | |
| if: >- | |
| always() && | |
| (startsWith(github.ref, 'refs/tags/v') || | |
| (github.event_name == 'workflow_dispatch' && | |
| inputs.windows_package_smoke)) | |
| needs: [windows-package-build, windows-package-runtime-smoke, windows-package-harness-smoke] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Require every Windows package smoke | |
| env: | |
| WINDOWS_PACKAGE_BUILD_RESULT: ${{ needs.windows-package-build.result }} | |
| WINDOWS_PACKAGE_RUNTIME_RESULT: ${{ needs.windows-package-runtime-smoke.result }} | |
| WINDOWS_PACKAGE_HARNESS_RESULT: ${{ needs.windows-package-harness-smoke.result }} | |
| run: | | |
| test "$WINDOWS_PACKAGE_BUILD_RESULT" = success | |
| test "$WINDOWS_PACKAGE_RUNTIME_RESULT" = success | |
| test "$WINDOWS_PACKAGE_HARNESS_RESULT" = success | |
| windows-cross: | |
| name: Cross-target Windows build check | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.release_qualification) }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| targets: x86_64-pc-windows-gnu | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}- | |
| cargo-${{ runner.os }}-${{ runner.arch }}- | |
| - name: Install Windows GNU linker | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends gcc-mingw-w64-x86-64 | |
| - name: cargo build Windows target | |
| run: cargo build --workspace --locked --target x86_64-pc-windows-gnu | |
| windows-runtime-smoke: | |
| name: Native Windows runtime smoke | |
| if: >- | |
| startsWith(github.ref, 'refs/tags/v') || | |
| (github.event_name == 'workflow_dispatch' && | |
| inputs.windows_runtime_smoke) | |
| runs-on: windows-latest | |
| timeout-minutes: 90 | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 | |
| - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 | |
| with: | |
| toolchain: "1.96.1" | |
| - name: Cache cargo | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 | |
| with: | |
| path: | | |
| ~/.cargo/git | |
| ~/.cargo/registry | |
| target | |
| key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}-${{ hashFiles('Cargo.lock') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}-${{ runner.arch }}-${{ github.job }}- | |
| cargo-${{ runner.os }}-${{ runner.arch }}- | |
| - name: Windows hidden daemon smoke | |
| env: | |
| RMUX_ALLOW_INTERNAL_DAEMON_IN_CALLER_JOB: "1" | |
| run: cargo test --locked --test internal_daemon_windows -- --nocapture | |
| - name: Windows named-pipe IPC smoke | |
| run: cargo test -p rmux-ipc --locked --test named_pipe_integration -- --nocapture |