From d8cbea849d11a3d56cafb851ce2a4f8ae0243180 Mon Sep 17 00:00:00 2001 From: ciotlosm Date: Sat, 11 Jul 2026 15:26:15 +0300 Subject: [PATCH] feat(actions): drop pr-bump auto-increment mode from version-bump The pr-bump mode (auto-increment package.json#version on the PR branch from main+1) is removed. Callers must pass an explicit version-input; the action fails fast if the input is empty. Why: - n3ary/release-bot now handles version bumps org-wide on PR merge, opening a release/calver-* PR. The old pr-bump pattern conflicts: the publish flow was calling version-bump with empty version-input, which silently fell through to pr-bump mode and would re-bump on the bot's release PR merge. The empty-input case must now fail with a clear error pointing at the release bot. What stays: - The version-input mode is used by package/publish and adapters/publish composite actions. Unchanged behavior: bump to a specific version, commit, push, optional tag. What goes: - skip-check (metadata-only detection) - irrelevant when callers must specify a target version. - bump-skip-paths input - no longer used. - base-ref input - no longer used. - "Mark as skipped" step - no longer needed (skip is determined by the validate step now). README and action.yml updated. version-management.md in n3ary/standards will be updated separately to drop the "pr-bump mode is deprecated" line (it is no longer just deprecated, it is removed). Refs: n3ary/standards#22 --- .github/actions/version-bump/README.md | 111 +++++++++++------------ .github/actions/version-bump/action.yml | 113 +++++++----------------- README.md | 2 +- 3 files changed, 82 insertions(+), 144 deletions(-) diff --git a/.github/actions/version-bump/README.md b/.github/actions/version-bump/README.md index 1861268..121dbd5 100644 --- a/.github/actions/version-bump/README.md +++ b/.github/actions/version-bump/README.md @@ -1,104 +1,93 @@ # version-bump -Composite GitHub Action that bumps `package.json#version` on the PR branch to `main + 1`. +Composite GitHub Action that bumps `package.json#version` to a caller-supplied target version, commits the change, and pushes it. Optionally tags the bump commit. -## Why +## Why this is publish-only -The version in `package.json` is bumped on every PR via a bot commit on the PR branch. When the PR merges to `main`, `main` already has the new version. Other open PRs rebase onto `main` to pick up the new version (or get auto-rebased by Dependabot). +This action used to support two modes: + +- **pr-bump** (deprecated, removed): auto-increment `package.json#version` from `main + 1` on the PR branch. This is now handled by [`n3ary/release-bot`](https://github.com/n3ary/release-bot), which runs org-level on PR merge to main and opens a `release/calver-*` PR. +- **version-input** (this action): bump to an explicit target version. Used by the `package/publish` and `adapters/publish` composite actions when the publish workflow needs to force a specific version (e.g. a manual `workflow_dispatch` with `version-override`). + +Calling this action with empty `version-input` now fails fast. There is no fallback to auto-increment; if you need auto-bumps on PR merge, use `n3ary/release-bot`. ## Features -- **Pre-release safe**: handles `0.2.0-m1` → `0.2.1-m1`, `1.0.0-alpha.1` → `1.0.1-alpha.1`, `0.2.0-rc.1+build.5` → `0.2.1-rc.1+build.5`. -- **Metadata-aware**: skips the bump when the PR touches only metadata (paths configurable). -- **Idempotent**: no-op if the PR branch's version already matches `main + 1`. -- **Self-contained**: pushes the bot commit directly to the PR branch. +- **Semver validation**: rejects malformed `version-input` (`1.0.0`, `0.2.0-rc.1`, `1.0.0-alpha.1`, etc.). +- **Pre-release safe**: any pre-release tag (`-rc.1`, `-m1`, `-alpha.1`) is preserved; only `major.minor.patch` is written. +- **Idempotent**: if `package.json#version` is already at the target, the action no-ops. +- **Tag-on-bump**: optional `tag-name` (with `{version}` placeholder) creates + pushes a git tag for downstream consumers. +- **Race-safe push**: re-fetches and rebases on the remote before pushing, so concurrent admin merges don't leave the bot commit behind. ## Inputs | Input | Description | Default | Required | |---|---|---|---| -| `bump-skip-paths` | Comma-separated paths that, when changed alone, skip the bump. | `.github/,docs/,.gitignore,LICENSE` | no | -| `commit-message` | Commit message. `{version}` is replaced with the new version. | `chore(release): auto-bump to v{version}` | no | -| `base-ref` | Base ref to compare against. Defaults to the workflow's `GITHUB_BASE_REF`. | (empty) | no | +| `version-input` | Target version. **Required.** Action fails if empty. | (empty) | **yes** | +| `commit-message` | Commit message. `{version}` is replaced with the new version. | `chore(release): v{version}` | no | +| `commit-message-override` | Full commit message override. Useful for publish flows that want a custom message. | (empty) | no | +| `package-dir` | Directory containing the `package.json` to bump. For monorepo sub-packages (e.g. `packages/spec`). | `.` (repo root) | no | +| `tag-name` | If set, creates a git tag with this name on the bump commit and pushes it. Supports `{version}` placeholder (e.g. `packages/spec/v{version}`). | (empty, no tag) | no | +| `skip-commit` | Skip the commit + push (only compute + write to `package.json`). Callers that want to compose the commit themselves set this. | `false` | no | ## Outputs | Output | Description | |---|---| -| `previous-version` | The version on `origin/` before the bump. | +| `previous-version` | The version in `package.json` before the bump (or the current version when no bump was needed). | | `next-version` | The version after the bump (matches the new `package.json#version`). | -| `bumped` | `"true"` if the version was bumped; `"false"` if skipped (metadata-only diff or already correct). | +| `bumped` | `"true"` if the version was bumped; `"false"` if skipped (already at target). | +| `tag` | The tag name pushed (empty if `tag-name` input was unset or skipped). | ## Usage +This action is intended to be called from the `package/publish` or `adapters/publish` composite actions. Direct usage is rare; if you do call it directly: + ```yaml -name: PR Validation -on: - pull_request: - branches: [main] - -permissions: - contents: write # required for the bot commit + push - -jobs: - validate: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - with: - ref: ${{ github.event.pull_request.head.ref }} - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/setup-node@v6 - with: - node-version: 24 - cache: npm - - - run: npm ci - - - name: Auto-bump version - uses: n3ary/actions/.github/actions/version-bump@v1 - with: - bump-skip-paths: '.github/,docs/,.gitignore,LICENSE' - - # Your validation steps here — `npm test`, `npm run lint`, - # `npm run check`, etc. Whatever the repo needs. - - - run: npm test +- name: Bump to a specific version + uses: n3ary/actions/.github/actions/version-bump@v23 + with: + version-input: 0.3.7 + package-dir: adapters/cluj-napoca + tag-name: 'adapters/cluj-napoca/v{version}' + commit-message: 'chore(release): cluj-napoca v{version}' + github-token: ${{ secrets.GITHUB_TOKEN }} ``` ### Required repo settings -For the version-sequencing story to work end-to-end: - -1. **Branch protection on `main`**: "Require status checks to pass before merging" must include the workflow that runs this action (e.g. the `validate` job name). -2. **"Require branches to be up to date"** must be enabled (the `strict: true` setting on `required_status_checks`). Without it, a stale PR with the old `main` version can merge without the bump. -3. **PR required** (no direct pushes to `main`). +The publish flow (parent workflow) needs: -### Why these settings? - -- Without status checks: a PR with a broken `npm test` could merge. -- Without "branches up to date": the version bump assumes `main` is the latest. A stale PR with the old version would bump to an unexpected number. +- `permissions: contents: write` for the commit + push. +- `permissions: packages: write` for npm publish (handled by the parent action). +- A clean main branch (or auto-rebase enabled) so the `force-with-lease` push succeeds. ## Pinning -Reference the action by tag (`@v1`) or by SHA (`@`). Tag pinning is the standard for active maintenance; SHA pinning is for security-critical cases. +Reference the action by tag (`@v23`) or by SHA (`@`). Tag pinning is the standard for active maintenance; SHA pinning is for security-critical cases. ```yaml # Tag pinning (recommended for active use) -uses: n3ary/actions/.github/actions/version-bump@v1 +uses: n3ary/actions/.github/actions/version-bump@v23 # SHA pinning (security-critical; updates require manual edit) uses: n3ary/actions/.github/actions/version-bump@ ``` +## Migration from pr-bump mode + +If your workflow previously called this action with no `version-input` (relying on auto-increment), migrate to `n3ary/release-bot`: + +- Remove the call to `version-bump` from your `pr-validation.yml` / `pr-check.yml`. +- Remove the `auto-bump` job. +- The release-bot will pick up PR merges automatically (assuming the bot App is installed on your org/repo). +- For manual bumps, use `workflow_dispatch` with `version-override` on the publish workflow, which forwards to `version-input` here. + ## Consumers -- [`n3ary/app`](https://github.com/n3ary/app) — the consumer PWA -- [`n3ary/gtfs`](https://github.com/n3ary/gtfs) — the producer pipeline -- `n3ary/gtfs-adapters/tree/main/adapters/cluj-napoca` -- the Cluj adapter (inside the `gtfs-adapters` monorepo; the legacy standalone repo is archived as `n3ary/archived-adapter`) +- [`n3ary/gtfs-publisher`](https://github.com/n3ary/gtfs-publisher) - `release-gtfs-spec.yml` (publishes `libs/spec` with explicit version) +- [`n3ary/gtfs-adapters`](https://github.com/n3ary/gtfs-adapters) - `release-gtfs-adapter.yml` (publishes per-adapter with explicit version) ## License -MIT. \ No newline at end of file +MIT. diff --git a/.github/actions/version-bump/action.yml b/.github/actions/version-bump/action.yml index f6e5010..2fedfa9 100644 --- a/.github/actions/version-bump/action.yml +++ b/.github/actions/version-bump/action.yml @@ -1,28 +1,20 @@ -name: 'Auto-bump version on PR' -description: 'Bumps package.json#version on the PR branch, either auto-incrementing from main+1 (default) or to an explicit override (version-input). Preserves pre-release tags and commits pnpm-lock.yaml if dirty. Returns the bumped version + whether anything actually changed.' +name: 'Bump version to a specific target (publish flow)' +description: 'Bumps package.json#version to a caller-supplied target (version-input), commits, pushes, and optionally tags. Used by the package/publish + adapters/publish composite actions. The pr-bump auto-increment mode is REMOVED - use n3ary/release-bot for auto-bumping on PR merge.' author: 'n3ary' inputs: - bump-skip-paths: - description: 'Comma-separated paths that, when changed alone, should skip the bump. Ignored when version-input is set.' - required: false - default: '.github/,docs/,.gitignore,LICENSE' + version-input: + description: 'REQUIRED. Explicit target version (e.g. "0.3.4"). Bumps to this value. The action fails if this is empty - auto-bump on PR is handled by n3ary/release-bot, not this action.' + required: true + default: '' commit-message: description: 'Commit message. {version} is replaced with the new version. Ignored when commit-message-override is set.' required: false - default: 'chore(release): auto-bump to v{version}' + default: 'chore(release): v{version}' commit-message-override: description: 'Full commit message override. Ignored when unset. Useful for publish flows that want a custom message.' required: false default: '' - base-ref: - description: 'Base ref to compare against. Defaults to the workflow base ref.' - required: false - default: '' - version-input: - description: 'Explicit target version (e.g. "0.3.4"). When set, skips auto-increment and uses this value. Used by publish flows (publish-spec, adapters/publish) that need a precise target rather than main+1. Ignores bump-skip-paths.' - required: false - default: '' package-dir: description: 'Directory containing the package.json to bump. Defaults to repo root. Used by publish flows for monorepo sub-packages (e.g. "packages/spec").' required: false @@ -31,9 +23,9 @@ inputs: description: >- If set, creates a git tag with this name on the bump commit and pushes it to origin. Supports the {version} placeholder (e.g. - 'packages/spec/v{version}'). When unset, no tag is created - - this is the auto-bump-on-PR default. Used by publish flows that - need a tag to trigger a downstream publish job. + 'packages/spec/v{version}'). When unset, no tag is created. + Used by publish flows that need a tag to trigger a downstream + publish job. required: false default: '' skip-commit: @@ -43,83 +35,54 @@ inputs: outputs: previous-version: - description: 'The version on origin/ before the bump (or current version when version-input is set and matches).' + description: 'The version in package.json before the bump (or current version when version-input matches current).' next-version: description: 'The version after the bump (matches the new package.json#version).' bumped: - description: '"true" if the version was bumped; "false" if skipped (metadata-only diff, already correct, or version-input matched current).' - skipped: - description: '"true" if the action skipped (metadata-only diff OR version-input matched current).' + description: '"true" if the version was bumped; "false" if skipped (already at target).' + tag: + description: 'The tag name pushed (empty if tag-name input was unset).' runs: using: composite steps: - - name: Skip if metadata-only diff - id: skip-check - if: inputs.version-input == '' + - name: Validate inputs + id: validate shell: bash env: - BUMP_SKIP_PATHS: ${{ inputs.bump-skip-paths }} + VERSION_INPUT: ${{ inputs.version-input }} run: | - BASE="${BASE_REF:-${GITHUB_BASE_REF:-main}}" - CHANGED=$(git diff --name-only "origin/${BASE}"...HEAD) - SKIP_RE=$(printf '%s' "$BUMP_SKIP_PATHS" | tr ',' '\n' | sed 's:.*:^&:; s:/$::' | paste -sd'|' -) - NON_META=$(echo "$CHANGED" | grep -vE "$SKIP_RE" || true) - if [ -z "$NON_META" ]; then - echo "Only metadata changes — skipping version bump." - echo "skip=true" >> "$GITHUB_OUTPUT" - else - echo "skip=false" >> "$GITHUB_OUTPUT" + if [ -z "$VERSION_INPUT" ]; then + cat <<'EOF' >&2 +::error::version-input is required. The pr-bump auto-increment mode has been removed from this action - n3ary/release-bot handles auto-bumps on PR merge. This action is now publish-only: callers must pass an explicit target version. If you are wiring this into a workflow, pass `version-input: `. +EOF + exit 1 fi - name: Compute target version id: compute - if: steps.skip-check.outputs.skip != 'true' shell: bash env: VERSION_INPUT: ${{ inputs.version-input }} PACKAGE_DIR: ${{ inputs.package-dir }} run: | PKG_JSON="${PACKAGE_DIR:-.}/package.json" - if [ -n "$VERSION_INPUT" ]; then - # Validate semver-ish format. Matches the check in publish-spec - # + adapters/publish so the contract is consistent. - if ! echo "$VERSION_INPUT" | grep -qE \ - '^[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.-]+)?$'; then - echo "::error::Invalid version-input format: $VERSION_INPUT" - echo "Expected: e.g. 0.1.0, 0.2.0-rc.1, 1.0.0-alpha.1" - exit 1 - fi - TARGET="$VERSION_INPUT" - CURRENT=$(node -p "require('./${PKG_JSON}').version") - echo "version-input: $TARGET (current: $CURRENT, pkg: $PKG_JSON)" - else - BASE="${BASE_REF:-${GITHUB_BASE_REF:-main}}" - MAIN_VERSION=$(git show "origin/${BASE}:${PKG_JSON}" | \ - node -p "JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')).version") - # Increment patch; preserve pre-release suffix. - # Handles: 1.5.68 -> 1.5.69, 0.2.0-m1 -> 0.2.1-m1, - # 1.0.0-alpha.1 -> 1.0.1-alpha.1, - # 0.2.0-rc.1+build.5 -> 0.2.1-rc.1+build.5. - TARGET=$(node -e ' - const v = process.argv[1].match(/^(\d+)\.(\d+)\.(\d+)(-.+)?$/); - if (!v) { - console.error("Unparseable version:", process.argv[1]); - process.exit(1); - } - const [, major, minor, patchStr, prerelease = ""] = v; - console.log(`${major}.${minor}.${Number(patchStr) + 1}${prerelease}`); - ' "$MAIN_VERSION") - CURRENT=$(node -p "require('./${PKG_JSON}').version") - echo "Auto-bump: $MAIN_VERSION -> $TARGET (PR branch: $CURRENT, pkg: $PKG_JSON)" + if ! echo "$VERSION_INPUT" | grep -qE \ + '^[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z0-9.-]+)?$'; then + echo "::error::Invalid version-input format: $VERSION_INPUT" + echo "Expected: e.g. 0.1.0, 0.2.0-rc.1, 1.0.0-alpha.1" + exit 1 fi + TARGET="$VERSION_INPUT" + CURRENT=$(node -p "require('./${PKG_JSON}').version") + echo "version-input: $TARGET (current: $CURRENT, pkg: $PKG_JSON)" echo "previous=$CURRENT" >> "$GITHUB_OUTPUT" echo "next=$TARGET" >> "$GITHUB_OUTPUT" echo "package_dir=$PKG_JSON" >> "$GITHUB_OUTPUT" - name: Apply bump (commit + push) id: apply - if: steps.skip-check.outputs.skip != 'true' && inputs.skip-commit != 'true' + if: inputs.skip-commit != 'true' shell: bash env: COMMIT_MSG_OVERRIDE: ${{ inputs.commit-message-override }} @@ -200,17 +163,3 @@ runs: fi echo "bumped=true" >> "$GITHUB_OUTPUT" - echo "skipped=false" >> "$GITHUB_OUTPUT" - - - name: Mark as skipped - if: steps.skip-check.outputs.skip == 'true' - shell: bash - env: - PACKAGE_DIR: ${{ inputs.package-dir }} - run: | - PKG_JSON="${PACKAGE_DIR:-.}/package.json" - CURRENT=$(node -p "require('./${PKG_JSON}').version" 2>/dev/null || echo "") - echo "previous=${CURRENT}" >> "$GITHUB_OUTPUT" - echo "next=${CURRENT}" >> "$GITHUB_OUTPUT" - echo "bumped=false" >> "$GITHUB_OUTPUT" - echo "skipped=true" >> "$GITHUB_OUTPUT" \ No newline at end of file diff --git a/README.md b/README.md index 12fc58c..acc2688 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Composite GitHub Actions and reusable workflows for the [n3ary org](https://gith | Action | Description | |---|---| -| [version-bump](.github/actions/version-bump) | Bumps `package.json#version` on the PR branch to `main + 1`. Handles pre-release tags. | +| [version-bump](.github/actions/version-bump) | Publish-only. Bumps `package.json#version` to a caller-supplied target, commits, pushes, optionally tags. **Not** for auto-bumping on PR — use [n3ary/release-bot](https://github.com/n3ary/release-bot) for that. | | [ascii-commits](.github/actions/ascii-commits) | Fails the build if any commit message (subject + body) on the PR branch, or the PR title, contains non-ASCII characters. | ## Reusable workflows