Skip to content

ci: align publishing to router with main/-pre/-maint#10283

Merged
birkskyum merged 8 commits intomainfrom
pre-release-changeset-workflow
Mar 19, 2026
Merged

ci: align publishing to router with main/-pre/-maint#10283
birkskyum merged 8 commits intomainfrom
pre-release-changeset-workflow

Conversation

@birkskyum
Copy link
Member

@birkskyum birkskyum commented Mar 17, 2026

This makes TanStack Query have the same publishing flow as TanStack Router.

This introduce a workflow that allow single/group of packages packages to pre-release or do maint-releases, ahead of replacing the stable version on main.

For this workflow there are some special branch name conventions, namely:

  • branch name end on -pre for prereleases
  • branch name end on -maint for maintenance releases (also added the v[0-9] brances for backwards compat)
  • main for regular stable releases - works like normal.

That means that we can make a solid-query-v6-pre branch, and from that do alpha/beta/rc releases, and only merge into main when it's ready to go stable. And that that time we can take what's on main for solid and put on a solid-query-v5-maint branch.

Summary by CodeRabbit

  • Chores

    • Broadened branch patterns that trigger CI; simplified concurrency and skip guards; added a check for changesets and made release pipeline steps conditional so versioning, tests, and related steps run only when changesets exist. Replaced prior automated sequence with an explicit, multi-step release flow (enter pre-release, version, commit, determine tag, publish, create release).
  • New Features

    • Added an automated release-creation tool that detects bumped packages, builds grouped changelogs from commits/PRs with author attribution, supports prereleases/tags, creates/releases Git tags, and cleans up on failure.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

CI branch triggers were changed to pattern-based matching; the release workflow was refactored to a manual multi-step release flow driven by changeset detection; and a new Node.js script was added to generate changelogs, detect bumped packages, manage tags, and create GitHub releases.

Changes

Cohort / File(s) Summary
Workflow Trigger Pattern Updates
.github/workflows/autofix.yml
Replaced fixed push branch list with pattern-based branch matching (main, v[0-9], *-pre, *-maint).
Release Workflow Refactoring
.github/workflows/release.yml
Broadened trigger patterns; simplified concurrency key and disabled cancel-in-progress; added changeset detection flag; made agent/test/publish steps conditional on changesets; removed automated changeset publish sequence and introduced explicit steps for pre-release mode, versioning, commit & push, dist-tag determination, publish, and GitHub release creation; adjusted token usage and guard conditions.
Release Automation Script
scripts/create-github-release.mjs
Added a standalone Node.js script that finds the previous release, diffs packages/*/package.json to detect bumps, aggregates conventional commits into a changelog, resolves authors/PRs via the GitHub API (with caching), manages tags, and creates/updates GitHub releases with cleanup on failure.

Sequence Diagram

sequenceDiagram
    actor Workflow
    participant Git
    participant API as GitHub API
    participant ReleaseScript as create-github-release.mjs

    Workflow->>Git: Check for .changeset files
    Git-->>Workflow: has_changesets

    alt has_changesets = true
        Workflow->>Workflow: Run changeset:version
        Workflow->>Git: Commit & push version changes
        Workflow->>ReleaseScript: Execute release script (flags)
        ReleaseScript->>Git: Find previous release tag/commit
        ReleaseScript->>Git: Diff packages/*/package.json to detect bumps
        ReleaseScript->>Git: Collect conventional commits between releases
        ReleaseScript->>API: Resolve authors, PR metadata
        API-->>ReleaseScript: Author & PR info
        ReleaseScript->>Git: Create/update tag
        ReleaseScript->>Git: Push tags
        ReleaseScript->>API: Create/update GitHub Release (notes, prerelease/latest)
        API-->>ReleaseScript: Release result
        alt release creation failed (new tag)
            ReleaseScript->>Git: Delete remote tag
            ReleaseScript->>Git: Delete local tag
        end
    else
        Workflow->>Workflow: Skip agent/test/publish steps
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 I nibbled through diffs beneath a silver light,

