From 6d8263c5b56e37483e87432b8871f25a1d99aabc Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Sun, 31 May 2026 16:26:31 -0500 Subject: [PATCH] fix(ci): consolidate all auto-tag changelog fixes Three issues addressed together: 1. Race condition (was PR #56): changelog job now CREATES the Gitea release rather than assuming build jobs have already created it. Build jobs continue to use create-or-skip + upload unchanged. 2. Detached HEAD push: 'git push origin master' fails when HEAD is detached (no local branch named master). Changed to 'HEAD:master'. 3. git-cliff tag guard: verify tag is present locally before running git-cliff, to fail fast with a clear message rather than silently generating a wrong changelog. 4. git commit idiom: replaced 'git commit || echo' (swallows all non-zero exit codes including real failures) with an explicit 'git diff --staged --quiet' guard so set -euo pipefail is not undermined. --- .gitea/workflows/auto-tag.yml | 64 +++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/.gitea/workflows/auto-tag.yml b/.gitea/workflows/auto-tag.yml index 8066dcc0..4e043d42 100644 --- a/.gitea/workflows/auto-tag.yml +++ b/.gitea/workflows/auto-tag.yml @@ -125,22 +125,28 @@ jobs: RELEASE_TAG: ${{ needs.autotag.outputs.release_tag }} run: | set -eu - # Use the tag output from autotag — never rely on git describe CURRENT_TAG="${RELEASE_TAG}" echo "Building changelog for $CURRENT_TAG" + + # Verify the tag is present locally after fetch + if ! git rev-parse "refs/tags/${CURRENT_TAG}" >/dev/null 2>&1; then + echo "ERROR: tag ${CURRENT_TAG} not found locally after fetch" + exit 1 + fi + git-cliff --config cliff.toml --output CHANGELOG.md PREV_TAG=$(git tag --sort=-version:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \ | grep -v "^${CURRENT_TAG}$" | head -1 || echo "") if [ -n "$PREV_TAG" ]; then git-cliff --config cliff.toml --tag "$CURRENT_TAG" --strip all > /tmp/release_body.md || true else - echo "=== No previous tag found, generating from git commits ===" + echo "No previous tag found, generating from git commits" git log --pretty=format:"- %s" > /tmp/release_body.md || true fi echo "=== Release body preview ===" cat /tmp/release_body.md - - name: Update Gitea release body + - name: Create or update Gitea release env: RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} RELEASE_TAG: ${{ needs.autotag.outputs.release_tag }} @@ -148,18 +154,41 @@ jobs: set -eu TAG="${RELEASE_TAG}" API="http://172.0.0.29:3000/api/v1/repos/$GITHUB_REPOSITORY" - RELEASE_ID=$(curl -sf "$API/releases/tags/$TAG" \ - -H "Authorization: token $RELEASE_TOKEN" | jq -r '.id') + + # Try to find an existing release for this tag + RELEASE_ID=$(curl -s "$API/releases/tags/$TAG" \ + -H "Authorization: token $RELEASE_TOKEN" | jq -r '.id // empty') + + if [ -z "$RELEASE_ID" ]; then + # First run: changelog job owns release creation so build jobs + # never race against a missing release object + echo "Creating release $TAG..." + RELEASE_ID=$(jq -n \ + --arg tag "$TAG" \ + --arg name "TFTSR $TAG" \ + --rawfile body /tmp/release_body.md \ + '{tag_name: $tag, name: $name, body: $body, draft: false}' \ + | curl -sf -X POST "$API/releases" \ + -H "Authorization: token $RELEASE_TOKEN" \ + -H "Content-Type: application/json" \ + --data @- \ + | jq -r '.id') + echo "✓ Release created (id=$RELEASE_ID)" + else + # Re-run: patch the body only + echo "Updating existing release $TAG (id=$RELEASE_ID)..." + jq -n --rawfile body /tmp/release_body.md '{body: $body}' \ + | curl -sf -X PATCH "$API/releases/$RELEASE_ID" \ + -H "Authorization: token $RELEASE_TOKEN" \ + -H "Content-Type: application/json" \ + --data @- + echo "✓ Release body updated" + fi + if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then - echo "ERROR: Could not find release for tag $TAG" + echo "ERROR: Failed to create or locate release for $TAG" exit 1 fi - BODY=$(jq -n --rawfile note /tmp/release_body.md '{body: $note}') - curl -sf -X PATCH "$API/releases/$RELEASE_ID" \ - -H "Authorization: token $RELEASE_TOKEN" \ - -H "Content-Type: application/json" \ - -d "$BODY" - echo "✓ Release body updated" - name: Commit CHANGELOG.md to master env: @@ -167,13 +196,20 @@ jobs: run: | set -euo pipefail TAG="${RELEASE_TAG}" - # Validate tag format to prevent shell injection in commit message / JSON if ! echo "$TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then echo "ERROR: Unexpected tag format: $TAG" exit 1 fi git add CHANGELOG.md - git commit -m "chore: update CHANGELOG.md for ${TAG} [skip ci]" || echo "No changes to commit" + # Only commit if CHANGELOG.md actually changed — avoids ambiguous + # exit-code handling from 'git commit || echo' with set -e + if git diff --staged --quiet; then + echo "No CHANGELOG.md changes to commit" + else + git commit -m "chore: update CHANGELOG.md for ${TAG} [skip ci]" + fi + # HEAD:master works in detached HEAD state; 'git push origin master' + # would fail because there is no local branch named master git push origin HEAD:master echo "✓ CHANGELOG.md committed to master"