Skip to content

feat: unified GitHub release, server change tracking, and enhanced release PR#3085

Draft
ericallam wants to merge 7 commits intomainfrom
feature/tri-7505-improve-changeset-and-release-workflows-and-communication
Draft

feat: unified GitHub release, server change tracking, and enhanced release PR#3085
ericallam wants to merge 7 commits intomainfrom
feature/tri-7505-improve-changeset-and-release-workflows-and-communication

Conversation

@ericallam
Copy link
Member

  • Add .server-changes/ convention for tracking server-only changes
  • Create scripts/enhance-release-pr.mjs to deduplicate and categorize changeset PR body
  • Create scripts/generate-github-release.mjs to format unified GitHub release body
  • Change release.yml to create one unified GitHub release instead of per-package releases
  • Add update-release job to patch Docker image link after images are pushed to GHCR
  • Update changesets-pr.yml to trigger on .server-changes, enhance PR body, and clean up consumed files
  • Document server changes in CLAUDE.md, CONTRIBUTING.md, CHANGESETS.md, and RELEASE.md

…lease PR

- Add .server-changes/ convention for tracking server-only changes
- Create scripts/enhance-release-pr.mjs to deduplicate and categorize changeset PR body
- Create scripts/generate-github-release.mjs to format unified GitHub release body
- Change release.yml to create one unified GitHub release instead of per-package releases
- Add update-release job to patch Docker image link after images are pushed to GHCR
- Update changesets-pr.yml to trigger on .server-changes, enhance PR body, and clean up consumed files
- Document server changes in CLAUDE.md, CONTRIBUTING.md, CHANGESETS.md, and RELEASE.md
@changeset-bot
Copy link

changeset-bot bot commented Feb 18, 2026

⚠️ No Changeset found

Latest commit: 9cbb740

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

Walkthrough

