Skip to content

feat(release): bind publication to immutable RC (#492) #330

feat(release): bind publication to immutable RC (#492)

feat(release): bind publication to immutable RC (#492) #330

Workflow file for this run

name: trust
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
actions: read
checks: read
jobs:
trust:
name: trust
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
- name: Classify an exact metadata-only child
id: child
env:
EVENT_NAME: ${{ github.event_name }}
HEAD_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
shell: bash
run: |
set -euo pipefail
{
echo "reuse=false"
echo "kind=implementation"
echo "parent_sha="
} >> "$GITHUB_OUTPUT"
if [[ "$EVENT_NAME" != "pull_request" ]]; then
exit 0
fi
parent_line="$(git rev-list --parents -n 1 "$HEAD_SHA")"
if [[ "$(wc -w <<<"$parent_line" | tr -d ' ')" != "2" ]]; then
exit 0
fi
parent_sha="$(git rev-parse "${HEAD_SHA}^")"
classification="$(
git diff --name-status -z -M "$parent_sha" "$HEAD_SHA" \
| .github/scripts/classify-ci-paths.sh \
"$GITHUB_WORKSPACE" false name-status
)"
if grep -Fxq "review_only=true" <<<"$classification"; then
kind=review
elif grep -Fxq "archive_only=true" <<<"$classification"; then
kind=archive
else
exit 0
fi
{
echo "reuse=true"
echo "kind=$kind"
echo "parent_sha=$parent_sha"
} >> "$GITHUB_OUTPUT"
- name: Reuse exact parent trust provenance
if: steps.child.outputs.reuse == 'true'
env:
GH_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
SERVER_URL: ${{ github.server_url }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PARENT_SHA: ${{ steps.child.outputs.parent_sha }}
CHILD_KIND: ${{ steps.child.outputs.kind }}
shell: bash
run: |
set -euo pipefail
checks_file="${RUNNER_TEMP}/specsync-parent-trust-checks.json"
gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"repos/${REPOSITORY}/commits/${PARENT_SHA}/check-runs?per_page=100" \
> "$checks_file"
export CHECKS_FILE="$checks_file"
python3 - <<'PY'
import json
import os
import re
import subprocess
parent = os.environ["PARENT_SHA"]
repository = os.environ["REPOSITORY"]
server_url = os.environ["SERVER_URL"].rstrip("/")
pull_request = int(os.environ["PR_NUMBER"])
with open(os.environ["CHECKS_FILE"], encoding="utf-8") as handle:
checks = json.load(handle).get("check_runs", [])
def api(endpoint: str) -> dict:
output = subprocess.check_output(
[
"gh",
"api",
"-H",
"Accept: application/vnd.github+json",
"-H",
"X-GitHub-Api-Version: 2022-11-28",
endpoint,
],
text=True,
)
return json.loads(output)
github_actions_app = api("apps/github-actions")
if (
github_actions_app.get("slug") != "github-actions"
or github_actions_app.get("name") != "GitHub Actions"
or (github_actions_app.get("owner") or {}).get("login") != "github"
):
raise SystemExit("could not resolve the official GitHub Actions app")
errors = []
candidates = sorted(
(check for check in checks if check.get("name") == "trust"),
key=lambda check: int(check.get("id", 0)),
reverse=True,
)
for check in candidates:
try:
if check.get("head_sha") != parent:
raise ValueError("wrong head SHA")
if (
check.get("status") != "completed"
or check.get("conclusion") != "success"
):
raise ValueError("check is not successful")
app = check.get("app") or {}
if (
app.get("id") != github_actions_app.get("id")
or app.get("slug") != github_actions_app.get("slug")
):
raise ValueError("check is not from GitHub Actions")
details_url = str(check.get("details_url") or "")
match = re.fullmatch(
(
rf"{re.escape(server_url)}/{re.escape(repository)}"
r"/actions/runs/([0-9]+)/job/[0-9]+"
),
details_url,
)
if match is None:
raise ValueError("invalid GitHub Actions details URL")
run_id = int(match.group(1))
workflow_run = api(
f"repos/{repository}/actions/runs/{run_id}"
)
if workflow_run.get("id") != run_id:
raise ValueError("wrong workflow run ID")
if workflow_run.get("head_sha") != parent:
raise ValueError("workflow run has the wrong head SHA")
if workflow_run.get("event") != "pull_request":
raise ValueError("workflow run is not for a pull request")
if (
workflow_run.get("status") != "completed"
or workflow_run.get("conclusion") != "success"
):
raise ValueError("workflow run is not successful")
if (
str(workflow_run.get("path") or "").split("@", 1)[0]
!= ".github/workflows/trust.yml"
):
raise ValueError("workflow run has the wrong path")
if (workflow_run.get("repository") or {}).get(
"full_name"
) != repository:
raise ValueError("workflow run belongs to another repository")
if not any(
item.get("number") == pull_request
for item in workflow_run.get("pull_requests") or []
):
raise ValueError("workflow run is not bound to this PR")
print(
f"Reused trust run {run_id} from exact parent {parent} "
f"for the {os.environ['CHILD_KIND']} child."
)
break
except (
json.JSONDecodeError,
subprocess.CalledProcessError,
TypeError,
ValueError,
) as error:
errors.append(f"check {check.get('id')}: {error}")
else:
detail = "; ".join(errors) if errors else "no matching trust checks"
raise SystemExit(f"parent trust provenance is not reusable: {detail}")
PY
- uses: dtolnay/rust-toolchain@1.89.0
if: steps.child.outputs.reuse != 'true'
with:
components: rustfmt, clippy
- name: Package SpecSync release candidate for Trust
if: steps.child.outputs.reuse != 'true'
shell: bash
run: |
set -euo pipefail
cargo build --release --locked
mirror="${RUNNER_TEMP}/specsync-trust-mirror"
binary="specsync-linux-x86_64"
mkdir -p "$mirror"
cp target/release/specsync "$mirror/$binary"
tar -czf "$mirror/$binary.tar.gz" -C "$mirror" "$binary"
(
cd "$mirror"
sha256sum "$binary.tar.gz" > "$binary.tar.gz.sha256"
)
# Trust revalidates this checksum-protected runner-local mirror before
# exercising the pull-request binary as the immutable contract gate.
- name: CorvidLabs Trust gate
if: steps.child.outputs.reuse != 'true'
id: trust
uses: CorvidLabs/trust@a239f78658e5ad0f12fa230f494890e40c6e4d7b # v1.1.1
with:
specsync-version: "6.0.0"
specsync-download-base-url: file://${{ runner.temp }}/specsync-trust-mirror