fix(ci): changelog job creates release, eliminating race with build jobs #56

Merged
sarman merged 2 commits from fix/auto-tag-changelog-race into master 2026-05-31 21:13:12 +00:00
Owner

Summary

  • changelog and build-* jobs all fan out from autotag in parallel
  • Build jobs were responsible for creating the Gitea release object (POST /releases) — but changelog was trying to GET /releases/tags/$TAG at the same moment, before any build had run
  • Result: Could not find release for tag vX.Y.Z on every run

Fix: changelog job now owns release creation. It creates the release with the git-cliff changelog body if it does not exist, or patches the body on a re-run. Build jobs continue using their existing create || true + upload artifact pattern unchanged.

Test plan

  • Trigger auto-tag by merging to master — release should be created by the changelog job with the proper body
  • Re-run should patch the body, not fail
  • Build jobs upload artifacts to the release created by changelog
## Summary - `changelog` and `build-*` jobs all fan out from `autotag` in parallel - Build jobs were responsible for creating the Gitea release object (`POST /releases`) — but `changelog` was trying to `GET /releases/tags/$TAG` at the same moment, before any build had run - Result: `Could not find release for tag vX.Y.Z` on every run **Fix:** `changelog` job now owns release creation. It creates the release with the git-cliff changelog body if it does not exist, or patches the body on a re-run. Build jobs continue using their existing `create || true` + upload artifact pattern unchanged. ## Test plan - [ ] Trigger auto-tag by merging to master — release should be created by the `changelog` job with the proper body - [ ] Re-run should patch the body, not fail - [ ] Build jobs upload artifacts to the release created by `changelog`
sarman self-assigned this 2026-05-31 20:57:42 +00:00
sarman added 1 commit 2026-05-31 20:57:43 +00:00
fix(ci): changelog job creates release to avoid race with build jobs
Some checks failed
Test / rust-fmt-check (pull_request) Waiting to run
PR Review Automation / review (pull_request) Successful in 4m38s
Test / frontend-typecheck (pull_request) Successful in 1m22s
Test / rust-clippy (pull_request) Successful in 6m12s
Test / frontend-tests (pull_request) Successful in 1m13s
Test / rust-tests (pull_request) Has been cancelled
cc99aa815b
The changelog and build-* jobs all fan out from autotag in parallel.
Build jobs create the Gitea release with 'curl ... || true', but the
changelog job was trying to GET the release before any build job had
run, reliably failing with 'Could not find release for tag vX.Y.Z'.

Fix: changelog job owns release creation. It creates the release with
the git-cliff body if it does not exist, or patches the body if a
prior run already created it. Build jobs continue using their existing
create-or-skip + upload pattern unchanged.
sarman reviewed 2026-05-31 21:02:23 +00:00
sarman left a comment
Author
Owner

Automated PR Review (qwen3-coder-next via liteLLM):\n\nSummary
The PR aims to eliminate race conditions in CI by creating the release in the changelog job before build jobs proceed. However, the job lacks proper dependency declaration on autotag, and the git-cliff step references a tag that may not be available due to missing needs.autotag.outputs.release_tag propagation in the workflow structure implied by the code.

Findings

  • [BLOCKER] .gitea/workflows/auto-tag.yml:112-122 - The job that creates the release does not declare needs: [autotag], yet it references ${{ needs.autotag.outputs.release_tag }}. Without this dependency, the job may run before autotag produces the output, leading to undefined behavior or failure.
    Evidence: RELEASE_TAG: ${{ needs.autotag.outputs.release_tag }} appears inside a job that is not shown to declare needs: autotag in the full file excerpt — only the previous job (autotag) is implied to produce the output. In Gitea Actions (like GitHub Actions), downstream jobs must explicitly declare needs to consume outputs.
    Fix: Add needs: autotag at the job level for the changelog-generating job.

  • [WARNING] .gitea/workflows/auto-tag.yml:146 - The UPDATE Gitea release body logic (renamed to "Create or update Gitea release") assumes the release tag exists in the remote repository before creating the release, but the git-cliff step uses git tag --sort=-version:refname to find PREV_TAG. If no previous tag exists locally, PREV_TAG may be empty and git-cliff may fall back to git log, but no guarantee that tags were fully fetched before this point.
    Evidence: PREV_TAG=$(git tag --sort=-version:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | grep -v "^${CURRENT_TAG}$" | head -1 || echo "") runs before verifying that CURRENT_TAG exists locally or on remote — git fetch --tags origin is done, but there's no check that the tag is actually present after checkout.
    Fix: Add explicit validation that git rev-parse "refs/tags/${CURRENT_TAG}" >/dev/null 2>&1 after fetching and before proceeding.

