From 3720f0f3f2306db9faa4bf1a68bf928e319a04a3 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Thu, 23 Apr 2026 10:22:26 +0500 Subject: [PATCH] fix(ci): harden tags.yaml changelog job against agent misbehavior Three changes to the generate-changelog job to fix the v1.3.0 release pipeline failure (run 24765377017) and make the job robust to whatever state the Copilot step leaves behind. 1. Add `timeout-minutes: 30` to the Generate changelog using AI step. On the v1.3.0 re-run the step hung silently for 10+ minutes; with no timeout a hung Copilot would hold a self-hosted runner for up to 6 hours (job default). The previous successful run took ~26 minutes, so 30 is a reasonable ceiling. 2. Replace the terse, ambiguous Copilot prompt with a one-liner that invokes docs/agents/changelog.md directly. The "Scope and boundaries" section added to that doc in the previous commit is now the single source of truth for what the agent may and may not do, so the workflow only needs to pass the version and point at the relevant doc. VERSION is moved to step env: to match GitHub's workflow-injection hardening guidance. 3. Rewrite the Create changelog branch and commit step: - add `set -euo pipefail` so any failure is visible - validate the file exists up front and fail loud with `::error::` if not - copy the file to a tempfile BEFORE `git checkout -b`, so the checkout to `origin/main` cannot remove it (this is the fix for the original pathspec error the v1.3.0 run hit) - use `trap` to clean up the tempfile on any exit path - move VERSION to env - drop the dead "no changes to commit" branch: the check_changelog step earlier in the job gates this step on the file being absent from origin/main, so `git add` + `git commit` must produce a diff. If they don't (e.g. Copilot emitted an empty file), fail loud instead of pushing an empty branch. Signed-off-by: Myasnikov Daniil --- .github/workflows/tags.yaml | 67 ++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/.github/workflows/tags.yaml b/.github/workflows/tags.yaml index b10897cf..0ec0217d 100644 --- a/.github/workflows/tags.yaml +++ b/.github/workflows/tags.yaml @@ -303,51 +303,56 @@ jobs: - name: Generate changelog using AI if: steps.check_changelog.outputs.exists == 'false' + timeout-minutes: 30 env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} GH_TOKEN: ${{ steps.app-token.outputs.token }} + VERSION: ${{ steps.tag.outputs.version }} run: | - copilot --prompt "prepare changelog file for tagged release v${{ steps.tag.outputs.version }}, use @docs/agents/changelog.md for it. Create the changelog file at docs/changelogs/v${{ steps.tag.outputs.version }}.md" \ + copilot \ + --prompt "Generate the release changelog for tag v${VERSION}. Follow the instructions in @docs/agents/changelog.md exactly, including the 'Scope and boundaries' section at the top. Your deliverable is the single file docs/changelogs/v${VERSION}.md — write it and exit; this workflow handles branching, committing, pushing, and opening the PR." \ --allow-all-tools --allow-all-paths < /dev/null - name: Create changelog branch and commit if: steps.check_changelog.outputs.exists == 'false' env: APP_TOKEN: ${{ steps.app-token.outputs.token }} + VERSION: ${{ steps.tag.outputs.version }} run: | - git config user.name "cozystack-ci[bot]" - git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" - git remote set-url origin https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY} - - CHANGELOG_FILE="docs/changelogs/v${{ steps.tag.outputs.version }}.md" - CHANGELOG_BRANCH="changelog-v${{ steps.tag.outputs.version }}" - - if [ -f "$CHANGELOG_FILE" ]; then - # Fetch latest main branch - git fetch origin main - - # Delete local branch if it exists - git branch -D "$CHANGELOG_BRANCH" 2>/dev/null || true - - # Create and checkout new branch from main - git checkout -b "$CHANGELOG_BRANCH" origin/main - - # Add and commit changelog - git add "$CHANGELOG_FILE" - if git diff --staged --quiet; then - echo "⚠️ No changes to commit (file may already be committed)" - else - git commit -m "docs: add changelog for v${{ steps.tag.outputs.version }}" -s - echo "✅ Changelog committed to branch $CHANGELOG_BRANCH" - fi - - # Push the branch (force push to update if it exists) - git push -f origin "$CHANGELOG_BRANCH" - else - echo "⚠️ Changelog file was not generated" + set -euo pipefail + + CHANGELOG_FILE="docs/changelogs/v${VERSION}.md" + CHANGELOG_BRANCH="changelog-v${VERSION}" + + if [ ! -f "$CHANGELOG_FILE" ]; then + echo "::error::Changelog file $CHANGELOG_FILE was not produced by the Generate changelog using AI step" exit 1 fi + # Snapshot the file across the branch switch — the checkout below + # resets tracked files to match origin/main. + TEMP_FILE="$(mktemp)" + trap 'rm -f "$TEMP_FILE"' EXIT + cp "$CHANGELOG_FILE" "$TEMP_FILE" + + git config user.name "cozystack-ci[bot]" + git config user.email "274107086+cozystack-ci[bot]@users.noreply.github.com" + git remote set-url origin "https://x-access-token:${APP_TOKEN}@github.com/${GITHUB_REPOSITORY}" + + git fetch origin main + git branch -D "$CHANGELOG_BRANCH" 2>/dev/null || true + git checkout -b "$CHANGELOG_BRANCH" origin/main + + mkdir -p "$(dirname "$CHANGELOG_FILE")" + cp "$TEMP_FILE" "$CHANGELOG_FILE" + + # The `check_changelog` step gated this job on the file being absent + # from origin/main, so `git add` + `git commit` must produce a diff. + # If they don't, something is wrong (e.g. empty file) — fail loud. + git add "$CHANGELOG_FILE" + git commit -m "docs: add changelog for v${VERSION}" -s + git push -f origin "$CHANGELOG_BRANCH" + - name: Create PR for changelog if: steps.check_changelog.outputs.exists == 'false' uses: actions/github-script@v7