diff --git a/.github/workflows/release-pr-distribution-acceptance.yml b/.github/workflows/release-pr-distribution-acceptance.yml new file mode 100644 index 0000000000..d0ab026152 --- /dev/null +++ b/.github/workflows/release-pr-distribution-acceptance.yml @@ -0,0 +1,113 @@ +# Run the Linux release battery on the release PR itself. +# +# Twelve beta tags in a row published with zero assets because the first run +# of `scripts/check-distribution-acceptance.sh` happened after release-please +# had already tagged and published the prerelease. PR CI never exercises the +# release-only steps, so a green PR said nothing about whether the tree could +# ship. This workflow runs the same x86_64-linux steps as +# `.github/workflows/release-beta.yml` and `release.yml` on the +# `chore(release)` PR, so a tree that cannot ship never gets a tag. + +name: Release PR distribution acceptance + +on: + pull_request: + branches: + - master + - codex/tracedecay-total-redesign-plan-reopened + +permissions: + contents: read + +concurrency: + group: release-pr-distribution-acceptance-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + distribution-acceptance: + name: Release PR distribution acceptance (x86_64-linux) + if: >- + startsWith(github.head_ref, 'release-please--') && + github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-22.04 + timeout-minutes: 180 + env: + TARGET: x86_64-unknown-linux-gnu + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: "22.23.2" + cache: npm + cache-dependency-path: dashboard/package-lock.json + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12.10" + + - name: Build dashboard + working-directory: dashboard + run: | + npm ci + npm run build + + - name: Validate portable distribution harnesses + run: | + python3 scripts/test-check-packaged-lsp-bridge.py + python3 scripts/test-build-mcpb.py + python3 scripts/test-resolve-installed-binary.py + python3 scripts/test-check-packaged-mcp-stdio.py + python3 scripts/test-check-distribution-feature-wiring.py + python3 scripts/test-resolve-release-source-profile.py + python3 scripts/test-package-release-archive.py + bash scripts/test-check-distribution-clean-source.sh + bash scripts/test-check-distribution-snapshot.sh + + - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable + with: + targets: ${{ env.TARGET }} + + - name: Install cargo-nextest + uses: taiki-e/install-action@acdba816b0980ba6b63f1109f89a046e15fc301a # nextest + + - name: Install mold + uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 + with: + mold-version: 2.41.0 + make-default: true + + - name: Cache Rust build + uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 + with: + shared-key: release-beta-${{ env.TARGET }} + cache-on-failure: true + + - name: Test release distribution + run: | + python3 scripts/build-controlled-workload-hotpath-helpers.py \ + --source . \ + --profile release \ + --target ${{ env.TARGET }} + cargo nextest run -p tracedecay-search-eval --release --target ${{ env.TARGET }} \ + --all-features --locked \ + --run-ignored only \ + -E 'test(=controlled_workloads::tests::hotpath_off_vs_on_durable_results_are_identical)' \ + --no-tests=fail + cargo build --workspace --bins --release --target ${{ env.TARGET }} --all-features --locked + cargo test --workspace --release --target ${{ env.TARGET }} --all-features --locked + + - name: Verify all-feature release build compiles + run: cargo build --package tracedecay-cli --bin tracedecay --release --target ${{ env.TARGET }} --all-features --locked + + - name: Run distribution acceptance + run: | + set -euo pipefail + cargo fetch --locked --target ${{ env.TARGET }} + lockfile_sha="$(python3 -c 'import hashlib; print(hashlib.sha256(open("Cargo.lock", "rb").read()).hexdigest())')" + CARGO_NET_OFFLINE=true bash scripts/check-distribution-acceptance.sh + actual_lockfile_sha="$(python3 -c 'import hashlib; print(hashlib.sha256(open("Cargo.lock", "rb").read()).hexdigest())')" + test "$actual_lockfile_sha" = "$lockfile_sha" diff --git a/scripts/check-distribution-acceptance.sh b/scripts/check-distribution-acceptance.sh index 51b5a42e79..47088f96f9 100755 --- a/scripts/check-distribution-acceptance.sh +++ b/scripts/check-distribution-acceptance.sh @@ -567,17 +567,28 @@ for package in sorted(metadata["packages"], key=lambda value: value["name"]): path = packages / f'{package["name"]}-{package["version"]}' print(f'{json.dumps(package["name"])} = {{ path = {json.dumps(str(path))} }}') # Packaged crates resolve outside the workspace, so the workspace manifest's -# git patches (e.g. the pinned tree-sitter-rust fork) must be re-applied here -# for the extracted trees to see the same patched dependencies. +# patches must be re-applied here for the extracted trees to see the same +# patched dependencies: git patches (e.g. the pinned tree-sitter-rust fork) +# verbatim, and path patches (e.g. the vendored hotpath-macros) against the +# staged workspace root, which carries the same `vendor` tree. +staged_root = pathlib.Path(sys.argv[3]).parent for name, spec in workspace_manifest.get("patch", {}).get("crates-io", {}).items(): - if not isinstance(spec, dict) or "git" not in spec: + if not isinstance(spec, dict): continue - entry = f'{json.dumps(name)} = {{ git = {json.dumps(spec["git"])}' - for key in ("rev", "branch", "tag"): - if key in spec: - entry += f", {key} = {json.dumps(spec[key])}" - entry += " }" - print(entry) + if "git" in spec: + entry = f'{json.dumps(name)} = {{ git = {json.dumps(spec["git"])}' + for key in ("rev", "branch", "tag"): + if key in spec: + entry += f", {key} = {json.dumps(spec[key])}" + entry += " }" + print(entry) + elif "path" in spec: + patched = staged_root / spec["path"] + if not (patched / "Cargo.toml").is_file(): + raise SystemExit( + f"distribution acceptance: workspace path patch {name} is not staged at {patched}" + ) + print(f'{json.dumps(name)} = {{ path = {json.dumps(str(patched))} }}') PY verify_feature_wiring \