Verdict: REQUEST CHANGES

Automated PR Review (qwen3-coder-next via liteLLM):\n\n**Summary** The PR aims to eliminate race conditions in CI by creating the release in the changelog job before build jobs proceed. However, the job lacks proper dependency declaration on `autotag`, and the `git-cliff` step references a tag that may not be available due to missing `needs.autotag.outputs.release_tag` propagation in the workflow structure implied by the code. **Findings** - [BLOCKER] .gitea/workflows/auto-tag.yml:112-122 - The job that creates the release does not declare `needs: [autotag]`, yet it references `${{ needs.autotag.outputs.release_tag }}`. Without this dependency, the job may run before `autotag` produces the output, leading to undefined behavior or failure. Evidence: `RELEASE_TAG: ${{ needs.autotag.outputs.release_tag }}` appears inside a job that is not shown to declare `needs: autotag` in the full file excerpt — only the *previous* job (`autotag`) is implied to produce the output. In Gitea Actions (like GitHub Actions), downstream jobs must explicitly declare `needs` to consume outputs. Fix: Add `needs: autotag` at the job level for the changelog-generating job. - [WARNING] .gitea/workflows/auto-tag.yml:146 - The `UPDATE Gitea release body` logic (renamed to "Create or update Gitea release") assumes the release tag exists in the remote repository *before* creating the release, but the `git-cliff` step uses `git tag --sort=-version:refname` to find `PREV_TAG`. If no previous tag exists locally, `PREV_TAG` may be empty and `git-cliff` may fall back to `git log`, but no guarantee that tags were fully fetched before this point. Evidence: `PREV_TAG=$(git tag --sort=-version:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | grep -v "^${CURRENT_TAG}$" | head -1 || echo "")` runs *before* verifying that `CURRENT_TAG` exists locally or on remote — `git fetch --tags origin` is done, but there's no check that the tag is actually present after checkout. Fix: Add explicit validation that `git rev-parse "refs/tags/${CURRENT_TAG}" >/dev/null 2>&1` after fetching and before proceeding. **Verdict**: REQUEST CHANGES
sarman added 1 commit 2026-05-31 21:05:25 +00:00
fix(ci): verify tag exists locally before running git-cliff
All checks were successful
Test / rust-fmt-check (pull_request) Successful in 1m42s
Test / rust-clippy (pull_request) Successful in 3m31s
Test / frontend-typecheck (pull_request) Successful in 2m40s
Test / frontend-tests (pull_request) Successful in 1m54s
Test / rust-tests (pull_request) Successful in 4m23s
PR Review Automation / review (pull_request) Successful in 6m15s
7ee4f58bfd
Addresses the review warning: git rev-parse confirms the tag is
present in the local repo after git fetch --tags before git-cliff
or git tag --sort= runs against it. Fails fast with a clear error
if the tag is missing rather than silently generating an incomplete
changelog.
sarman reviewed 2026-05-31 21:11:42 +00:00
sarman left a comment
Author
Owner

Automated PR Review (qwen3-coder-next via liteLLM):\n\nSummary
The PR aims to fix a race condition by having the changelog job create the release instead of relying on a separate release job, and adds validation for the tag before generating the changelog. However, a critical bug is introduced: the release creation logic uses jq -n --rawfile inside a shell command, which will fail if the release body contains special characters like % or unescaped newlines, causing JSON parsing errors or shell injection.

