fix(release-pr): stop dropping changeset entries and stripping code blocks (#3954)

## Summary

The script that generates the changeset release PR description was
silently dropping some changelog entries and stripping code examples. In
[#3932](https://github.com/triggerdotdev/trigger.dev/pull/3932), entry
[#3937](https://github.com/triggerdotdev/trigger.dev/pull/3937) was
missing entirely from the Improvements list and
[#3952](https://github.com/triggerdotdev/trigger.dev/pull/3952)'s code
block was gone, even though both were present in the raw changeset
output.

## Root cause

`parsePrBody` parsed the raw changeset body line by line:

- The dependency-bump filter matched any entry whose text *began* with a
backticked package name, so a real changelog entry like ``
`@trigger.dev/sdk` now bundles... `` got thrown out along with the
genuine version-bump lines.
- Only the first line of each bullet was kept, so fenced code blocks,
sub-bullets, and continuation paragraphs were discarded.

## Fix

Group each top-level bullet with its indented continuation (code blocks,
sub-bullets, paragraphs), dedent it, and re-emit it intact. The
dependency filter is now anchored so it only matches lines that are
*entirely* a package bump, leaving real entries that merely start with a
package name.

Verified by replaying #3932's raw body through the script: #3937 returns
to the list, #3952's code block is preserved, and #3936's sub-bullets
nest correctly under their parent.
This commit is contained in:
Eric Allam
2026-06-15 16:33:09 +01:00
committed by GitHub
parent 545ecf7beb
commit 709477168f
+65 -20
View File
@@ -39,29 +39,70 @@ function parsePrBody(body) {
const seen = new Set();
const prPattern = /\[#(\d+)\]\(([^)]+)\)/;
// A standalone dependency-bump list item, e.g. "`@trigger.dev/core@4.5.0-rc.7`"
// or "trigger.dev@4.5.0-rc.7". These normally appear nested under
// "Updated dependencies:" (and so get swallowed into that item below), but we
// guard against them showing up on their own too. Crucially this only matches
// a line that is *entirely* a package bump, so a real changeset that merely
// begins with a package name (e.g. "`@trigger.dev/sdk` now bundles ...") is
// kept.
const depBumpPattern = /^`?(?:@trigger\.dev\/[\w-]+|trigger\.dev)@[\w.+-]+`?$/;
// Group lines into top-level list items. A top-level item starts with a bullet
// at column 0 ("- " / "* "); every indented or blank line below it (sub-bullets,
// fenced code blocks, continuation paragraphs) belongs to that same item.
const items = [];
let current = null;
const flush = () => {
if (!current) return;
while (current.length > 1 && current[current.length - 1].trim() === "") {
current.pop();
}
items.push(current);
current = null;
};
for (const line of body.split("\n")) {
const trimmed = line.trim();
if (!trimmed.startsWith("- ") && !trimmed.startsWith("* ")) continue;
const isTopLevelBullet = /^[-*]\s+/.test(line);
if (isTopLevelBullet) {
flush();
current = [line];
} else if (current) {
if (line.trim() === "" || /^\s/.test(line)) {
current.push(line);
} else {
// A non-indented, non-blank, non-bullet line (heading or prose) ends the item
flush();
}
}
}
flush();
let text = trimmed.replace(/^[-*]\s+/, "").trim();
if (!text) continue;
for (const itemLines of items) {
const headLine = itemLines[0].replace(/^[-*]\s+/, "").trim();
if (!headLine) continue;
// Skip dependency-only updates (e.g. "Updated dependencies:" or "@trigger.dev/core@4.4.2")
if (text.startsWith("Updated dependencies")) continue;
if (text.startsWith("`@trigger.dev/")) continue;
if (text.startsWith("@trigger.dev/")) continue;
if (text.startsWith("`trigger.dev@")) continue;
if (text.startsWith("trigger.dev@")) continue;
// Skip dependency-only updates
if (headLine.startsWith("Updated dependencies")) continue;
if (depBumpPattern.test(headLine)) continue;
const prMatch = trimmed.match(prPattern);
// Deduplicate by PR number (the changeset link lives on the head line)
const prMatch = itemLines[0].match(prPattern);
if (prMatch) {
const prNumber = prMatch[1];
if (seen.has(prNumber)) continue;
seen.add(prNumber);
}
// Categorize
const lower = text.toLowerCase();
// Reconstruct the full item: head line + dedented continuation lines, so
// code blocks and sub-bullets survive. Continuation under a "- " item is
// indented 4 spaces; strip up to 4 to bring it back to the base level.
const continuation = itemLines.slice(1).map((l) => l.replace(/^ {1,4}/, ""));
const text = [headLine, ...continuation].join("\n").replace(/\s+$/, "");
// Categorize off the head line
const lower = headLine.toLowerCase();
let type = "improvement";
if (lower.startsWith("fix") || lower.includes("bug fix")) {
type = "fix";
@@ -206,6 +247,12 @@ function parseFrontmatter(content) {
// --- Format the enhanced PR body ---
// Render an entry as a list item, re-indenting continuation lines (code blocks,
// sub-bullets, paragraphs) by 2 spaces so they stay inside the "- " bullet.
function renderEntry(text) {
return `- ${text.replace(/\n/g, "\n ")}`;
}
function formatPrBody({ version, packageEntries, serverEntries, rawBody }) {
const lines = [];
@@ -238,7 +285,7 @@ function formatPrBody({ version, packageEntries, serverEntries, rawBody }) {
// Breaking changes
if (breaking.length > 0 || serverBreaking.length > 0) {
lines.push("## Breaking changes");
for (const entry of [...breaking, ...serverBreaking]) lines.push(`- ${entry.text}`);
for (const entry of [...breaking, ...serverBreaking]) lines.push(renderEntry(entry.text));
lines.push("");
}
@@ -247,7 +294,7 @@ function formatPrBody({ version, packageEntries, serverEntries, rawBody }) {
lines.push("## Highlights");
lines.push("");
for (const entry of features) {
lines.push(`- ${entry.text}`);
lines.push(renderEntry(entry.text));
}
lines.push("");
}
@@ -255,14 +302,14 @@ function formatPrBody({ version, packageEntries, serverEntries, rawBody }) {
// Improvements
if (improvements.length > 0) {
lines.push("## Improvements");
for (const entry of improvements) lines.push(`- ${entry.text}`);
for (const entry of improvements) lines.push(renderEntry(entry.text));
lines.push("");
}
// Bug fixes
if (fixes.length > 0) {
lines.push("## Bug fixes");
for (const entry of fixes) lines.push(`- ${entry.text}`);
for (const entry of fixes) lines.push(renderEntry(entry.text));
lines.push("");
}
@@ -274,9 +321,7 @@ function formatPrBody({ version, packageEntries, serverEntries, rawBody }) {
lines.push("These changes affect the self-hosted Docker image and Trigger.dev Cloud:");
lines.push("");
for (const entry of allServer) {
// Indent continuation lines so multi-line entries stay inside the list item
const indented = entry.text.replace(/\n/g, "\n ");
lines.push(`- ${indented}`);
lines.push(renderEntry(entry.text));
}
lines.push("");
}