feat(ci): add AI-powered release notes generation#35028
Conversation
Add an automated pipeline that generates developer-facing release notes for every dotCMS GitHub release. A TypeScript script gathers structured PR data from the GitHub API, Claude writes polished changelog prose, and a deterministic shell step updates the release description. Components: - TypeScript data-gathering script (.github/scripts/gather-release-data/) - Prompt template for Claude changelog writing - Reusable workflow component (cicd_comp_ai-release-notes-phase.yml) - Backfill workflow for past releases (cicd_ai-release-notes-backfill.yml) - New release-notes job in cicd_6-release.yml (non-blocking) - Reproducible test harness script Tested end-to-end in dotCMS/core-workflow-test: - Backfill workflow: generates notes and updates release description - Tag filtering: CLI and LTS releases correctly skipped - Label signals: Changelog:Skip omits PRs, Not Safe To Rollback triggers warning - 30 Jest tests passing for categorization and GitHub API helpers Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Claude finished @sfreudenthaler's task in 1m 29s —— View job Rollback Safety Analysis
Result: ✅ Safe to Roll BackAnalyzed the diff ( What this PR changes:
Why it's safe:
Rolling back to N-1 removes these workflow files and the |
The 500-char cap was discarding 72% of PR body content across the repo, causing Claude to hallucinate categorization from titles alone. Analysis of the last 1000 merged PRs shows the max body is ~31K chars, so 50K is a safe ceiling with negligible token cost impact. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
sfreudenthaler
left a comment
There was a problem hiding this comment.
happy with it after these and will mark as ready for review when addressed
- Use org-standard runner version variable instead of hardcoded ubuntu-24.04 - Bump Node runtime from 20 to 22 to match @types/node ^22 in package.json - Replace static PROMPT_EOF heredoc delimiter with a random one generated via openssl at runtime, preventing PR body content from poisoning the GITHUB_ENV multiline variable Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- PR fetch errors now warn and continue instead of failing the entire run — a single deleted/transferred PR no longer blocks note generation - Remove dead label type guard (GitHub REST API always returns objects) - Remove unreachable commit cap warning (pagination fetches all commits) - Add untrusted data warning to prompt template to defend against prompt injection via PR bodies Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add build: conventional commit prefix to title heuristics so these PRs land in Infrastructure instead of uncategorized. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1b127e3fac
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- Move continue-on-error to an allow_failure input (default false) so the backfill workflow fails visibly while the release pipeline stays non-blocking - Rollback-unsafe PRs can no longer be skipped via Changelog: Skip — prevents orphaned caution blocks referencing PRs absent from the notes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
-
We need to create these labels:
- Human: Not Safe To Rollback
-
Probably we need to automate and promote the use of
Changelog: Skip -
We also have the label
Doc: Not Needed. Should we consider that or maybe delete it and only use theChangelog: Skip -
We should automate PR title with Claude code, since by looking at the last 50 PRs, around 10 PRs don't follow conventional commits
-
We should include
taskandrefactorsince these are used as conventional commits as well.
Addresses some of Ericks comments. Decided to keep chore/task in `feature` because it's called features and enhancements and i'm not sure there's a distinction from task/chore output there from a user perspective
addressed in 8bc9e78
Great call, Will make sure we capture in our Ai process improvement roadmap
ok i put |
## Summary Adds an automated pipeline that generates developer-facing release notes for every dotCMS GitHub release. Closes #35011. - **TypeScript data-gathering script** extracts PR details, labels, and categorization from the GitHub API - **Claude AI** writes polished changelog prose from the structured JSON data - **GitHub Actions** orchestrates the pipeline as a non-blocking job in the existing release workflow ### Release pipeline integration The new `release-notes` job slots into the existing release pipeline after `release` completes: ``` release-prepare → build → deployment → release → release-notes (non-blocking) ↘ ↘ finalize (always) report (always) ``` ### Release notes generation flow Within the `release-notes` job, these steps execute sequentially: ``` ┌─────────────────────────────────────────────────────────────────┐ │ 1. Validate release exists │ │ └─ gh release view $TAG │ │ │ │ 2. Gather release data (TypeScript) │ │ ├─ Resolve previous tag from GitHub releases API │ │ ├─ Fetch commit range via Compare API (with pagination) │ │ ├─ Extract PR numbers from commit messages │ │ ├─ Batch-fetch PR details (title, labels, body) │ │ ├─ Detect label signals: │ │ │ ├─ Changelog: Skip → omit from output │ │ │ └─ Not Safe To Rollback → [!CAUTION] warning │ │ ├─ Pre-categorize into 4 sections │ │ └─ Output structured JSON to /tmp/release-data.json │ │ │ │ 3. Assemble prompt │ │ └─ prompt-template.md + release-data.json → prompt string │ │ │ │ 4. Claude writes release notes (Write tool only) │ │ └─ Outputs /tmp/release-notes.md │ │ │ │ 5. Update release description (deterministic shell step) │ │ └─ gh release edit $TAG --notes-file /tmp/release-notes.md │ └─────────────────────────────────────────────────────────────────┘ ``` ### Backfill workflow (standalone) For populating notes on past releases or re-generating notes manually: ``` workflow_dispatch(release_tag, previous_tag?) └─ calls cicd_comp_ai-release-notes-phase.yml └─ same flow as above ``` ### Tag filtering (monorepo) The workflow skips non-standard releases automatically: - `dotcms-cli-*` → skipped (CLI product) - `*_lts_*` → skipped (LTS releases) - Must start with `v` → skipped otherwise ### Key design decisions - **Separation of concerns**: Claude only writes prose (`Write` tool). The `gh release edit` runs as a deterministic shell step — auditable and retryable - **Non-blocking**: `release-notes` job uses `if: success()` and doesn't block `finalize` or `report` - **Always-overwrite**: Re-running produces consistent results - **Shell-injection safe**: Tag values referenced via `env:` variables, not direct `${{ }}` interpolation ### Files added/changed | File | Purpose | |------|---------| | `.github/scripts/gather-release-data/src/` | TypeScript source (index, github, categorize, types) | | `.github/scripts/gather-release-data/src/*.test.ts` | Jest tests (30 passing) | | `.github/scripts/gather-release-data/prompt-template.md` | Prompt template for Claude | | `.github/scripts/gather-release-data/package.json` | Dependencies (`@octokit/rest`) | | `.github/scripts/gather-release-data/test/create-test-harness.sh` | Reproducible test harness | | `.github/workflows/cicd_comp_ai-release-notes-phase.yml` | Reusable workflow component | | `.github/workflows/cicd_ai-release-notes-backfill.yml` | Standalone backfill workflow | | `.github/workflows/cicd_6-release.yml` | Added `release-notes` job (13 lines) | ### Secrets required - `ANTHROPIC_API_KEY` — already exists in dotCMS/core ✅ ## Test plan Tested end-to-end in [dotCMS/core-workflow-test](https://github.com/dotCMS/core-workflow-test): - [x] **Backfill workflow** ([run #440](https://github.com/dotCMS/core-workflow-test/actions/runs/23248206205)): Generated notes and updated [release v26.03.17-02](https://github.com/dotCMS/core-workflow-test/releases/tag/v26.03.17-02) - [x] **LTS tag filter**: Workflow correctly skipped for `v26.03.17_lts_v01` - [x] **CLI tag filter**: Workflow correctly skipped for `dotcms-cli-26.03.17-01` - [x] **Changelog: Skip label**: PR dotCMS#449 omitted from output - [x] **Infrastructure categorization**: PR dotCMS#450 correctly placed in Infrastructure section - [x] **Jest tests**: 30 passing (categorization logic, PR extraction, tag resolution) - [ ] Validate on next real release — or trigger backfill against a recent tag after merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Adds an automated pipeline that generates developer-facing release notes for every dotCMS GitHub release. Closes #35011.
Release pipeline integration
The new
release-notesjob slots into the existing release pipeline afterreleasecompletes:Release notes generation flow
Within the
release-notesjob, these steps execute sequentially:Backfill workflow (standalone)
For populating notes on past releases or re-generating notes manually:
Tag filtering (monorepo)
The workflow skips non-standard releases automatically:
dotcms-cli-*→ skipped (CLI product)*_lts_*→ skipped (LTS releases)v→ skipped otherwiseKey design decisions
Writetool). Thegh release editruns as a deterministic shell step — auditable and retryablerelease-notesjob usesif: success()and doesn't blockfinalizeorreportenv:variables, not direct${{ }}interpolationFiles added/changed
.github/scripts/gather-release-data/src/.github/scripts/gather-release-data/src/*.test.ts.github/scripts/gather-release-data/prompt-template.md.github/scripts/gather-release-data/package.json@octokit/rest).github/scripts/gather-release-data/test/create-test-harness.sh.github/workflows/cicd_comp_ai-release-notes-phase.yml.github/workflows/cicd_ai-release-notes-backfill.yml.github/workflows/cicd_6-release.ymlrelease-notesjob (13 lines)Secrets required
ANTHROPIC_API_KEY— already exists in dotCMS/core ✅Test plan
Tested end-to-end in dotCMS/core-workflow-test:
v26.03.17_lts_v01dotcms-cli-26.03.17-01🤖 Generated with Claude Code
This PR fixes: #35011