chore(release): adopt new release pipeline (App auth + CHANGELOG extract)#31
chore(release): adopt new release pipeline (App auth + CHANGELOG extract)#31
Conversation
…act) Replaces manual-release.yaml with release.yaml modeled on bmad-builder's newly-adopted pattern. Uses BMAD Release Bot App token for pushes, runs full `npm test` validation stack (lint + lint:md + format:check), extracts release body from CHANGELOG.md `## vX.Y.Z` section, fires Discord notification on success. No functional changes. cis already has a clean package.json test script; no legacy scripts to remove.
WalkthroughThe manual release workflow is entirely removed and replaced with a new Release workflow. The new workflow triggers via Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 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)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🤖 Augment PR SummarySummary: This PR replaces the legacy manual release workflow with the standardized BMAD release pipeline. Changes:
Why: Align CIS’s release automation with the newer approach already used across other BMAD external modules. 🤖 Was this summary useful? React with 👍 or 👎 |
| echo "tag=v${VERSION}" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Push version commit and tag | ||
| run: git push origin main --follow-tags |
There was a problem hiding this comment.
In .github/workflows/release.yaml:71, git push origin main --follow-tags may fail to push the version tag created by npm version if it is lightweight, leaving the tag only local. That can make the subsequent gh release create "$TAG" operate on a missing/mismatched remote tag and potentially release the wrong commit.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| TAG="${{ steps.version.outputs.tag }}" | ||
| VERSION="${{ steps.version.outputs.version }}" | ||
| BODY=$(awk -v ver="$VERSION" ' | ||
| /^## v/ { if (found) exit; if (index($0, "## v" ver)) found=1; next } |
There was a problem hiding this comment.
In .github/workflows/release.yaml:78, the index($0, "## v" ver) check can match version prefixes (e.g., 1.2.3 would also match a ## v1.2.30 ... heading), which could extract the wrong CHANGELOG section. That would publish incorrect release notes for the tag.
Severity: low
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| default: "patch" | ||
| type: choice | ||
| options: | ||
| - patch |
There was a problem hiding this comment.
In .github/workflows/release.yaml:11-14, the dispatch bump options only allow patch|minor|major, whereas the removed workflow (and package.json scripts) previously supported prerelease bumps. Confirm this restriction is intentional so you don’t lose the ability to cut alpha/beta-style releases if you still need them.
Severity: low
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/release.yaml:
- Around line 77-80: The awk index(...) check is too loose and can match
prefixes like "v0.2.00"; update the header check to a strict anchored regex
instead of index: build a pattern such as "^## v" ver "([[:space:]]|-|$)" and
replace the index($0, "## v" ver) condition with a regex match ($0 ~ pattern or
match($0, pattern)) so only exact semver headers (followed by space, dash, or
EOL) are accepted; adjust the awk snippet that defines BODY accordingly.
- Around line 59-89: The workflow currently pushes the npm version commit/tag in
the "Push version commit and tag" step before validating CHANGELOG.md, causing
orphan tags if validation fails; move the CHANGELOG extraction/validation (the
awk logic that builds BODY and checks for a v${VERSION} section) into a new step
placed after "Capture new version" (steps: "Capture new version" -> new
"Generate release notes" step that sets an output like notes.body) and before
"Push version commit and tag", fail the workflow there if the notes.body is
empty, then update "Create GitHub Release" to consume steps.notes.outputs.body
(instead of re-running awk) and keep the actual git push in "Push version commit
and tag" so no commit/tag is pushed unless CHANGELOG validation succeeded.
- Around line 95-101: The Discord message string is being escaped by the esc
function causing markdown (bold + link) to be rendered as literal text; update
the MSG construction so the printf template '**[Creative Intelligence Suite %s
released](<%s>)**' is not piped through esc — remove the `| esc` usage when
building MSG (the variable and pipeline that calls esc) and leave the rest of
the pipeline (jq ... | curl ...) intact so TAG and RELEASE_URL are inserted raw
and the message renders with bold and a clickable link; reference the MSG
assignment and the esc helper in discord-helpers.sh when making this change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f35b5b85-2492-4696-81f8-2f225743f387
📒 Files selected for processing (2)
.github/workflows/manual-release.yaml.github/workflows/release.yaml
💤 Files with no reviewable changes (1)
- .github/workflows/manual-release.yaml
| - name: Bump version | ||
| run: | | ||
| npm version ${{ inputs.bump }} -m "chore(release): v%s [skip ci]" | ||
|
|
||
| - name: Capture new version | ||
| id: version | ||
| run: | | ||
| VERSION=$(node -p "require('./package.json').version") | ||
| echo "version=${VERSION}" >> $GITHUB_OUTPUT | ||
| echo "tag=v${VERSION}" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Push version commit and tag | ||
| run: git push origin main --follow-tags | ||
|
|
||
| - name: Create GitHub Release | ||
| run: | | ||
| TAG="${{ steps.version.outputs.tag }}" | ||
| VERSION="${{ steps.version.outputs.version }}" | ||
| BODY=$(awk -v ver="$VERSION" ' | ||
| /^## v/ { if (found) exit; if (index($0, "## v" ver)) found=1; next } | ||
| found { print } | ||
| ' CHANGELOG.md) | ||
| if [ -z "$BODY" ]; then | ||
| echo "::error::No CHANGELOG.md entry found for $TAG. Add a '## v${VERSION} - DATE' section before releasing." | ||
| exit 1 | ||
| fi | ||
| gh release create "$TAG" \ | ||
| --title "Creative Intelligence Suite $TAG" \ | ||
| --notes "$BODY" | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
There was a problem hiding this comment.
CHANGELOG validation runs after the tag is pushed — failure leaves an orphan tag on main.
The npm version commit + tag are pushed to origin main at line 71, but the CHANGELOG section check (lines 77–84) happens afterward. If ## v<version> - DATE is missing from CHANGELOG.md, the workflow exits 1 after the commit/tag are already on the remote, leaving a version bump commit and vX.Y.Z tag with no corresponding GitHub Release. Recovery requires a manual tag/commit revert.
Validate the CHANGELOG entry before mutating state — ideally before npm version, or at minimum before git push.
🛠️ Suggested reordering
Move the CHANGELOG extraction to run after Capture new version and before Push version commit and tag:
- name: Capture new version
id: version
run: |
VERSION=$(node -p "require('./package.json').version")
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "tag=v${VERSION}" >> $GITHUB_OUTPUT
+ - name: Extract release notes from CHANGELOG
+ id: notes
+ run: |
+ VERSION="${{ steps.version.outputs.version }}"
+ TAG="${{ steps.version.outputs.tag }}"
+ BODY=$(awk -v ver="$VERSION" '
+ /^## v/ { if (found) exit; if (index($0, "## v" ver)) found=1; next }
+ found { print }
+ ' CHANGELOG.md)
+ if [ -z "$BODY" ]; then
+ echo "::error::No CHANGELOG.md entry found for $TAG. Add a '## v${VERSION} - DATE' section before releasing."
+ exit 1
+ fi
+ {
+ echo 'body<<EOF_NOTES'
+ printf '%s\n' "$BODY"
+ echo 'EOF_NOTES'
+ } >> $GITHUB_OUTPUT
- name: Push version commit and tag
run: git push origin main --follow-tagsThen consume ${{ steps.notes.outputs.body }} in the Create GitHub Release step.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yaml around lines 59 - 89, The workflow currently
pushes the npm version commit/tag in the "Push version commit and tag" step
before validating CHANGELOG.md, causing orphan tags if validation fails; move
the CHANGELOG extraction/validation (the awk logic that builds BODY and checks
for a v${VERSION} section) into a new step placed after "Capture new version"
(steps: "Capture new version" -> new "Generate release notes" step that sets an
output like notes.body) and before "Push version commit and tag", fail the
workflow there if the notes.body is empty, then update "Create GitHub Release"
to consume steps.notes.outputs.body (instead of re-running awk) and keep the
actual git push in "Push version commit and tag" so no commit/tag is pushed
unless CHANGELOG validation succeeded.
| BODY=$(awk -v ver="$VERSION" ' | ||
| /^## v/ { if (found) exit; if (index($0, "## v" ver)) found=1; next } | ||
| found { print } | ||
| ' CHANGELOG.md) |
There was a problem hiding this comment.
index() match on the version header is a loose substring match.
index($0, "## v" ver) will match any header whose version starts with ver — e.g., with ver=0.2.0 it would also match ## v0.2.00 - ... or ## v0.2.0-rc1 - .... npm version <bump> produces exact semver so this is unlikely to bite today, but a trivial tighter anchor avoids future surprises:
🛠️ Suggested tighter match
- BODY=$(awk -v ver="$VERSION" '
- /^## v/ { if (found) exit; if (index($0, "## v" ver)) found=1; next }
- found { print }
- ' CHANGELOG.md)
+ BODY=$(awk -v ver="$VERSION" '
+ /^## v/ { if (found) exit; if ($0 ~ "^## v" ver "([^0-9].*)?$") found=1; next }
+ found { print }
+ ' CHANGELOG.md)This requires the character after the version to be non-numeric (space, dash, end-of-line), preventing v0.2.0 from matching v0.2.00.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yaml around lines 77 - 80, The awk index(...)
check is too loose and can match prefixes like "v0.2.00"; update the header
check to a strict anchored regex instead of index: build a pattern such as "^##
v" ver "([[:space:]]|-|$)" and replace the index($0, "## v" ver) condition with
a regex match ($0 ~ pattern or match($0, pattern)) so only exact semver headers
(followed by space, dash, or EOL) are accepted; adjust the awk snippet that
defines BODY accordingly.
| set -o pipefail | ||
| source .github/scripts/discord-helpers.sh | ||
| [ -z "$WEBHOOK" ] && exit 0 | ||
| TAG="${{ steps.version.outputs.tag }}" | ||
| RELEASE_URL="${{ github.server_url }}/${{ github.repository }}/releases/tag/${TAG}" | ||
| MSG=$(printf '🎨 **[Creative Intelligence Suite %s released](<%s>)**' "$TAG" "$RELEASE_URL" | esc) | ||
| jq -n --arg content "$MSG" '{content: $content}' | curl -sf --retry 2 -X POST "$WEBHOOK" -H "Content-Type: application/json" -d @- |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check the discord-helpers.sh file
cat -n .github/scripts/discord-helpers.sh | head -30Repository: bmad-code-org/bmad-module-creative-intelligence-suite
Length of output: 1295
🏁 Script executed:
# Also look for bmad-builder reference
grep -r "bmad-builder" . --max-count=5Repository: bmad-code-org/bmad-module-creative-intelligence-suite
Length of output: 528
🏁 Script executed:
# Get full context of the release workflow
sed -n '85,110p' .github/workflows/release.yamlRepository: bmad-code-org/bmad-module-creative-intelligence-suite
Length of output: 1107
Discord message markdown is broken by piping the template through esc.
The esc function (line 14 of .github/scripts/discord-helpers.sh) escapes markdown syntax characters including *, [, ], (, ) to safe Discord display — but it skips content inside <URL> wrappers. The template **[Creative Intelligence Suite %s released](<%s>)** has all its markdown syntax outside the <URL> wrapper. After piping through esc, it becomes \*\*\[Creative Intelligence Suite vX.Y.Z released\](<URL>)\*\*, which Discord renders as plain text with visible backslashes instead of bold and a clickable link.
Since $TAG (semantic version) and $RELEASE_URL (GitHub-generated URL) contain no markdown-significant characters, remove the | esc pipe:
🛠️ Proposed fix
- MSG=$(printf '🎨 **[Creative Intelligence Suite %s released](<%s>)**' "$TAG" "$RELEASE_URL" | esc)
+ MSG=$(printf '🎨 **[Creative Intelligence Suite %s released](<%s>)**' "$TAG" "$RELEASE_URL")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release.yaml around lines 95 - 101, The Discord message
string is being escaped by the esc function causing markdown (bold + link) to be
rendered as literal text; update the MSG construction so the printf template
'**[Creative Intelligence Suite %s released](<%s>)**' is not piped through esc —
remove the `| esc` usage when building MSG (the variable and pipeline that calls
esc) and leave the rest of the pipeline (jq ... | curl ...) intact so TAG and
RELEASE_URL are inserted raw and the message renders with bold and a clickable
link; reference the MSG assignment and the esc helper in discord-helpers.sh when
making this change.
Summary
manual-release.yamlwithrelease.yamlmodeled on the pattern adopted inbmad-builder(see bmad-builder#79)CHANGELOG.md## vX.Y.Zsection at release timeWhy
Aligns cis release infrastructure with the new pattern being rolled out across all BMad external modules. No functional or content changes; cis's existing
npm testscript (lint + lint:md + format:check) is intact and used as the pre-release validation step.This PR is infrastructure only. No fresh release is triggered by this change. Retroactive v0.2.0 tag (matching the existing CHANGELOG entry at commit
14a63b8) will be pushed separately after this merges.Test plan
14a63b8and create GitHub Release from CHANGELOGSummary by CodeRabbit