Move the working version on after a release
package.json has sat at 0.1.0 since the first commit, through three releases, because nothing read it. That is fine right up until something does — an image label, a health endpoint, a bug report quoting a version — at which point the tree claims to be a version that shipped long ago. A new `bump` job takes the tag that was just published, works out the next patch from it, and commits that to main. After 1.2.0 the tree says 1.2.1: not a version that exists, which is the point. A build from main is then legible as "after 1.2.0" rather than as 1.2.0 itself. It sits in publish.yml rather than a workflow of its own so that it can say `needs: build`. A version that failed to publish has not been released, and moving past it would say that it had. Prereleases are skipped for the same reason -- 1.2.3-rc1 is a candidate for a version that has not shipped, so there is nothing yet to move past. The bump goes through `npm version` rather than editing the file. The version is in the lockfile too, in two places, and a tree where those disagree is worse than one that is merely out of date. Three smaller things. The patch arithmetic forces base ten, because a patch number written 08 is otherwise read as octal and kills the job. The commit carries `[skip ci]`, or pushing it starts another build of the image that was just published. And the committer is a name that is not a person at a reserved address that can never become one, so nothing here names the instance it runs on. Pushing to main needs a token that may write to the repository. The Actions task token can where the instance allows it; where it does not, setting a VERSION_BUMP_TOKEN secret overrides it. A push that is refused fails the job with both of those as the suggestion rather than a bare 403. package.json goes to 1.2.1 here, which is where the job would have left it had it existed when 1.2.0 went out. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_017nMQ2eDKnqALYhAibpTKTu
This commit is contained in:
@@ -141,3 +141,100 @@ jobs:
|
||||
- name: Log out
|
||||
if: always() && github.event_name != 'pull_request'
|
||||
run: docker logout "${{ vars.REGISTRY }}" || true
|
||||
|
||||
# Once a release is out, the version in package.json has already shipped.
|
||||
# Moving it on to the next patch means the working tree is never sitting on
|
||||
# a number that is published and immutable, and that a build from main is
|
||||
# always identifiable as "after 1.2.0" rather than "1.2.0, but not really".
|
||||
#
|
||||
# `needs: build` is the point of putting this here rather than in a workflow
|
||||
# of its own: a version that failed to publish has not been released, and
|
||||
# bumping past it would say it had.
|
||||
bump:
|
||||
name: Move the working version on
|
||||
needs: build
|
||||
# Tags only, and only final ones. A prerelease has not shipped the version
|
||||
# it is a candidate for, so there is nothing yet to move past.
|
||||
if: github.ref_type == 'tag' && !contains(github.ref_name, '-')
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:22
|
||||
steps:
|
||||
# The tag names a commit in main's history, but the bump belongs on the
|
||||
# branch, so this checks out main rather than the tag.
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
# The task token can push only if the instance allows Actions to
|
||||
# write to the repository. Where it does not, set VERSION_BUMP_TOKEN
|
||||
# to a personal access token with write access and it is used
|
||||
# instead.
|
||||
token: ${{ secrets.VERSION_BUMP_TOKEN || secrets.GITEA_TOKEN }}
|
||||
|
||||
- name: Work out the next patch version
|
||||
id: next
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
version="${{ github.ref_name }}"
|
||||
version="${version#v}"
|
||||
|
||||
major="${version%%.*}"
|
||||
rest="${version#*.}"
|
||||
minor="${rest%%.*}"
|
||||
patch="${rest##*.}"
|
||||
|
||||
# `10#` forces base ten: a patch number written 08 would otherwise be
|
||||
# read as octal and fail to parse.
|
||||
next="${major}.${minor}.$((10#${patch} + 1))"
|
||||
|
||||
echo "next=${next}" >> "$GITHUB_OUTPUT"
|
||||
echo "Released ${version}; the working version becomes ${next}"
|
||||
|
||||
- name: Bump package.json
|
||||
id: bump
|
||||
env:
|
||||
NEXT: ${{ steps.next.outputs.next }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
current="$(node -p "require('./package.json').version")"
|
||||
if [ "${current}" = "${NEXT}" ]; then
|
||||
echo "package.json is already ${NEXT}; nothing to do."
|
||||
echo "changed=false" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# npm rather than editing the file: the version is in the lockfile
|
||||
# too, in more than one place, and they have to agree.
|
||||
npm version "${NEXT}" --no-git-tag-version --allow-same-version >/dev/null
|
||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||
echo "package.json ${current} -> ${NEXT}"
|
||||
|
||||
- name: Commit it to main
|
||||
if: steps.bump.outputs.changed == 'true'
|
||||
env:
|
||||
NEXT: ${{ steps.next.outputs.next }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# A name that is not a person, and a reserved address that can never
|
||||
# resolve to one. Nothing here names the instance it runs on.
|
||||
git config user.name 'Release bot'
|
||||
git config user.email '[email protected]'
|
||||
|
||||
git add package.json package-lock.json
|
||||
# `[skip ci]` because this commit is a number and nothing else:
|
||||
# without it the push to main starts another build of the very image
|
||||
# that was just published.
|
||||
git commit -m "Set the working version to ${NEXT} [skip ci]"
|
||||
|
||||
if ! git push origin HEAD:main; then
|
||||
echo >&2
|
||||
echo "Could not push the version bump to main. Either the Actions" >&2
|
||||
echo "token has no write access to this repository, or main is" >&2
|
||||
echo "protected against direct pushes. Set VERSION_BUMP_TOKEN to a" >&2
|
||||
echo "token that may push to main, or allow that token past the" >&2
|
||||
echo "branch protection." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user