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