From 5eb1dad32c7dd79633dfd13bc9ff8faed0fb114f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 17:03:47 +0000 Subject: [PATCH] ci: make Cut Release version input optional, defaulting to the next patch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An empty version (now the workflow default) makes release.sh compute the next patch version from package.json — matching npm-version-patch semantics, prereleases promote to their release — and run the same gate, tag, and push flow. An explicit semver still pins the version, and the interactive prompt accepts an empty answer for the same patch-bump behavior. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01LekzRRztX696SLGyefENYm --- .github/workflows/cut-release.yml | 14 ++++++++++---- release.sh | 30 ++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/.github/workflows/cut-release.yml b/.github/workflows/cut-release.yml index 3e70438..143feaa 100644 --- a/.github/workflows/cut-release.yml +++ b/.github/workflows/cut-release.yml @@ -1,7 +1,8 @@ name: Cut Release # CI replacement for running ./release.sh on a laptop. Kick it off from the -# Actions tab ("Cut Release" → Run workflow) with the version to release. +# Actions tab ("Cut Release" → Run workflow). Leave the version empty to cut +# the next patch release; give an explicit version to pin it. # # This runs release.sh itself — one source of truth: bump package.json → # typecheck (against the live prod OpenAPI spec) + lint + test + build → @@ -14,12 +15,16 @@ name: Cut Release # pushed with GITHUB_TOKEN do not trigger other workflows, so release.yml # would never fire. +run-name: >- + Cut Release — ${{ inputs.version || 'next patch' }}${{ inputs.dry_run && ' (dry run)' || '' }} + on: workflow_dispatch: inputs: version: - description: 'Version to release (no v prefix, e.g. 0.3.0)' - required: true + description: 'Version to release (leave empty for the next patch; no v prefix, e.g. 0.3.0)' + required: false + default: '' type: string dry_run: description: 'Dry run: bump + run all checks, but do not commit/tag/push' @@ -86,7 +91,8 @@ jobs: # release.sh runs the full gate before touching git: typecheck (codegen # against the live prod spec — hard gate, the polylane.com installer - # serves the latest release), lint, test, build. + # serves the latest release), lint, test, build. An empty VERSION makes + # release.sh bump the next patch version; the log shows the result. - name: Run release.sh env: VERSION: ${{ inputs.version }} diff --git a/release.sh b/release.sh index 27343e8..f6f2fdb 100755 --- a/release.sh +++ b/release.sh @@ -2,14 +2,16 @@ # Cut a new release of the polylane CLI. # # ./release.sh 0.1.0 -# ./release.sh # prompts for version (interactive only) +# ./release.sh # prompts for version (interactive only); +# # empty answer = next patch version # # Steps: bump package.json → typecheck + lint + test + build → commit → tag → # push. `release.yml` takes over once the v* tag reaches origin. # -# CI mode (CI env var set, as in GitHub Actions): no prompts — the version -# argument is required and the branch must be main. Run via the "Cut Release" -# workflow (.github/workflows/cut-release.yml) instead of a laptop. +# CI mode (CI env var set, as in GitHub Actions): no prompts — no version +# argument means "bump the next patch version" and the branch must be main. +# Run via the "Cut Release" workflow (.github/workflows/cut-release.yml) +# instead of a laptop. # # RELEASE_DRY_RUN=1 runs the version bump and all checks, then reverts the # bump and exits before committing, tagging, or pushing. @@ -29,16 +31,24 @@ if [ -n "$(git status --porcelain)" ]; then die "working tree is dirty — commit or stash first" fi -# Version: arg or prompt (never prompt in CI). +# Version: arg or prompt (never prompt in CI). Empty means "next patch". VERSION="${1:-}" -if [ -z "$VERSION" ]; then - if [ -n "${CI:-}" ]; then - die "version argument required in CI: ./release.sh " - fi - read -rp "Release version (no v prefix, e.g. 0.1.0): " VERSION +if [ -z "$VERSION" ] && [ -z "${CI:-}" ]; then + read -rp "Release version (no v prefix, e.g. 0.1.0; empty = next patch): " VERSION fi VERSION="${VERSION#v}" +# No version given: compute the next patch from package.json, matching what +# `npm version patch` would produce (a prerelease is promoted to its release). +if [ -z "$VERSION" ]; then + CURRENT="$(node -p 'require("./package.json").version')" + VERSION="$(node -p ' + const m = process.argv[1].match(/^(\d+)\.(\d+)\.(\d+)(-[0-9A-Za-z.-]*)?/); + m[4] ? `${m[1]}.${m[2]}.${m[3]}` : `${m[1]}.${m[2]}.${Number(m[3]) + 1}` + ' "$CURRENT")" || die "could not compute next patch version from '$CURRENT'" + info "no version given — next patch: $CURRENT → $VERSION" +fi + # Reject anything that isn't semver x.y.z[-prerelease][+build]. if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then die "invalid semver: '$VERSION' (expected x.y.z)"