gathered bumps and notes to stitch the changelog tight.
Tags tied with care, authors named in song,
I nudged the release out — then hopped along. 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The PR description clearly explains the changes and motivation, detailing new branch naming conventions and release workflow, aligning with template requirements.
Title check ✅ Passed The pull request title 'ci: align publishing to router with main/-pre/-maint' directly reflects the main objective: implementing branch-based release workflow conventions (main, -pre, -maint branches) for controlling publishing behavior.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pre-release-changeset-workflow
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@nx-cloud
Copy link

nx-cloud bot commented Mar 17, 2026

View your CI Pipeline Execution ↗ for commit 8bd3c96

Command Status Duration Result
nx run-many --target=build --exclude=examples/*... ✅ Succeeded <1s View ↗
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 27s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-18 00:42:24 UTC

@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2026

🚀 Changeset Version Preview

1 package(s) bumped directly, 0 bumped as dependents.

🟩 Patch bumps

Package Version Reason
@tanstack/eslint-plugin-query 5.91.4 → 5.91.5 Changeset

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 17, 2026

More templates

@tanstack/angular-query-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-experimental@10283

@tanstack/eslint-plugin-query

npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@10283

@tanstack/preact-query

npm i https://pkg.pr.new/@tanstack/preact-query@10283

@tanstack/preact-query-devtools

npm i https://pkg.pr.new/@tanstack/preact-query-devtools@10283

@tanstack/preact-query-persist-client

npm i https://pkg.pr.new/@tanstack/preact-query-persist-client@10283

@tanstack/query-async-storage-persister

npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@10283

@tanstack/query-broadcast-client-experimental

npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@10283

@tanstack/query-core

npm i https://pkg.pr.new/@tanstack/query-core@10283

@tanstack/query-devtools

npm i https://pkg.pr.new/@tanstack/query-devtools@10283

@tanstack/query-persist-client-core

npm i https://pkg.pr.new/@tanstack/query-persist-client-core@10283

@tanstack/query-sync-storage-persister

npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@10283

@tanstack/react-query

npm i https://pkg.pr.new/@tanstack/react-query@10283

@tanstack/react-query-devtools

npm i https://pkg.pr.new/@tanstack/react-query-devtools@10283

@tanstack/react-query-next-experimental

npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@10283

@tanstack/react-query-persist-client

npm i https://pkg.pr.new/@tanstack/react-query-persist-client@10283

@tanstack/solid-query

npm i https://pkg.pr.new/@tanstack/solid-query@10283

@tanstack/solid-query-devtools

npm i https://pkg.pr.new/@tanstack/solid-query-devtools@10283

@tanstack/solid-query-persist-client

npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@10283

@tanstack/svelte-query

npm i https://pkg.pr.new/@tanstack/svelte-query@10283

@tanstack/svelte-query-devtools

npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@10283

@tanstack/svelte-query-persist-client

npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@10283

@tanstack/vue-query

npm i https://pkg.pr.new/@tanstack/vue-query@10283

@tanstack/vue-query-devtools

npm i https://pkg.pr.new/@tanstack/vue-query-devtools@10283

commit: 8bd3c96

@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2026

size-limit report 📦

Path Size
react full 11.92 KB (0%)
react minimal 8.95 KB (0%)

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 (3)
scripts/create-github-release.mjs (3)

41-45: Consider using GITHUB_REPOSITORY environment variable.

The repository name is hardcoded. Using the GITHUB_REPOSITORY environment variable would make the script more portable and maintainable.

♻️ Proposed refactor
+const repo = process.env.GITHUB_REPOSITORY || 'TanStack/query'
+
 async function resolveAuthorForPR(prNumber) {
   if (prAuthorCache[prNumber] !== undefined) return prAuthorCache[prNumber]

   if (!ghToken) {
     prAuthorCache[prNumber] = null
     return null
   }

   try {
     const res = await fetch(
-      `https://api.github.com/repos/TanStack/query/pulls/${prNumber}`,
+      `https://api.github.com/repos/${repo}/pulls/${prNumber}`,
       { headers: { Authorization: `token ${ghToken}` } },
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/create-github-release.mjs` around lines 41 - 45, The fetch call in
scripts/create-github-release.mjs hardcodes the repo "TanStack/query"; update
the URL construction to use process.env.GITHUB_REPOSITORY instead (e.g., build
the pull URL with `${process.env.GITHUB_REPOSITORY}/pulls/${prNumber}`), add a
guard that throws or logs a clear error if GITHUB_REPOSITORY is not set, and
keep using ghToken and prNumber as before so the request behavior is unchanged.

17-28: Consider checking response status for better error visibility.

The API calls don't verify res.ok before parsing JSON. While the optional chaining handles error responses gracefully, checking the status could help with debugging rate limiting or authentication issues.

♻️ Optional: Add status check
   try {
     const res = await fetch(`https://api.github.com/search/users?q=${email}`, {
       headers: { Authorization: `token ${ghToken}` },
     })
+    if (!res.ok) {
+      usernameCache[email] = null
+      return null
+    }
     const data = await res.json()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/create-github-release.mjs` around lines 17 - 28, The fetch call that
looks up GitHub users doesn't check HTTP status before parsing JSON, so update
the logic around the fetch/res handling (the block that assigns to res, data,
login and usernameCache) to check res.ok after the fetch; if not ok, set
usernameCache[email] = null and surface or log the error/status (include
res.status and res.statusText or the parsed error body) before returning null,
otherwise parse JSON and proceed to extract data?.items?.[0]?.login as before.
Ensure the catch still sets usernameCache[email] = null and returns null.

1-1: Consider removing @ts-nocheck or adding proper types.

If TypeScript checking is configured for this file, consider addressing type issues rather than suppressing all checks. If TypeScript doesn't check .mjs files in this project, this directive is unnecessary.

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

In `@scripts/create-github-release.mjs` at line 1, Remove the blanket "//
`@ts-nocheck`" directive at the top of the module and either fix the underlying
type issues or add targeted type annotations/JSdoc comments; if certain lines
legitimately need suppression, replace the file-wide directive with localized //
`@ts-ignore` or // `@ts-expect-error` comments only where needed, and add explicit
JSDoc types or convert functions to typed exports so the module
(create-github-release.mjs) can be type-checked rather than entirely bypassed.
🤖 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/create-github-release.mjs`:
- Around line 167-169: The filtering logic around the variable type is inverted:
currently commits matching user-facing types (feat, fix, perf, refactor, build,
chore) are skipped; change the condition so only those user-facing types are
included in the changelog by continuing when the type is NOT in the allowed list
(i.e., negate the includes check), and optionally remove non-user-facing types
like build and chore from the allowed list so the allowed array becomes
['feat','fix','perf','refactor'] and the guard becomes continue when
!allowed.includes(type).

---

Nitpick comments:
In `@scripts/create-github-release.mjs`:
- Around line 41-45: The fetch call in scripts/create-github-release.mjs
hardcodes the repo "TanStack/query"; update the URL construction to use
process.env.GITHUB_REPOSITORY instead (e.g., build the pull URL with
`${process.env.GITHUB_REPOSITORY}/pulls/${prNumber}`), add a guard that throws
or logs a clear error if GITHUB_REPOSITORY is not set, and keep using ghToken
and prNumber as before so the request behavior is unchanged.
- Around line 17-28: The fetch call that looks up GitHub users doesn't check
HTTP status before parsing JSON, so update the logic around the fetch/res
handling (the block that assigns to res, data, login and usernameCache) to check
res.ok after the fetch; if not ok, set usernameCache[email] = null and surface
or log the error/status (include res.status and res.statusText or the parsed
error body) before returning null, otherwise parse JSON and proceed to extract
data?.items?.[0]?.login as before. Ensure the catch still sets
usernameCache[email] = null and returns null.
- Line 1: Remove the blanket "// `@ts-nocheck`" directive at the top of the module
and either fix the underlying type issues or add targeted type annotations/JSdoc
comments; if certain lines legitimately need suppression, replace the file-wide
directive with localized // `@ts-ignore` or // `@ts-expect-error` comments only
where needed, and add explicit JSDoc types or convert functions to typed exports
so the module (create-github-release.mjs) can be type-checked rather than
entirely bypassed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 51c47261-e21b-4789-b785-db65547a0cfc

📥 Commits

Reviewing files that changed from the base of the PR and between 84a8ff1 and a865845.

📒 Files selected for processing (3)
  • .github/workflows/autofix.yml
  • .github/workflows/release.yml
  • scripts/create-github-release.mjs

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: 3

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

Inline comments:
In `@scripts/create-github-release.mjs`:
- Around line 162-165: The regex in the conventional commit parsing (the
conventionalMatch assignment that inspects subject) doesn't accept a trailing
"!" for breaking changes, so types like "feat!:" or "feat(scope)!:" are treated
as 'other'; update the regex to allow an optional "!" after the type/scope, e.g.
change the pattern to /^(\w+)(?:\(([^)]*)\))?(!)?:\s*(.*)$/ when creating
conventionalMatch, then keep using conventionalMatch[1] as type,
conventionalMatch[2] as scope (or ''), and the final capture as the message;
apply the same change where the same regex is used later (the second
conventionalMatch at 167-169).
- Line 213: The script currently infers "latest" implicitly; change the CLI
parsing so that latest is only enabled when the caller passes an explicit
--latest flag instead of defaulting for non-prerelease runs: replace any logic
that sets latestFlag by default with a check like
process.argv.includes('--latest') (alongside the existing isPrerelease =
process.argv.includes('--prerelease')), only append the '--latest' option when
that explicit flag is present, and update workflows to pass --latest only for
the stable main branch; locate references to isPrerelease and latestFlag in the
script and adjust their creation/usage accordingly.
- Line 4: The script imports globSync from node:fs which requires Node 22+;
replace this with a Node 16+ compatible glob implementation by removing the
node:fs globSync import and using the standalone "glob" package instead (e.g.,
add "glob" to devDependencies and import its sync API), then update the code
paths that call globSync in scripts/create-github-release.mjs to use the glob
package's sync method (retain the same pattern for discovering package files so
behavior is unchanged).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06327b19-4ab9-460a-9b46-2fdc85378ca1

📥 Commits

Reviewing files that changed from the base of the PR and between a865845 and fee2bc2.

📒 Files selected for processing (1)
  • scripts/create-github-release.mjs

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: 3

♻️ Duplicate comments (1)
scripts/create-github-release.mjs (1)

162-165: ⚠️ Potential issue | 🟠 Major

Keep feat!: / fix!: commits in the changelog.

Lines 162-165 still parse feat!: and feat(scope)!: as other, so the filter at Lines 167-169 drops breaking changes from the release notes.

📝 Suggested fix
-  const conventionalMatch = subject.match(/^(\w+)(?:\(([^)]*)\))?:\s*(.*)$/)
+  const conventionalMatch = subject.match(
+    /^(\w+)(?:\(([^)]*)\))?(!)?:\s*(.*)$/,
+  )
   const type = conventionalMatch ? conventionalMatch[1] : 'other'
   const scope = conventionalMatch ? conventionalMatch[2] || '' : ''
-  const message = conventionalMatch ? conventionalMatch[3] : subject
+  const message = conventionalMatch ? conventionalMatch[4] : subject

Also applies to: 167-169

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

In `@scripts/create-github-release.mjs` around lines 162 - 165, The regex and
capture usage for conventional commits ignores a trailing "!" (breaking change)
so types like "feat!:" or "feat(scope)!:" get parsed as other; update the regex
in the parsing block (where conventionalMatch is created) to capture an optional
bang: change /^(\w+)(?:\(([^)]*)\))?:\s*(.*)$/ to
/^(\w+)(?:\(([^)]*)\))?(!)?:\s*(.*)$/ and then adjust the captures used by type,
scope and message (type = conventionalMatch[1], scope = conventionalMatch[2] ||
'', message = conventionalMatch[4]) and ensure the later breaking-change filter
(the logic around lines referencing conventionalMatch and type/scope/message)
also checks the captured bang (conventionalMatch[3]) as a signal for a breaking
change so those commits are not filtered out.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/release.yml:
- Line 22: The release job's initial condition if:
"!contains(github.event.head_commit.message, 'ci: changeset release')" causes
reruns to skip after the job itself creates the version commit, so recoveries
fail; change the workflow so publish and release steps don't depend on the
incoming commit message: either split the pipeline into two jobs (one that
performs version bump/commit and a downstream job that runs changeset:publish
and create-github-release.mjs triggered by the push event or by the upstream job
using needs and run-on-failure/retry semantics), or replace the commit-message
condition with a check of branch/ref state or a dedicated output from the
versioning step to decide whether to run publish/release; target the release job
and the steps invoking changeset:publish and create-github-release.mjs when
making this change.
- Around line 49-50: The workflow runs the Version Packages step which invokes
the changeset:version script (which runs changeset version && pnpm install
--no-frozen-lockfile && pnpm format) before any prerelease mode is entered, so
*-pre branches produce stable semvers; fix by ensuring Changesets prerelease
mode is active before running changeset:version: either commit a
.changeset/pre.json into the repo prior to the Version Packages step or add a
step that runs changeset pre enter <tag> (or an npm script that invokes it)
before invoking the changeset:version step so the versions created by
changeset:version will be prereleases; update the workflow step ordering to run
the pre-enter step (or ensure pre.json exists) before the Version Packages step
and keep the existing prerelease output logic intact.

In `@scripts/create-github-release.mjs`:
- Around line 83-85: Replace the use of execSync that interpolates relPath into
a shell command to prevent shell injection: stop building a command string and
instead call child_process.execFileSync (or execFileSync via
require('child_process')) with an argument array such as ['git', 'show',
`${previousRelease}:packages/${relPath}`], preserving the same options
(encoding, stdio) so prevContent is read the same way; specifically update the
line that assigns prevContent (the execSync call) to use execFileSync and pass
previousRelease and relPath as arguments rather than embedding relPath in a
shell string.

---

Duplicate comments:
In `@scripts/create-github-release.mjs`:
- Around line 162-165: The regex and capture usage for conventional commits
ignores a trailing "!" (breaking change) so types like "feat!:" or
"feat(scope)!:" get parsed as other; update the regex in the parsing block
(where conventionalMatch is created) to capture an optional bang: change
/^(\w+)(?:\(([^)]*)\))?:\s*(.*)$/ to /^(\w+)(?:\(([^)]*)\))?(!)?:\s*(.*)$/ and
then adjust the captures used by type, scope and message (type =
conventionalMatch[1], scope = conventionalMatch[2] || '', message =
conventionalMatch[4]) and ensure the later breaking-change filter (the logic
around lines referencing conventionalMatch and type/scope/message) also checks
the captured bang (conventionalMatch[3]) as a signal for a breaking change so
those commits are not filtered out.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3b0905ea-09a2-440e-84fc-c64851f01341

📥 Commits

Reviewing files that changed from the base of the PR and between fee2bc2 and f2a2770.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • scripts/create-github-release.mjs

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

♻️ Duplicate comments (1)
scripts/create-github-release.mjs (1)

83-85: ⚠️ Potential issue | 🔴 Critical

Prevent shell injection in git show path handling.

Line 83 builds a shell command string with relPath. Since relPath comes from filesystem entries, this allows shell metacharacter injection during release automation.

🔒 Suggested fix
-import { execSync } from 'node:child_process'
+import { execFileSync, execSync } from 'node:child_process'
@@
-      const prevContent = execSync(
-        `git show ${previousRelease}:packages/${relPath}`,
+      const prevContent = execFileSync(
+        'git',
+        ['show', `${previousRelease}:packages/${relPath}`],
         { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] },
       )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/create-github-release.mjs` around lines 83 - 85, The current use of
execSync with a constructed shell string using relPath (in the call that assigns
prevContent) is vulnerable to shell injection; change the call to use a safe
exec variant that accepts command + argument array (e.g.,
child_process.execFileSync or spawnSync) and pass previousRelease and the path
segment as separate arguments instead of interpolating relPath into a shell
string, or alternatively sanitize/escape relPath reliably before use; update the
call site where prevContent is assigned (the execSync invocation) to use the
secure API and ensure the git args are passed as separate parameters to prevent
injection.
🧹 Nitpick comments (1)
scripts/create-github-release.mjs (1)

18-19: URL-encode email before sending the GitHub search query.

Line 18 interpolates raw email into the query string. Addresses containing +, spaces, or special characters can be misparsed and resolve the wrong user.

♻️ Suggested fix
-    const res = await fetch(`https://api.github.com/search/users?q=${email}`, {
+    const query = new URLSearchParams({ q: `${email} in:email` })
+    const res = await fetch(`https://api.github.com/search/users?${query}`, {
       headers: { Authorization: `token ${ghToken}` },
     })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/create-github-release.mjs` around lines 18 - 19, The GitHub search
request interpolates the raw variable email into the query URL (the fetch call)
which can break on special characters; update the code that builds the URL for
the fetch (where `email` is used in
`https://api.github.com/search/users?q=${email}`) to URL-encode the email (e.g.,
use encodeURIComponent(email)) before interpolation and keep the existing
`headers: { Authorization: \`token ${ghToken}\` }` usage unchanged.
🤖 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/create-github-release.mjs`:
- Around line 214-215: The tagName/titleDate generation (variables tagName and
titleDate) uses only minute precision, risking collisions across concurrent
branches; update tagName to include higher-precision time (seconds or
milliseconds) or a short unique suffix (e.g., short commit SHA or GITHUB_RUN_ID)
and update titleDate similarly so releases in the same minute are unique; apply
the same change to the other equivalent block that constructs a release
tag/title (the one around the second occurrence mentioned) so both flows use the
higher-precision or unique-suffix strategy.

---

Duplicate comments:
In `@scripts/create-github-release.mjs`:
- Around line 83-85: The current use of execSync with a constructed shell string
using relPath (in the call that assigns prevContent) is vulnerable to shell
injection; change the call to use a safe exec variant that accepts command +
argument array (e.g., child_process.execFileSync or spawnSync) and pass
previousRelease and the path segment as separate arguments instead of
interpolating relPath into a shell string, or alternatively sanitize/escape
relPath reliably before use; update the call site where prevContent is assigned
(the execSync invocation) to use the secure API and ensure the git args are
passed as separate parameters to prevent injection.

---

Nitpick comments:
In `@scripts/create-github-release.mjs`:
- Around line 18-19: The GitHub search request interpolates the raw variable
email into the query URL (the fetch call) which can break on special characters;
update the code that builds the URL for the fetch (where `email` is used in
`https://api.github.com/search/users?q=${email}`) to URL-encode the email (e.g.,
use encodeURIComponent(email)) before interpolation and keep the existing
`headers: { Authorization: \`token ${ghToken}\` }` usage unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ce76b169-93dd-4e96-b2d6-740ec37d4adb

📥 Commits

Reviewing files that changed from the base of the PR and between f2a2770 and bf5766e.

📒 Files selected for processing (1)
  • scripts/create-github-release.mjs

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.

♻️ Duplicate comments (3)
scripts/create-github-release.mjs (2)

214-216: ⚠️ Potential issue | 🟠 Major

Tag naming is still collision-prone, and git push --tags is overly broad.

Line 215 is only minute-granular, and Line 244 pushes every local tag. This can still break or misroute concurrent release runs.

🛠️ Suggested hardening
 const now = new Date()
 const date = now.toISOString().slice(0, 10)
-const time = now.toISOString().slice(11, 16).replace(':', '')
-const tagName = `release-${date}-${time}`
-const titleDate = `${date} ${now.toISOString().slice(11, 16)}`
+const time = now.toISOString().slice(11, 19).replace(/:/g, '')
+const unique = process.env.GITHUB_RUN_ID || execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).trim()
+const tagName = `release-${date}-${time}-${unique}`
+const titleDate = `${date} ${now.toISOString().slice(11, 19)}`
@@
 if (!tagExists) {
   execSync(`git tag -a -m "${tagName}" ${tagName}`)
-  execSync('git push --tags')
+  execSync(`git push origin ${tagName}`)
 }

Also applies to: 243-245

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

In `@scripts/create-github-release.mjs` around lines 214 - 216, The tag naming
uses minute-only granularity (variables time, tagName, titleDate) which can
collide across concurrent runs and the script currently does a broad git push
--tags; fix by making tagName unique (e.g., include seconds or milliseconds from
now.toISOString(), or append a short commit SHA or random suffix) and change the
push to only push the specific tag instead of all local tags (replace git push
--tags with a targeted push of tagName). Update any references that build the
release title (titleDate) to keep human-readable time while ensuring tagName is
the unique machine-safe identifier.

4-4: ⚠️ Potential issue | 🟠 Major

node:fs globSync still ties this script to newer Node runtimes.

Line 4/Line 72 can fail at import/runtime on runners not providing a sufficiently new Node version. This is still unresolved from earlier feedback.

#!/bin/bash
set -euo pipefail

echo "== Node-version-sensitive API usage in release script =="
rg -n "from 'node:fs'|globSync\\(" scripts/create-github-release.mjs

echo
echo "== Node runtime pinning in workflows =="
fd -t f '.*\.ya?ml$' .github/workflows --exec rg -n "setup-node|node-version|node-version-file|TanStack/config/.github/setup" {}

If no explicit Node >= 22 pin is present in the effective setup path, this is a real runtime compatibility risk.

Also applies to: 72-72

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

In `@scripts/create-github-release.mjs` at line 4, The script imports globSync
from the Node builtin "node:fs" which fails on older runners; replace that usage
with a runtime-compatible solution (e.g., use the third-party "glob" package or
a small recursive fs.readdir-based helper) so the script no longer requires a
specific Node version; update the import/usage of globSync in
create-github-release.mjs (replace the "import { globSync } from 'node:fs'" and
any calls to globSync) to instead import/use the cross-compatible glob API (or
implement the fallback helper) and adjust any call sites to match the chosen
API.
.github/workflows/release.yml (1)

22-22: ⚠️ Potential issue | 🟠 Major

Job-level commit-message skip still blocks rerun recovery.

Line 22 can skip the entire job on reruns after Line 62 creates/pushes ci: changeset release, leaving failed publish/release steps unrecoverable via rerun.

♻️ Minimal fix direction
-    if: "!contains(github.event.head_commit.message, 'ci: changeset release')"
+    # Allow reruns; step-level guards already prevent duplicate publish/release work.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml at line 22, Remove the job-level guard
"!contains(github.event.head_commit.message, 'ci: changeset release')" so reruns
aren't entirely skipped; instead, keep the check only on the step that
creates/pushes the "ci: changeset release" commit (the step that
generates/pushes that commit), or add a step-level if around that commit/push
step using contains(github.event.head_commit.message, 'ci: changeset release')
to skip only the commit creation, allowing the rest of the job (including retry
of publish/release steps) to run on rerun.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In @.github/workflows/release.yml:
- Line 22: Remove the job-level guard
"!contains(github.event.head_commit.message, 'ci: changeset release')" so reruns
aren't entirely skipped; instead, keep the check only on the step that
creates/pushes the "ci: changeset release" commit (the step that
generates/pushes that commit), or add a step-level if around that commit/push
step using contains(github.event.head_commit.message, 'ci: changeset release')
to skip only the commit creation, allowing the rest of the job (including retry
of publish/release steps) to run on rerun.

In `@scripts/create-github-release.mjs`:
- Around line 214-216: The tag naming uses minute-only granularity (variables
time, tagName, titleDate) which can collide across concurrent runs and the
script currently does a broad git push --tags; fix by making tagName unique
(e.g., include seconds or milliseconds from now.toISOString(), or append a short
commit SHA or random suffix) and change the push to only push the specific tag
instead of all local tags (replace git push --tags with a targeted push of
tagName). Update any references that build the release title (titleDate) to keep
human-readable time while ensuring tagName is the unique machine-safe
identifier.
- Line 4: The script imports globSync from the Node builtin "node:fs" which
fails on older runners; replace that usage with a runtime-compatible solution
(e.g., use the third-party "glob" package or a small recursive fs.readdir-based
helper) so the script no longer requires a specific Node version; update the
import/usage of globSync in create-github-release.mjs (replace the "import {
globSync } from 'node:fs'" and any calls to globSync) to instead import/use the
cross-compatible glob API (or implement the fallback helper) and adjust any call
sites to match the chosen API.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5c04da23-dc9b-48cb-b67b-e7009739f437

📥 Commits

Reviewing files that changed from the base of the PR and between bf5766e and 8bd3c96.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • scripts/create-github-release.mjs

@birkskyum birkskyum changed the title ci: pre/maint-release workflow ci: main/-pre/-maint publishing workflow Mar 19, 2026
@birkskyum birkskyum changed the title ci: main/-pre/-maint publishing workflow ci: align publishing to router with main/-pre/-maint (#10283) Mar 19, 2026
@birkskyum birkskyum changed the title ci: align publishing to router with main/-pre/-maint (#10283) ci: align publishing to router with main/-pre/-maint Mar 19, 2026
@birkskyum birkskyum merged commit e79176a into main Mar 19, 2026
8 checks passed
@birkskyum birkskyum deleted the pre-release-changeset-workflow branch March 19, 2026 21:01
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