Skip to content
Merged
Changes from 1 commit
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
89 changes: 70 additions & 19 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@
types: [opened, ready_for_review]
pull_request_review_comment:
types: [created, edited]
release:
types: [created]
workflow_run:
workflows: ["PyPi"]
types: [completed]

permissions:
contents: write # Allows Claude to post suggested changes (requires write for GitHub API)
Expand All @@ -46,12 +47,12 @@
concurrency:
group: claude-pr-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

Check failure

Code scanning / CodeQL

Checkout of untrusted code in privileged context without privileged context use High

Potential execution of untrusted code on a privileged workflow (
issue_comment
)
Potential execution of untrusted code on a privileged workflow (
workflow_run
)
- name: Generate PR diff
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down Expand Up @@ -256,12 +257,12 @@
concurrency:
group: claude-pr-comment-${{ github.event.pull_request.number }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

Check failure

Code scanning / CodeQL

Checkout of untrusted code in privileged context without privileged context use High

Potential execution of untrusted code on a privileged workflow (
issue_comment
)
Potential execution of untrusted code on a privileged workflow (
workflow_run
)
- name: Generate PR diff
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down Expand Up @@ -485,82 +486,120 @@
- Assume good intent
- Welcome contributors

# Generate release notes when a new release is created
# Generate release notes when PyPi workflow completes successfully on a tag
release-notes:
if: github.event_name == 'release'
if: |
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
startsWith(github.event.workflow_run.head_branch, 'v')
runs-on: ubuntu-latest
permissions:
contents: write # For committing release notes
issues: write # For creating failure notification issue
concurrency:
group: claude-release-notes-${{ github.event.release.tag_name }}
group: claude-release-notes-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: false
steps:
- name: Extract and validate tag name
id: tag
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_NAME="${{ github.event.workflow_run.head_branch }}"
Comment thread Fixed
echo "Candidate tag: $TAG_NAME"

# Validate semantic version format (vX.Y.Z)
if [[ ! "$TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "❌ '$TAG_NAME' is not a valid semantic version tag (expected vX.Y.Z)"
echo "SKIP=true" >> $GITHUB_OUTPUT
exit 0
fi

# Verify the GitHub release exists
if ! gh release view "$TAG_NAME" --repo "$GITHUB_REPOSITORY" > /dev/null 2>&1; then
echo "❌ GitHub release for '$TAG_NAME' does not exist yet"
echo "SKIP=true" >> $GITHUB_OUTPUT
exit 0
fi

echo "✅ Valid tag and release exists: $TAG_NAME"
echo "TAG_NAME=$TAG_NAME" >> $GITHUB_OUTPUT
echo "SKIP=false" >> $GITHUB_OUTPUT

- name: Checkout repository
if: steps.tag.outputs.SKIP != 'true'
uses: actions/checkout@v6
with:
ref: main # Checkout main branch (not tag) so we can commit
fetch-depth: 0 # Full history for tag comparison
token: ${{ secrets.RELEASE_PAT }} # PAT needed to push past branch protection

- name: Fetch all tags
if: steps.tag.outputs.SKIP != 'true'
run: |
git fetch --tags --force
echo "Tags available:"
git tag -l | tail -10

- name: Generate release context
if: steps.tag.outputs.SKIP != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CURRENT_TAG: ${{ github.event.release.tag_name }}
CURRENT_TAG: ${{ steps.tag.outputs.TAG_NAME }}
run: |
echo "Generating release context for $CURRENT_TAG"

# Ensure docs/releases directory exists
mkdir -p docs/releases

# Find previous tag using git describe (more reliable)
PREVIOUS_TAG=$(git describe --tags --abbrev=0 "$CURRENT_TAG^" 2>/dev/null || echo "")
if [ -z "$PREVIOUS_TAG" ]; then
# No previous tag found, use first commit
PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD)
echo "No previous tag found, using initial commit: $PREVIOUS_TAG"
else
echo "Previous tag: $PREVIOUS_TAG"
fi
echo "PREVIOUS_TAG=$PREVIOUS_TAG" >> $GITHUB_ENV

# Generate diff statistics
echo "## Release: $CURRENT_TAG" > release-context.txt
echo "## Previous: $PREVIOUS_TAG" >> release-context.txt
echo "" >> release-context.txt

# Get commit count and contributor count
COMMIT_COUNT=$(git rev-list --count $PREVIOUS_TAG..$CURRENT_TAG)
CONTRIBUTOR_COUNT=$(git log $PREVIOUS_TAG..$CURRENT_TAG --format='%aN' | sort -u | wc -l)
echo "**$COMMIT_COUNT commits** from **$CONTRIBUTOR_COUNT contributors**" >> release-context.txt
echo "" >> release-context.txt

# Get contributors
echo "## Contributors" >> release-context.txt
git log $PREVIOUS_TAG..$CURRENT_TAG --format='%aN' | sort -u >> release-context.txt
echo "" >> release-context.txt

# Get commit log with PR references (limit to 100 for large releases)
echo "## Commit Log" >> release-context.txt
git log $PREVIOUS_TAG..$CURRENT_TAG --oneline --no-merges | head -100 >> release-context.txt
TOTAL_COMMITS=$(git log $PREVIOUS_TAG..$CURRENT_TAG --oneline --no-merges | wc -l)
if [ "$TOTAL_COMMITS" -gt 100 ]; then
echo "... and $((TOTAL_COMMITS - 100)) more commits" >> release-context.txt
fi
echo "" >> release-context.txt

# Get merged PRs using GitHub's compare API (more accurate)
echo "## Merged Pull Requests" >> release-context.txt
gh api "repos/$GITHUB_REPOSITORY/compare/$PREVIOUS_TAG...$CURRENT_TAG" \
--jq '.commits[].commit.message' 2>/dev/null | \
grep -oE '#[0-9]+' | sort -u | while read pr; do
pr_num="${pr#\#}"
pr_info=$(gh pr view "$pr_num" --json title,author --jq '"\(.title) (@\(.author.login))"' 2>/dev/null || echo "")
if [ -n "$pr_info" ]; then
echo "#$pr_num - $pr_info" >> release-context.txt
fi
done
echo "" >> release-context.txt

Check failure

Code scanning / CodeQL

Environment variable built from user-controlled sources Critical

Potential environment variable injection in [echo "Generating release context for $CURRENT_TAG" Ensure docs/releases directory exists mkdir -p docs/releases The previous FINAL: an RC or an agent-pkg-* tag would shrink the notes to a sliver. PREVIOUS_TAG=$(git describe --tags --abbrev=0 --match 'v\[0-9\]' --exclude 'v-rc*' "$CURRENT_TAG^" 2>/dev/null || echo "")
if \[ -z "$PREVIOUS_TAG" \]; then No previous tag found, use first commit PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD)
echo "No previous tag found, using initial commit: $PREVIOUS_TAG"
else
echo "Previous tag: $PREVIOUS_TAG"
fi
echo "PREVIOUS_TAG=$PREVIOUS_TAG" >> $GITHUB_ENV Generate diff statistics echo "## Release: $CURRENT_TAG" > release-context.txt
echo "## Previous: $PREVIOUS_TAG" >> release-context.txt
echo "" >> release-context.txt Get commit count and contributor count COMMIT_COUNT=$(git rev-list --count $PREVIOUS_TAG..$CURRENT_TAG)
CONTRIBUTOR_COUNT=$(git log $PREVIOUS_TAG..$CURRENT_TAG --format='%aN' | sort -u | wc -l)
echo "$COMMIT_COUNT commits from $CONTRIBUTOR_COUNT contributors" >> release-context.txt
echo "" >> release-context.txt Get contributors echo "## Contributors" >> release-context.txt
git log $PREVIOUS_TAG..$CURRENT_TAG --format='%aN' | sort -u >> release-context.txt
echo "" >> release-context.txt Get commit log with PR references (limit to 100 for large releases) echo "## Commit Log" >> release-context.txt
git log $PREVIOUS_TAG..$CURRENT_TAG --oneline --no-merges | head -100 >> release-context.txt
TOTAL_COMMITS=$(git log $PREVIOUS_TAG..$CURRENT_TAG --oneline --no-merges | wc -l)
if \[ "$TOTAL_COMMITS" -gt 100 \]; then
echo "... and $((TOTAL_COMMITS - 100)) more commits" >> release-context.txt
fi
echo "" >> release-context.txt Get merged PRs using GitHub's compare API (more accurate). --paginate is REQUIRED: /compare caps commits at 250 per page, so on a large release an unpaginated call silently drops every PR past the first page and the notes read complete while omitting work (#2306 class). v0.21.2..v0.22.0 carried 199 commits — under the cap, but close enough that the next big release would have lost data. Do NOT add --slurp: gh rejects it in combination with --jq. Do NOT re-add 2>/dev/null — it is what hid the earlier bad invocation of this very command. echo "## Merged Pull Requests" >> release-context.txt
gh api --paginate "repos/$GITHUB_REPOSITORY/compare/$PREVIOUS_TAG...$CURRENT_TAG" \\
--jq '.commits\[\].commit.message' | \\
grep -oE '#\[0-9\]+' | sort -u | while read pr; do
pr_num="${pr#\\#}"
pr_info=$(gh pr view "$pr_num" --json title,author --jq '"\\(.title) (@\\(.author.login))"' 2>/dev/null || echo "")
if \[ -n "$pr_info" \]; then
echo "#$pr_num - $pr_info" >> release-context.txt
fi
done
echo "" >> release-context.txt Get file change summary (not full diff - too large) echo "## Files Changed Summary" >> release-context.txt
git diff --stat $PREVIOUS_TAG..$CURRENT_TAG | tail -20 >> release-context.txt
echo "" >> release-context.txt Get changed directories for context echo "## Changed Directories" >> release-context.txt
git diff --name-only $PREVIOUS_TAG..$CURRENT_TAG | xargs -I {} dirname {} | sort -u | head -30 >> release-context.txt
echo "" >> release-context.txt Split diff into component-based files for iterative processing mkdir -p release-diffs Create diffs by top-level directory for manageable chunks for dir in src tests docs .github installer examples; do
if git diff --name-only $PREVIOUS_TAG..$CURRENT_TAG | grep -q "^$dir/"; then
git diff $PREVIOUS_TAG..$CURRENT_TAG -- "$dir/" > "release-diffs/$dir.diff"
LINES=$(wc -l < "release-diffs/$dir.diff")
echo " - release-diffs/$dir.diff: $LINES lines" >> release-context.txt
fi
done Capture any remaining changes not in known directories git diff $PREVIOUS_TAG..$CURRENT_TAG -- ':!src/' ':!tests/' ':!docs/' ':!.github/' ':!installer/' ':!examples/' > release-diffs/other.diff 2>/dev/null || true
if \[ -s "release-diffs/other.diff" \]; then
LINES=$(wc -

# Get file change summary (not full diff - too large)
echo "## Files Changed Summary" >> release-context.txt
Expand Down Expand Up @@ -602,6 +641,7 @@
ls -la release-diffs/ 2>/dev/null || echo "No diff files"

- name: Generate release notes with Claude
if: steps.tag.outputs.SKIP != 'true'
id: generate-notes
uses: anthropics/claude-code-action@beta
with:
Expand All @@ -610,7 +650,7 @@
max_turns: 30
model: claude-opus-4-5-20251101
prompt: |
Generate comprehensive release notes for GAIA version ${{ github.event.release.tag_name }}.
Generate comprehensive release notes for GAIA version ${{ steps.tag.outputs.TAG_NAME }}.

## FIRST ACTIONS
1. Read release-context.txt - Contains commit log, contributors, PRs, and list of diff files
Expand Down Expand Up @@ -640,22 +680,22 @@
### 1. `RELEASE_NOTES.md` - For GitHub Release
Plain markdown for the GitHub release page.

### 2. `docs/releases/${{ github.event.release.tag_name }}.mdx` - For Documentation Site
### 2. `docs/releases/${{ steps.tag.outputs.TAG_NAME }}.mdx` - For Documentation Site
MDX format with Mintlify frontmatter for the documentation website.

## Release Notes Format (use for BOTH files)

For the MDX file, add this frontmatter at the top:
```
---
title: "${{ github.event.release.tag_name }}"
title: "${{ steps.tag.outputs.TAG_NAME }}"
description: "[One-line summary of this release]"
---
```

Then use this content structure for both files:

# GAIA ${{ github.event.release.tag_name }} Release Notes
# GAIA ${{ steps.tag.outputs.TAG_NAME }} Release Notes

## Overview

Expand Down Expand Up @@ -692,7 +732,7 @@
Key PRs:
- #[number] - [PR title]

Full Changelog: [${{ env.PREVIOUS_TAG }}...${{ github.event.release.tag_name }}](https://github.com/amd/gaia/compare/${{ env.PREVIOUS_TAG }}...${{ github.event.release.tag_name }})
Full Changelog: [${{ env.PREVIOUS_TAG }}...${{ steps.tag.outputs.TAG_NAME }}](https://github.com/amd/gaia/compare/${{ env.PREVIOUS_TAG }}...${{ steps.tag.outputs.TAG_NAME }})

## Guidelines

Expand All @@ -712,7 +752,7 @@

IMPORTANT: You MUST write BOTH files:
1. `RELEASE_NOTES.md` (for GitHub release)
2. `docs/releases/${{ github.event.release.tag_name }}.mdx` (for documentation site)
2. `docs/releases/${{ steps.tag.outputs.TAG_NAME }}.mdx` (for documentation site)

## MANDATORY: Review Your Changes
After writing the release notes files, you MUST:
Expand All @@ -725,8 +765,9 @@
Do NOT skip this review step - always verify your output before completing.

- name: Verify and validate release notes
if: steps.tag.outputs.SKIP != 'true'
env:
TAG_NAME: ${{ github.event.release.tag_name }}
TAG_NAME: ${{ steps.tag.outputs.TAG_NAME }}
run: |
echo "Checking for generated release notes..."

Expand All @@ -746,8 +787,9 @@
python util/validate_release_notes.py "docs/releases/$TAG_NAME.mdx" --tag "$TAG_NAME"

- name: Update docs.json with new release
if: steps.tag.outputs.SKIP != 'true'
env:
TAG_NAME: ${{ github.event.release.tag_name }}
TAG_NAME: ${{ steps.tag.outputs.TAG_NAME }}
run: |
# Add the new release to docs.json navigation
# Insert at the beginning of the releases array (newest first)
Expand Down Expand Up @@ -801,13 +843,15 @@

with open(docs_json_path, 'w') as f:
json.dump(docs, f, indent=2)
f.write('\n') # Ensure trailing newline for linting

print(f"✅ Added {new_page} to docs.json")
EOF

- name: Bump version.py to next patch version
if: steps.tag.outputs.SKIP != 'true'
env:
TAG_NAME: ${{ github.event.release.tag_name }}
TAG_NAME: ${{ steps.tag.outputs.TAG_NAME }}
run: |
# Extract version from tag (strip 'v' prefix if present)
RELEASED_VERSION="${TAG_NAME#v}"
Expand Down Expand Up @@ -838,9 +882,10 @@
echo "✅ version.py bumped to $NEXT_VERSION"

- name: Commit release notes and version bump
if: steps.tag.outputs.SKIP != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ github.event.release.tag_name }}
TAG_NAME: ${{ steps.tag.outputs.TAG_NAME }}
NEXT_VERSION: ${{ env.NEXT_VERSION }}
run: |
git config user.name "github-actions[bot]"
Expand All @@ -858,20 +903,26 @@
fi

- name: Update GitHub Release
if: steps.tag.outputs.SKIP != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ github.event.release.tag_name }}
TAG_NAME: ${{ steps.tag.outputs.TAG_NAME }}
run: |
echo "Updating GitHub release with generated notes..."
gh release edit "$TAG_NAME" --notes-file RELEASE_NOTES.md
echo "✅ GitHub release updated successfully"

- name: Notify on failure
if: failure()
if: failure() && steps.tag.outputs.SKIP != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ github.event.release.tag_name }}
TAG_NAME: ${{ steps.tag.outputs.TAG_NAME || github.event.workflow_run.head_branch }}
run: |
# Fallback if TAG_NAME is empty
if [ -z "$TAG_NAME" ]; then
TAG_NAME="unknown"
fi

gh issue create \
--title "⚠️ Release notes generation failed for $TAG_NAME" \
--body "The automated release notes generation for **$TAG_NAME** failed.
Expand Down
Loading