Adds a server-only changes convention with a new .server-changes/ README and updates docs (CHANGESETS.md, RELEASE.md, CONTRIBUTING.md, CLAUDE.md) to describe its usage. Updates CI workflows (.github/workflows/changesets-pr.yml and release.yml) to trigger on .server-changes/**, enhance PR titles and bodies (via a Node script), clean up non-README .server-changes files before lockfile commits, create a unified GitHub release, and update release bodies with tag-specific Docker links. Introduces two Node.js scripts: scripts/enhance-release-pr.mjs (composes enhanced PR bodies including .server-changes entries) and scripts/generate-github-release.mjs (formats GitHub release notes).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changes: unified GitHub release creation, server change tracking convention, and enhanced release PR body formatting.
Description check ✅ Passed The description covers all major changes with a bulleted list of key modifications, though some template sections (Testing, Screenshots, Checklist items) are missing.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/tri-7505-improve-changeset-and-release-workflows-and-communication

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (6)
.server-changes/README.md (1)

17-19: Missing language on fenced code block (markdownlint MD040).

🔧 Proposed fix
-```
+```text
 .server-changes/fix-batch-queue-stalls.md
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @.server-changes/README.md around lines 17 - 19, The README's fenced code
block showing ".server-changes/fix-batch-queue-stalls.md" is missing a language
specifier (markdownlint MD040); update the fenced block in
.server-changes/README.md to include a language token (e.g., add "text" after
the opening ), so the block becomes text ... ```, ensuring the code fence
around ".server-changes/fix-batch-queue-stalls.md" is annotated.


</details>

</blockquote></details>
<details>
<summary>.github/workflows/changesets-pr.yml (1)</summary><blockquote>

`66-73`: **Redundant stdin pipe; `CHANGESET_PR_BODY` always takes precedence.**

In `enhance-release-pr.mjs` (line 231), `process.env.CHANGESET_PR_BODY` is checked first and stdin is only read when that env var is falsy. Since `CHANGESET_PR_BODY="$RAW_BODY"` is always set here, the `echo "$RAW_BODY" |` pipe is dead code. Remove it to clarify intent.

<details>
<summary>🔧 Proposed fix</summary>

```diff
-           ENHANCED_BODY=$(echo "$RAW_BODY" | CHANGESET_PR_BODY="$RAW_BODY" node scripts/enhance-release-pr.mjs "$VERSION")
+           ENHANCED_BODY=$(CHANGESET_PR_BODY="$RAW_BODY" node scripts/enhance-release-pr.mjs "$VERSION")
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/changesets-pr.yml around lines 66 - 73, The pipe `echo
"$RAW_BODY" |` is redundant because enhance-release-pr.mjs checks
process.env.CHANGESET_PR_BODY first; set the env var directly and remove the
stdin pipe: export/pass CHANGESET_PR_BODY="$RAW_BODY" when invoking node and
call node scripts/enhance-release-pr.mjs "$VERSION" (so CHANGESET_PR_BODY is
available to the script), and delete the unused echo/stdin pipeline; update the
block that defines RAW_BODY/ENHANCED_BODY to only set
ENHANCED_BODY=$(CHANGESET_PR_BODY="$RAW_BODY" node
scripts/enhance-release-pr.mjs "$VERSION") or equivalent and remove the dead
pipe.
```

</details>

</blockquote></details>
<details>
<summary>CHANGESETS.md (1)</summary><blockquote>

`1-46`: **LGTM.** The expanded guidance is clear and consistent with the rest of the PR.

One minor item flagged by `markdownlint` (MD059): the link text `"here"` on line 7 is non-descriptive. Consider replacing it with something like `"the changesets adding-a-changeset guide"`.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@CHANGESETS.md` around lines 1 - 46, Replace the non-descriptive link text
"here" in the "To add a changeset (package changes)" paragraph with a
descriptive label such as "the changesets adding-a-changeset guide" (i.e.,
update the markdown link text that currently reads
`[here](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md)`),
so the link is self-descriptive and satisfies MD059.
```

</details>

</blockquote></details>
<details>
<summary>scripts/generate-github-release.mjs (3)</summary><blockquote>

`88-101`: **Contributor with a real name but a `noreply.github.com` email is silently dropped if the regex doesn't match.**

On line 91, if `email?.includes("noreply.github.com")` is true but the regex on line 93 fails (e.g., the email format is `user@github.noreply.github.com` — an unexpected subdomain), the contributor is skipped entirely via `continue`. The `name` is available and valid in that case but never recorded. CodeQL also flags the substring check as an incomplete match.

Consider falling back to `name` when the noreply regex doesn't match:


<details>
<summary>Proposed fix</summary>

```diff
     for (const line of log.split("\n").filter(Boolean)) {
       const [name, email] = line.split("|");
-      if (!name || email?.includes("noreply.github.com")) {
-        // Try to extract username from noreply email
-        const match = email?.match(/(\d+\+)?(.+)@users\.noreply\.github\.com/);
+      if (email?.endsWith("@users.noreply.github.com")) {
+        const match = email.match(/(\d+\+)?(.+)@users\.noreply\.github\.com/);
         if (match) {
           const username = match[2];
           contributors.set(username, (contributors.get(username) || 0) + 1);
-        }
-        continue;
+        } else if (name) {
+          contributors.set(name, (contributors.get(name) || 0) + 1);
+        }
+        continue;
       }
+      if (!name) continue;
       contributors.set(name, (contributors.get(name) || 0) + 1);
     }
```
</details>

Using `endsWith` instead of `includes` also addresses the CodeQL substring sanitization warning.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate-github-release.mjs` around lines 88 - 101, The contributors
aggregation currently skips entries when email.includes("noreply.github.com") is
true but the regex on email.match(...) fails; update the loop that builds the
contributors Map so it uses email?.endsWith("noreply.github.com") instead of
includes(), and if the noreply regex does not match, fall back to using the
provided name (i.e., call contributors.set(name, ...) rather than continue).
Keep the existing logic that increments counts when a username is extracted from
the regex (contributors.set(username, ...)) and only fall back to name when
regex returns null.
```

</details>

---

`113-131`: **Prefer `readFileSync` over shelling out to `cat`.**

`execSync("cat .changeset/config.json", …)` spawns an unnecessary shell process. `readFileSync` is simpler, faster, and portable.


<details>
<summary>Proposed change</summary>

```diff
+import { readFileSync } from "fs";
+
 function getPublishedPackages() {
   try {
-    const config = JSON.parse(
-      execSync("cat .changeset/config.json", {
-        cwd: ROOT_DIR,
-        encoding: "utf-8",
-      })
-    );
+    const config = JSON.parse(
+      readFileSync(join(ROOT_DIR, ".changeset", "config.json"), "utf-8")
+    );
     const fixed = config.fixed?.[0] || [];
     return fixed;
   } catch {
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate-github-release.mjs` around lines 113 - 131, The function
getPublishedPackages currently shells out with execSync("cat
.changeset/config.json", ...) which is inefficient; replace that call by reading
the file directly using fs.readFileSync(path, "utf-8") (require/import fs at top
if needed), then JSON.parse the returned string to populate config and derive
fixed as config.fixed?.[0] || []; keep the same try/catch behavior and the same
fallback array when reading/parsing fails so behavior of getPublishedPackages
remains unchanged.
```

</details>

---

`78-101`: **Unsanitized `version` interpolated into `execSync` shell string — potential command injection.**

`version` comes directly from `process.argv[2]` and is interpolated into the shell command on line 83. While this is a CI script with presumably controlled inputs, a malformed version like `1.0.0; rm -rf /` would be executed by the shell. Prefer `execFileSync` (which bypasses the shell) or at minimum validate the version format at the top of the script.


<details>
<summary>Option A: validate version format early (minimal change)</summary>

```diff
 const version = process.argv[2];
 if (!version) {
   console.error("Usage: node scripts/generate-github-release.mjs <version>");
   process.exit(1);
 }
+if (!/^\d+\.\d+\.\d+(-[\w.]+)?$/.test(version)) {
+  console.error(`Invalid version format: ${version}`);
+  process.exit(1);
+}
```
</details>

<details>
<summary>Option B: use execFileSync to avoid shell interpolation</summary>

```diff
-    const log = execSync(`git log ${range} --format="%aN|%aE" --no-merges`, {
+    const log = execFileSync("git", ["log", range, "--format=%aN|%aE", "--no-merges"], {
       cwd: ROOT_DIR,
       encoding: "utf-8",
     });
```

(Remember to import `execFileSync` from `child_process`.)
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate-github-release.mjs` around lines 78 - 101, The git range
string in getContributors interpolates previousVersion into an execSync shell
command (execSync(`git log ${range} ...`)), which allows command injection via a
malicious version; fix by either validating previousVersion early (ensure it
matches a strict semver or /^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$/) before
using it, or switch to a shell-avoiding call: replace the execSync usage with
execFileSync/child_process.spawnSync passing ['log', range,
'--format=...','--no-merges'] (or split the range into a single safe arg) so no
shell interpolation occurs; update imports to include execFileSync/child_process
as needed and keep the rest of getContributors logic intact.
```

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used**: Repository UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between eb0f96339378ed6e08810a6ca442f3e5af47830f and 579248d929c022a54ab41a95a24b182a35683e3a.

</details>

<details>
<summary>📒 Files selected for processing (10)</summary>

* `.github/workflows/changesets-pr.yml`
* `.github/workflows/release.yml`
* `.server-changes/.gitkeep`
* `.server-changes/README.md`
* `CHANGESETS.md`
* `CLAUDE.md`
* `CONTRIBUTING.md`
* `RELEASE.md`
* `scripts/enhance-release-pr.mjs`
* `scripts/generate-github-release.mjs`

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>📓 Path-based instructions (1)</summary>

<details>
<summary>**/*.{js,ts,jsx,tsx,json,md,yaml,yml}</summary>


