ci(publish): organize GitHub Releases like burn#55
Conversation
Mirror the burn repo's release-notes structure so each workforce GitHub Release includes the actual curated changes — not just `Released v…` stamps — and a top-level cross-package narrative. - Per-package changelog generator now reads `## [Unreleased]` and promotes hand-curated content verbatim into the new versioned block, resetting `[Unreleased]` to empty. Falls back to bucketed git-log inference (Conventional Commits + imperative-verb heuristics, with unclassified commits landing in `Changed`) only when Unreleased is empty. This is why prior releases stamped `### Released - vX.Y.Z` even though every package had real curated bullets sitting in `[Unreleased]`. - New `Generate root changelog` step performs the same Unreleased→ `[x.y.z]` promotion for a root `CHANGELOG.md`, anchored on the umbrella `agentworkforce` version. No git-log fallback — empty `[Unreleased]` just means "no narrative-worthy changes this release" and the file is left alone. - `Build combined release notes` step now extracts the root version block as `## Release Notes` (top-level cross-package narrative) and inlines per-package changelog bodies as `## Package Changelogs` / `### <npm-name>`, matching the burn release page layout. - `publish` job exposes `release_version` (the umbrella version) for the create-release job; bump step records it; commit step now stages root `CHANGELOG.md` alongside per-package files. - Add a root `CHANGELOG.md` seed so the new step has something to operate on. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a canonical ChangesChangelog Promotion & Release Workflow
Sequence DiagramsequenceDiagram
participant Workflow as Publish Workflow
participant BumpStep as bump step
participant PkgGen as Per-Pkg Changelog Gen
participant RootGen as Root Changelog Gen
participant CommitStep as Commit Step
participant ReleaseBuilder as Release Notes Builder
participant GitHub as GitHub Release
Workflow->>BumpStep: Iterate packages
BumpStep->>BumpStep: Capture agentworkforce as RELEASE_VERSION
BumpStep->>Workflow: Output release_version
Workflow->>PkgGen: Process each package with RELEASE_VERSION context
PkgGen->>PkgGen: Promote curated [Unreleased] or infer from git log
Workflow->>RootGen: Generate root changelog using RELEASE_VERSION
RootGen->>RootGen: Promote root [Unreleased] to versioned entry
RootGen->>CommitStep: Mark CHANGELOG.md ready
CommitStep->>CommitStep: Stage root + package CHANGELOG.md & package.json
CommitStep->>GitHub: Push version bump commit
Workflow->>ReleaseBuilder: Pass RELEASE_VERSION
ReleaseBuilder->>ReleaseBuilder: Extract root [Release Notes]
ReleaseBuilder->>ReleaseBuilder: Merge root + package changelog content
ReleaseBuilder->>GitHub: Create GitHub Release with merged notes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
.github/workflows/publish.yml (1)
280-295: 💤 Low valueConsider extracting the shared
splitAtUnreleasedfunction.This function is duplicated verbatim in the root changelog generator (lines 477–492). While inline scripts in YAML make sharing difficult, you could extract it to a small
.mjsfile in.github/scripts/and import it from both generators to reduce maintenance burden.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/publish.yml around lines 280 - 295, Extract the duplicated splitAtUnreleased function into a shared module (e.g., .github/scripts/changelog-utils.mjs) and export it so both YAML inline scripts can import and reuse it; update the workflows that currently define splitAtUnreleased (the function named splitAtUnreleased in the publish.yml generator and the identical one in the root changelog generator) to replace the inline definition with an import of the shared function, ensuring the exported symbol name matches (splitAtUnreleased) and adjusting any relative paths or module import syntax used by the workflow step runtimes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In @.github/workflows/publish.yml:
- Around line 280-295: Extract the duplicated splitAtUnreleased function into a
shared module (e.g., .github/scripts/changelog-utils.mjs) and export it so both
YAML inline scripts can import and reuse it; update the workflows that currently
define splitAtUnreleased (the function named splitAtUnreleased in the
publish.yml generator and the identical one in the root changelog generator) to
replace the inline definition with an import of the shared function, ensuring
the exported symbol name matches (splitAtUnreleased) and adjusting any relative
paths or module import syntax used by the workflow step runtimes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: b55bfff2-5a17-49bb-b779-3c832da46e91
📒 Files selected for processing (2)
.github/workflows/publish.ymlCHANGELOG.md
| if (type === 'test' || type === 'ci') return 'reliability'; | ||
| if (type === 'chore' && scope === 'release') return 'release'; | ||
| if (type === 'chore') return 'deps'; | ||
| return 'other'; |
There was a problem hiding this comment.
🟡 Conventional docs: commits misclassified as other instead of docs
The getType function matches docs in the conventional commit regex (line 339) but the if-chain (lines 343–349) has no branch for type === 'docs', so it falls through to return 'other' at line 350. This routes conventional docs: commits into cats.other, which is merged into the Changed section (line 374). Meanwhile, the imperative-verb fallback at line 360 correctly returns 'docs' for subjects like "Document …", routing them into the Documentation section (line 376). The result is an inconsistency: docs: update README → Changed, but Document the API → Documentation. The cats.docs bucket and Documentation section were clearly added to capture documentation commits, but the conventional-commit code path bypasses them.
| return 'other'; | |
| if (type === 'docs') return 'docs'; | |
| return 'other'; |
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Good catch — fixed in aa557d4. Added if (type === 'docs') return 'docs'; before the final return 'other'; so the conventional-commit path matches what the imperative-verb fallback was already doing.
The getType conventional-commit branch matched `docs` in the regex but had no `if (type === 'docs')` clause, so `docs:` subjects fell through to `'other'` and ended up in Changed. The imperative-verb fallback already routed "Document …" subjects to `'docs'`/Documentation, so the two paths disagreed. Add the missing branch so both classify the same. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
The workforce GitHub Releases page (
agentworkforce@0.14.0,0.13.0, …) currently ships### Released\n- vX.Y.Zstubs for every package — even though each package'sCHANGELOG.mdhas hand-curated## [Unreleased]bullets ready to ship. This PR ports the burn repo's release-notes infrastructure to workforce so the public Releases page mirrors burn's layout:## Packages,## Release Notes(top-level cross-package narrative),## Package Changelogswith per-package### <npm-name>/#### Added/#### Fixed/ etc.Concretely:
## [Unreleased]and promotes hand-curated content verbatim into the new versioned block, resetting[Unreleased]to empty. The git-log fallback (with Conventional Commits + imperative-verb bucketing) only fires when[Unreleased]is empty.CHANGELOG.mdseed file +Generate root changelogstep that performs the same Unreleased→[x.y.z]promotion anchored on the umbrellaagentworkforceversion. No git-log fallback — empty[Unreleased]just leaves the file alone.Build combined release notesstep now extracts the root version block as## Release Notesand inlines per-package changelog bodies as## Package Changelogs/### <npm-name>.publishjob exposesrelease_version(the umbrella version) for the create-release job; bump step records it; commit step now stages rootCHANGELOG.mdalongside per-package files.Smoke-tested locally against the current
[Unreleased]content in each package's CHANGELOG and against a synthetic root[Unreleased]block — both paths produce the expected nested-heading layout.Test plan
dry_run: true) and confirm the generated/tmp/release-notes.mdshows real### Added/### Changedbullets rather than### Releasedstubs.mainafter merge, confirm the GitHub Release shows## Packages,## Release Notes(if root[Unreleased]was non-empty), and## Package Changelogswith curated per-package content — matching the layout of e.g. https://github.com/AgentWorkforce/burn/releases/tag/relayburn-v2.5.0.CHANGELOG.mdfiles have their[Unreleased]blocks reset to empty after the publish, with the new[x.y.z] - DATEblock carrying the prior Unreleased bullets.🤖 Generated with Claude Code