diff --git a/.gitignore b/.gitignore index 106bd4de..d9aa2a11 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ docs/llvm-ppc64le-readvolatile-issue.md proptest-regressions *.proptest-regressions assets/distribution/ +.ruff_cache/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 89eab840..34d0ad64 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,53 +1,131 @@ # Contributing -`rscrypto` is a cryptography primitives crate. Keep changes small, measured, -and scoped to the primitive or tool path they affect. +Use a short-lived feature branch for every change. `main` is the protected, +releasable history; it is not a working branch. -## Before Opening A PR +## What the gates do -Run the narrowest checks that prove the change: +| Action | Purpose | +|---|---| +| Commit | Creates a local, reviewable checkpoint. | +| Push | Shares the branch after fast, change-aware local checks. | +| Pull request | Declares merge intent and runs CI on the proposed change. | +| Merge | Adds the reviewed change to protected `main`. | +| `main` CI | Records proof for the exact commit that can become a release. | + +Pull-request and `main` CI may test the same source tree, but they authorize +different events. Pull-request CI decides whether a change may merge. `main` +CI proves the exact protected-branch commit used by the release gate. + +## Daily branch workflow + +Start from current `main`: + +```bash +git switch main +git pull --ff-only +git switch -c +``` + +Make one focused change. Add a `.changes/*.md` file when crate users will +observe the result: an API, behavior, security, performance, compatibility, or +release-artifact change. Internal-only tooling and maintainer-documentation +changes normally do not need one. + +```bash +just release-change patch "Describe the user-visible result." +``` + +Use `minor` or `major` instead of `patch` when the compatibility impact +requires it. The pre-push check is the final authority on whether release +intent is missing. + +Run checks proportional to the change. Common starting points are: ```bash just check just test ``` -For release-facing or shared changes, run: +For release-facing or broad shared changes, run: ```bash just check-all just test --all ``` -Use the deeper lanes when relevant: +Use deeper checks where the risk requires them: | Change | Required validation | |---|---| | Parser, import, DER, PHC, hex, or untrusted input | `just test-fuzz ` or `just test-fuzz --all` | -| `unsafe`, SIMD, ASM, or dispatch | backend equivalence tests plus `just test-fuzz-asan --all` where the target runs natively | +| `unsafe`, SIMD, ASM, or dispatch | Backend equivalence tests and `just test-fuzz-asan --all` where the target runs natively | | Portable unsafe path | `just test-miri` | -| Constant-time claim boundary | `just ct-full --target ` and update `ct.toml` only with matching evidence | +| Constant-time claim boundary | `just ct-full --target `; update `ct.toml` only with matching evidence | | Public API change | `cargo semver-checks --package rscrypto --all-features` | | Dependency or release change | `cargo deny check all` and `cargo audit --ignore RUSTSEC-2023-0071` | -## Security Boundaries +Inspect and commit only the intended files: + +```bash +git status --short +git diff --check +git add +git diff --cached +git commit -m "module: imperative outcome" +``` + +Push the current branch with its upstream. No extra Git flags are needed: + +```bash +just push +``` + +`just push` runs the light, change-aware pre-push plan once and then pushes the +current branch. No rscrypto Git-hook installation is required. Use `just +push-full` when the change is unusually broad or release-sensitive. + +Open the pull request. A branch push alone does not start the normal PR suite: + +```bash +gh pr create --base main --fill +``` + +Wait for the required `Complete` check. Because the repository currently has +one maintainer, no second approval is required; review the final diff yourself, +resolve any open threads, and merge in the GitHub UI. + +After the merge: + +```bash +git switch main +git pull --ff-only +git branch -d +``` + +GitHub can delete the remote branch during the merge. Do not create release +tags during daily development. + +## Security boundaries -Do not broaden constant-time, FIPS, audit, or compliance claims without matching -evidence. The constant-time boundary is the `ct_claimed` set in -[`ct.toml`](ct.toml), interpreted by [`docs/constant-time.md`](docs/constant-time.md). -The external audit entry point is [`THREAT_MODEL.md`](THREAT_MODEL.md). +Do not broaden constant-time, FIPS, audit, or compliance claims without +matching evidence. The constant-time boundary is the `ct_claimed` set in +[`ct.toml`](ct.toml), interpreted by +[`docs/constant-time.md`](docs/constant-time.md). The external audit entry +point is [`THREAT_MODEL.md`](THREAT_MODEL.md). -Report real vulnerabilities through GitHub Private Vulnerability Reporting, not -public issues. See [`SECURITY.md`](SECURITY.md). +Report real vulnerabilities through GitHub Private Vulnerability Reporting, +not public issues. See [`SECURITY.md`](SECURITY.md). -## Fuzz Corpus +## Fuzz corpus Fuzz targets live in [`fuzz/`](fuzz/) and feature-scoped packages live in [`fuzz-packages/`](fuzz-packages/). Commit small, stable corpus seeds that exercise real parser or primitive paths. Do not commit `target/`, `artifacts/`, coverage output, crashers, or bulk local corpus output without minimization. -## Release Changes +## Releases -Normal releases are tag-triggered and CI-published. Do not run `cargo publish` -locally for a normal release. Use [`docs/release.md`](docs/release.md). +Releases add stronger identity, evidence, and publication gates to this daily +loop. Follow the canonical [release runbook](docs/release.md); do not publish +with a local crates.io token. diff --git a/README.md b/README.md index e21b02b8..00ce9b25 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,8 @@ or [`SECURITY.md`](SECURITY.md), not public issues. [`docs/platforms.md`](docs/platforms.md), [`benchmark_results/OVERVIEW.md`](benchmark_results/OVERVIEW.md) - Switching crates: [`docs/migration/`](docs/migration/) -- Releases: [`CHANGELOG.md`](CHANGELOG.md) +- Contributing: [`CONTRIBUTING.md`](CONTRIBUTING.md) +- Releases: [`CHANGELOG.md`](CHANGELOG.md), [`docs/release.md`](docs/release.md) ## MSRV diff --git a/docs/release.md b/docs/release.md index 3a46dbbd..2ce25f6d 100644 --- a/docs/release.md +++ b/docs/release.md @@ -1,13 +1,92 @@ # Release Process -`rscrypto` releases are human-tagged and CI-published. +`rscrypto` releases are approved by the maintainer and published by CI. +`cargo-rail` prepares a release pull request and creates the signed tag. GitHub +Actions builds, attests, and publishes the artifacts. A normal release must +never run `cargo publish` locally. -`cargo-rail` owns the local release mutation: reviewed change file, version -bump, changelog, release commit, signed tag, and tag push. GitHub Actions owns -crates.io publishing and the GitHub Release. Do not run `cargo publish` -locally for normal releases. +## Release at a glance -## One-Time Setup +1. Start from a clean, current `main` after all intended feature pull requests + have merged. Every user-visible change must already have a reviewed + `.changes/*.md` file. + + ```bash + git switch main + git pull --ff-only + git status --short + just release-check + ``` + +2. Prepare the release: + + ```bash + just release-prepare + ``` + + This creates a `rail/release-*` branch, commits the generated version and + changelog, opens a pull request, refreshes the standalone constant-time tool + lockfiles, and pushes that follow-up commit. It does not tag or publish. + +3. Wait for the release pull request's required `Complete` check. Review the + version, changelog, and lockfile diff, then merge it in the GitHub UI. + +4. Record the exact merged release candidate: + + ```bash + git switch main + git pull --ff-only + candidate=$(git rev-parse HEAD) + ``` + +5. Wait for `main` CI to pass, then dispatch the expensive release evidence on + that commit. Do this before another pull request merges into `main`. + + ```bash + gh workflow run weekly.yaml --ref main + gh workflow run riscv.yaml --ref main -f mode=evidence + ``` + + Confirm that both runs report `$candidate` as their head SHA. If code, + dependencies, features, build inputs, or test policy change afterward, rerun + both workflows. + +6. After exact-commit CI and both evidence workflows are green, create and push + the signed tag: + + ```bash + test "$(git rev-parse HEAD)" = "$candidate" + just release-tag + ``` + + `release-tag` rechecks live repository controls, exact-commit CI, and release + evidence before allowing the tag. It never publishes to crates.io locally. + +7. The tag starts the `Release` workflow. Approve its `crates-io` environment + job after the prerequisite jobs pass. CI publishes and verifies the immutable + GitHub Release before publishing the same crate through crates.io Trusted + Publishing. + +8. Run the commands in [Verification](#verification). + +## Why each gate exists + +| Gate | What it prevents | +|---|---| +| Release pull request | An unreviewed version or changelog mutation reaching protected `main`. | +| `main` CI | Tagging a commit that was never proven in its final protected-branch identity. | +| Weekly and RISC-V evidence | Publishing cryptographic claims without the required platform and timing evidence. | +| Signed immutable tag | Moving a released version to different source later. | +| Immutable, attested GitHub Release | Publishing artifacts that cannot be tied back to the tag and build. | +| Environment approval | A tag or compromised workflow publishing to crates.io without a final human decision. | +| Trusted Publishing | Long-lived crates.io credentials becoming a repository secret. | + +The repeated pull-request and `main` checks are intentional. The first answers +"may this change merge?" The second proves "this exact protected-branch commit +may become a release." The expensive Weekly and RISC-V evidence is not repeated +on every pull request; it runs only for a release candidate. + +## One-time setup Configure the crate on crates.io: @@ -24,174 +103,84 @@ token publishing for new versions. Configure the GitHub repository: -1. Create the active `protect-main` branch ruleset described by - [`.github/rulesets/protect-main.json`](../.github/rulesets/protect-main.json), with no bypass actors. -2. Create the active `protect-release-tags` tag ruleset described by - [`.github/rulesets/protect-release-tags.json`](../.github/rulesets/protect-release-tags.json), with no bypass - actors. It permits a new `v*` tag but prevents updating or deleting an existing one. -3. In **Settings → General → Releases**, enable release immutability as described by - [`.github/repository-settings/release-immutability.json`](../.github/repository-settings/release-immutability.json). +1. Activate the `protect-main` branch ruleset described by + [`.github/rulesets/protect-main.json`](../.github/rulesets/protect-main.json), + with no bypass actors. +2. Activate the `protect-release-tags` tag ruleset described by + [`.github/rulesets/protect-release-tags.json`](../.github/rulesets/protect-release-tags.json), + with no bypass actors. It permits a new `v*` tag but prevents updating or + deleting an existing one. +3. In **Settings → General → Releases**, enable release immutability as described + by [`.github/repository-settings/release-immutability.json`](../.github/repository-settings/release-immutability.json). It applies only to releases published after the setting is enabled. -4. Create an environment named `crates-io`. -5. Add the current maintainer as its required reviewer. While the project has one maintainer, permit self-review; - enable independent review only after a second trusted maintainer exists. -6. Keep long-lived crates.io publish tokens out of repository secrets. - -The environment name must match both crates.io and -[`.github/workflows/release.yaml`](../.github/workflows/release.yaml). If it -does not match, `rust-lang/crates-io-auth-action` cannot exchange the GitHub -OIDC token for a temporary crates.io token. - -## Release Commands - -Add release intent with a cargo-rail change file. Commit the change file with -the code change when possible; if the code was already committed, add the -change file before cutting the release. Change files live in `.changes/`. -Use `--name` to choose the filename slug. The file may be renamed after -creation as long as it remains a `.changes/*.md` file. Changelog sections use -only these reviewed change-file bodies; commit subjects are engineering history, -not release notes. +4. Create an environment named `crates-io` and add the current maintainer as its + required reviewer. Permit self-review while the project has one maintainer, + but disable administrator bypass. Require independent approval after a second + trusted maintainer exists. +5. Keep long-lived crates.io publish tokens out of repository secrets. -```bash -cargo rail change add rscrypto --bump patch --name concise-change-name --message "Describe the user-visible change." -cargo rail change status -``` +The environment name must match crates.io and +[`.github/workflows/release.yaml`](../.github/workflows/release.yaml), or the +OIDC token exchange will fail. -Use `--bump minor` or `--bump major` for larger user-visible changes. - -Run the pre-release checks from a clean tree: - -```bash -git status --short -cargo rail config validate --strict -cargo rail config sync --check -cargo rail unify --check --explain -cargo rail release check rscrypto --extended -cargo rail release run rscrypto --bump auto --skip-publish --check -``` +## Release intent -The same flow is available through the justfile: +Commit a cargo-rail change file with each user-visible change when possible. +Change files live in `.changes/`; their reviewed bodies become the changelog. +Commit subjects remain engineering history, not release notes. ```bash -just release-change patch "Fixed ECDSA oracle compatibility with p256." +just release-change patch "Describe the user-visible result." just release-status -just release-check ``` -Pull-request CI uses cargo-rail-action v5 with cargo-rail 0.17.3. The action's -surface outputs decide whether the single-crate CI suite is required, and its -resolved base ref feeds `cargo rail change check --required`. Execution scope -comes from the planner contract; workflows and scripts must not reconstruct it -from diagnostic impact fields. +Use `minor` or `major` when compatibility requires it. Before preparing a +release, `just release-check` validates configuration, dependency unification, +pending intent, SemVer advice, and the generated release plan. -Compiler-backed unification runs in the dedicated blocking Cargo Graph -Assurance job, not the fast Quality lane. The planner selects it for Cargo, -Rust source, build, test, bench, example, toolchain, or rail configuration -changes. Pushes to `main`, Weekly, and release preflight always run the complete -19-target proof. The job retains cargo-rail's structured JSON result and its -identity-validated compiler-evidence cache. - -When the plan is correct, prepare the release candidate without creating a -tag: - -```bash -just release-prepare -``` - -`release-prepare` lets cargo-rail create and push the version/changelog release -commit with `--skip-tag`, refreshes all standalone CT tool lockfiles against -that new version, and pushes one normal `workspace:` follow-up commit. This -keeps the release candidate reproducible without modifying a signed tag or -hand-editing generated lockfiles. - -Wait for CI on the resulting `main` commit, then manually dispatch -`weekly.yaml` and the `evidence` mode of `riscv.yaml` on that exact commit. -Weekly owns the non-RISC-V CT matrix and dedicated RSA workflow; RISC-V owns -its native and CT evidence. Both retain the raw CT artifacts needed for the -release bundle. If runtime code, dependencies, features, build inputs, or test -policy changes, both evidence workflows must pass again before tagging. - -A release-tooling-only repair may promote the newest successful ancestor with -paired Weekly and RISC-V evidence. The evidence checker permits only changelog, -release/CT tooling, and root-package version changes. It parses and compares -`Cargo.toml` and every CT lockfile after normalizing only the local `rscrypto` -version, and rejects every other changed path. The evidence bundle records both -the release commit and the promoted evidence commit. This exception does not -apply to runtime, dependency, feature, build, or test changes. - -Once CI and both evidence workflows are green, finalize the release: - -```bash -just release-tag -``` +Pull-request CI uses cargo-rail's planner to select checks from the actual +changed surfaces. Pushes to `main`, Weekly, and release preflight run the full +Cargo graph proof because those commits can become release inputs. -The `release-tag` recipe reruns strict configuration and Cargo-graph validation, -uses the maintainer's GitHub access to check the complete live ruleset—including -the empty bypass list—against the checked-in policy, requires successful push CI -and Cargo Graph Assurance for the exact candidate, and refuses to tag without -complete CT and RSA evidence from either that commit or a proven release-only -ancestor. It then uses `cargo rail release finalize` to validate the materialized -release, create the signed tag, and push it. It does not rerun `cargo rail release -check`: that command validates pending release intent and correctly fails after -`release-prepare` consumes the change files. Finalization must not publish to -crates.io; the `--skip-publish` flag is part of the release contract. +`release-prepare` consumes the change files. After its pull request merges, +`release-tag` deliberately does not rerun the pending-intent check. Instead, it +validates the materialized release and uses `cargo rail release finalize +--skip-publish` to create and push the signed tag. -To check GitHub controls without running the release flow, use: +To inspect live repository controls without starting a release: ```bash just check-repository-controls ``` -This is the only routine local check that reads live GitHub settings. Normal -checks and pre-push validation remain offline. The command writes the captured -JSON to `target/repository-controls.json` and prints its SHA-256. - -## CI Release Gate - -Pushing a `vX.Y.Z` tag starts the `Release` workflow. The workflow: - -1. Requires an annotated SSH-signed tag trusted by - [`.github/allowed-signers`](../.github/allowed-signers). -2. Requires the tag target to match the checked-out commit. -3. Requires the tag version, `Cargo.toml` version, and `CHANGELOG.md` heading - to match. -4. Validates and sync-checks `.config/rail.toml`, then proves the unified Cargo - graph is clean with cargo-rail 0.17.3. -5. Runs `cargo deny check all`, `cargo audit`, and the single hard SemVer check - against the finalized release version. Before tagging, cargo-rail's automatic - release plan uses compatibility analysis to select the required version bump; - the extended release check reports the advisory verdict. -6. Builds the `.crate` once with `cargo package --locked` and creates a deterministic source archive from the exact - tag commit, then transfers both validated artifacts to the publish job. -7. Rejects dirty or mismatched VCS metadata, private or local-only package contents, and source archives that cannot - be reproduced from the tag commit. -8. Waits for the `CI` workflow on the same commit to pass. -9. Verifies both transferred SHA-256 values, then attests the crate and source archive with GitHub build provenance. -10. Compares every rule visible to the restricted workflow token with the - checked-in default-branch and release-tag policies and attests the resulting repository controls JSON. GitHub - redacts bypass actors from this token; the artifact labels those fields as redacted instead of pretending the - workflow reverified the pre-tag gate. -11. Requires successful Weekly CT/RSA evidence and RISC-V native/CT evidence - from the exact tag commit or the same mechanically proven release-only - ancestor, then downloads raw CT artifacts from both runs. The tag workflow - does not rerun either multi-hour suite. -12. Validates the evidence commit's CT lane set, version, clean provenance, - tool versions, timing coverage, BINSEC coverage, and raw artifact hashes. - The bundle separately records the release commit and evidence commit. -13. Attests the CT evidence bundle with GitHub build provenance. -14. Writes and attests a release identity manifest binding the tag, commit, tree, pinned and active Rust toolchain, - Cargo lockfile, release workflow, source archive, crate, CT evidence, and repository controls. -15. Writes and attests `SHA256SUMS` for every published release asset. -16. Creates a draft GitHub Release, attaches every asset, publishes it as an immutable release, and verifies GitHub's - release attestation before any irreversible registry change. A rerun may repair an unpublished draft, but an - already-published release is verified instead of modified. -17. Uses crates.io Trusted Publishing to get a temporary publish token. -18. Publishes with `cargo publish -p rscrypto --locked`. -19. Downloads the crate from crates.io and verifies the SHA-256 against the attested package. - -The publish job uses the `crates-io` GitHub environment. GitHub requests -approval only after preflight and CI have passed, and before the OIDC token is -issued. +This is the only routine local check that reads live GitHub settings. It writes +the captured JSON to `target/repository-controls.json`; normal checks and +pre-push validation remain offline. + +## What the tag workflow proves + +Pushing a `vX.Y.Z` tag starts the `Release` workflow. Before crates.io can +receive anything, the workflow: + +1. Verifies the annotated SSH signature, tag target, crate version, and + changelog version. +2. Revalidates configuration, the unified Cargo graph, dependency policy, + audit results, SemVer, and successful CI for the exact commit. +3. Requires complete Weekly CT/RSA and RISC-V native/CT evidence from that + commit or a mechanically proven release-tooling-only ancestor. +4. Builds the `.crate` once, reproduces the source archive from the tag, and + rejects dirty, private, local-only, or mismatched package contents. +5. Captures repository controls and writes provenance attestations, an identity + manifest, and `SHA256SUMS` for the artifacts and evidence. +6. Publishes and verifies the immutable GitHub Release, obtains a temporary + crates.io token through OIDC, publishes the same crate, then downloads it + from crates.io and verifies its SHA-256. + +A release-tooling-only repair may reuse the newest successful ancestor with +paired Weekly and RISC-V evidence. The checker permits only changelog, +release/CT tooling, root-package version, and normalized local CT lockfile +version changes. Runtime, dependency, feature, build, or test changes invalidate +that exception. ## Recovery diff --git a/justfile b/justfile index 540b543e..05d8e1d0 100644 --- a/justfile +++ b/justfile @@ -216,7 +216,7 @@ check-repository-controls: release-prepare: just check-unify cargo rail release check rscrypto --extended - RSCRYPTO_RELEASE_PUSH=1 cargo rail release run rscrypto --bump auto --yes --skip-publish --skip-tag + cargo rail release run rscrypto --bump auto --yes --pr cargo update --manifest-path tools/ct-harness/Cargo.toml -p rscrypto cargo update --manifest-path tools/ct-dudect/Cargo.toml -p rscrypto cargo update --manifest-path tools/ct-binsec-harness/Cargo.toml -p rscrypto @@ -260,13 +260,13 @@ check-actions: @actionlint @zizmor .github/workflows .github/actions -push *args="": +push remote="origin": @scripts/ci/pre-push.sh --light - git push --no-verify {{ args }} + git push --set-upstream "{{ remote }}" HEAD -push-full *args="": +push-full remote="origin": @scripts/ci/pre-push.sh --full - git push --no-verify {{ args }} + git push --set-upstream "{{ remote }}" HEAD # Assets diff --git a/scripts/README.md b/scripts/README.md index dd426b9b..ce614c49 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -39,7 +39,7 @@ its caller. | `ci/release-recipes-test.sh` | `just check-actions` | | `ci/upgrade-actions.sh` | `scripts/update/update-all.sh` | | `ci/upgrade-actions-test.sh` | `just check-actions` | -| `ci/pre-push.sh` | `just push`, `just push-full`, optional `.git/hooks/pre-push` | +| `ci/pre-push.sh` | `just push`, `just push-full` | | `update/update-all.sh` | `just update`, `just update-check` | ## Cross-platform Check Helpers diff --git a/scripts/ci/pre-push-test.sh b/scripts/ci/pre-push-test.sh index 6bf9bd26..f12f49a8 100755 --- a/scripts/ci/pre-push-test.sh +++ b/scripts/ci/pre-push-test.sh @@ -41,8 +41,8 @@ if ( PATH="$fake_bin:$PATH" \ MOCK_LOG="$mock_log" \ RAIL_PLAN_JSON_CACHE="$plan" \ - RAIL_SCOPE_JSON= \ - RAIL_SCOPE_JSON_CACHE= \ + RAIL_SCOPE_JSON='' \ + RAIL_SCOPE_JSON_CACHE='' \ scripts/ci/pre-push.sh --light ) >"$normal_output" 2>&1; then echo "ordinary pushes must fail when release intent coverage fails" >&2 @@ -50,26 +50,4 @@ if ( fi grep -Fq "cargo rail change check --merge-base --required" "$mock_log" -: >"$mock_log" -release_output="$TMP_ROOT/release.out" -( - cd "$fixture" - HOME="$TMP_ROOT/home" \ - PATH="$fake_bin:$PATH" \ - MOCK_LOG="$mock_log" \ - RAIL_PLAN_JSON_CACHE="$plan" \ - RAIL_SCOPE_JSON= \ - RAIL_SCOPE_JSON_CACHE= \ - RSCRYPTO_RELEASE_PUSH=1 \ - scripts/ci/pre-push.sh --light -) >"$release_output" 2>&1 - -if grep -Fq "cargo rail change check --merge-base --required" "$mock_log"; then - echo "cargo-rail release pushes must not recheck consumed change files" >&2 - exit 1 -fi -grep -Fq "cargo rail unify --check --explain" "$mock_log" -grep -Fq "just check" "$mock_log" -grep -Fq "cargo-rail validated and consumed the release change files" "$release_output" - -echo "Pre-push release regression tests passed" +echo "Pre-push regression tests passed" diff --git a/scripts/ci/pre-push.sh b/scripts/ci/pre-push.sh index ba41b73a..19179217 100755 --- a/scripts/ci/pre-push.sh +++ b/scripts/ci/pre-push.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -# Pre-push hook: quality checks before allowing push. -# Install: -# ln -sf ../../scripts/ci/pre-push.sh .git/hooks/pre-push +# Change-aware quality checks used by the supported push recipes. # Profiles: # --light skip the exhaustive rscrypto feature matrix (default) # --full preserve the existing full host lane @@ -269,11 +267,7 @@ fi if [[ "$RAIL_READY" != true ]] || rail_surface_is_enabled build || rail_surface_is_enabled test; then start_task "cargo-rail unify" run_rail_unify_check - if [[ "${RSCRYPTO_RELEASE_PUSH:-}" == "1" ]]; then - skip "Release intent coverage" "cargo-rail validated and consumed the release change files" - else - start_task "release intent coverage" run_rail_change_check - fi + start_task "release intent coverage" run_rail_change_check else skip "Cargo-rail unify" "no build/test surface" skip "Release intent coverage" "no build/test surface" diff --git a/scripts/ci/release-plan-check.sh b/scripts/ci/release-plan-check.sh index ce1c1c61..187d13cb 100755 --- a/scripts/ci/release-plan-check.sh +++ b/scripts/ci/release-plan-check.sh @@ -4,7 +4,7 @@ set -euo pipefail crate="${1:-rscrypto}" status=0 -cargo rail release run "$crate" --bump auto --skip-publish --check || status=$? +cargo rail release run "$crate" --bump auto --pr --check || status=$? # cargo-rail uses 1 for check-mode pending mutations. For this local gate, # "a release plan exists" is success; genuine tool errors still fail. diff --git a/scripts/ci/release-recipes-test.sh b/scripts/ci/release-recipes-test.sh index cd7289f5..96ef3531 100755 --- a/scripts/ci/release-recipes-test.sh +++ b/scripts/ci/release-recipes-test.sh @@ -7,12 +7,16 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" prepare_recipe=$(cd "$REPO_ROOT" && just --show release-prepare) tag_recipe=$(cd "$REPO_ROOT" && just --show release-tag) controls_recipe=$(cd "$REPO_ROOT" && just --show check-repository-controls) +push_recipe=$(cd "$REPO_ROOT" && just --dry-run push 2>&1) +push_full_recipe=$(cd "$REPO_ROOT" && just --dry-run push-full 2>&1) grep -Fq "cargo rail release check rscrypto --extended" <<<"$prepare_recipe" -grep -Fq "RSCRYPTO_RELEASE_PUSH=1 cargo rail release run rscrypto --bump auto --yes --skip-publish --skip-tag" \ - <<<"$prepare_recipe" +grep -Fq "cargo rail release run rscrypto --bump auto --yes --pr" <<<"$prepare_recipe" +grep -Fq "git push" <<<"$prepare_recipe" grep -Fq "cargo rail release finalize rscrypto --yes --skip-publish" <<<"$tag_recipe" +# shellcheck disable=SC2016 # Match the literal command rendered by just. grep -Fq 'scripts/ci/release-ci-check.sh --commit "$(git rev-parse HEAD)"' <<<"$tag_recipe" +# shellcheck disable=SC2016 # Match the literal command rendered by just. grep -Fq 'scripts/ci/release-evidence-check.sh --commit "$(git rev-parse HEAD)"' <<<"$tag_recipe" grep -Fq 'just check-repository-controls' <<<"$tag_recipe" grep -Fq 'scripts/ci/repository-controls-evidence.sh' <<<"$controls_recipe" @@ -35,4 +39,17 @@ if grep -Fq "cargo rail release check" <<<"$tag_recipe"; then exit 1 fi +grep -Fq 'scripts/ci/pre-push.sh --light' <<<"$push_recipe" +grep -Fq 'git push --set-upstream "origin" HEAD' <<<"$push_recipe" +grep -Fq 'scripts/ci/pre-push.sh --full' <<<"$push_full_recipe" +grep -Fq 'git push --set-upstream "origin" HEAD' <<<"$push_full_recipe" +if grep -Fq -- '--no-verify' <<<"$push_recipe$push_full_recipe"; then + echo "supported push recipes must not bypass Git hooks" >&2 + exit 1 +fi +if grep -Fq 'RSCRYPTO_PRE_PUSH_' <<<"$push_recipe$push_full_recipe"; then + echo "supported push recipes must not depend on hook coordination state" >&2 + exit 1 +fi + echo "Release recipe regression tests passed"