**📄 CodeRabbit inference engine (AGENTS.md)**

> Format code using Prettier before committing

Files:
- `CONTRIBUTING.md`
- `RELEASE.md`
- `CHANGESETS.md`
- `CLAUDE.md`

</details>

</details><details>
<summary>🧠 Learnings (5)</summary>

<details>
<summary>📓 Common learnings</summary>

```
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`
```

</details>
<details>
<summary>📚 Learning: 2026-01-15T11:50:06.067Z</summary>

```
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`
```

**Applied to files:**
- `CONTRIBUTING.md`
- `RELEASE.md`
- `.github/workflows/changesets-pr.yml`
- `CHANGESETS.md`
- `CLAUDE.md`
- `.server-changes/README.md`

</details>
<details>
<summary>📚 Learning: 2026-01-15T11:50:06.067Z</summary>

```
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Default to patch version for changesets for bug fixes and minor changes; confirm with maintainers before selecting minor or major
```

**Applied to files:**
- `CLAUDE.md`

</details>
<details>
<summary>📚 Learning: 2025-11-27T16:26:58.661Z</summary>

```
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/app/services/**/*.server.{ts,tsx} : Separate testable services from configuration files; follow the pattern of `realtimeClient.server.ts` (testable service) and `realtimeClientGlobal.server.ts` (configuration) in the webapp
```

**Applied to files:**
- `.server-changes/README.md`

</details>
<details>
<summary>📚 Learning: 2025-11-26T14:40:07.146Z</summary>

```
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 2710
File: packages/schema-to-json/package.json:0-0
Timestamp: 2025-11-26T14:40:07.146Z
Learning: Node.js 24+ has native TypeScript support and can execute .ts files directly without tsx or ts-node for scripts that use only erasable TypeScript syntax (type annotations, interfaces, etc.). The trigger.dev repository uses Node.js 24.11.1+ and scripts like updateVersion.ts can be run with `node` instead of `tsx`.
```

**Applied to files:**
- `scripts/generate-github-release.mjs`

</details>

</details><details>
<summary>🧬 Code graph analysis (2)</summary>

<details>
<summary>scripts/enhance-release-pr.mjs (1)</summary><blockquote>

<details>
<summary>scripts/generate-github-release.mjs (4)</summary>

* `version` (22-22)
* `ROOT_DIR` (28-28)
* `lines` (43-43)
* `lines` (147-147)

</details>

</blockquote></details>
<details>
<summary>scripts/generate-github-release.mjs (1)</summary><blockquote>

<details>
<summary>scripts/enhance-release-pr.mjs (7)</summary>

* `version` (23-23)
* `ROOT_DIR` (29-29)
* `body` (241-246)
* `lines` (127-127)
* `match` (110-110)
* `parts` (151-151)
* `chunks` (233-233)

</details>

</blockquote></details>

</details><details>
<summary>🪛 GitHub Check: CodeQL</summary>

<details>
<summary>scripts/generate-github-release.mjs</summary>

[failure] 91-91: Incomplete URL substring sanitization
'[noreply.github.com](1)' can be anywhere in the URL, and arbitrary hosts may come before or after it.

</details>

</details>
<details>
<summary>🪛 LanguageTool</summary>

<details>
<summary>RELEASE.md</summary>

[uncategorized] ~8-~8: The official name of this software platform is spelled with a capital “H”.
Context: ...only changes). 2. The [changesets-pr.yml](./.github/workflows/changesets-pr.yml) workflow a...

(GITHUB)

---

[uncategorized] ~10-~10: The official name of this software platform is spelled with a capital “H”.
Context: ...ease PR into `main`. 4. The [release.yml](./.github/workflows/release.yml) workflow automat...

(GITHUB)

</details>
<details>
<summary>CHANGESETS.md</summary>

[uncategorized] ~43-~43: The official name of this software platform is spelled with a capital “H”.
Context: ...he `main` branch, the [changesets-pr.yml](./.github/workflows/changesets-pr.yml) workflow w...

(GITHUB)

---

[uncategorized] ~46-~46: The official name of this software platform is spelled with a capital “H”.
Context: ... is merged into `main`, the [release.yml](./.github/workflows/release.yml) workflow will au...

(GITHUB)

</details>
<details>
<summary>.server-changes/README.md</summary>

[grammar] ~3-~3: Ensure spelling is correct
Context: ...acks changes to server-only components (webapp, supervisor, coordinator, etc.) that ar...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

</details>

</details>
<details>
<summary>🪛 markdownlint-cli2 (0.21.0)</summary>

<details>
<summary>CHANGESETS.md</summary>

[warning] 7-7: Link text should be descriptive

