#!/bin/sh set -eu # Release front-door. Run as `npm run create:release`. # # How we got here (short): we want hand-written Keep-a-Changelog notes, an # [Unreleased] -> "## [x.y.z] - DATE" graduation, and a tag that marks the exact # commit on main that gets published. No single tool did BOTH the [Unreleased] # graduation AND the package.json bump. So split by strength: `pubv` (tiny, # changelog-driven) owns preflight + the interactive major/minor/patch heuristic # + graduating/committing CHANGELOG.md (no tag, no push); `npm version` syncs # package.json + the lockfile; `--amend` folds them into pubv's single commit; # tag AFTER the amend (so the tag is never orphaned) and push. # # Rejected: the conventional-commits family (our history is gitmoji, not # Conventional; and we want hand-written notes); changesets/rtk (config + a # heavier version/publish flow that fights our CI-only publish); knope/kacl/ # bestikk (changelog-only — don't bump package.json; plus 5yr/2yr/brand-new # maintenance); pubv alone (verified it never writes package.json). We also # tried `versions` (silverwind) — great Gitea support — but pairing it with a # hand-rolled promote became a ~180-line script we'd have to maintain, which is # exactly what this ~30-line version replaces. CHANGELOG="CHANGELOG.md" BASE="main" if ! command -v code >/dev/null 2>&1; then echo "Error: 'code' (VS Code CLI) not found; install it or remove the editor step." >&2 exit 1 fi # Releases are cut from `main` (see CONTRIBUTING § Publishing workflow). Make # that explicit rather than relying on pubv's default-branch check, so the # error names `main` even when the remote's default is configured differently. CURRENT=$(git symbolic-ref --quiet --short HEAD || true) if [ "${CURRENT}" != "${BASE}" ]; then echo "Error: releases are cut from '${BASE}', but HEAD is '${CURRENT:-detached}'." >&2 exit 1 fi # pubv decides the "default branch" by reading the *local* # `refs/remotes/origin/HEAD`, not by asking the remote, and `git fetch` never # updates that ref. After a default-branch change — or a clone from when the # default was different — it goes stale and pubv warns/fails because the # current branch (main) does not match it, even though main *is* the remote # default. Refresh it from the remote first, so pubv's branch preflight # compares against reality. (Without a network this fails, but so would the # push pubv is about to do, so it is a real error rather than one to swallow.) if ! git remote set-head origin --auto >/dev/null 2>&1; then echo "Error: could not refresh origin/HEAD; check connectivity to origin." >&2 exit 1 fi echo "Running pubv..." pubv --no-tag --no-push --tag-prefix=none echo "Opening ${CHANGELOG} in VS Code..." code --wait "${CHANGELOG}" echo "Reading version from ${CHANGELOG}..." VERSION=$( sed -nE 's/^## \[([0-9]+\.[0-9]+\.[0-9]+)\].*/\1/p' "${CHANGELOG}" | head -n 1 ) if [ -z "${VERSION}" ]; then echo "Error: Could not determine release version from ${CHANGELOG}." >&2 exit 1 fi echo "Release version: ${VERSION}" echo "Updating package.json and package-lock.json..." npm version "${VERSION}" --no-git-tag-version echo "Amending release commit..." git add package.json package-lock.json "${CHANGELOG}" git commit --amend -m ":bookmark: Release ${VERSION}" echo "Creating tag ${VERSION}..." git tag "${VERSION}" echo "Pushing release..." git push git push --tags echo "Release ${VERSION} completed."