Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions scripts/check-release-pr-integrity.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#!/usr/bin/env bash
# Validates a release PR: only release metadata changed, and Cargo.lock matches
# the bumped manifests. Before merging a release PR by hand, run it with the PR
# head checked out: scripts/check-release-pr-integrity.sh origin/master HEAD
set -euo pipefail

usage() {
Expand Down Expand Up @@ -70,3 +73,45 @@ if ((${#unexpected[@]})); then
echo "release PR integrity: explicitly approved extra paths:" >&2
printf ' %s\n' "${unexpected[@]}" >&2
fi

# Release-please rewrites the release branch on every master push, dropping
# the lockfile commit, and every `--locked` build fails once a lagging lock
# merges. Compare each local package's manifest version with its lock entry.
python3 - <<'PY'
import glob
import sys
import tomllib
from pathlib import Path


def load(path):
return tomllib.loads(Path(path).read_text())


root = load("Cargo.toml")
workspace = root.get("workspace", {})
workspace_version = workspace.get("package", {}).get("version")
packages = [root["package"]] if "package" in root else []
for pattern in workspace.get("members", []):
for member in sorted(glob.glob(pattern)):
packages.append(load(Path(member, "Cargo.toml"))["package"])

locked = {
entry["name"]: entry["version"]
for entry in load("Cargo.lock").get("package", [])
if "source" not in entry
}
stale = []
for package in packages:
version = package.get("version", "0.0.0")
if version == {"workspace": True}:
version = workspace_version
if locked.get(package["name"]) != version:
stale.append(f"{package['name']}: Cargo.toml {version}, Cargo.lock {locked.get(package['name'], 'missing')}")
Comment on lines +109 to +110

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate lock consistency instead of only package versions

When the allow-listed root Cargo.toml changes a dependency constraint, patch, or resolver setting such that the existing resolution is stale, this loop still succeeds as long as each local package retains the same version. The release integrity check can therefore approve a lockfile that Cargo must update, recreating the broken locked-build outcome it is intended to prevent; validate the checked-out manifests and lock through Cargo's resolver rather than a name/version source-shape scan.

AGENTS.md reference: AGENTS.md:L172-L177

Useful? React with 👍 / 👎.


if stale:
print("release PR integrity: Cargo.lock disagrees with the manifest versions:", file=sys.stderr)
print("\n".join(f" {line}" for line in stale), file=sys.stderr)
print("Refresh Cargo.lock; on a release branch run scripts/update-release-pr-lockfile.sh.", file=sys.stderr)
sys.exit(1)
PY
28 changes: 27 additions & 1 deletion tests/release_pr_integrity_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ new_repo() {
git -C "$repo" config user.name "Release Guard Test"
git -C "$repo" config user.email "release-guard@example.com"
printf '[package]\nname = "fixture"\nversion = "0.1.0"\n' >"$repo/Cargo.toml"
printf 'version = 3\n' >"$repo/Cargo.lock"
printf 'version = 3\n\n[[package]]\nname = "fixture"\nversion = "0.1.0"\n' >"$repo/Cargo.lock"
printf '# Changelog\n' >"$repo/CHANGELOG.md"
printf '0.1.0\n' >"$repo/version.txt"
printf '{"version":"0.1.0"}\n' >"$repo/server.json"
Expand Down Expand Up @@ -46,6 +46,7 @@ new_repo
base=$(head_sha)
printf '\n## 0.2.0\n' >>"$repo/CHANGELOG.md"
printf '[package]\nname = "fixture"\nversion = "0.2.0"\n' >"$repo/Cargo.toml"
printf 'version = 3\n\n[[package]]\nname = "fixture"\nversion = "0.2.0"\n' >"$repo/Cargo.lock"
printf '0.2.0\n' >"$repo/version.txt"
printf '{"version":"0.2.0"}\n' >"$repo/server.json"
printf '{".":"0.2.0"}\n' >"$repo/.release-please-manifest.json"
Expand Down Expand Up @@ -83,3 +84,28 @@ head=$(head_sha)
run_guard "$head" "$head" --allow-extra-files
gate_expect_failure "tracked ignored files must fail even with extra-file approval"
gate_output_contains "tracked ignored file" "tracked.tmp"

# The master break after the 1.0.0-beta.56 release merge: the workspace
# version moved but the lock still recorded the inheriting members at beta.55.
Comment on lines +88 to +89

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Describe the invariant instead of the historical incident

This comment anchors a durable regression test to a specific master incident and beta release, so it becomes stale release-history narration rather than explaining why the fixture matters. Rephrase it around the invariant that workspace-inherited versions must agree with their lock entries, leaving the incident provenance in commit history.

AGENTS.md reference: AGENTS.md:L195-L197

Useful? React with 👍 / 👎.

write_workspace_release() {
local lock_version=$1
mkdir -p "$repo/crates/tracedecay" "$repo/crates/tracedecay-api"
printf '[workspace]\nmembers = ["crates/tracedecay", "crates/tracedecay-api"]\n\n[workspace.package]\nversion = "1.0.0-beta.56"\n' >"$repo/Cargo.toml"
printf '[package]\nname = "tracedecay"\nversion.workspace = true\n' >"$repo/crates/tracedecay/Cargo.toml"
printf '[package]\nname = "tracedecay-api"\nversion = "0.1.0"\n' >"$repo/crates/tracedecay-api/Cargo.toml"
printf 'version = 4\n\n[[package]]\nname = "serde"\nversion = "1.0.0"\nsource = "registry+https://github.com/rust-lang/crates.io-index"\n\n[[package]]\nname = "tracedecay"\nversion = "%s"\n\n[[package]]\nname = "tracedecay-api"\nversion = "0.1.0"\n' "$lock_version" >"$repo/Cargo.lock"
commit_all "workspace release with lock at $lock_version"
}

new_repo
base=$(head_sha)
write_workspace_release 1.0.0-beta.55
run_guard "$base" "$(head_sha)" --allow-extra-files
gate_expect_failure "a lock lagging the workspace version must fail"
gate_output_contains "lagging lock" "tracedecay: Cargo.toml 1.0.0-beta.56, Cargo.lock 1.0.0-beta.55"

new_repo
base=$(head_sha)
write_workspace_release 1.0.0-beta.56
run_guard "$base" "$(head_sha)" --allow-extra-files
gate_expect_success "a lock synced to the workspace version"
Loading