Changelog draft #62
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Changelog draft | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| since: | |
| description: "Override start date YYYY-MM-DD (with 'until')" | |
| required: false | |
| until: | |
| description: "Override end date YYYY-MM-DD (with 'since')" | |
| required: false | |
| schedule: | |
| - cron: "0 13 * * *" # 13:00 UTC daily (9am EDT / 8am EST) — drafts the previous Eastern day | |
| concurrency: | |
| group: changelog-draft | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| id-token: write | |
| env: | |
| BUN_VERSION: "1.3.14" | |
| jobs: | |
| draft: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - name: Skip if an open draft PR already exists | |
| id: existing | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| count=$(gh pr list --state open --search "head:changelog/draft" --json number --jq 'length') | |
| echo "open=$count" >> "$GITHUB_OUTPUT" | |
| if [ "$count" != "0" ]; then echo "An open changelog draft PR exists; skipping."; fi | |
| - name: Resolve window | |
| id: window | |
| if: steps.existing.outputs.open == '0' | |
| env: | |
| INPUT_SINCE: ${{ github.event.inputs.since }} | |
| INPUT_UNTIL: ${{ github.event.inputs.until }} | |
| run: bun scripts/changelog/compute-window.ts | |
| - name: Draft the changelog | |
| if: steps.existing.outputs.open == '0' && steps.window.outputs.skip == 'false' | |
| uses: anthropics/claude-code-action@62238ddb33772a079b0a6d8665a1ff3043583067 # v1.0.114 | |
| with: | |
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| claude_args: '--allowed-tools "Bash(gh pr:*),Bash(git:*),Read,Edit,Write"' | |
| prompt: | | |
| You are drafting the daily self-changelog for releases.sh. | |
| FIRST read docs/changelog-style.md in full — it defines the audience, the | |
| include/exclude rules, the conventional-commit prior, the format, and the voice. The | |
| top sections of CHANGELOG.md are your voice/density reference. | |
| Changelog days are Eastern (America/New_York) calendar days, NOT UTC days. GitHub | |
| timestamps are UTC; bucket each PR into its Eastern day using these UTC ranges | |
| (a PR belongs to the day where startUtc <= mergedAt < endUtc — ISO timestamps | |
| compare as plain strings): | |
| ${{ steps.window.outputs.day_bounds }} | |
| The window to draft is ${{ steps.window.outputs.since }} through | |
| ${{ steps.window.outputs.until }} (inclusive). The first entry in the ranges above | |
| is one day BEFORE the window — an overlap guard, handled in step 4. | |
| 1. List every PR merged in the window with ONE search: | |
| gh pr list --state merged --base main --json number,title,body,mergedAt,labels \ | |
| --search "merged:${{ steps.window.outputs.search_start }}..${{ steps.window.outputs.search_end }}" \ | |
| --limit 200 | |
| Bucket the results by Eastern day. Ignore any PR that falls outside every | |
| listed range. | |
| 2. Keep only user-facing changes per the style guide; inspect a PR's diff with | |
| `gh pr diff <number>` when its user impact is unclear. | |
| 3. For each window day with user-facing survivors, PREPEND a `## <Month D, YYYY>` | |
| section to CHANGELOG.md (newest at the top, directly under the `# Changelog` | |
| preamble), with **Added**/**Changed**/**Fixed** sections. A quiet/all-internal | |
| day → write nothing. | |
| 4. Overlap-guard day (the day before the window): if CHANGELOG.md already has a | |
| section for it, treat it as already curated — do NOT rewrite or reorder it; | |
| only APPEND a clearly user-facing PR that is missing from it (the publish step | |
| updates the published entry in place). If it has no section, treat it like a | |
| window day. | |
| CHANGELOG.md is the ONLY file you may modify. If a pre-commit hook, lint rule, or | |
| any other tooling blocks your commit, that is a bug to report in the PR body — do | |
| not edit config, workflows, or `package.json` to work around it, and do not bypass | |
| the hook. If you cannot commit without touching another file, stop and open no PR. | |
| If you wrote nothing, stop and open no PR. | |
| If CHANGELOG.md changed, commit it on a new branch named | |
| `changelog/draft-${{ steps.window.outputs.until }}` and open a PR titled | |
| `changelog: ${{ steps.window.outputs.since }}..${{ steps.window.outputs.until }}` | |
| with a one-line body listing the dates you drafted. Do not merge it. | |
| - name: Guard — the draft may only touch CHANGELOG.md | |
| if: steps.existing.outputs.open == '0' && steps.window.outputs.skip == 'false' | |
| env: | |
| BRANCH: changelog/draft-${{ steps.window.outputs.until }} | |
| run: | | |
| git fetch --quiet origin main "$BRANCH" || git fetch --quiet origin main | |
| if ! git rev-parse --verify --quiet "origin/$BRANCH" >/dev/null; then | |
| echo "No draft branch pushed; nothing to guard." | |
| exit 0 | |
| fi | |
| changed=$(git diff --name-only "origin/main...origin/$BRANCH") | |
| echo "Files changed on $BRANCH:" | |
| echo "$changed" | |
| unexpected=$(echo "$changed" | grep -v '^CHANGELOG\.md$' || true) | |
| if [ -n "$unexpected" ]; then | |
| echo "::error::The changelog draft modified files other than CHANGELOG.md:" | |
| echo "$unexpected" | |
| exit 1 | |
| fi |