feat: add discord automated release notifications#564
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (10)
📝 WalkthroughWalkthroughAdds a composite GitHub Action to compute range-based release notes, a reusable Discord notification workflow, updates nightly/release workflows to generate and publish range notes, and introduces Node.js/Just tooling, policy config, and a generator script to produce JSON/Markdown release notes and predicted versions. Changes
Sequence DiagramsequenceDiagram
participant Trigger as GitHub Event
participant Workflow as Workflow
participant Resolver as resolve_ref Job
participant Generator as generate-notes Job
participant Action as compute-release-notes Action
participant Tooling as Release Tooling (just / Node)
participant Notifier as notify-discord Job
participant Discord as Discord Webhook
Trigger->>Workflow: trigger (schedule/dispatch/dispatch)
Workflow->>Resolver: run resolve_ref (checkout, compute tags/refs)
Resolver-->>Workflow: export refs/outputs
Workflow->>Generator: run generate-notes with refs
Generator->>Action: invoke composite action
Action->>Tooling: install & run just release range-notes
Tooling-->>Action: produce JSON/MD/log
Action-->>Generator: set outputs (compare_url, predicted_version, release_notes, ...)
Workflow->>Notifier: call notify-discord with metadata
Notifier->>Discord: POST webhook (embed + buttons)
Discord-->>Notifier: response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
✨ Simplify code
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 |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (6)
tools/release/scripts/generate-range-notes.mjs (3)
178-205:detectReleaseTypeduplicates/drifts fromrelease-policy.cjs.The release impact for
feat/fix/perf/revert/refactoris hardcoded here rather than derived frompolicyByType.get(type).release. This diverges from the single source of truth: e.g.,featis implicitlyminorhere but has noreleasefield in the policy;fix/perfare hardcodedpatchwith no policy entry; and if someone later setsrelease: "minor"for, say,perfinrelease-policy.cjs, the semantic-release flow picks it up but this range-notes generator does not — producing inconsistent predicted versions between stable and RC/nightly flows.Consider deriving directly from the policy, with
featas the only special-case (conventionalcommits default → minor) when no explicitreleaseis configured:♻️ Proposed refactor
function detectReleaseType(conventional, body) { if (!conventional) { return "none"; } if (conventional.breaking || findBreakingNotes(body, conventional.subject).length > 0) { return "major"; } - if (conventional.type === "feat") { - return "minor"; - } - - if (conventional.type === "fix" || conventional.type === "perf" || conventional.type === "revert") { - return "patch"; - } - - if (conventional.type === "refactor") { - return "patch"; - } - const policy = policyByType.get(conventional.type); - if (policy?.release === false) { - return "none"; - } - - return "none"; + if (policy && typeof policy.release === "string") { + return policy.release; + } + if (policy?.release === false) { + return "none"; + } + // conventionalcommits defaults when not overridden by policy + if (conventional.type === "feat") return "minor"; + if (conventional.type === "fix" || conventional.type === "perf") return "patch"; + return "none"; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tools/release/scripts/generate-range-notes.mjs` around lines 178 - 205, detectReleaseType currently hardcodes release mapping for conventional.type (feat/fix/perf/revert/refactor) instead of consulting the single source of truth in policyByType (from release-policy.cjs), causing drift; update detectReleaseType to derive the release kind from policyByType.get(conventional.type)?.release when conventional is present, treating the special case that if no explicit policy release exists and conventional.type === "feat" then return "minor", and still return "major" when conventional.breaking or findBreakingNotes(body, conventional.subject).length > 0; ensure the final fallback returns "none" only when policy explicitly disables release or no rule applies (use symbols detectReleaseType, policyByType, conventional.type, findBreakingNotes to locate changes).
207-216: Defensive: guard against unknown release types inmaxReleaseType.If
nextis ever a value not inrank(e.g., future addition like"prerelease"),rank.get(next)isundefinedandundefined > numberisfalse, so the new value is silently dropped. A cheap guard + thrown error would catch misuse immediately. Optional.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tools/release/scripts/generate-range-notes.mjs` around lines 207 - 216, The maxReleaseType function should defensively validate release types from the rank map before comparing; update maxReleaseType to check rank.has(next) and rank.has(current) (or check rank.get(...) !== undefined) and throw a clear TypeError including the offending value(s) if an unknown release type is encountered, so unknown types like "prerelease" are caught instead of silently dropped.
119-141: Minor: redundantfindBreakingNotesinvocation per commit.
findBreakingNotes(body, subject)runs during commit construction (line 132), then runs again insidedetectReleaseType(line 183) on every commit. For large ranges this doubles the body-line scanning. Consider computing the notes once and passing them intodetectReleaseType, or checkingbreakingNotes.lengthalready attached to the commit. Low impact; nit.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tools/release/scripts/generate-range-notes.mjs` around lines 119 - 141, In parseCommits, avoid calling findBreakingNotes twice by computing const breakingNotes = findBreakingNotes(body, subject) once when building each commit object and attaching it to the commit, then update the call to detectReleaseType to accept or use that precomputed breakingNotes (e.g., detectReleaseType(conventional, body, breakingNotes) or have detectReleaseType check commit.breakingNotes where it's invoked) and update any other callers of detectReleaseType accordingly so the heavy body scan is not repeated..github/workflows/release-candidate.yml (1)
97-113: Minor: redundantalways()and unconditional Discord post.
if: ${{ always() && needs.generate-notes.result == 'success' }}— thealways()is redundant;needs.generate-notes.result == 'success'already implies both upstream jobs completed (sincegenerate-notesdepends onresolve_ref).- The job fires even when
needs.generate-notes.outputs.released == 'true'is false (no releasable commits in range). For manual RC dispatch this is likely intentional (operator asked for the broadcast), but worth confirming — otherwise add&& needs.generate-notes.outputs.released == 'true'to suppress empty RC pings.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release-candidate.yml around lines 97 - 113, Update the notify-discord job condition: remove the redundant always() wrapper and set if to require successful generate-notes (use if: ${{ needs.generate-notes.result == 'success' }}), and — unless you intentionally want manual RC dispatches to always post — add the released guard to suppress empty pings by changing the condition to if: ${{ needs.generate-notes.result == 'success' && needs.generate-notes.outputs.released == 'true' }}; reference the notify-discord job and its if line in the workflow to make this change..github/actions/compute-release-notes/action.yml (1)
72-113: LGTM — small note on output size.Extraction fields (
compareUrl,nextVersion,releaseType,released,toSha) match the JSON payload intools/release/scripts/generate-range-notes.mjs, and the randomEOF_<hex>delimiter guards against content collisions inrelease_notes. Just be aware thatrelease_notespropagated as a composite-action output + reusable-workflow output can approach the 1 MB step output limit for large ranges (e.g., a stale base tag on a very active develop). The artifact upload at lines 131–139 mitigates loss, so this is informational.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/actions/compute-release-notes/action.yml around lines 72 - 113, The step "Extract Range Outputs" can emit a very large release_notes value and risk hitting the 1 MB GitHub step output limit; update the logic handling release_notes (the release_notes<<${release_notes_delim} block that reads tools/release/range-release-notes.md) to guard against oversized output by either truncating or compressing the content before appending to GITHUB_OUTPUT (or alternatively always persist the full file as an artifact and only emit a small summary to GITHUB_OUTPUT), and ensure the changes reference the same symbols: the release_notes variable, tools/release/range-release-notes.md, and the "Generate Range Notes" / "Extract Range Outputs" steps so the output remains safe for composite-action and reusable-workflow usage..github/workflows/notify-discord-release-notes.yml (1)
136-145: Constrain mention parsing for Discord notifications.
CONTENTcan include configurablemention_text; addallowed_mentionsso a bad variable value does not accidentally ping@everyone/@herewhile still allowing role/user mentions.🔒 Suggested payload guard
{ content: $content, + allowed_mentions: { + parse: ["users", "roles"] + }, embeds: [ { title: "Build Details",Please verify this matches the current Discord webhook
allowed_mentionsbehavior if@here/@everyonepings are intentionally supported.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/notify-discord-release-notes.yml around lines 136 - 145, The Discord webhook payload currently builds an object with content and embeds (title "Build Details", description $description) but lacks an allowed_mentions field; update the webhook payload object to include an allowed_mentions property that explicitly restricts parsing to only "users" and "roles" (or an explicit list) so user/role pings remain allowed but `@here`/`@everyone` cannot be triggered by a bad mention_text value; reference the payload object where content/$content and embeds (title "Build Details", description $description) are defined and add allowed_mentions accordingly, and verify the final behavior matches Discord's webhook semantics if `@here`/`@everyone` support is intentional.
🤖 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/publish-nightly.yml:
- Around line 57-61: The current check uses git rev-parse which only verifies
existence, not ancestry; change the logic around
previous_nightly_sha/from_ref/base_tag to verify ancestry with git merge-base
--is-ancestor (e.g., git merge-base --is-ancestor "${previous_nightly_sha}"
HEAD) and only set from_ref when that returns success; otherwise fall back to
base_tag but also ensure base_tag is an ancestor (git merge-base --is-ancestor
"${base_tag}" HEAD) before using it, and emit the warning when neither is a
valid ancestor. Use the existing variable names previous_nightly_sha, from_ref,
and base_tag to locate and update the conditional.
In @.github/workflows/release-candidate.yml:
- Around line 52-66: The current_version value is computed from the latest
publish-nightly.yml run on develop regardless of the dispatched candidate_ref,
which can show a misleading nightly identifier when promoting a non-develop ref;
change the logic in this block (variables: current_version, latest_nightly_sha,
latest_nightly_created_at, candidate_ref, base_tag) to only derive the
nightly-style name when candidate_ref == 'develop' and otherwise set
current_version to an RC-style identifier (e.g.,
rc-<YYYYMMDD>-<candidate-short-sha> derived from candidate_sha or use base_tag),
or alternatively rename the output to reflect it always means
"last_published_nightly" if you want to keep the existing behavior; update the
echo to emit the chosen value.
In @.github/workflows/release-main.yml:
- Around line 140-204: The prepare-release-notification job exports a
release_notes output (release_notes) that notify-discord never consumes; either
remove the release_notes export from prepare-release-notification or pass it
into the notify-discord workflow invocation (uses:
./.github/workflows/notify-discord-release-notes.yml) as an input (e.g., add
release_notes: ${{ needs.prepare-release-notification.outputs.release_notes }}
and update the called workflow to accept/use that input), and delete the
echo/printing lines that emit release_notes if you choose to drop the output to
avoid large job-output size issues.
In `@tools/release/scripts/generate-range-notes.mjs`:
- Around line 100-107: coerceVersion currently only matches X.Y.Z and returns
"0.0.0" for tags with prerelease or build metadata (e.g., v1.4.0-rc.1), so
update coerceVersion to strip leading "v"/whitespace and remove any
prerelease/build suffix before matching (or replace the regex approach by using
a semver parser like semver.parse); specifically adjust the function
coerceVersion to first normalize input (trim, remove leading "v") and strip
anything from the first "-" or "+" onward, then extract the three numeric
segments and return them, ensuring baseTag values such as "v1.4.0-rc.1" produce
"1.4.0" instead of "0.0.0".
---
Nitpick comments:
In @.github/actions/compute-release-notes/action.yml:
- Around line 72-113: The step "Extract Range Outputs" can emit a very large
release_notes value and risk hitting the 1 MB GitHub step output limit; update
the logic handling release_notes (the release_notes<<${release_notes_delim}
block that reads tools/release/range-release-notes.md) to guard against
oversized output by either truncating or compressing the content before
appending to GITHUB_OUTPUT (or alternatively always persist the full file as an
artifact and only emit a small summary to GITHUB_OUTPUT), and ensure the changes
reference the same symbols: the release_notes variable,
tools/release/range-release-notes.md, and the "Generate Range Notes" / "Extract
Range Outputs" steps so the output remains safe for composite-action and
reusable-workflow usage.
In @.github/workflows/notify-discord-release-notes.yml:
- Around line 136-145: The Discord webhook payload currently builds an object
with content and embeds (title "Build Details", description $description) but
lacks an allowed_mentions field; update the webhook payload object to include an
allowed_mentions property that explicitly restricts parsing to only "users" and
"roles" (or an explicit list) so user/role pings remain allowed but
`@here`/`@everyone` cannot be triggered by a bad mention_text value; reference
the payload object where content/$content and embeds (title "Build Details",
description $description) are defined and add allowed_mentions accordingly, and
verify the final behavior matches Discord's webhook semantics if
`@here`/`@everyone` support is intentional.
In @.github/workflows/release-candidate.yml:
- Around line 97-113: Update the notify-discord job condition: remove the
redundant always() wrapper and set if to require successful generate-notes (use
if: ${{ needs.generate-notes.result == 'success' }}), and — unless you
intentionally want manual RC dispatches to always post — add the released guard
to suppress empty pings by changing the condition to if: ${{
needs.generate-notes.result == 'success' &&
needs.generate-notes.outputs.released == 'true' }}; reference the notify-discord
job and its if line in the workflow to make this change.
In `@tools/release/scripts/generate-range-notes.mjs`:
- Around line 178-205: detectReleaseType currently hardcodes release mapping for
conventional.type (feat/fix/perf/revert/refactor) instead of consulting the
single source of truth in policyByType (from release-policy.cjs), causing drift;
update detectReleaseType to derive the release kind from
policyByType.get(conventional.type)?.release when conventional is present,
treating the special case that if no explicit policy release exists and
conventional.type === "feat" then return "minor", and still return "major" when
conventional.breaking or findBreakingNotes(body, conventional.subject).length >
0; ensure the final fallback returns "none" only when policy explicitly disables
release or no rule applies (use symbols detectReleaseType, policyByType,
conventional.type, findBreakingNotes to locate changes).
- Around line 207-216: The maxReleaseType function should defensively validate
release types from the rank map before comparing; update maxReleaseType to check
rank.has(next) and rank.has(current) (or check rank.get(...) !== undefined) and
throw a clear TypeError including the offending value(s) if an unknown release
type is encountered, so unknown types like "prerelease" are caught instead of
silently dropped.
- Around line 119-141: In parseCommits, avoid calling findBreakingNotes twice by
computing const breakingNotes = findBreakingNotes(body, subject) once when
building each commit object and attaching it to the commit, then update the call
to detectReleaseType to accept or use that precomputed breakingNotes (e.g.,
detectReleaseType(conventional, body, breakingNotes) or have detectReleaseType
check commit.breakingNotes where it's invoked) and update any other callers of
detectReleaseType accordingly so the heavy body scan is not repeated.
🪄 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: 773dd9cf-ea8e-442e-a183-257648ffe15e
⛔ Files ignored due to path filters (1)
tools/release/yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (10)
.github/actions/compute-release-notes/action.yml.github/workflows/notify-discord-release-notes.yml.github/workflows/publish-nightly.yml.github/workflows/release-candidate.yml.github/workflows/release-main.ymltools/release/Justfiletools/release/package.jsontools/release/release-policy.cjstools/release/release.config.cjstools/release/scripts/generate-range-notes.mjs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Test Suite / Frontend Tests
- GitHub Check: Test Suite / Backend Tests
- GitHub Check: Analyze (java-kotlin)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (11)
.github/workflows/release-candidate.yml (1)
82-95: Nit: redundant pre-checkout before composite action.The
Checkout Workflow Repositorystep at fetch-depth 1 is immediately superseded by the composite action's ownactions/checkout@v6at fetch-depth 0 withref: to_ref. You can drop this step — the composite already brings in the repo (including its ownaction.yml) once GitHub resolvesuses: ./.github/actions/compute-release-notes… actually that resolution requires the workflow repo checkout, so this step is needed to locate the local action. Keep as-is; ignore this nit.tools/release/Justfile (1)
18-20: LGTM.The new
range-notesrecipe cleanly wires to therelease:range-notesyarn script and aligns with the composite action consumers.tools/release/package.json (1)
11-18: LGTM.Script addition and plugin devDependencies align with the new range-notes tooling and shared release policy usage in
release.config.cjs.tools/release/release.config.cjs (1)
18-20: Behavior change:refactorandrevertnow trigger patch releases.The derived
releaseRulesemits{ release: "patch", type: "refactor" }and{ release: "patch", type: "revert" }(and explicitrelease: falseforchore/docs/ci/build/test/style). Under the default conventionalcommits preset,refactor/revertwould not normally produce a release, so merging one of these types intomainwill now cut a patch release on the next semantic-release run. Please confirm this is intentional and aligned with the RC/nightly flows that derive release type from the same policy.tools/release/release-policy.cjs (1)
1-21: LGTM.Clean extraction of shared release policy. Consumed consistently by
release.config.cjsandgenerate-range-notes.mjs..github/workflows/notify-discord-release-notes.yml (2)
60-120: Message composition looks solid.The channel-specific header, fallback URL selection, and multiline
$GITHUB_OUTPUThandling are clear and match the caller context.
146-191: Webhook send path looks good.The conditional changelog button and failing
curlon non-2xx responses should make notification failures visible..github/workflows/publish-nightly.yml (4)
7-20: Manual smoke-test controls look good.The
publish_imageinput plus minimalactions: read/contents: readpermissions fit the new nightly notification flow.
68-138: Nightly image metadata flow looks consistent.The conditional publish path and recorded
nightly_tagoutput line up with the Docker tag/build-arg usage.
169-196: Notification gating matches the intended nightly flow.The job correctly allows Discord notifications after notes generation when image publishing either succeeds or is intentionally skipped.
155-168: The shallow checkout ingenerate-notesis not a problem. Thecompute-release-notescomposite action performs its own checkout withfetch-depth: 0and explicitly checks out the target ref (to_ref), making it self-contained and independent of the parent job's checkout depth. The action already has access to the full git history and tags needed to generate the changelog range properly.> Likely an incorrect or invalid review comment.
| if [ -n "$previous_nightly_sha" ] && git rev-parse --verify --quiet "${previous_nightly_sha}^{commit}" >/dev/null; then | ||
| from_ref="$previous_nightly_sha" | ||
| elif [ -n "$previous_nightly_sha" ]; then | ||
| echo "::warning::Ignoring invalid nightly baseline ${previous_nightly_sha}; using ${base_tag}" | ||
| fi |
There was a problem hiding this comment.
Verify the previous nightly SHA is actually an ancestor.
rev-parse only proves the commit exists locally. If develop was rewritten, using a non-ancestor nightly SHA can generate misleading changelog ranges; fall back to base_tag unless it is on the current history.
🛠 Suggested ancestry check
- if [ -n "$previous_nightly_sha" ] && git rev-parse --verify --quiet "${previous_nightly_sha}^{commit}" >/dev/null; then
- from_ref="$previous_nightly_sha"
- elif [ -n "$previous_nightly_sha" ]; then
- echo "::warning::Ignoring invalid nightly baseline ${previous_nightly_sha}; using ${base_tag}"
- fi
+ if [ -n "$previous_nightly_sha" ] && git rev-parse --verify --quiet "${previous_nightly_sha}^{commit}" >/dev/null; then
+ if git merge-base --is-ancestor "$previous_nightly_sha" HEAD; then
+ from_ref="$previous_nightly_sha"
+ else
+ echo "::warning::Ignoring non-ancestor nightly baseline ${previous_nightly_sha}; using ${base_tag}"
+ fi
+ elif [ -n "$previous_nightly_sha" ]; then
+ echo "::warning::Ignoring invalid nightly baseline ${previous_nightly_sha}; using ${base_tag}"
+ fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if [ -n "$previous_nightly_sha" ] && git rev-parse --verify --quiet "${previous_nightly_sha}^{commit}" >/dev/null; then | |
| from_ref="$previous_nightly_sha" | |
| elif [ -n "$previous_nightly_sha" ]; then | |
| echo "::warning::Ignoring invalid nightly baseline ${previous_nightly_sha}; using ${base_tag}" | |
| fi | |
| if [ -n "$previous_nightly_sha" ] && git rev-parse --verify --quiet "${previous_nightly_sha}^{commit}" >/dev/null; then | |
| if git merge-base --is-ancestor "$previous_nightly_sha" HEAD; then | |
| from_ref="$previous_nightly_sha" | |
| else | |
| echo "::warning::Ignoring non-ancestor nightly baseline ${previous_nightly_sha}; using ${base_tag}" | |
| fi | |
| elif [ -n "$previous_nightly_sha" ]; then | |
| echo "::warning::Ignoring invalid nightly baseline ${previous_nightly_sha}; using ${base_tag}" | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/publish-nightly.yml around lines 57 - 61, The current
check uses git rev-parse which only verifies existence, not ancestry; change the
logic around previous_nightly_sha/from_ref/base_tag to verify ancestry with git
merge-base --is-ancestor (e.g., git merge-base --is-ancestor
"${previous_nightly_sha}" HEAD) and only set from_ref when that returns success;
otherwise fall back to base_tag but also ensure base_tag is an ancestor (git
merge-base --is-ancestor "${base_tag}" HEAD) before using it, and emit the
warning when neither is a valid ancestor. Use the existing variable names
previous_nightly_sha, from_ref, and base_tag to locate and update the
conditional.
| latest_nightly_json="$( | ||
| gh api "/repos/${GITHUB_REPOSITORY}/actions/workflows/publish-nightly.yml/runs?branch=develop&status=success&per_page=1" | ||
| )" | ||
| latest_nightly_sha="$(printf '%s' "$latest_nightly_json" | jq -r '.workflow_runs[0].head_sha // ""')" | ||
| latest_nightly_created_at="$(printf '%s' "$latest_nightly_json" | jq -r '.workflow_runs[0].created_at // ""')" | ||
| current_version="$base_tag" | ||
| if [ -n "$latest_nightly_sha" ] && [ -n "$latest_nightly_created_at" ]; then | ||
| latest_nightly_date="$(date -u -d "$latest_nightly_created_at" +%Y%m%d)" | ||
| latest_nightly_short_sha="$(printf '%s' "$latest_nightly_sha" | cut -c1-7)" | ||
| current_version="nightly-${latest_nightly_date}-${latest_nightly_short_sha}" | ||
| fi | ||
|
|
||
| echo "candidate_sha=${candidate_sha}" >> "$GITHUB_OUTPUT" | ||
| echo "base_tag=${base_tag}" >> "$GITHUB_OUTPUT" | ||
| echo "current_version=${current_version}" >> "$GITHUB_OUTPUT" |
There was a problem hiding this comment.
current_version semantics may not match the dispatched candidate_ref.
current_version is computed from the latest successful publish-nightly.yml run on develop (regardless of the selected candidate_ref). When an operator dispatches an RC against a non-develop ref (e.g., a feature branch or a specific SHA), the Discord embed will show nightly-YYYYMMDD-<develop-sha> as the "current version", which has no relation to the candidate being promoted. Consider:
- gating the nightly-derived identifier on
candidate_ref == 'develop', and otherwise using arc-<date>-<candidate-short-sha>identifier; or - documenting clearly that this field always reflects "last published nightly" and renaming it accordingly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/release-candidate.yml around lines 52 - 66, The
current_version value is computed from the latest publish-nightly.yml run on
develop regardless of the dispatched candidate_ref, which can show a misleading
nightly identifier when promoting a non-develop ref; change the logic in this
block (variables: current_version, latest_nightly_sha,
latest_nightly_created_at, candidate_ref, base_tag) to only derive the
nightly-style name when candidate_ref == 'develop' and otherwise set
current_version to an RC-style identifier (e.g.,
rc-<YYYYMMDD>-<candidate-short-sha> derived from candidate_sha or use base_tag),
or alternatively rename the output to reflect it always means
"last_published_nightly" if you want to keep the existing behavior; update the
echo to emit the chosen value.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
.github/workflows/publish-nightly.yml (1)
46-63:⚠️ Potential issue | 🟡 MinorValidate
base_tagbefore using it as the fallback range start.The previous nightly SHA is now ancestry-checked, but when it is absent or unusable,
from_refremainsbase_tag. If that stable tag is not ondevelophistory, the nightly range can still be misleading.🛠️ Suggested fallback hardening
base_tag="$(git describe --tags --abbrev=0 --match 'v*' origin/main)" from_ref="$base_tag" + if ! git merge-base --is-ancestor "$base_tag" HEAD; then + base_merge="$(git merge-base "$base_tag" HEAD || true)" + if [ -z "$base_merge" ]; then + echo "::error::Stable base ${base_tag} is unrelated to HEAD" + exit 1 + fi + echo "::warning::Stable base ${base_tag} is not an ancestor of HEAD; using merge-base ${base_merge}" + from_ref="$base_merge" + fi previous_nightly_sha=""🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/publish-nightly.yml around lines 46 - 63, Validate that base_tag is a reachable ancestor of the current branch before using it as the fallback for from_ref: check that base_tag exists (git rev-parse --verify) and is an ancestor of HEAD (git merge-base --is-ancestor); if either check fails, emit a warning and choose a safer fallback (for example use the repository root commit or leave from_ref unset/use HEAD's first parent commit) instead of blindly using base_tag. Update the logic around base_tag, from_ref, and previous_nightly_sha to perform these checks and set from_ref only when base_tag is verified as a valid ancestor; keep the existing warnings for unusable baselines and ensure process uses the verified symbol names base_tag, from_ref, and previous_nightly_sha.
🤖 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/publish-nightly.yml:
- Around line 157-169: The checkout step named "Checkout Workflow Repository"
currently does a shallow fetch (fetch-depth: 1) which conflicts with the
downstream compute-release-notes action; remove the shallow fetch and add an
explicit ref so the job checks out the exact commit used by the notes action —
update the "Checkout Workflow Repository" actions/checkout step to use
fetch-depth: 0 (or remove fetch-depth) and include ref: ${{
needs.resolve_ref.outputs.commit_sha }} so the subsequent "Build Full Changelog"
(id: notes, uses: ./.github/actions/compute-release-notes) runs against the
correct commit.
In `@tools/release/scripts/generate-range-notes.mjs`:
- Around line 124-134: The breakingNotes currently only come from footer parsing
via findBreakingNotes(body, subject), which misses commits marked with a bang in
the subject (conventional.breaking). Update the map logic that builds each entry
(where parseConventionalSubject is used, detectReleaseType is called, and
breakingNotes is set) to fallback to using the subject as a breaking note when
conventional?.breaking is true and findBreakingNotes returned empty; i.e., set
breakingNotes to the result of findBreakingNotes(...) || (conventional?.breaking
? [subject] : []), so feat!: / fix!: commits produce a Breaking Changes entry
even without footers.
---
Duplicate comments:
In @.github/workflows/publish-nightly.yml:
- Around line 46-63: Validate that base_tag is a reachable ancestor of the
current branch before using it as the fallback for from_ref: check that base_tag
exists (git rev-parse --verify) and is an ancestor of HEAD (git merge-base
--is-ancestor); if either check fails, emit a warning and choose a safer
fallback (for example use the repository root commit or leave from_ref unset/use
HEAD's first parent commit) instead of blindly using base_tag. Update the logic
around base_tag, from_ref, and previous_nightly_sha to perform these checks and
set from_ref only when base_tag is verified as a valid ancestor; keep the
existing warnings for unusable baselines and ensure process uses the verified
symbol names base_tag, from_ref, and previous_nightly_sha.
🪄 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: 171b9ab7-c9a5-4795-afad-e4a38640a9e5
📒 Files selected for processing (3)
.github/workflows/publish-nightly.yml.github/workflows/release-main.ymltools/release/scripts/generate-range-notes.mjs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Test Suite / Backend Tests
- GitHub Check: Test Suite / Frontend Tests
- GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (1)
.github/workflows/release-main.yml (1)
134-197: Stable Discord notification flow looks sound.The notification is gated after release publishing, derives stable release metadata, and forwards the consumed outputs to the reusable Discord workflow.
| .map((entry) => { | ||
| const [hash, subject, body = ""] = entry.split("\x1f"); | ||
| const conventional = parseConventionalSubject(subject); | ||
| const type = conventional?.type || ""; | ||
| const policy = policyByType.get(type); | ||
|
|
||
| return { | ||
| body, | ||
| breakingNotes: findBreakingNotes(body, subject), | ||
| hash, | ||
| releaseType: detectReleaseType(conventional, body), |
There was a problem hiding this comment.
Populate breaking notes for ! commits without footers.
detectReleaseType marks feat!:/fix!: commits as major, but breakingNotes only comes from body footers, so the rendered changelog can omit the “Breaking Changes” section for those commits. Use the subject as a fallback note when conventional.breaking is true.
🐛 Proposed fix
const [hash, subject, body = ""] = entry.split("\x1f");
const conventional = parseConventionalSubject(subject);
+ const footerBreakingNotes = findBreakingNotes(body, subject);
+ const breakingNotes =
+ conventional?.breaking && footerBreakingNotes.length === 0
+ ? [conventional.subject || subject]
+ : footerBreakingNotes;
const type = conventional?.type || "";
const policy = policyByType.get(type);
return {
body,
- breakingNotes: findBreakingNotes(body, subject),
+ breakingNotes,
hash,
releaseType: detectReleaseType(conventional, body),Also applies to: 178-185, 236-245
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tools/release/scripts/generate-range-notes.mjs` around lines 124 - 134, The
breakingNotes currently only come from footer parsing via
findBreakingNotes(body, subject), which misses commits marked with a bang in the
subject (conventional.breaking). Update the map logic that builds each entry
(where parseConventionalSubject is used, detectReleaseType is called, and
breakingNotes is set) to fallback to using the subject as a breaking note when
conventional?.breaking is true and findBreakingNotes returned empty; i.e., set
breakingNotes to the result of findBreakingNotes(...) || (conventional?.breaking
? [subject] : []), so feat!: / fix!: commits produce a Breaking Changes entry
even without footers.
c9122dc to
9f20cf5
Compare
9f20cf5 to
50bf8c4
Compare
Description
Add a unified release notification flows for stable releases, nightly builds, & manually triggered release candidate comms.
This introduced Discord notifications for a consistent message + embed format, generates full changelogs from explicit git ranges, and links Discord users to the changelog in GitHub.
Nightly notifications now compare the current develop head against the previous successful scheduled nightly run, release candidate notifications compare the selected candidate against the latest stable tag, and stable notifications link directly to the GitHub release notes.
Changes
Summary by CodeRabbit
New Features
Chores