diff --git a/.github/workflows/actions.lock b/.github/workflows/actions.lock index 403b20e..746dbf0 100644 --- a/.github/workflows/actions.lock +++ b/.github/workflows/actions.lock @@ -146,7 +146,7 @@ dependencies: owner_id: 44036562 repo_id: 192625955 'actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9': - ref: 'v7.0.2' + ref: 'cf430e030ddbb5b0abf93d22962f4752f3646cd9' commit: 'sha1-cf430e030ddbb5b0abf93d22962f4752f3646cd9' owner_id: 44036562 repo_id: 192625955 @@ -163,7 +163,7 @@ dependencies: uses: - 'actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f' 'denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed': - ref: 'v2.0.5' + ref: '22d081ff2d3a40755e97629de92e3bcbfa7cf2ed' commit: 'sha1-22d081ff2d3a40755e97629de92e3bcbfa7cf2ed' owner_id: 42048915 repo_id: 356423100 @@ -173,7 +173,7 @@ dependencies: owner_id: 27347476 repo_id: 371068214 'dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f': - ref: 'v1' + ref: 'e2a55d2ffb04f378e9626c28d38b36d230d1e12f' commit: 'sha1-e2a55d2ffb04f378e9626c28d38b36d230d1e12f' owner_id: 1940490 repo_id: 260749683 @@ -265,7 +265,7 @@ dependencies: owner_id: 67707773 repo_id: 421101922 'oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6': - ref: 'v2.2.0' + ref: '0c5077e51419868618aeaa5fe8019c62421857d6' commit: 'sha1-0c5077e51419868618aeaa5fe8019c62421857d6' owner_id: 108928776 repo_id: 512644635 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index ff00f43..77510c4 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -21,6 +21,7 @@ jobs: # without security-events: write here the scorecard SARIF upload fails with # startup_failure (hypatia WF018). id-token: write enables OIDC publish. permissions: + actions: read contents: read security-events: write id-token: write diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index d8aa1d3..744f792 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,21 +1,20 @@ == Changelog -== 2026-10-14 — minted, and the estate checks made green - -* Minted with `just repo-init`. The repository had been created from - rsr-template-repo df09a6c with GitHub's template copy and never minted, - so it still declared itself the template. The deed is now - `resource-algebra_chora.deed` (clade `fv`), placeholders are filled, - and CLAUDE.md and the repository map describe this repository. -* `build/just/repo-init.just`: the non-interactive mint now completes; - just's own interpolation, the arrival pack, the repository map, the - state-validation detector and the OpenSSF audit table survive the mint. -* `scripts/check-agda.sh`: each expected rejection must land on the - fixture's `invalid` definition. -* `PROOF-STATUS.adoc` moved to `docs/status/PROOF-STATUS.adoc`. -* CI: the standards reusables re-pinned to standards main, with a closed - `actions.lock`; `CITATION.cff` gained its required `message`. +== 2026-10-14 — governance pin `da2c748` → `934b879` + +* The six standards reusables (governance, hypatia-scan, mirror, rust-ci, + scorecard, secret-scanner) move to hyperpolymath/standards `934b879`. + At `da2c748`, governance ran its lock gate from a fixed helper pin with an + unpinned gh-actions-lock. Under v0.2.0 that gate failed *Actions lockfile + verify* on every job-level reusable ref, although it had accepted each one + as coverage. The new pin runs the gate at its own commit, with v0.2.0 + pinned (standards#1250). +* `actions.lock`: the `hyperpolymath/standards` record is rewritten at the + new pin with its called workflows' `uses:` union, and closed over the four + refs that union adds (setup-bun, setup-deno, upload-artifact, + rust-toolchain). The old record and the three refs only it named are + removed. == 2026-10-14 — MinMax and morphisms (resource-algebra#3) diff --git a/tests/workflows/actions_lock_sha_refs_test.sh b/tests/workflows/actions_lock_sha_refs_test.sh new file mode 100755 index 0000000..20218c0 --- /dev/null +++ b/tests/workflows/actions_lock_sha_refs_test.sh @@ -0,0 +1,116 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# +# PR #7 regression: the four SHA-keyed leaves added by standards must keep +# their immutable ref, rather than the tag gh-actions-lock assigned to them. +# Separate tag-keyed records remain valid. This test is offline. +# Usage: bash tests/workflows/actions_lock_sha_refs_test.sh [workflow-directory] +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +WF_DIR="${1:-$REPO/.github/workflows}" +LOCK="$WF_DIR/actions.lock" +FIXTURE=$(mktemp -d) +trap 'rm -rf "$FIXTURE"' EXIT + +fail() { echo "FAIL: $*" >&2; exit 1; } +command -v jq >/dev/null || fail "jq is required" +yq --version | grep -q 'github.com/mikefarah/yq/' || fail "mikefarah yq v4 is required" +yq -o=json '.' "$LOCK" > "$FIXTURE/lock.json" + +# Use the standards pin actually called by governance, so a later standards +# update does not require copying its commit into this test. +STANDARDS=$(jq -er ' + [.workflows[".github/workflows/governance.yml"][] + | select(startswith("hyperpolymath/standards@"))] + | select(length == 1) | .[0] +' "$FIXTURE/lock.json") || fail "governance must lock exactly one standards reusable" + +# Assertions on parsed data, independent of the sync gate (which checks edges +# but deliberately does not inspect ref/commit metadata). +immutable_record() { + local key="$1" file="$2" + jq -e --arg key "$key" ' + ($key | split("@")[1]) as $sha + | .dependencies[$key] as $record + | ($sha | test("^[0-9a-f]{40}$")) + and ($record.ref == $sha) + and ($record.commit == ("sha1-" + $sha)) + ' "$file" >/dev/null +} + +# Run the production gate on the real workflows before negative controls, so +# an unrelated fixture/setup failure cannot masquerade as a useful rejection. +bash "$REPO/scripts/check-lock-sync.sh" "$WF_DIR" > "$FIXTURE/sync.log" 2>&1 \ + || { cat "$FIXTURE/sync.log" >&2; fail "the current lock must be in sync and closed"; } +mkdir -p "$FIXTURE/workflows" +cp -a "$WF_DIR/." "$FIXTURE/workflows/" + +checked=0 +while IFS='|' read -r action former_ref owner_id repo_id; do + key=$(jq -er --arg standards "$STANDARDS" --arg action "$action" ' + [.dependencies[$standards].uses[] | select(startswith($action + "@"))] + | select(length == 1) | .[0] + ' "$FIXTURE/lock.json") || fail "$action must occur exactly once in standards transitive coverage" + + immutable_record "$key" "$FIXTURE/lock.json" \ + || fail "$key must have matching immutable ref and commit" + jq -e --arg key "$key" --argjson owner "$owner_id" --argjson repo "$repo_id" ' + .dependencies[$key] | .owner_id == $owner and .repo_id == $repo + ' "$FIXTURE/lock.json" >/dev/null || fail "$key lost its repository identity" + + # Reproduce each of the four exact pre-PR tag substitutions. Even though + # the key and commit still carry the SHA, ref must not float to a tag. + jq --arg key "$key" --arg tag "$former_ref" ' + .dependencies[$key].ref = $tag + ' "$FIXTURE/lock.json" > "$FIXTURE/mutated.json" + if immutable_record "$key" "$FIXTURE/mutated.json"; then + fail "$key accepted its former floating ref $former_ref" + fi + + # A valid-looking, different SHA is still a broken key/ref/commit binding. + jq --arg key "$key" ' + .dependencies[$key].commit = "sha1-0000000000000000000000000000000000000000" + ' "$FIXTURE/lock.json" > "$FIXTURE/mutated.json" + if immutable_record "$key" "$FIXTURE/mutated.json"; then + fail "$key accepted a mismatched commit" + fi + + # Delete only this leaf record, keeping its incoming standards edge and + # all other workflow/lock data intact. Preserve gh-actions-lock layout: + # the production gate parses that layout, not yq's reformatted output. + awk -v key="$key" ' + $0 == " \047" key "\047:" { skip = 1; next } + skip && /^ \047/ { skip = 0 } + !skip { print } + ' "$LOCK" > "$FIXTURE/workflows/actions.lock" + if bash "$REPO/scripts/check-lock-sync.sh" "$FIXTURE/workflows" > "$FIXTURE/sync.log" 2>&1; then + fail "sync gate accepted a missing record for $key" + fi + if ! grep -qF 'DANGLING EDGES' "$FIXTURE/sync.log" \ + || ! grep -qF "$key" "$FIXTURE/sync.log"; then + cat "$FIXTURE/sync.log" >&2 + fail "sync gate did not identify the missing $key" + fi + + # upload-artifact and setup-bun also have direct tag users. Their records + # must coexist with the SHA records; the repair must not globally ban tags. + if [[ "$action" == actions/upload-artifact || "$action" == oven-sh/setup-bun ]]; then + jq -e --arg key "$key" --arg alias "$action@$former_ref" --arg tag "$former_ref" ' + .dependencies[$alias].ref == $tag + and (.dependencies[$alias].commit == .dependencies[$key].commit) + and ([.workflows[][] | select(. == $alias)] | length > 0) + ' "$FIXTURE/lock.json" >/dev/null || fail "$action lost its separate referenced tag record" + fi + + checked=$((checked + 1)) + echo "PASS: $action SHA binding, identity, negative controls and closure" +done <<'CASES' +actions/upload-artifact|v7.0.2|44036562|192625955 +denoland/setup-deno|v2.0.5|42048915|356423100 +dtolnay/rust-toolchain|v1|1940490|260749683 +oven-sh/setup-bun|v2.2.0|108928776|512644635 +CASES + +[[ "$checked" == 4 ]] || fail "expected to check all four changed dependency records" +echo "PASS: actions.lock SHA-ref regression controls ($checked dependencies)"