Merge pull request 'Open the version bump as a pull request' (#6) from bump-version-by-pull-request into main
Reviewed-on: #6
This commit was merged in pull request #6.
This commit is contained in:
@@ -157,19 +157,26 @@ jobs:
|
|||||||
# it is a candidate for, so there is nothing yet to move past.
|
# it is a candidate for, so there is nothing yet to move past.
|
||||||
if: github.ref_type == 'tag' && !contains(github.ref_name, '-')
|
if: github.ref_type == 'tag' && !contains(github.ref_name, '-')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# npm does the bump, so this one job wants node. A job in a container is
|
||||||
|
# given `sh -e {0}` as its shell rather than the bash the runner's own jobs
|
||||||
|
# get, and dash has neither `pipefail` nor the `10#` below — which failed
|
||||||
|
# the first line of the first step the one time this ran. node:22 is Debian
|
||||||
|
# and carries bash, so asking for it keeps these scripts the same as the
|
||||||
|
# ones in the job above.
|
||||||
container:
|
container:
|
||||||
image: node:22
|
image: node:22
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
steps:
|
steps:
|
||||||
# The tag names a commit in main's history, but the bump belongs on the
|
# 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.
|
# branch, so this checks out main rather than the tag. The full history
|
||||||
|
# because a shallow clone cannot reliably push a branch back.
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
# The task token can push only if the instance allows Actions to
|
fetch-depth: 0
|
||||||
# write to the repository. Where it does not, set VERSION_BUMP_TOKEN
|
token: ${{ secrets.GITHUB_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
|
- name: Work out the next patch version
|
||||||
id: next
|
id: next
|
||||||
@@ -211,30 +218,59 @@ jobs:
|
|||||||
echo "changed=true" >> "$GITHUB_OUTPUT"
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
||||||
echo "package.json ${current} -> ${NEXT}"
|
echo "package.json ${current} -> ${NEXT}"
|
||||||
|
|
||||||
- name: Commit it to main
|
# The bump arrives as a pull request rather than as a commit straight to
|
||||||
|
# main. Pushing a branch asks nothing of the task token beyond ordinary
|
||||||
|
# write access, so nothing here depends on being allowed past whatever
|
||||||
|
# protects main; and the pull request puts the changed package.json
|
||||||
|
# through the build before it lands. Since main is never pushed, the
|
||||||
|
# `[skip ci]` that would otherwise be needed to stop this rebuilding the
|
||||||
|
# image just published is not.
|
||||||
|
- name: Open a pull request for it
|
||||||
if: steps.bump.outputs.changed == 'true'
|
if: steps.bump.outputs.changed == 'true'
|
||||||
env:
|
env:
|
||||||
NEXT: ${{ steps.next.outputs.next }}
|
NEXT: ${{ steps.next.outputs.next }}
|
||||||
|
RELEASED: ${{ github.ref_name }}
|
||||||
|
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
branch="release/bump-${NEXT}"
|
||||||
|
|
||||||
# A name that is not a person, and a reserved address that can never
|
# 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.
|
# resolve to one. Nothing here names the instance it runs on.
|
||||||
git config user.name 'Release bot'
|
git config user.name 'Release bot'
|
||||||
git config user.email '[email protected]'
|
git config user.email '[email protected]'
|
||||||
|
|
||||||
|
git checkout -b "${branch}"
|
||||||
git add package.json package-lock.json
|
git add package.json package-lock.json
|
||||||
# `[skip ci]` because this commit is a number and nothing else:
|
git commit -m "Set the working version to ${NEXT}"
|
||||||
# without it the push to main starts another build of the very image
|
git push origin "${branch}"
|
||||||
# that was just published.
|
|
||||||
git commit -m "Set the working version to ${NEXT} [skip ci]"
|
|
||||||
|
|
||||||
if ! git push origin HEAD:main; then
|
# node rather than jq to build the request body: jq is not in this
|
||||||
echo >&2
|
# image, and node is the one thing that certainly is.
|
||||||
echo "Could not push the version bump to main. Either the Actions" >&2
|
payload="$(BRANCH="${branch}" node -e 'process.stdout.write(JSON.stringify({
|
||||||
echo "token has no write access to this repository, or main is" >&2
|
head: process.env.BRANCH,
|
||||||
echo "protected against direct pushes. Set VERSION_BUMP_TOKEN to a" >&2
|
base: "main",
|
||||||
echo "token that may push to main, or allow that token past the" >&2
|
title: `Set the working version to ${process.env.NEXT}`,
|
||||||
echo "branch protection." >&2
|
body: `${process.env.RELEASED} has shipped, so the tree was left on a version that is published and immutable. This moves it on to ${process.env.NEXT}, which is deliberately not a version that exists.`,
|
||||||
|
}))')"
|
||||||
|
|
||||||
|
api="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
|
||||||
|
response="$(mktemp)"
|
||||||
|
code="$(curl -sS -o "${response}" -w '%{http_code}' \
|
||||||
|
-X POST "${api}/pulls" \
|
||||||
|
-H "Authorization: token ${TOKEN}" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d "${payload}")"
|
||||||
|
|
||||||
|
case "${code}" in
|
||||||
|
201) echo "Opened ${branch} against main." ;;
|
||||||
|
# A release re-run that got this far: the branch and its pull
|
||||||
|
# request are already there, which is the state we wanted anyway.
|
||||||
|
409) echo "A pull request for ${branch} is already open." ;;
|
||||||
|
*)
|
||||||
|
echo "Could not open the pull request (HTTP ${code}):" >&2
|
||||||
|
cat "${response}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
;;
|
||||||
|
esac
|
||||||
|
|||||||
@@ -210,16 +210,22 @@ The registry comes from the `REGISTRY` repository variable, the image name from
|
|||||||
`IMAGE_NAME` or the repository name, and credentials from `REGISTRY_USER` and the
|
`IMAGE_NAME` or the repository name, and credentials from `REGISTRY_USER` and the
|
||||||
`REGISTRY_TOKEN` secret. Nothing about any particular deployment is committed here.
|
`REGISTRY_TOKEN` secret. Nothing about any particular deployment is committed here.
|
||||||
|
|
||||||
A release also moves `package.json` on to the next patch version, committed to main by
|
A release also moves `package.json` on to the next patch version, opened as a pull
|
||||||
the `bump` job — so the number in the tree is never one that has already shipped and
|
request by the `bump` job — so the number in the tree is never one that has already
|
||||||
been made immutable. It lives in `publish.yml` rather than a workflow of its own so it
|
shipped and been made immutable. It lives in `publish.yml` rather than a workflow of its
|
||||||
can say `needs: build`: a version that failed to publish has not been released, and
|
own so it can say `needs: build`: a version that failed to publish has not been
|
||||||
bumping past it would claim otherwise. Prereleases are skipped, being candidates for a
|
released, and bumping past it would claim otherwise. Prereleases are skipped, being
|
||||||
version that has not shipped. The bump goes through `npm version` rather than an edit in
|
candidates for a version that has not shipped. The bump goes through `npm version`
|
||||||
place, because the version is in the lockfile too, in more than one place, and the two
|
rather than an edit in place, because the version is in the lockfile too, in more than
|
||||||
have to agree. The commit carries `[skip ci]`, or pushing it would rebuild the image
|
one place, and the two have to agree.
|
||||||
that was just published. Pushing to main needs a token with write access —
|
|
||||||
`VERSION_BUMP_TOKEN` overrides the task token where that one cannot.
|
It arrives as a pull request rather than as a commit straight to main: pushing a branch
|
||||||
|
asks nothing of the task token beyond ordinary write access, so it does not matter what
|
||||||
|
protects main, and the changed `package.json` goes through a build before it lands.
|
||||||
|
Nothing pushes to main, so no `[skip ci]` is needed to stop the bump rebuilding the
|
||||||
|
image just published. The job is the only one that runs in a container (`node:22`, for
|
||||||
|
npm), and a job in a container is handed `sh`, not bash — hence the explicit
|
||||||
|
`shell: bash`, without which `set -o pipefail` fails the first line of the first step.
|
||||||
|
|
||||||
Two things any deployment has to get right, both learned the hard way:
|
Two things any deployment has to get right, both learned the hard way:
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "antisocial",
|
"name": "antisocial",
|
||||||
"version": "1.2.1",
|
"version": "1.2.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "antisocial",
|
"name": "antisocial",
|
||||||
"version": "1.2.1",
|
"version": "1.2.2",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/static": "10.1.3",
|
"@fastify/static": "10.1.3",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "antisocial",
|
"name": "antisocial",
|
||||||
"version": "1.2.1",
|
"version": "1.2.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Reads social posts back to you without the app.",
|
"description": "Reads social posts back to you without the app.",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
|
|||||||
Reference in New Issue
Block a user