Skip to content

Verify Spec Kit Release #200

Verify Spec Kit Release

Verify Spec Kit Release #200

name: Verify Spec Kit Release
on:
workflow_run:
workflows:
- Publish Agent Skills
types:
- completed
workflow_dispatch:
permissions:
contents: read
concurrency:
group: verify-spec-kit-release
cancel-in-progress: false
jobs:
verify:
name: Verify released Spec Kit extension
if: >-
(github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push') ||
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout trusted main
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: main
fetch-depth: 0
persist-credentials: false
- name: Resolve and verify source commit
id: source
env:
GH_TOKEN: ${{ github.token }}
SOURCE_RUN_ID: ${{ github.event.workflow_run.id }}
shell: bash
run: |
set -euo pipefail
if [[ "${GITHUB_EVENT_NAME}" == "workflow_run" ]]; then
if [[ ! "$SOURCE_RUN_ID" =~ ^[0-9]+$ ]]; then
echo "Unable to resolve the completed publisher run." >&2
exit 1
fi
run_path="repos/${GITHUB_REPOSITORY}/actions/runs/${SOURCE_RUN_ID}"
source_event="$(gh api "$run_path" --jq '.event')"
source_branch="$(gh api "$run_path" --jq '.head_branch')"
sha="$(gh api "$run_path" --jq '.head_sha')"
if [[ "$source_event" != "push" || "$source_branch" != "main" ]]; then
echo "Refusing publisher run ${SOURCE_RUN_ID}: expected a push on main." >&2
exit 1
fi
else
sha="${GITHUB_SHA}"
fi
if [[ ! "$sha" =~ ^[0-9a-f]{40}$ ]]; then
echo "Unable to resolve a valid main commit to verify." >&2
exit 1
fi
# actions/checkout only checks out fixed trusted main. Before any
# repository code runs, prove the completed publisher SHA belongs to
# that main history, then detach to the exact release source.
git fetch --force origin main
if ! git merge-base --is-ancestor "$sha" origin/main; then
echo "Refusing to verify $sha because it is not in trusted main history." >&2
exit 1
fi
git checkout --detach "$sha"
echo "sha=$sha" >> "$GITHUB_OUTPUT"
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"
cache: npm
- name: Install repository dependencies
run: npm ci
- name: Validate Spec Kit package and shared version
run: node tests/validate-spec-kit.mjs
- name: Install pinned Spec Kit CLI
shell: bash
run: |
set -euo pipefail
python3 -m pip install --user --disable-pip-version-check \
"git+https://github.com/github/spec-kit.git@ead30d9cfb99c07b3073afa73e7b40a64015f17f"
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Verify shared release owns the exact Spec Kit package
id: release
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -euo pipefail
VERSION="$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).version")"
TAG="v${VERSION}"
PACKAGE_PATHS=(extension.yml commands README.md LICENSE)
if ! gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
echo "Expected shared Agent Skills release $TAG does not exist yet." >&2
exit 1
fi
git fetch --force --tags origin
TAG_COMMIT="$(git rev-list -n 1 "$TAG")"
if [[ -z "$TAG_COMMIT" ]]; then
echo "Unable to resolve commit for $TAG." >&2
exit 1
fi
CURRENT_PACKAGE_DIGEST="$(git ls-tree -r "${{ steps.source.outputs.sha }}" -- "${PACKAGE_PATHS[@]}" | sha256sum | awk '{print $1}')"
TAG_PACKAGE_DIGEST="$(git ls-tree -r "$TAG_COMMIT" -- "${PACKAGE_PATHS[@]}" | sha256sum | awk '{print $1}')"
if [[ "$TAG_PACKAGE_DIGEST" != "$CURRENT_PACKAGE_DIGEST" ]]; then
echo "$TAG exists but does not contain the current Spec Kit package. Bump package.json and extension.yml together before publishing." >&2
exit 1
fi
# Bind installation to the exact commit whose package digest was verified.
# Keep the human-readable tag only as reporting metadata.
ARCHIVE_URL="https://github.com/${GITHUB_REPOSITORY}/archive/${TAG_COMMIT}.zip"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "archive_url=$ARCHIVE_URL" >> "$GITHUB_OUTPUT"
- name: Smoke-test exact release archive
env:
ARCHIVE_URL: ${{ steps.release.outputs.archive_url }}
shell: bash
run: |
set -euo pipefail
workdir="$(mktemp -d)"
trap 'rm -rf "$workdir"' EXIT
cd "$workdir"
specify init --here --force --non-interactive --ignore-agent-tools
# Direct --from installs intentionally require explicit user consent.
# Provide exactly one affirmative response for this verified release URL.
printf 'y\n' | specify extension add hol-guard --from "$ARCHIVE_URL"
specify extension list | tee installed-extensions.txt
grep -F "hol-guard" installed-extensions.txt
- name: Report verified release
env:
TAG: ${{ steps.release.outputs.tag }}
ARCHIVE_URL: ${{ steps.release.outputs.archive_url }}
run: |
echo "Verified HOL Guard Spec Kit extension release $TAG"
echo "Archive: $ARCHIVE_URL"