From 63df3d572d930077e707395a5126d2f8d5f02c7e Mon Sep 17 00:00:00 2001 From: John Coffey Date: Wed, 28 Oct 2026 09:35:15 -0700 Subject: [PATCH] Version 27.1-rc.1: the suite's series versions inbuxa-version.json is now 27.1-rc.1, the version the whole suite moves to. The console shows just that: the "(base 1.0.11)" suffix named the release the fork started from, and no upstream version is shown any more. tools/release-version.sh holds the rule (POSIX sh, for the docker:cli image): a release moves its series tag and :latest; a release candidate its series' -rc tag, never :latest. Both CIs publish v27.1, v27.1.N and v27.1-rc.N tags as well as the dated ones from before. The weekly release cuts the next patch of the series (27.1 -> 27.1.1), and while main is on a release candidate it says so and stops: a series and its candidates are cut by hand. --- .gitea/workflows/ci.yml | 17 ++++++++---- .gitea/workflows/weekly-release.yml | 24 ++++++++++------ .github/workflows/ci.yml | 23 +++++++++++---- README.md | 9 ++++-- inbuxa-version.json | 2 +- tools/release-version.sh | 43 +++++++++++++++++++++++++++++ tools/release-version.test.sh | 24 ++++++++++++++++ vite.config.ts | 6 ++-- 8 files changed, 123 insertions(+), 25 deletions(-) create mode 100755 tools/release-version.sh create mode 100755 tools/release-version.test.sh diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 863945a..4ddb8c0 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -38,10 +38,14 @@ name: ci on: push: branches: [main] - # Only date tags publish (v2026.9.21, v2026.9.21.2). The repository still - # carries the inherited v1.0.x tags, and a tag of any other shape pushed - # by hand is not a release. + # Only release tags publish: a series (v27.1), its patches (v27.1.3) and + # release candidates (v27.1-rc.1), and the dated ones from before 27.1 + # (v2026.9.21, v2026.9.21.2). The repository still carries the inherited + # v1.0.x tags, and a tag of any other shape pushed by hand is not a release. tags: + - 'v[0-9][0-9].[0-9]+' + - 'v[0-9][0-9].[0-9]+.[0-9]+' + - 'v[0-9][0-9].[0-9]+-rc.[0-9]+' - 'v[0-9][0-9][0-9][0-9].[0-9]+.[0-9]+' - 'v[0-9][0-9][0-9][0-9].[0-9]+.[0-9]+.[0-9]+' pull_request: @@ -66,6 +70,7 @@ jobs: - run: npm run lint - run: npm run i18n:check - run: npm test + - run: sh tools/release-version.test.sh - run: npm run build # A changed upstream file without the AGPL 5(a) notice, as inbuxa-server's @@ -132,6 +137,9 @@ jobs: exit 1 fi echo "VERSION=$VERSION" >> "$GITHUB_ENV" + # A release moves its series and latest; a release candidate its + # series' -rc tag, never latest. + echo "TAGS=$(sh tools/release-version.sh tags "$VERSION" | tr '\n' ' ')" >> "$GITHUB_ENV" - run: | test -n "$REGISTRY" test -n "$PACKAGE_TOKEN" || { echo "PACKAGE_TOKEN secret is not set on this repository" >&2; exit 1; } @@ -144,8 +152,7 @@ jobs: docker buildx build \ --platform linux/amd64,linux/arm64 \ --provenance=false --sbom=false \ - --tag "$IMAGE:$VERSION" \ - --tag "$IMAGE:latest" \ + $(for t in $TAGS; do printf '%s %s:%s ' --tag "$IMAGE" "$t"; done) \ --push . docker buildx imagetools inspect "$IMAGE:$VERSION" # Gitea keeps a container package on its owner; linking it shows it on diff --git a/.gitea/workflows/weekly-release.yml b/.gitea/workflows/weekly-release.yml index 00d0e99..4abca4e 100644 --- a/.gitea/workflows/weekly-release.yml +++ b/.gitea/workflows/weekly-release.yml @@ -5,9 +5,12 @@ # release with nothing in it moves :latest to an identical build, spends a # version number, and notifies everybody about nothing. # -# The version is the date, YYYY.M.D unpadded, with a .N suffix from 2 for a -# second release on one day. It is committed to main in inbuxa-version.json -# and the tag names that commit, so the commit is the release. +# Since 27.1 the version is a series and its patches: this cuts the next patch +# of the current series (27.1 -> 27.1.1 -> 27.1.2, tools/release-version.sh +# next). A new series and its release candidates (27.1-rc.N) are cut by hand, +# so while main is on a release candidate it says so and stops. The version is +# committed to main in inbuxa-version.json and the tag names that commit, so +# the commit is the release. # # Mondays 09:37 UTC, as release.yml did. Run it by hand from the Actions tab # (workflow_dispatch); dry_run defaults to true, so a manual run shows the @@ -74,6 +77,12 @@ jobs: set -euo pipefail API="${CI_SERVER_INTERNAL}/api/v1/repos/${GITHUB_REPOSITORY}" sha="$(git rev-parse HEAD)" + current="$(jq -er .version inbuxa-version.json)" + version="$(sh tools/release-version.sh next "$current")" + if [ -z "$version" ]; then + echo "main is on release candidate ${current}: release candidates are cut by hand, so there's no weekly release." + exit 0 + fi # The newest published release, or empty on a project that has never # had one -- in which case everything counts as new. @@ -92,12 +101,11 @@ jobs: echo "Nothing to release: no commits since ${previous}."; exit 0 fi - today="$(date -u +%Y.%-m.%-d)" - version="$today"; n=2 - while git show-ref --verify --quiet "refs/tags/v${version}"; do - version="${today}.${n}"; n=$((n + 1)) - done tag="v${version}" + if git show-ref --verify --quiet "refs/tags/${tag}"; then + echo "${tag} exists but main still says ${current}; fix main first." >&2 + exit 1 + fi echo "Releasing ${tag} -- ${count} commit(s) since ${previous:-the beginning}, from ${sha}." if [ "$DRY_RUN" = "1" ]; then echo "Dry run (RELEASE_LIVE='${{ vars.RELEASE_LIVE }}'): stopping here."; exit 0; fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8582247..479f128 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,10 +32,14 @@ name: ci on: push: branches: ['**'] - # Only date tags publish (v2026.9.21, v2026.9.21.2), the same filter as - # Gitea's ci.yml. The repository still carries the inherited v1.0.x tags, - # and a tag of any other shape pushed by hand is not a release. + # Only release tags publish, the same filter as Gitea's ci.yml: a series (v27.1), its patches (v27.1.3) and + # release candidates (v27.1-rc.1), and the dated ones from before 27.1 + # (v2026.9.21, v2026.9.21.2). The repository still carries the inherited + # v1.0.x tags, and a tag of any other shape pushed by hand is not a release. tags: + - 'v[0-9][0-9].[0-9]+' + - 'v[0-9][0-9].[0-9]+.[0-9]+' + - 'v[0-9][0-9].[0-9]+-rc.[0-9]+' - 'v[0-9][0-9][0-9][0-9].[0-9]+.[0-9]+' - 'v[0-9][0-9][0-9][0-9].[0-9]+.[0-9]+.[0-9]+' workflow_dispatch: @@ -88,6 +92,7 @@ jobs: - run: npm run lint - run: npm run i18n:check - run: npm test + - run: sh tools/release-version.test.sh - run: npm run build # ----------------------------------------------------------- publish ------ @@ -100,6 +105,9 @@ jobs: runs-on: ubuntu-latest outputs: version: ${{ steps.v.outputs.version }} + # A release moves its series and latest; a release candidate its + # series' -rc tag, never latest. + tags: ${{ steps.v.outputs.tags }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - id: v @@ -111,6 +119,7 @@ jobs: exit 1 fi echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "tags=$(sh tools/release-version.sh tags "$VERSION" | tr '\n' ' ')" >> "$GITHUB_OUTPUT" # Each architecture on its own native runner, pushed as an untagged image by # digest; `publish` joins the two digests into one multi-arch tag. @@ -186,10 +195,12 @@ jobs: - name: Create the manifest env: VERSION: ${{ needs.version.outputs.version }} + TAGS: ${{ needs.version.outputs.tags }} run: | refs=() for f in /tmp/digests/*; do refs+=("${IMAGE}@sha256:$(basename "$f")"); done - docker buildx imagetools create -t "${IMAGE}:${VERSION}" -t "${IMAGE}:latest" "${refs[@]}" + tags=(); for t in $TAGS; do tags+=(-t "${IMAGE}:${t}"); done + docker buildx imagetools create "${tags[@]}" "${refs[@]}" docker buildx imagetools inspect "${IMAGE}:${VERSION}" # Shows the package on the repository's Packages tab. Idempotent. - env: @@ -265,13 +276,15 @@ jobs: - env: GH_TOKEN: ${{ github.token }} TAG: ${{ needs.version.outputs.version }} + TAGS: ${{ needs.version.outputs.tags }} run: | set -euo pipefail src="${{ vars.REGISTRY }}/${GITHUB_REPOSITORY,,}" dst="ghcr.io/${GITHUB_REPOSITORY,,}" tag="$TAG" echo "$GH_TOKEN" | docker login ghcr.io -u "$GITHUB_ACTOR" --password-stdin - docker buildx imagetools create -t "$dst:$tag" -t "$dst:latest" "$src:$tag" + args=(); for t in $TAGS; do args+=(-t "$dst:$t"); done + docker buildx imagetools create "${args[@]}" "$src:$tag" want="$(docker buildx imagetools inspect "$src:$tag" --format '{{json .Manifest.Digest}}')" got="$(docker buildx imagetools inspect "$dst:$tag" --format '{{json .Manifest.Digest}}')" echo "registry $src:$tag = $want" diff --git a/README.md b/README.md index 5994598..af8166b 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,12 @@ features are this fork's own. ## Versions -INBUXA Admin has its own dated version (`inbuxa-version.json`), shown with the -upstream release it was forked from: `INBUXA Admin 2026.9.18 (base 1.0.11)`. -`package.json` keeps that release's version. +INBUXA Admin is versioned with the rest of the suite (`inbuxa-version.json`): +a series and its patches (`27.1`, `27.1.3`), and release candidates before a +series ships (`27.1-rc.1`). Releases before 27.1 were dated (`2026.10.26.2`). +The weekly release cuts the next patch; a series and its release candidates +are cut by hand. `package.json`'s version is a leftover from the fork and +isn't shown anywhere. ## Source code diff --git a/inbuxa-version.json b/inbuxa-version.json index a226cb5..5ae3c1a 100644 --- a/inbuxa-version.json +++ b/inbuxa-version.json @@ -1,3 +1,3 @@ { - "version": "2026.10.26.2" + "version": "27.1-rc.1" } diff --git a/tools/release-version.sh b/tools/release-version.sh new file mode 100755 index 0000000..855852d --- /dev/null +++ b/tools/release-version.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# SPDX-FileCopyrightText: 2026 Coffey Labs LLC +# SPDX-License-Identifier: AGPL-3.0-only +# +# The suite's release versions, as tags and images name them: 27.1 for a +# series, 27.1.3 for its patches, 27.1-rc.1 for a release candidate. The +# server's tools/release/version.py is the same rule in its Cargo form. +# +# release-version.sh tags VERSION image tags, one per line: a release +# moves VERSION, its series and latest; +# a release candidate VERSION and +# -rc, never latest +# release-version.sh prerelease VERSION exit 0 for a release candidate +# release-version.sh next VERSION the next weekly patch, or nothing +# while on a release candidate +# +# Anything else, including the old dated versions, is refused (exit 2). +# POSIX sh: it runs in the docker:cli image, which has no bash. +set -eu + +usage() { sed -n '9,18p' "$0" >&2; exit 2; } +[ $# -eq 2 ] || usage +cmd=$1 v=$2 + +echo "$v" | grep -Eq '^[1-9][0-9]\.([1-9]|1[0-2])(\.[1-9][0-9]*|-rc\.[1-9][0-9]*)?$' \ + || { echo "$v is not YY.M, YY.M.N or YY.M-rc.N" >&2; exit 2; } +series=$(echo "$v" | sed -E 's/^([0-9]+\.[0-9]+).*/\1/') +case $v in *-rc.*) rc=1 ;; *) rc=0 ;; esac + +case $cmd in + tags) + if [ $rc = 1 ]; then printf '%s\n%s-rc\n' "$v" "$series" + elif [ "$v" = "$series" ]; then printf '%s\nlatest\n' "$v" + else printf '%s\n%s\nlatest\n' "$v" "$series" + fi ;; + prerelease) [ $rc = 1 ] ;; + next) + if [ $rc = 1 ]; then : + elif [ "$v" = "$series" ]; then echo "$series.1" + else echo "$series.$(( ${v##*.} + 1 ))" + fi ;; + *) usage ;; +esac diff --git a/tools/release-version.test.sh b/tools/release-version.test.sh new file mode 100755 index 0000000..586951a --- /dev/null +++ b/tools/release-version.test.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# SPDX-FileCopyrightText: 2026 Coffey Labs LLC +# SPDX-License-Identifier: AGPL-3.0-only +# Tests for release-version.sh: sh tools/release-version.test.sh +set -u +here=$(dirname "$0"); s="$here/release-version.sh"; fail=0 +check() { # expected, then the arguments + want=$1; shift + got=$(sh "$s" "$@" 2>/dev/null | tr '\n' ' ')"rc=$(sh "$s" "$@" >/dev/null 2>&1; echo $?)" + [ "$got" = "$want" ] || { echo "FAIL: $* -> '$got', want '$want'"; fail=1; } +} +check '27.1-rc.2 27.1-rc rc=0' tags 27.1-rc.2 +check '27.1 latest rc=0' tags 27.1 +check '27.1.4 27.1 latest rc=0' tags 27.1.4 +check 'rc=0' prerelease 27.1-rc.1 +check 'rc=1' prerelease 27.1 +check 'rc=0' next 27.1-rc.3 +check '27.1.1 rc=0' next 27.1 +check '27.1.10 rc=0' next 27.1.9 +for bad in 2026.10.26.2 2026.10.26 27.1.0 27.1.0-rc.1 27.13 27.1-rc.0 27.01 v27.1 ''; do + check 'rc=2' tags "$bad" +done +[ $fail = 0 ] && echo "release-version: all passed" +exit $fail diff --git a/vite.config.ts b/vite.config.ts index 269ceb1..e9faf5a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,15 +8,15 @@ import path from 'path' import { configDefaults, defineConfig } from 'vitest/config' import inbuxa from './inbuxa-version.json' with { type: 'json' } -import { version } from './package.json' with { type: 'json' } import { sievePlayground } from './sieve-playground.ts' export default defineConfig({ // Relative, so one build works at any mount point (/admin, /account). base: './', define: { - // The release's dated version; package.json still carries upstream's. - __APP_VERSION__: JSON.stringify(`${inbuxa.version} (base ${version})`), + // The release version as tags name it (27.1, 27.1.3, 27.1-rc.1). Only + // inbuxa's: package.json's is a leftover nothing shows. + __APP_VERSION__: JSON.stringify(inbuxa.version), }, plugins: [react(), tailwindcss(), sievePlayground()], resolve: { alias: { '@': path.resolve(import.meta.dirname, './src') } }, -- 2.54.0