name: Daily Changelog
on:
schedule:
- cron: '0 4 * * *' # 8 PM PT daily (4 AM UTC next day)
workflow_dispatch: # Allow manual triggering
permissions:
contents: write
pull-requests: write
id-token: write
jobs:
changelog:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Get full history for changelog generation
token: ${{ github.token }}
- name: Configure git
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
- name: Environment check
env:
GH_TOKEN: ${{ github.token }}
run: |
echo "=== Environment Check ==="
echo "Repository: ${{ github.repository }}"
echo "Actor: ${{ github.actor }}"
echo "Ref: ${{ github.ref }}"
# Check tools availability
git --version
gh --version || echo "WARNING: gh CLI not available"
# Check permissions
echo "Testing git write permissions..."
git status
echo "Testing GitHub API access..."
gh auth status || echo "WARNING: GitHub auth may have issues"
# Check recent commits
echo "Recent commits (last 24h):"
COMMITS=$(git log --since="1 day ago" --oneline --no-merges || echo "No commits found")
echo "$COMMITS"
if [ -z "$COMMITS" ] || [ "$COMMITS" = "No commits found" ]; then
echo "No commits in last 24 hours - workflow may exit early"
fi
- name: Generate changelog with Claude
uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ github.token }} # Ensure gh CLI has auth inside Claude's Bash
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
claude_args: >-
--model claude-haiku-4-5
--max-turns 15
--allowedTools "Read,Edit,Write,Bash(git:*),Bash(gh pr list:*),Bash(gh pr create:*),Bash(date:*)"
prompt: |
Generate a daily changelog by analyzing commits since yesterday.
IMPORTANT POLICY:
- The changelog should only include **meaningful product changes**:
- Bug fixes
- New features
- Refactors or performance/UX improvements
- A commit is considered "meaningful" only if its subject line starts with one of:
- "Fix:", "Feature:", "Refactor:", "Perf:", "UI:", or "UX:"
- Commits with other prefixes (for example "Chore:", "Docs:", "Test:") should generally be ignored.
BEFORE creating any branch or editing files, you MUST:
1. Check if there is already an open Daily changelog PR using:
gh pr list --state open --search "chore: Daily changelog for" --limit 1
- If there is an open PR, log a short message like
"Open Daily changelog PR already exists - skipping changelog update" and then exit without changes.
When there is no existing Daily changelog PR and there ARE meaningful commits in the last 24 hours, use the following workflow:
1. Review git commits from the last 24 hours using: git log --since="1 day ago" --oneline --no-merges
2. Filter to only those commits whose subject starts with one of:
"Fix:", "Feature:", "Refactor:", "Perf:", "UI:", "UX:"
3. If there are **no** such commits, log "No meaningful changes since yesterday - skipping changelog update" and exit without making any git changes.
4. If there ARE meaningful commits, create a branch: git checkout -b chore/daily-changelog-$(date +%Y-%m-%d)
5. Update CHANGELOG.md by adding a new section after "## [Unreleased]"
6. Commit changes: git add CHANGELOG.md && git commit -m "chore: Daily changelog for $(date +%Y-%m-%d)"
7. Push branch: git push -u origin HEAD
8. Create PR using: gh pr create --title "chore: Daily changelog for $(date +%Y-%m-%d)" --body "Automated daily changelog generation"
Format the changelog entry as:
## [YYYY-MM-DD] - Daily Update
### Added
- New features with PR/issue references
### Fixed
- Bug fixes with PR/issue references
### Changed
- Refactoring/improvements with PR/issue references
### Documentation
- Docs updates with PR/issue references
Rules:
- Only include meaningful changes (exclude merge commits and non-meaningful prefixes)
- Include PR/issue numbers where available
- If no meaningful changes, output "No meaningful changes since yesterday - skipping changelog update"
- Group related changes logically
- Use past tense for all entries
- name: Fallback changelog generation
if: failure()
env:
GH_TOKEN: ${{ github.token }}
run: |
echo "Claude action failed, using fallback method..."
# Avoid duplicate PRs: skip if an open Daily changelog PR already exists
# Use --jq 'length' to correctly detect zero vs non-zero results (empty array [] is non-empty string)
EXISTING_PR_COUNT=$(gh pr list --state open --search "chore: Daily changelog for" --limit 1 --json number --jq 'length' 2>/dev/null || echo 0)
if [ "$EXISTING_PR_COUNT" -gt 0 ]; then
echo "Open Daily changelog PR already exists - skipping fallback changelog update"
exit 0
fi
# Get commits from last 24 hours
# Only treat commits as meaningful if they use conventional prefixes
# that indicate bugs, new features, or UX/performance improvements.
# Examples: "Fix: ...", "Feature: ...", "Refactor: ...", "Perf: ...", "UI: ...", "UX: ...".
COMMITS=$(git log --since="1 day ago" --format='%h %s' --no-merges | grep -E '^[0-9a-f]+ (Fix:|Feature:|Refactor:|Perf:|UI:|UX:)' || true)
if [ -z "$COMMITS" ]; then
echo "No meaningful changes since yesterday - skipping changelog update"
exit 0
fi
# Create changelog entry
TODAY=$(date +%Y-%m-%d)
BRANCH_NAME="chore/daily-changelog-${TODAY}"
git checkout -b "$BRANCH_NAME"
# Prepare changelog content
CHANGELOG_ENTRY="## [$TODAY] - Daily Update\n\n"
# Categorize commits
FEATURES=$(echo "$COMMITS" | grep -E '^[0-9a-f]+ (Feature:)' || true)
FIXES=$(echo "$COMMITS" | grep -E '^[0-9a-f]+ (Fix:)' || true)
CHANGES=$(echo "$COMMITS" | grep -E '^[0-9a-f]+ (Refactor:|Perf:|UI:|UX:)' || true)
if [ ! -z "$FEATURES" ]; then
CHANGELOG_ENTRY="${CHANGELOG_ENTRY}### Added\n"
while read -r line; do
if [ ! -z "$line" ]; then
CHANGELOG_ENTRY="${CHANGELOG_ENTRY}- ${line#* }\n"
fi
done <<< "$FEATURES"
CHANGELOG_ENTRY="${CHANGELOG_ENTRY}\n"
fi
if [ ! -z "$FIXES" ]; then
CHANGELOG_ENTRY="${CHANGELOG_ENTRY}### Fixed\n"
while read -r line; do
if [ ! -z "$line" ]; then
CHANGELOG_ENTRY="${CHANGELOG_ENTRY}- ${line#* }\n"
fi
done <<< "$FIXES"
CHANGELOG_ENTRY="${CHANGELOG_ENTRY}\n"
fi
if [ ! -z "$CHANGES" ]; then
CHANGELOG_ENTRY="${CHANGELOG_ENTRY}### Changed\n"
while read -r line; do
if [ ! -z "$line" ]; then
CHANGELOG_ENTRY="${CHANGELOG_ENTRY}- ${line#* }\n"
fi
done <<< "$CHANGES"
CHANGELOG_ENTRY="${CHANGELOG_ENTRY}\n"
fi
# Insert into CHANGELOG.md after "## [Unreleased]"
sed -i "/^## \[Unreleased\]/a\\
$CHANGELOG_ENTRY" CHANGELOG.md
# Commit and push
git add CHANGELOG.md
git commit -m "chore: Daily changelog for $TODAY"
git push -u origin HEAD
# Create PR
gh pr create --title "chore: Daily changelog for $TODAY" --body "Automated daily changelog generation (fallback method)"
echo "Fallback changelog generation completed successfully"
- name: Workflow summary
if: always()
run: |
echo "=== Daily Changelog Workflow Summary ==="
echo "Date: $(date +%Y-%m-%d)"
echo "Repository: ${{ github.repository }}"
echo "Triggered by: ${{ github.event_name }}"
TODAY=$(date +%Y-%m-%d)
BRANCH_EXISTS=$(git ls-remote --heads origin chore/daily-changelog-${TODAY} || true)
if [ "${{ job.status }}" = "success" ] && [ ! -z "$BRANCH_EXISTS" ]; then
echo "✅ Status: SUCCESS - Daily changelog branch created (check PRs for details)"
elif [ "${{ job.status }}" = "success" ]; then
echo "✅ Status: SUCCESS - No daily changelog branch created (no meaningful changes or existing PR)"
elif [ "${{ job.status }}" = "failure" ]; then
echo "❌ Status: FAILED - Check logs for details"
else
echo "⚠️ Status: ${{ job.status }}"
fi
# Check if PR was created
if [ ! -z "$BRANCH_EXISTS" ]; then
echo "📝 Branch created: chore/daily-changelog-${TODAY}"
echo "🔗 Check for PR at: https://github.com/${{ github.repository }}/pulls"
else
echo "📝 No branch created - likely no meaningful changes"
fi