(MD059, descriptive-link-text)

</details>
<details>
<summary>CLAUDE.md</summary>

[warning] 85-85: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

---

[warning] 94-94: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>
<details>
<summary>.server-changes/README.md</summary>

[warning] 17-17: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

</details>

<details>
<summary>⏰ 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). (26)</summary>

* GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
* GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
* GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
* GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
* GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
* GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
* GitHub Check: sdk-compat / Cloudflare Workers
* GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
* GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
* GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
* GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
* GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
* GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
* GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
* GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
* GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
* GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
* GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
* GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
* GitHub Check: typecheck / typecheck
* GitHub Check: sdk-compat / Deno Runtime
* GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
* GitHub Check: sdk-compat / Bun Runtime
* GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
* GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
* GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)

</details>

<details>
<summary>🔇 Additional comments (5)</summary><blockquote>

<details>
<summary>CONTRIBUTING.md (1)</summary><blockquote>

`270-301`: **LGTM.** New section is clear and consistent with CHANGESETS.md and CLAUDE.md.

</blockquote></details>
<details>
<summary>RELEASE.md (1)</summary><blockquote>

`3-22`: **LGTM.** Automated release documentation clearly describes the v4+ flow and engineer responsibilities.

</blockquote></details>
<details>
<summary>CLAUDE.md (1)</summary><blockquote>

`70-99`: **LGTM.** Guidance is consistent with CHANGESETS.md and CONTRIBUTING.md. The `markdownlint` MD040 warnings on lines 85 and 94 are false positives — the linter is misinterpreting `---` YAML frontmatter delimiters inside the heredoc as fenced code block markers.

</blockquote></details>
<details>
<summary>scripts/generate-github-release.mjs (2)</summary><blockquote>

`40-74`: **`extractChangesFromPrBody`: once `<details>` is entered, `inDetails` is never reset — all subsequent content is dropped.**

This appears intentional (the `<details>` block and everything after it is raw changeset output that should be stripped). Just confirming: if the enhanced PR body ever contains a `<details>` block *within* the curated sections (e.g., collapsible sub-sections), that content and everything below will be silently lost.

If that's the expected format, this is fine. Otherwise, pair the flag with `</details>` tracking.

---

`210-231`: **Main orchestration looks correct.**

The stdin fallback, extraction, and formatting flow are well-structured. One minor observation: `getPreviousVersion(version)` is computed both here (line 220) and inside `formatRelease` (line 198). You could compute it once and pass it through, but it's a pure function so this is harmless.

</blockquote></details>

</blockquote></details>

