From 99cf9712f497c3f5ce309ffae7e9a81953008b48 Mon Sep 17 00:00:00 2001 From: mathieuHa Date: Thu, 30 Jul 2026 20:18:15 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20cicd:=20bump=20the=20version=20befo?= =?UTF-8?q?re=20tagging=20so=20releases=20report=20their=20own=20version?= =?UTF-8?q?=20(#365)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ cicd: bump the version before tagging instead of after The version reported to the Crowdsec LAPI lives in version.go, so it must be correct in the very commit the tag points at. Every mechanism so far updated it *after* the tag existed, which cannot work: - release.yml ran on `release: published` and force-moved the tag. It also failed on all four of its runs and was removed in #360. - The Renovate customManager on version.go uses the github-tags datasource, so it can only propose vX once vX is already tagged. The bump always lands after the tag. Result: v1.7.0 is tagged at a commit reading v1.6.0 (#363), same shape as the earlier #322. Replace both with a two-step flow that bumps first and tags last, so the released source always matches its tag. Co-Authored-By: Claude Opus 5 (1M context) * :bento: reduce loc + remove claude code comment * :bento: remove useless spellcheck disable --------- Co-authored-by: Claude Opus 5 (1M context) Co-authored-by: maxlerebourg --- .github/workflows/release-prepare.yml | 83 +++++++++++++++++++++++++++ .github/workflows/release-publish.yml | 53 +++++++++++++++++ renovate.json | 10 ---- version.go | 5 +- 4 files changed, 139 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/release-prepare.yml create mode 100644 .github/workflows/release-publish.yml diff --git a/.github/workflows/release-prepare.yml b/.github/workflows/release-prepare.yml new file mode 100644 index 0000000..79947c6 --- /dev/null +++ b/.github/workflows/release-prepare.yml @@ -0,0 +1,83 @@ +name: Release (1/2) Prepare + +# Step 1 of the release process: bump pluginVersion *before* the tag exists. +# +# The version reported to the Crowdsec LAPI lives in version.go, so it has to +# be correct in the very commit the tag points at. Anything that patches +# version.go after the release is published is too late: Traefik's plugin +# service caches the plugin archive per module+version, so users keep the +# source that was there when the tag was first resolved (see #322, #363). +# +# This workflow opens a "release" PR containing only that bump. Merging it +# triggers Release (2/2) Publish, which creates the tag and the GitHub release +# on the merged commit. + +on: + workflow_dispatch: + inputs: + version: + description: "Version to release, e.g. v1.7.1 or v1.8.0-alpha" + required: true + type: string + +permissions: + contents: write + pull-requests: write + +jobs: + prepare: + name: Open release PR for ${{ inputs.version }} + runs-on: ubuntu-latest + steps: + - name: Check out main + uses: actions/checkout@v7 + with: + ref: main + fetch-depth: 0 + + - name: Validate version + env: + VERSION: ${{ inputs.version }} + run: | + if ! [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$ ]]; then + echo "::error::'$VERSION' is not a vX.Y.Z / vX.Y.Z-suffix version" + exit 1 + fi + if git rev-parse -q --verify "refs/tags/$VERSION" >/dev/null; then + echo "::error::tag $VERSION already exists" + exit 1 + fi + + - name: Bump version.go + env: + VERSION: ${{ inputs.version }} + run: | + sed -i 's/pluginVersion = "[^"]*"/pluginVersion = "'"$VERSION"'"/' version.go + cat version.go + if git diff --quiet -- version.go; then + echo "::error::version.go already reads $VERSION, nothing to release" + exit 1 + fi + + - name: Push release branch and open PR + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ inputs.version }} + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git switch -c "release/$VERSION" + git commit -am "🔖 release $VERSION" + git push -u origin "release/$VERSION" + + cat > /tmp/pr-body.md < Keep the PR title as-is: **Release (2/2) Publish** matches on it. + EOF + + gh pr create --base main --head "release/$VERSION" --title "🔖 release $VERSION" --body-file /tmp/pr-body.md diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml new file mode 100644 index 0000000..7e21347 --- /dev/null +++ b/.github/workflows/release-publish.yml @@ -0,0 +1,53 @@ +name: Release (2/2) Publish + +# Step 2 of the release process: tag and publish the commit prepared by +# Release (1/2) Prepare. +# +# Triggered by the release PR landing on main. The tag is created on that +# commit, so version.go inside the released source always matches the tag — +# no post-release patching, no force-moved tags. + +on: + push: + branches: [main] + paths: ["version.go"] + +permissions: + contents: write + +jobs: + publish: + name: Tag and publish + runs-on: ubuntu-latest + steps: + - name: Check out the pushed commit + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Resolve release version + id: resolve + run: | + version="$(git log -1 --format='%B' | grep -oP '🔖 release \Kv[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?' || true)" + [ -z "$version" ] && { echo "version.go changed outside a release commit, nothing to do"; echo "release=false" >> "$GITHUB_OUTPUT"; exit 0; } + + in_source="$(sed -n 's/.*pluginVersion = "\([^"]*\)".*/\1/p' version.go)" + [ "$in_source" != "$version" ] && { echo "::error::commit says $version but version.go reads $in_source"; exit 1; } + git rev-parse -q --verify "refs/tags/$version" >/dev/null && { echo "::error::tag $version already exists"; exit 1; } + + echo "release=true" >> "$GITHUB_OUTPUT" + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "prerelease=$([[ "$version" == *-* ]] && echo '--prerelease')" >> "$GITHUB_OUTPUT" + + - name: Tag and create the GitHub release + if: steps.resolve.outputs.release == 'true' + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ steps.resolve.outputs.version }} + PRERELEASE: ${{ steps.resolve.outputs.prerelease }} + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag -a "$VERSION" -m "$VERSION" + git push origin "$VERSION" + gh release create "$VERSION" --title "$VERSION" --generate-notes $PRERELEASE diff --git a/renovate.json b/renovate.json index 7a3248c..51ef9ca 100644 --- a/renovate.json +++ b/renovate.json @@ -25,16 +25,6 @@ } ], "customManagers": [ - { - "description": "Plugin self-pin in version.go (pluginVersion)", - "customType": "regex", - "managerFilePatterns": ["/^version\\.go$/"], - "matchStrings": [ - "pluginVersion\\s*=\\s*\"(?v[0-9]+\\.[0-9]+\\.[0-9]+)\"" - ], - "depNameTemplate": "maxlerebourg/crowdsec-bouncer-traefik-plugin", - "datasourceTemplate": "github-tags" - }, { "description": "Plugin self-pin in docker-compose CLI args (--experimental.plugins.bouncer.version=vX)", "customType": "regex", diff --git a/version.go b/version.go index 03d61a5..a7c006e 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,5 @@ package crowdsec_bouncer_traefik_plugin //nolint:revive,stylecheck -// pluginVersion is updated automatically by the release workflow and Renovate. -var pluginVersion = "v1.7.0" //nolint:gochecknoglobals +// pluginVersion is what the plugin reports to the Crowdsec LAPI. +// Do not edit by hand: the "Release (1/2) Prepare" workflow bumps it. +var pluginVersion = "v1.6.0" //nolint:gochecknoglobals