Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .github/workflows/auto-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Auto-release on upstream version bump

on:
push:
branches: [main]
paths:
- docker-bake.hcl

permissions: {}

jobs:
check-version-bump:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
bumped: ${{ steps.check.outputs.bumped }}
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false

- name: Check for tracked upstream version bump
id: check
run: |
set -euo pipefail
BEFORE="${{ github.event.before }}"
if [ -z "${BEFORE//0/}" ]; then
echo "No previous commit to diff against; skipping."
echo "bumped=false" >> "$GITHUB_OUTPUT"
exit 0
fi

version_of() {
git show "$1:docker-bake.hcl" | awk -v var="$2" '
$0 ~ "variable \"" var "\"" { found=1 }
found && /default *=/ { print; exit }
'
}

bumped=false
for var in modsec2-version modsec3-version httpd-version nginx-version; do
old=$(version_of "$BEFORE" "$var")
new=$(version_of "${{ github.sha }}" "$var")
if [ "$old" != "$new" ]; then
echo "Version bump detected for $var: $old -> $new"
bumped=true
fi
done
echo "bumped=$bumped" >> "$GITHUB_OUTPUT"

tag-and-release:
needs: check-version-bump
if: needs.check-version-bump.outputs.bumped == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # actions/checkout@v5
with:
persist-credentials: false

- name: Create release tag and GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
base="release/$(date -u +%Y%m%d)"
tag="$base"
suffix=1
while gh api "repos/${{ github.repository }}/git/ref/tags/$tag" >/dev/null 2>&1; do
tag="${base}-${suffix}"
suffix=$((suffix + 1))
done

gh release create "$tag" \
--target "${{ github.sha }}" \
--title "$tag" \
--generate-notes
Comment on lines +66 to +82

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Race window in tag-existence check; no run-level concurrency control.

Two pushes to main touching docker-bake.hcl in quick succession can trigger overlapping workflow runs. Both tag-and-release jobs would independently loop-check git/ref/tags/$tag, and since the existence check and the gh release create call aren't atomic, both could pass the check for the same $tag before either creates it, causing one run to fail with a duplicate-tag error. Additionally, since only the gh api exit code is checked, a transient non-404 failure (auth hiccup, rate limit, network blip) would be indistinguishable from "tag not found" and cause the script to proceed as if the name were free.

Add a workflow-level concurrency group to serialize runs on main.

🔒 Suggested fix
 on:
   push:
     branches: [main]
     paths:
       - docker-bake.hcl
 
+concurrency:
+  group: auto-release-${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: false
+
 permissions: {}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/auto-release.yml around lines 66 - 82, Add workflow-level
concurrency to the auto-release workflow so only one release run on main can
create tags at a time. The issue is in the Create release tag and GitHub release
step: the gh api loop in tag-and-release is a non-atomic existence check, so
overlapping runs can still pick the same tag and collide. Fix it by adding a
top-level concurrency group keyed to the workflow/ref so runs serialize before
reaching gh release create, and keep the tag-generation logic in the
tag-and-release job as the unique symbol to locate.


publish:
needs: tag-and-release
permissions:
contents: read
packages: write
id-token: write
uses: ./.github/workflows/publish.yml
secrets:
dockerhub_user: ${{ secrets.dockerhub_user }}
dockerhub_token: ${{ secrets.dockerhub_token }}
6 changes: 6 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ name: Build and publish images for container registries
on:
release:
types: [published]
workflow_call:
secrets:
dockerhub_user:
required: true
dockerhub_token:
required: true

permissions: {}

Expand Down
Loading