</details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/changesets-pr.yml:

  • Around line 101-110: The cleanup loop using set -e iterates
    files=(.server-changes/*.md) and runs git rm "$f" which will cause the step to
    fail if a file is untracked; update the git rm invocation in that for loop to
    use git rm --ignore-unmatch "$f" so removals of untracked paths don't return
    non-zero and the step remains idempotent (refer to the files array, the for f in
    "${files[@]}" loop, and the git rm command).

In @.github/workflows/release.yml:

  • Around line 176-192: The VERSION_ID capture and URL replacement are fragile:
    ensure VERSION_ID yields a single id by piping the gh/jq output through head -1
    (or use jq limit) so VERSION_ID is never multiline; then make the replacement
    idempotent by only substituting GENERIC_URL occurrences that are not already
    part of DOCKER_URL (e.g., check CURRENT_BODY for the presence of DOCKER_URL and
    skip replacement if found, or replace only exact matches of GENERIC_URL rather
    than substrings inside existing DOCKER_URL) so UPDATED_BODY cannot double-expand
    the URL; update the logic around VERSION_ID, DOCKER_URL, GENERIC_URL,
    CURRENT_BODY and UPDATED_BODY accordingly.
  • Around line 125-136: The RELEASE_PR_BODY env var is empty on workflow_dispatch
    because github.event.pull_request is undefined; update the job to detect an
    empty RELEASE_PR_BODY and, before calling node
    scripts/generate-github-release.mjs, populate it by querying the most-recently
    merged release PR body (e.g., use gh api or gh pr list/gh pr view filtered by
    label/title and state=merged to fetch the body) and export that as
    RELEASE_PR_BODY so generate-github-release.mjs always receives a non-empty body;
    keep existing use of RELEASE_PR_BODY and the node script but add the fallback
    lookup step and assignment when github.event.pull_request.body is missing.

In @scripts/enhance-release-pr.mjs:

  • Around line 92-96: The fs.readFile call inside the files loop can throw and
    crash the script; wrap the await fs.readFile(join(dir, file), "utf-8") in a
    try/catch inside the loop (the block that iterates over files in
    scripts/enhance-release-pr.mjs) so that on error you log a warning (console.warn
    or the existing logger) saying which file failed and why, then continue to the
    next file instead of letting the exception propagate to the top; ensure you only
    call parseFrontmatter(content) when the read succeeded.

In @scripts/generate-github-release.mjs:

  • Around line 133-142: getPreviousVersion currently returns the same version for
    major releases like "4.0.0" because neither the minor nor patch branches run;
    update getPreviousVersion to detect the major-release case (when parts[1] === 0
    && parts[2] === 0) and handle it by decrementing the major (parts[0]--) and
    setting parts[1] and parts[2] to large sentinel values (e.g., 999) so the
    previous-version string is strictly less than the current one; this change in
    getPreviousVersion will ensure getContributors' git log range and the "Full
    changelog" comparison link are not empty/self-referencing.

Nitpick comments:
In @.github/workflows/changesets-pr.yml:

  • Around line 66-73: The pipe echo "$RAW_BODY" | is redundant because
    enhance-release-pr.mjs checks process.env.CHANGESET_PR_BODY first; set the env
    var directly and remove the stdin pipe: export/pass
    CHANGESET_PR_BODY="$RAW_BODY" when invoking node and call node
    scripts/enhance-release-pr.mjs "$VERSION" (so CHANGESET_PR_BODY is available to
    the script), and delete the unused echo/stdin pipeline; update the block that
    defines RAW_BODY/ENHANCED_BODY to only set
    ENHANCED_BODY=$(CHANGESET_PR_BODY="$RAW_BODY" node
    scripts/enhance-release-pr.mjs "$VERSION") or equivalent and remove the dead
    pipe.

In @.server-changes/README.md:

  • Around line 17-19: The README's fenced code block showing
    ".server-changes/fix-batch-queue-stalls.md" is missing a language specifier
    (markdownlint MD040); update the fenced block in .server-changes/README.md to
    include a language token (e.g., add "text" after the opening ), so the block becomes text ... ```, ensuring the code fence around
    ".server-changes/fix-batch-queue-stalls.md" is annotated.

In @CHANGESETS.md:

  • Around line 1-46: Replace the non-descriptive link text "here" in the "To add
    a changeset (package changes)" paragraph with a descriptive label such as "the
    changesets adding-a-changeset guide" (i.e., update the markdown link text that
    currently reads
    [here](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md)),
    so the link is self-descriptive and satisfies MD059.

In @scripts/generate-github-release.mjs:

  • Around line 88-101: The contributors aggregation currently skips entries when
    email.includes("noreply.github.com") is true but the regex on email.match(...)
    fails; update the loop that builds the contributors Map so it uses
    email?.endsWith("noreply.github.com") instead of includes(), and if the noreply
    regex does not match, fall back to using the provided name (i.e., call
    contributors.set(name, ...) rather than continue). Keep the existing logic that
    increments counts when a username is extracted from the regex
    (contributors.set(username, ...)) and only fall back to name when regex returns
    null.
  • Around line 113-131: The function getPublishedPackages currently shells out
    with execSync("cat .changeset/config.json", ...) which is inefficient; replace
    that call by reading the file directly using fs.readFileSync(path, "utf-8")
    (require/import fs at top if needed), then JSON.parse the returned string to
    populate config and derive fixed as config.fixed?.[0] || []; keep the same
    try/catch behavior and the same fallback array when reading/parsing fails so
    behavior of getPublishedPackages remains unchanged.
  • Around line 78-101: The git range string in getContributors interpolates
    previousVersion into an execSync shell command (execSync(git log ${range} ...)), which allows command injection via a malicious version; fix by either
    validating previousVersion early (ensure it matches a strict semver or
    /^[0-9]+.[0-9]+.[0-9]+(-[0-9A-Za-z.-]+)?$/) before using it, or switch to a
    shell-avoiding call: replace the execSync usage with
    execFileSync/child_process.spawnSync passing ['log', range,
    '--format=...','--no-merges'] (or split the range into a single safe arg) so no
    shell interpolation occurs; update imports to include execFileSync/child_process
    as needed and keep the rest of getContributors logic intact.

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
scripts/generate-github-release.mjs (1)

224-228: getPreviousVersion(version) is computed twice — minor redundancy.

It is called in main() (Line 246, as the argument to getContributors) and again inside formatRelease() (Line 224). Consider computing it once in main() and passing it into formatRelease.

♻️ Proposed refactor
-function formatRelease({ version, changesContent, contributors, packages }) {
+function formatRelease({ version, changesContent, contributors, packages, prevVersion }) {
   const lines = [];
   // ...
   // Comparison link
-  const prevVersion = getPreviousVersion(version);
   if (prevVersion) {
+  const prevVersion = getPreviousVersion(version);
   const changesContent = extractChangesFromPrBody(prBody);
-  const contributors = getContributors(getPreviousVersion(version));
+  const contributors = getContributors(prevVersion);
   const packages = getPublishedPackages();

   const body = formatRelease({
     version,
     changesContent,
     contributors,
     packages,
+    prevVersion,
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate-github-release.mjs` around lines 224 - 228, Compute
getPreviousVersion(version) once in main(), store it (e.g., prevVersion) and
pass that value into formatRelease(prevVersion, version, ...) instead of calling
getPreviousVersion inside formatRelease; also update the getContributors call in
main() to use the same prevVersion variable so both getContributors(...) and
formatRelease(...) consume the single precomputed prevVersion, and remove the
redundant getPreviousVersion call from inside formatRelease.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 579248d and eff9377.

📒 Files selected for processing (3)
  • .github/workflows/changesets-pr.yml
  • .github/workflows/release.yml
  • scripts/generate-github-release.mjs
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`

Applied to files:

  • .github/workflows/changesets-pr.yml
  • scripts/generate-github-release.mjs
📚 Learning: 2025-09-03T14:35:52.384Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/utils/pathBuilder.ts:144-146
Timestamp: 2025-09-03T14:35:52.384Z
Learning: In the trigger.dev codebase, organization slugs are safe for URL query parameters and don't require URL encoding, as confirmed by the maintainer in apps/webapp/app/utils/pathBuilder.ts.

Applied to files:

  • scripts/generate-github-release.mjs
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build

Applied to files:

  • scripts/generate-github-release.mjs
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Applied to files:

  • scripts/generate-github-release.mjs
🪛 GitHub Check: CodeQL
scripts/generate-github-release.mjs

[failure] 102-102: Incomplete URL substring sanitization
'noreply.github.com' may be preceded by an arbitrary host name.

⏰ 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). (28)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
scripts/generate-github-release.mjs (1)

153-168: getPreviousVersion major-version case is now handled correctly — LGTM.

The parts[0] > 0 branch (Line 160) resolves the previously flagged issue where 4.0.0 would return itself. 4.0.03.0.0 is the correct previous published major, and the resulting v3.0.0...HEAD git range is valid.

.github/workflows/changesets-pr.yml (2)

101-110: git rm --ignore-unmatch fix applied — LGTM.

The cleanup loop is now idempotent: --ignore-unmatch exits cleanly when a .server-changes/*.md file is not tracked by git, and shopt -s nullglob correctly prevents the loop from running when no files match.


112-123: Commit step correctly covers both lockfile and .server-changes/ cleanup — LGTM.

git add pnpm-lock.yaml stages the lockfile, while git rm --ignore-unmatch from the previous step already staged the file deletions; git diff --cached --quiet therefore detects either type of change before committing.

.github/workflows/release.yml (2)

113-114: createGithubReleases: false correctly defers to the unified custom release step — LGTM.


156-195: update-release: prior VERSION_ID and idempotency issues are now resolved — LGTM.

  • head -1 (Line 179) guarantees a single ID even if the jq stream emits multiple matches.
  • The sed pattern s|${GENERIC_URL})|${DOCKER_URL})|g (Line 193) matches only the bare closing ) of the original placeholder link; the already-substituted URL ends with /VERSION_ID?tag=TAG) and will not re-match, making the step idempotent on retries.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/generate-github-release.mjs`:
- Around line 102-109: The endsWith check is too broad and the regex failure
causes contributors to be skipped; update the conditional to use
email?.endsWith("@users.noreply.github.com") (include the leading @) and, inside
the branch where you run const match =
email?.match(/(\d+\+)?(.+)@users\.noreply\.github\.com/), remove the
unconditional continue on regex failure so that when match is null you fall
through to the normal contributor-addition logic (using the existing name
variable or falling back to a sensible email-derived identifier) instead of
silently dropping the contributor; ensure you still increment contributors via
contributors.set(username, ...) when match succeeds.

---

Duplicate comments:
In @.github/workflows/release.yml:
- Around line 125-136: The workflow sets RELEASE_PR_BODY from
github.event.pull_request.body which is undefined for workflow_dispatch, causing
empty release notes; update the release job to detect when RELEASE_PR_BODY is
empty and fall back to retrieving the most recent merged release PR body (e.g.,
via gh pr list --state merged --head changeset-release/main --limit 1 --json
body) before running node scripts/generate-github-release.mjs, and pass that
fallback value into the RELEASE_PR_BODY env used by generate-github-release.mjs
so the script can include the "What's changed" section when no pull_request
payload exists.

---

Nitpick comments:
In `@scripts/generate-github-release.mjs`:
- Around line 224-228: Compute getPreviousVersion(version) once in main(), store
it (e.g., prevVersion) and pass that value into formatRelease(prevVersion,
version, ...) instead of calling getPreviousVersion inside formatRelease; also
update the getContributors call in main() to use the same prevVersion variable
so both getContributors(...) and formatRelease(...) consume the single
precomputed prevVersion, and remove the redundant getPreviousVersion call from
inside formatRelease.

Comment on lines 102 to 109
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

CodeQL: endsWith("noreply.github.com") is too broad — use "@users.noreply.github.com".

Any email ending with noreply.github.com matches the condition, including fabricated addresses like user@evil-noreply.github.com. When the regex /(\d+\+)?(.+)@users\.noreply\.github\.com/ fails to match such an address, the contributor is silently dropped from the list instead of being added by name.

🐛 Proposed fix
-      if (!name || email?.endsWith("noreply.github.com")) {
+      if (!name || email?.endsWith("@users.noreply.github.com")) {
📝 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.

Suggested change
if (!name || email?.endsWith("noreply.github.com")) {
// Try to extract username from noreply email
const match = email?.match(/(\d+\+)?(.+)@users\.noreply\.github\.com/);
if (match) {
const username = match[2];
contributors.set(username, (contributors.get(username) || 0) + 1);
}
continue;
if (!name || email?.endsWith("@users.noreply.github.com")) {
// Try to extract username from noreply email
const match = email?.match(/(\d+\+)?(.+)@users\.noreply\.github\.com/);
if (match) {
const username = match[2];
contributors.set(username, (contributors.get(username) || 0) + 1);
}
continue;
🧰 Tools
🪛 GitHub Check: CodeQL

[failure] 102-102: Incomplete URL substring sanitization
'noreply.github.com' may be preceded by an arbitrary host name.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate-github-release.mjs` around lines 102 - 109, The endsWith
check is too broad and the regex failure causes contributors to be skipped;
update the conditional to use email?.endsWith("@users.noreply.github.com")
(include the leading @) and, inside the branch where you run const match =
email?.match(/(\d+\+)?(.+)@users\.noreply\.github\.com/), remove the
unconditional continue on regex failure so that when match is null you fall
through to the normal contributor-addition logic (using the existing name
variable or falling back to a sensible email-derived identifier) instead of
silently dropping the contributor; ensure you still increment contributors via
contributors.set(username, ...) when match succeeds.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
scripts/generate-github-release.mjs (1)

236-254: getPreviousVersion(version) is computed twice — consider computing it once in main.

getPreviousVersion is a pure function but is called at Line 246 (to pass to getContributors) and again at Line 224 inside formatRelease. Unifying the call site makes the dependency explicit and removes the implicit coupling.

♻️ Proposed refactor
 async function main() {
   // ...
+  const prevVersion = getPreviousVersion(version);
-  const contributors = getContributors(getPreviousVersion(version));
+  const contributors = getContributors(prevVersion);
   const packages = getPublishedPackages();

   const body = formatRelease({
     version,
+    prevVersion,
     changesContent,
     contributors,
     packages,
   });
   // ...
 }
-function formatRelease({ version, changesContent, contributors, packages }) {
+function formatRelease({ version, prevVersion, changesContent, contributors, packages }) {
   // ...
-  const prevVersion = getPreviousVersion(version);
   if (prevVersion) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate-github-release.mjs` around lines 236 - 254, Compute
getPreviousVersion once in main and pass that value down instead of calling
getPreviousVersion(version) multiple times: in main, create a const
previousVersion = getPreviousVersion(version); then call
getContributors(previousVersion) and call formatRelease({version,
previousVersion, changesContent, contributors, packages}); update
formatRelease's signature (and any call sites) to accept previousVersion and
remove its internal call to getPreviousVersion so the dependency is explicit and
the duplicate computation is eliminated.
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eff9377 and dde60d6.

📒 Files selected for processing (1)
  • scripts/generate-github-release.mjs
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`
📚 Learning: 2025-11-26T14:40:07.146Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 2710
File: packages/schema-to-json/package.json:0-0
Timestamp: 2025-11-26T14:40:07.146Z
Learning: Node.js 24+ has native TypeScript support and can execute .ts files directly without tsx or ts-node for scripts that use only erasable TypeScript syntax (type annotations, interfaces, etc.). The trigger.dev repository uses Node.js 24.11.1+ and scripts like updateVersion.ts can be run with `node` instead of `tsx`.

Applied to files:

  • scripts/generate-github-release.mjs
📚 Learning: 2025-09-03T14:35:52.384Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/utils/pathBuilder.ts:144-146
Timestamp: 2025-09-03T14:35:52.384Z
Learning: In the trigger.dev codebase, organization slugs are safe for URL query parameters and don't require URL encoding, as confirmed by the maintainer in apps/webapp/app/utils/pathBuilder.ts.

Applied to files:

  • scripts/generate-github-release.mjs
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build

Applied to files:

  • scripts/generate-github-release.mjs
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`

Applied to files:

  • scripts/generate-github-release.mjs
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Applied to files:

  • scripts/generate-github-release.mjs
🧬 Code graph analysis (1)
scripts/generate-github-release.mjs (1)
scripts/enhance-release-pr.mjs (5)
  • version (23-23)
  • ROOT_DIR (29-29)
  • body (241-246)
  • lines (127-127)
  • dir (82-82)
⏰ 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: units / webapp / 📊 Merge Reports
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
🔇 Additional comments (1)
scripts/generate-github-release.mjs (1)

124-151: No actionable issues found. The repository does not have a top-level integrations/ directory; all packages are under packages/, which the function correctly scans. All 9 non-private packages are included.

Likely an incorrect or invalid review comment.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/generate-github-release.mjs`:
- Around line 100-116: The contributor parsing loop stores git author names
(from %aN) into the contributors Map which later causes formatRelease to emit
invalid `@mentions` for real names; update the parsing logic in the loop that
iterates over log.split(...) so that when you extract [name, email] you prefer a
GitHub login (from the noreply email match group) otherwise record a display
name without the leading '@' (or normalize by testing name against a
GitHub-handle pattern like /^[A-Za-z0-9-]+$/); ensure formatRelease uses the
stored value directly (no blind '@' prefix) so valid user logins are emitted as
`@username` and real names are emitted as plain text.

---

Duplicate comments:
In `@scripts/generate-github-release.mjs`:
- Around line 153-168: getPreviousVersion currently failed on major-version
rollbacks; update the function getPreviousVersion to decrement the major part
(parts[0]) and reset minor and patch to 0 when receiving X.0.0 (i.e., if
parts[0] > 0 then parts[0]--, parts[1]=0, parts[2]=0) and return null only for
0.0.0; ensure this logic is implemented in the getPreviousVersion function so
git log ranges and changelog links do not self-reference.
- Around line 102-110: The previous broad endsWith check has been corrected to
the anchored "@users.noreply.github.com"; leave the logic in the block using
email?.match(/(\d+\+)?(.+)@users\.noreply\.github\.com/) as-is, but ensure the
code continues to safely handle missing/undefined email and that match[2]
(username) is present before calling contributors.set(username, ...); no further
change required other than keeping the anchored string and the null-safe checks
around email and match.

---

Nitpick comments:
In `@scripts/generate-github-release.mjs`:
- Around line 236-254: Compute getPreviousVersion once in main and pass that
value down instead of calling getPreviousVersion(version) multiple times: in
main, create a const previousVersion = getPreviousVersion(version); then call
getContributors(previousVersion) and call formatRelease({version,
previousVersion, changesContent, contributors, packages}); update
formatRelease's signature (and any call sites) to accept previousVersion and
remove its internal call to getPreviousVersion so the dependency is explicit and
the duplicate computation is eliminated.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
scripts/generate-github-release.mjs (1)

228-228: getPreviousVersion is computed twice for the same version.

It's called in main (Line 250) to seed getContributors, and again inside formatRelease (Line 228) for the comparison link. Computing it once and threading the result through would be a minimal cleaner.

♻️ Suggested refactor
-  const contributors = getContributors(getPreviousVersion(version));
+  const prevVersion = getPreviousVersion(version);
+  const contributors = getContributors(prevVersion);
   const packages = getPublishedPackages();

   const body = formatRelease({
     version,
     changesContent,
     contributors,
     packages,
+    prevVersion,
   });

Then update formatRelease to accept prevVersion directly instead of recomputing it:

-function formatRelease({ version, changesContent, contributors, packages }) {
+function formatRelease({ version, changesContent, contributors, packages, prevVersion }) {
   // ...
-  const prevVersion = getPreviousVersion(version);

Also applies to: 250-250

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate-github-release.mjs` at line 228, getPreviousVersion(version)
is being called twice — once in main to seed getContributors and again inside
formatRelease for the comparison link; compute it once and thread the result
through. Change main to compute const prevVersion = getPreviousVersion(version)
and pass that prevVersion into formatRelease (and any other callers like
getContributors) so formatRelease(prevVersion, version, ...) uses the supplied
value instead of calling getPreviousVersion internally; update the signature of
formatRelease and any call sites accordingly (referencing getPreviousVersion,
main, formatRelease, getContributors, version, prevVersion).
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dde60d6 and 9cbb740.

📒 Files selected for processing (1)
  • scripts/generate-github-release.mjs
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`
📚 Learning: 2025-11-26T14:40:07.146Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 2710
File: packages/schema-to-json/package.json:0-0
Timestamp: 2025-11-26T14:40:07.146Z
Learning: Node.js 24+ has native TypeScript support and can execute .ts files directly without tsx or ts-node for scripts that use only erasable TypeScript syntax (type annotations, interfaces, etc.). The trigger.dev repository uses Node.js 24.11.1+ and scripts like updateVersion.ts can be run with `node` instead of `tsx`.

Applied to files:

  • scripts/generate-github-release.mjs
📚 Learning: 2025-09-03T14:35:52.384Z
Learnt from: myftija
Repo: triggerdotdev/trigger.dev PR: 2464
File: apps/webapp/app/utils/pathBuilder.ts:144-146
Timestamp: 2025-09-03T14:35:52.384Z
Learning: In the trigger.dev codebase, organization slugs are safe for URL query parameters and don't require URL encoding, as confirmed by the maintainer in apps/webapp/app/utils/pathBuilder.ts.

Applied to files:

  • scripts/generate-github-release.mjs
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Use build extensions in trigger.config.ts (additionalFiles, additionalPackages, aptGet, prismaExtension, etc.) to customize the build

Applied to files:

  • scripts/generate-github-release.mjs
📚 Learning: 2026-01-15T11:50:06.067Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-15T11:50:06.067Z
Learning: Applies to {packages,integrations}/**/* : Add a changeset when modifying any public package in `packages/*` or `integrations/*` using `pnpm run changeset:add`

Applied to files:

  • scripts/generate-github-release.mjs
📚 Learning: 2025-11-27T16:26:37.432Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-27T16:26:37.432Z
Learning: Applies to packages/trigger-sdk/**/*.{ts,tsx} : In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code

Applied to files:

  • scripts/generate-github-release.mjs
⏰ 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). (28)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
scripts/generate-github-release.mjs (5)

