The release job ran `npm run check` and `npm test` on a bare runner
with no `age` installed — ci.yml apt-installs it because the secrets
tests round-trip a real age identity, so the first real tag push would
have died at `npm test` and minted no release. The job now does exactly
what the flow (cast#96 / box#83) assigns it: `npm ci && npm run build
&& npm prune --omit=dev`, tar, `gh release create` — check and tests
already gated the merge commit the tag points at.
Also aligns CHANGELOG.md with the family preamble ("History before
0.1.0 lives in git") and with the workflow's actual build steps.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
74 lines
3 KiB
YAML
74 lines
3 KiB
YAML
name: release
|
|
|
|
# A release is a PR, then a tag (cast#96, the flow shared with box#83):
|
|
# the `release: X.Y.Z` PR bumps package.json and stamps the CHANGELOG's
|
|
# Unreleased section; merging and pushing the bare `X.Y.Z` tag lands here.
|
|
# This workflow is where cast differs from its siblings: box/rig are pure
|
|
# bash, so the source tarball IS the package — cast compiles, so the build
|
|
# happens ONCE, here, and the release carries a prebuilt `cast-X.Y.Z.tgz`
|
|
# the installer can drop in without npm ci or tsc on the operator's machine.
|
|
|
|
on:
|
|
push:
|
|
# Bare X.Y.Z tags (the family scheme — box's 0.6.0 set the precedent,
|
|
# no `v` prefix). The glob is loose; the assert step below is the gate.
|
|
tags: ["[0-9]*.*.*"]
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
jobs:
|
|
release:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: "22"
|
|
cache: npm
|
|
|
|
- name: assert tag == package.json version
|
|
# Fail loudly, create nothing: a tag that contradicts package.json
|
|
# would mint a release whose `cast --version` disagrees with its
|
|
# own name. The mismatch is a ritual error — retag, don't patch.
|
|
run: |
|
|
version="$(node -p 'require("./package.json").version')"
|
|
if [ "$GITHUB_REF_NAME" != "$version" ]; then
|
|
echo "tag '$GITHUB_REF_NAME' != package.json version '$version' — refusing to release" >&2
|
|
exit 1
|
|
fi
|
|
|
|
- name: extract the release notes from CHANGELOG.md
|
|
# The release body is the curated section we wrote, never the
|
|
# auto-generated PR list. Missing/empty section fails the release —
|
|
# before the tag has minted anything.
|
|
run: bash scripts/changelog-section.sh "$GITHUB_REF_NAME" CHANGELOG.md > /tmp/release-notes.md
|
|
|
|
- name: build the package, once
|
|
# Just the build — check and tests already gated the merge commit
|
|
# this tag points at (ci.yml, with its age dependency); the release
|
|
# job's whole job is packaging that green tree.
|
|
run: |
|
|
npm ci
|
|
npm run build
|
|
|
|
- name: assemble cast-${{ github.ref_name }}.tgz
|
|
# The runnable tree and nothing else: bin/, dist/, production
|
|
# node_modules/, package.json. Top-level dir named like a GitHub
|
|
# archive's, so the installer handles both shapes identically.
|
|
run: |
|
|
npm prune --omit=dev
|
|
stage="$(mktemp -d)/cast-$GITHUB_REF_NAME"
|
|
mkdir -p "$stage"
|
|
cp -R bin dist node_modules package.json "$stage/"
|
|
tar -C "$(dirname "$stage")" -czf "cast-$GITHUB_REF_NAME.tgz" "cast-$GITHUB_REF_NAME"
|
|
tar -tzf "cast-$GITHUB_REF_NAME.tgz" | head -5
|
|
|
|
- name: create the GitHub release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
gh release create "$GITHUB_REF_NAME" "cast-$GITHUB_REF_NAME.tgz" \
|
|
--verify-tag \
|
|
--title "cast $GITHUB_REF_NAME" \
|
|
--notes-file /tmp/release-notes.md
|