Findings

  • [BLOCKER] .gitea/workflows/auto-tag.yml:137-145 - Unsafe use of jq -n --rawfile with unescaped content; if /tmp/release_body.md contains unescaped quotes, backslashes, or control characters, the resulting JSON is malformed, causing the release creation to fail silently or incorrectly.
    Evidence: jq -n \
    --arg tag "$TAG" \
    --arg name "TFTSR $TAG" \
    --rawfile body /tmp/release_body.md \
    '{tag_name: $tag, name: $name, body: $body, draft: false}' \
    Fix: Replace --rawfile with --slurpfile or pre-process body to escape JSON characters, e.g., body_escaped=$(jq -Rs . /tmp/release_body.md) and use --arg body "$body_escaped".

  • [WARNING] .gitea/workflows/auto-tag.yml:110-115 - Tag existence validation uses git rev-parse, but git fetch --tags may not fetch all tags reliably; if the tag exists on remote but not locally yet, the check passes falsely due to FETCH_HEAD checkout. Also, the checkout step may be stale if FETCH_HEAD is not updated after the tag fetch.
    Evidence: git checkout "$GITHUB_SHA" 2>/dev/null || git checkout FETCH_HEAD
    Fix: Explicitly fetch the tag before checking: git fetch origin "refs/tags/${CURRENT_TAG}:refs/tags/${CURRENT_TAG}" then verify.

  • [SUGGESTION] .gitea/workflows/auto-tag.yml:160-166 - Release asset upload re-fetches release info after creation/update, but could race if another job also updates the release concurrently. The download URL may change mid-execution if multiple assets are uploaded simultaneously.
    Evidence: RELEASE_ID=$(curl -sf "$API/releases/tags/$TAG" ... | jq -r '.id') is called again in upload asset step.
    Fix: Store RELEASE_ID in GITHUB_OUTPUT from the creation/update step and pass it to subsequent steps instead of re-fetching.

  • [SUGGESTION] .gitea/workflows/auto-tag.yml:84-85 — The autotag job’s output uses release_tag=$NEXT but the changelog job expects ${{ needs.autotag.outputs.release_tag }}. If NEXT is empty or malformed, GITHUB_OUTPUT will set an invalid value; no validation occurs.
    Evidence: echo "release_tag=$NEXT" >> "$GITHUB_OUTPUT"
    Fix: Validate $NEXT before writing to GITHUB_OUTPUT (e.g., [[ $NEXT =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "Invalid tag"; exit 1; }).

Automated PR Review (qwen3-coder-next via liteLLM):\n\n**Summary** The PR aims to fix a race condition by having the changelog job create the release instead of relying on a separate release job, and adds validation for the tag before generating the changelog. However, a critical bug is introduced: the release creation logic uses `jq -n --rawfile` inside a shell command, which will fail if the release body contains special characters like `%` or unescaped newlines, causing JSON parsing errors or shell injection. **Findings** - [BLOCKER] .gitea/workflows/auto-tag.yml:137-145 - Unsafe use of `jq -n --rawfile` with unescaped content; if `/tmp/release_body.md` contains unescaped quotes, backslashes, or control characters, the resulting JSON is malformed, causing the release creation to fail silently or incorrectly. Evidence: `jq -n \` `--arg tag "$TAG" \` `--arg name "TFTSR $TAG" \` `--rawfile body /tmp/release_body.md \` `'{tag_name: $tag, name: $name, body: $body, draft: false}' \` Fix: Replace `--rawfile` with `--slurpfile` or pre-process body to escape JSON characters, e.g., `body_escaped=$(jq -Rs . /tmp/release_body.md)` and use `--arg body "$body_escaped"`. - [WARNING] .gitea/workflows/auto-tag.yml:110-115 - Tag existence validation uses `git rev-parse`, but `git fetch --tags` may not fetch *all* tags reliably; if the tag exists on remote but not locally yet, the check passes falsely due to `FETCH_HEAD` checkout. Also, the checkout step may be stale if `FETCH_HEAD` is not updated after the tag fetch. Evidence: `git checkout "$GITHUB_SHA" 2>/dev/null || git checkout FETCH_HEAD` Fix: Explicitly fetch the tag before checking: `git fetch origin "refs/tags/${CURRENT_TAG}:refs/tags/${CURRENT_TAG}"` then verify. - [SUGGESTION] .gitea/workflows/auto-tag.yml:160-166 - Release asset upload re-fetches release info *after* creation/update, but could race if another job also updates the release concurrently. The download URL may change mid-execution if multiple assets are uploaded simultaneously. Evidence: `RELEASE_ID=$(curl -sf "$API/releases/tags/$TAG" ... | jq -r '.id')` is called again in upload asset step. Fix: Store `RELEASE_ID` in `GITHUB_OUTPUT` from the creation/update step and pass it to subsequent steps instead of re-fetching. - [SUGGESTION] .gitea/workflows/auto-tag.yml:84-85 — The `autotag` job’s output uses `release_tag=$NEXT` but the changelog job expects `${{ needs.autotag.outputs.release_tag }}`. If `NEXT` is empty or malformed, `GITHUB_OUTPUT` will set an invalid value; no validation occurs. Evidence: `echo "release_tag=$NEXT" >> "$GITHUB_OUTPUT"` Fix: Validate `$NEXT` before writing to `GITHUB_OUTPUT` (e.g., `[[ $NEXT =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "Invalid tag"; exit 1; }`).
sarman merged commit 8eccea96ec into master 2026-05-31 21:13:12 +00:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: sarman/tftsr-devops_investigation#56
No description provided.