100-119: Both past issues with contributor extraction are resolved.

The endsWith("@users.noreply.github.com") guard (previously too broad, flagged by CodeQL) is now correct, and the continue at line 109 is safe because: when !name is the trigger and the email doesn't match the noreply regex, there is nothing meaningful to store; when the noreply email is the trigger, the regex invariably matches. Good.


153-168: getPreviousVersion major-version case is now correctly handled.

The else if (parts[0] > 0) branch (Line 160) addresses the previously reported defect where 4.0.0 would return itself unchanged, resulting in an empty git-log range and a self-referencing changelog link. The else { return null; } guard for 0.0.0 is also a nice touch.


219-223: Contributor @-prefix handling correctly addresses the previous concern.

The regex /^[A-Za-z0-9][-A-Za-z0-9]*$/ guards against prefixing @ on real names with spaces (e.g., "Jane Smith" is emitted as plain text, not @Jane Smith), fully resolving the prior comment about invalid GitHub mention strings.


124-151: The integrations/ directory does not exist in the repository. The current implementation of getPublishedPackages() correctly scans only the packages/ directory where packages are actually stored. No fix is needed.

Likely an incorrect or invalid review comment.


68-72: The concern about the inDetails flag is not valid. The enhance-release-pr.mjs script only emits a single <details> block at the very end of the PR body (wrapping the raw changeset output), not within content sections. Content extraction will not be prematurely terminated.

Likely an incorrect or invalid review comment.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/generate-github-release.mjs`:
- Line 228: getPreviousVersion(version) is being called twice — once in main to
seed getContributors and again inside formatRelease for the comparison link;
compute it once and thread the result through. Change main to compute const
prevVersion = getPreviousVersion(version) and pass that prevVersion into
formatRelease (and any other callers like getContributors) so
formatRelease(prevVersion, version, ...) uses the supplied value instead of
calling getPreviousVersion internally; update the signature of formatRelease and
any call sites accordingly (referencing getPreviousVersion, main, formatRelease,
getContributors, version, prevVersion).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments