From b1edc6ad58504b11d2e321331bf07dfaac7d0141 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Sat, 21 Mar 2026 11:56:08 +0000 Subject: [PATCH 1/3] Add release manifest checksum synthesis --- .github/workflows/ci.yml | 4 +- .github/workflows/release.yml | 35 +++++++++++ scripts/patch-dist-manifest-checksums.py | 75 ++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 scripts/patch-dist-manifest-checksums.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 250506aa9306d..3f10bdf7893b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,8 +76,8 @@ jobs: [[ "$file" == "rust-toolchain.toml" || "$file" =~ ^\.cargo/ ]] && rust_config_changed=1 [[ "$file" == "pyproject.toml" || "$file" =~ ^crates/.*/pyproject\.toml$ ]] && python_config_changed=1 [[ "$file" =~ ^\.github/workflows/.*\.yml$ ]] && workflow_changed=1 - [[ "$file" == ".github/workflows/build-release-binaries.yml" ]] && release_workflow_changed=1 - [[ "$file" == "scripts/check_uv_wheel_contents.py" ]] && release_build_changed=1 + [[ "$file" == ".github/workflows/build-release-binaries.yml" || "$file" == ".github/workflows/release.yml" ]] && release_workflow_changed=1 + [[ "$file" == "scripts/check_uv_wheel_contents.py" || "$file" == "scripts/patch-dist-manifest-checksums.py" ]] && release_build_changed=1 [[ "$file" == ".github/workflows/ci.yml" ]] && ci_workflow_changed=1 [[ "$file" == "uv.schema.json" ]] && schema_changed=1 [[ "$file" =~ ^crates/uv-publish/ || "$file" =~ ^scripts/publish/ || "$file" == "crates/uv/src/commands/publish.rs" ]] && publish_code_changed=1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 334840df1af56..fc183359beb0f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -113,12 +113,47 @@ jobs: "id-token": "write" "packages": "write" + synthesize-local-dist-manifest: + needs: + - plan + - custom-build-release-binaries + if: ${{ needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload' || inputs.tag == 'dry-run' }} + runs-on: "depot-ubuntu-latest-4" + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + submodules: recursive + - name: Install cached dist + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 + with: + name: cargo-dist-cache + path: ~/.cargo/bin/ + - run: chmod +x ~/.cargo/bin/dist + - name: Fetch local artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 + with: + pattern: artifacts-* + path: target/distrib/ + merge-multiple: true + - name: Generate local dist manifest + shell: bash + run: | + dist manifest ${{ needs.plan.outputs.tag-flag }} --output-format=json --no-local-paths --artifacts=local > target/distrib/local-dist-manifest.json + python3 scripts/patch-dist-manifest-checksums.py --manifest target/distrib/local-dist-manifest.json --artifacts-dir target/distrib + - name: Upload synthesized local dist manifest + uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47 + with: + name: artifacts-build-local-manifest + path: target/distrib/local-dist-manifest.json + # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - custom-build-release-binaries - custom-build-docker + - synthesize-local-dist-manifest runs-on: "depot-ubuntu-latest-4" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/scripts/patch-dist-manifest-checksums.py b/scripts/patch-dist-manifest-checksums.py new file mode 100644 index 0000000000000..d469b98147459 --- /dev/null +++ b/scripts/patch-dist-manifest-checksums.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Patch cargo-dist local manifest JSON with sidecar SHA-256 checksums. + +This is used by the release workflow when custom local artifact jobs build archives +outside of cargo-dist. cargo-dist's global installer generation can embed archive +checksums if they appear in dist-manifest.json, so we synthesize a local manifest +and then inject the checksums from the uploaded `*.sha256` sidecar files. +""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("--manifest", type=Path, required=True) + parser.add_argument("--artifacts-dir", type=Path, required=True) + return parser.parse_args() + + +def read_sha256(path: Path) -> str: + line = path.read_text(encoding="utf-8").strip() + if not line: + raise ValueError(f"empty checksum file: {path}") + checksum = line.split()[0] + if len(checksum) != 64: + raise ValueError(f"unexpected sha256 length in {path}: {checksum!r}") + return checksum + + +def main() -> int: + args = parse_args() + + manifest = json.loads(args.manifest.read_text(encoding="utf-8")) + artifacts: dict[str, dict] = manifest["artifacts"] + + patched = 0 + skipped = 0 + for checksum_path in sorted(args.artifacts_dir.glob("*.sha256")): + artifact_name = checksum_path.name[: -len(".sha256")] + artifact = artifacts.get(artifact_name) + if artifact is None: + print( + f"warning: checksum file {checksum_path.name} does not match any artifact in {args.manifest.name}", + file=sys.stderr, + ) + skipped += 1 + continue + + checksum = read_sha256(checksum_path) + artifact.setdefault("checksums", {})["sha256"] = checksum + patched += 1 + + if patched == 0: + print( + f"error: no artifact checksums were patched from {args.artifacts_dir}", + file=sys.stderr, + ) + return 1 + + args.manifest.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8") + print( + f"patched {patched} artifact checksum(s) in {args.manifest}" + + (f" ({skipped} checksum file(s) skipped)" if skipped else ""), + file=sys.stderr, + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 93b32a5865f37e341554de7c4dab4d26a5515313 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Sat, 21 Mar 2026 20:35:45 +0000 Subject: [PATCH 2/3] Fix synthesized manifest generation --- .github/workflows/release.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fc183359beb0f..5ec1e2fb63ce1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -139,8 +139,10 @@ jobs: - name: Generate local dist manifest shell: bash run: | - dist manifest ${{ needs.plan.outputs.tag-flag }} --output-format=json --no-local-paths --artifacts=local > target/distrib/local-dist-manifest.json - python3 scripts/patch-dist-manifest-checksums.py --manifest target/distrib/local-dist-manifest.json --artifacts-dir target/distrib + temp_manifest=target/local-dist-manifest.json.tmp + dist manifest ${{ needs.plan.outputs.tag-flag }} --output-format=json --no-local-paths --artifacts=local > "$temp_manifest" + python3 scripts/patch-dist-manifest-checksums.py --manifest "$temp_manifest" --artifacts-dir target/distrib + mv "$temp_manifest" target/distrib/local-dist-manifest.json - name: Upload synthesized local dist manifest uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47 with: From 2e2691f213f61701c378c0c3518fefc501042554 Mon Sep 17 00:00:00 2001 From: Zsolt Dollenstein Date: Mon, 23 Mar 2026 10:45:49 +0000 Subject: [PATCH 3/3] Fix zizmor alert --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5ec1e2fb63ce1..ce30e9c38bd06 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -138,9 +138,11 @@ jobs: merge-multiple: true - name: Generate local dist manifest shell: bash + env: + TAG: ${{ needs.plan.outputs.tag-flag }} run: | temp_manifest=target/local-dist-manifest.json.tmp - dist manifest ${{ needs.plan.outputs.tag-flag }} --output-format=json --no-local-paths --artifacts=local > "$temp_manifest" + dist manifest "$TAG" --output-format=json --no-local-paths --artifacts=local > "$temp_manifest" python3 scripts/patch-dist-manifest-checksums.py --manifest "$temp_manifest" --artifacts-dir target/distrib mv "$temp_manifest" target/distrib/local-dist-manifest.json - name: Upload synthesized local dist manifest