cast gets the family's release flow (box#83's shape), plus the piece unique to cast: because cast compiles, the source tarball is not the package — so release.yml builds ONCE in CI and attaches cast-X.Y.Z.tgz, and the installer's default channel extracts that asset instead of running npm ci + tsc on the operator's machine. - cast --version: package.json is the single source of truth (no VERSION file); prints the install root too, rig-style. - CHANGELOG.md with Unreleased; release notes are the curated section (scripts/changelog-section.sh), never the auto-generated PR list. - release.yml on a bare X.Y.Z tag: assert tag == package.json version, check + build + test, prune, tar the runnable tree, gh release create. - install.sh channels: unset → latest release asset (resolved via the releases/latest redirect — no API, no token); CAST_REF=X.Y.Z → that tag's asset; CAST_REF=<branch> → build-from-source, the old path. - Tests drive the REAL install.sh offline via curl/npm PATH shims (all three channels, plus the broken-asset and no-release refusals), and the real changelog-section.sh against fixture changelogs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
43 lines
1.6 KiB
Bash
43 lines
1.6 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# changelog-section.sh <version> [changelog-file]
|
|
#
|
|
# Print the body of CHANGELOG.md's `## <version>` section — everything
|
|
# between that heading and the next `## ` heading (or EOF), with the
|
|
# leading/trailing blank lines trimmed. release.yml uses this as the
|
|
# GitHub release body, so the notes are the curated prose we actually
|
|
# wrote, never an auto-generated PR list (cast#96 / box#83).
|
|
#
|
|
# Fails loudly when the section is missing or empty: a release with no
|
|
# written history is a release that skipped the changelog discipline,
|
|
# and the tag push is exactly the moment to catch that — before a
|
|
# release object exists.
|
|
|
|
version="${1:-}"
|
|
file="${2:-CHANGELOG.md}"
|
|
|
|
[ -n "$version" ] || { echo "usage: changelog-section.sh <version> [changelog-file]" >&2; exit 2; }
|
|
[ -f "$file" ] || { echo "changelog-section: no such file: $file" >&2; exit 1; }
|
|
|
|
# The heading is `## <version>` optionally followed by more (a date stamp:
|
|
# `## 0.1.0 — 2026-07-18`). Match on the version as the second word so the
|
|
# stamp's format never becomes load-bearing here.
|
|
section="$(awk -v ver="$version" '
|
|
/^## / { if (found) exit; if ($2 == ver) { found = 1; next } }
|
|
found { print }
|
|
END { exit found ? 0 : 3 }
|
|
' "$file")" || {
|
|
echo "changelog-section: no \"## $version\" section in $file" >&2
|
|
exit 1
|
|
}
|
|
|
|
# Trim leading blank lines; command substitution already ate the trailing ones.
|
|
section="$(printf '%s\n' "$section" | sed '/./,$!d')"
|
|
|
|
[ -n "$section" ] || {
|
|
echo "changelog-section: the \"## $version\" section in $file is empty" >&2
|
|
exit 1
|
|
}
|
|
|
|
printf '%s\n' "$section"
|