From c6c67e08c6778d8ebd5120a2f2097854fb0f2f3a Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Mon, 18 May 2026 12:50:41 -0400 Subject: [PATCH 1/4] Cleanup workflows --- .github/workflows/close-no-repro-issues.yml | 49 ---------- .../delete-changeset-bot-comments.yml | 34 ------- .github/workflows/preview.yml | 10 +- package.json | 4 +- .../changes/delete-changeset-bot-comments.ts | 89 ----------------- scripts/close-feature-pr.md | 7 -- scripts/close-no-repro-issues.md | 9 -- scripts/close-no-repro-issues.ts | 74 -------------- scripts/constants.js | 9 -- .../branch.ts} | 2 +- scripts/{pr-preview.ts => previews/pr.ts} | 8 +- scripts/utils.js | 96 ------------------- 12 files changed, 12 insertions(+), 379 deletions(-) delete mode 100644 .github/workflows/close-no-repro-issues.yml delete mode 100644 .github/workflows/delete-changeset-bot-comments.yml delete mode 100644 scripts/changes/delete-changeset-bot-comments.ts delete mode 100644 scripts/close-feature-pr.md delete mode 100644 scripts/close-no-repro-issues.md delete mode 100644 scripts/close-no-repro-issues.ts delete mode 100644 scripts/constants.js rename scripts/{setup-installable-branch.ts => previews/branch.ts} (98%) rename scripts/{pr-preview.ts => previews/pr.ts} (94%) delete mode 100644 scripts/utils.js diff --git a/.github/workflows/close-no-repro-issues.yml b/.github/workflows/close-no-repro-issues.yml deleted file mode 100644 index 98337a2d38..0000000000 --- a/.github/workflows/close-no-repro-issues.yml +++ /dev/null @@ -1,49 +0,0 @@ -# This is a bulk-close script that was used initially to find and close issues -# without a repro, but moving forward we'll likely use the singular version -# (close-no-repro-issue.yml) on new issues which is driven by a label added to -# the issue - -name: 🚪 Close issues without a reproduction - -on: - workflow_dispatch: - inputs: - dryRun: - type: boolean - description: "Dry Run? (no issues will be closed)" - default: false - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -jobs: - close-no-repro-issues: - name: 🚪 Close issues - if: github.repository == 'remix-run/react-router' - runs-on: ubuntu-latest - env: - CI: "true" - GH_TOKEN: ${{ github.token }} - steps: - - name: ⬇️ Checkout repo - uses: actions/checkout@v6 - - - name: 📦 Setup pnpm - uses: pnpm/action-setup@v6 - - - name: ⎔ Setup node - uses: actions/setup-node@v6 - with: - # required for --experimental-strip-types - node-version: 22 - cache: "pnpm" - - - name: 📥 Install deps - run: pnpm install --frozen-lockfile - - - name: 🚪 Close Issues (Dry Run) - if: ${{ inputs.dryRun }} - run: node --experimental-strip-types ./scripts/close-no-repro-issues.ts --dryRun - - - name: 🚪 Close Issues - if: ${{ ! inputs.dryRun }} - run: node --experimental-strip-types ./scripts/close-no-repro-issues.ts diff --git a/.github/workflows/delete-changeset-bot-comments.yml b/.github/workflows/delete-changeset-bot-comments.yml deleted file mode 100644 index 255c20ea91..0000000000 --- a/.github/workflows/delete-changeset-bot-comments.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: 🗑️ Delete Changeset Bot Comments - -on: - workflow_dispatch: - -jobs: - delete-comments: - name: 🗑️ Delete Changeset Bot Comments - if: github.repository == 'remix-run/react-router' - runs-on: ubuntu-latest - permissions: - pull-requests: write - - steps: - - name: ⬇️ Checkout repo - uses: actions/checkout@v6 - - - name: 📦 Setup pnpm - uses: pnpm/action-setup@v6 - - - name: ⎔ Setup node - uses: actions/setup-node@v6 - with: - node-version-file: ".nvmrc" - cache: pnpm - - - name: 📥 Install deps - run: pnpm install --frozen-lockfile - - - name: 🗑️ Delete changeset-bot comments - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - node scripts/changes/delete-changeset-bot-comments.ts diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index c78af646d0..d5d44d74d4 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -76,7 +76,7 @@ jobs: - name: Build/push branch (push) if: github.event_name == 'push' run: | - pnpm run setup-installable-branch preview/dev + pnpm run previews:branch preview/dev git push --force --set-upstream origin preview/dev echo "💿 pushed installable branch: https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)" @@ -86,17 +86,17 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} run: | - pnpm run setup-installable-branch preview/pr-${{ github.event.pull_request.number }} + pnpm run previews:branch preview/pr-${{ github.event.pull_request.number }} git push --force --set-upstream origin preview/pr-${{ github.event.pull_request.number }} echo "pushed installable branch: https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)" - pnpm run pr-preview comment ${{ github.event.pull_request.number }} preview/pr-${{ github.event.pull_request.number }} + pnpm run previews:pr comment ${{ github.event.pull_request.number }} preview/pr-${{ github.event.pull_request.number }} # Build and normal push for experimental releases to avoid unintended force # pushes over remote branches in case of a branch name collision - name: Build/push branch (workflow_dispatch) if: github.event_name == 'workflow_dispatch' run: | - pnpm run setup-installable-branch ${{ inputs.installableBranch }} + pnpm run previews:branch ${{ inputs.installableBranch }} git push --set-upstream origin ${{ inputs.installableBranch }} echo "💿 pushed installable branch: https://github.com/$GITHUB_REPOSITORY/commit/$(git rev-parse HEAD)" @@ -106,4 +106,4 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} run: | - pnpm run pr-preview cleanup ${{ github.event.pull_request.number }} preview/pr-${{ github.event.pull_request.number }} + pnpm run previews:pr cleanup ${{ github.event.pull_request.number }} preview/pr-${{ github.event.pull_request.number }} diff --git a/package.json b/package.json index 8afbd78863..f5ffa293f6 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,9 @@ "format:check": "prettier --ignore-path .prettierignore --check .", "lint": "eslint --cache .", "playground": "node ./scripts/playground.js", - "pr-preview": "node ./scripts/pr-preview.ts", + "previews:branch": "node ./scripts/previews/branch.ts", + "previews:pre": "node ./scripts/previews/pr.ts", "release-comments": "node scripts/release-comments.ts", - "setup-installable-branch": "node scripts/setup-installable-branch.ts", "test": "jest", "test:inspect": "node --inspect-brk ./node_modules/.bin/jest", "typegen": "pnpm run --recursive --parallel typegen", diff --git a/scripts/changes/delete-changeset-bot-comments.ts b/scripts/changes/delete-changeset-bot-comments.ts deleted file mode 100644 index 29d931e4d6..0000000000 --- a/scripts/changes/delete-changeset-bot-comments.ts +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Finds and deletes comments from `changeset-bot` on open PRs created since 1/1/2026 - * - * Usage: - * node scripts/changes/delete-changeset-bot-comments.ts [--dry-run] - * - * Environment: - * GITHUB_TOKEN - Required. GitHub token with pull-requests:write permission. - */ -import { - createPrComment, - deletePrComment, - getPrComments, - listOpenPrs, -} from "../utils/github.ts"; - -const CHANGESET_BOT = "changeset-bot[bot]"; -const CUTOFF = new Date(2026, 0, 1); - -const ADD_CHANGE_FILE = - "👋 We've moved away from Changesets to our own internal " + - "[changes process](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files). " + - "Please manually add a change file to this branch, or you can merge in the " + - "latest `dev` branch and run `pnpm run changes:add` to add a change file."; - -const MIGRATE_CHANGE_FILE = - "👋 We've moved away from Changesets to our own internal " + - "[changes process](https://github.com/remix-run/react-router/blob/main/docs/community/contributing.md#change-files). " + - "Please convert your changesets file to a change file in the proper package directory " + - "(i.e., `packages/react-router/.changes/patch.fix-some-bug.md`)."; - -const dryRun = process.argv.includes("--dry-run"); - -if (dryRun) { - console.log("[DRY RUN] No comments will be deleted.\n"); -} - -console.log( - `Fetching open PRs created after ${CUTOFF.toISOString().slice(0, 10)}...\n`, -); - -let prs = await listOpenPrs({ - createdAfter: CUTOFF, - base: "dev", -}); -console.log(`Found ${prs.length} open PR${prs.length === 1 ? "" : "s"}.\n`); - -let totalDeleted = 0; -let totalSkipped = 0; - -for (let pr of prs) { - let comments = await getPrComments(pr.number); - let botComments = comments.filter((c) => c.user?.login === CHANGESET_BOT); - - if (botComments.length === 0) continue; - - console.log(`PR #${pr.number}: ${pr.title}`); - - for (let comment of botComments) { - let preview = (comment.body ?? "").slice(0, 80).replace(/\n/g, " "); - if (dryRun) { - console.log( - ` [DRY RUN] Would delete comment #${comment.id}: "${preview}"`, - ); - totalSkipped++; - } else { - let hasChangeFile = comment.body?.includes("Changeset detected"); - await deletePrComment(comment.id); - await createPrComment( - pr.number, - hasChangeFile ? MIGRATE_CHANGE_FILE : ADD_CHANGE_FILE, - ); - console.log(` Deleted comment #${comment.id}: "${preview}"`); - totalDeleted++; - } - } - - console.log(); -} - -if (dryRun) { - console.log( - `Done (dry run): ${totalSkipped} comment${totalSkipped === 1 ? "" : "s"} would be deleted.`, - ); -} else { - console.log( - `Done: ${totalDeleted} comment${totalDeleted === 1 ? "" : "s"} deleted.`, - ); -} diff --git a/scripts/close-feature-pr.md b/scripts/close-feature-pr.md deleted file mode 100644 index 1609a73a52..0000000000 --- a/scripts/close-feature-pr.md +++ /dev/null @@ -1,7 +0,0 @@ -To align with our new [Open Governance](https://remix.run/blog/rr-governance) model, we are now asking that all new features go through the [Proposal/RFC process](https://github.com/remix-run/react-router/blob/main/GOVERNANCE.md#new-feature-process) and that we don't open PRs until a proposal has been accepted and advanced to Stage 1. - -If this feature doesn't have a Proposal, please [open one](https://github.com/remix-run/react-router/discussions/new?category=proposals) so we can evaluate/discuss the proposed feature. You can link to this PR as an example of a potential implementation and we can re-open it if the proposal advances. - -If this PR already has a Proposal but it has not yet been accepted, let's continue the discussion in the Proposal until it gets accepted and then we can look to open a PR. Feel free to link to this PR or to a branch in a forked repo to show what a potential implementation might look like. - -If you have any questions, you can always reach out on [Discord](https://rmx.as/discord). Thanks again for providing feedback and helping us make React Router even better! diff --git a/scripts/close-no-repro-issues.md b/scripts/close-no-repro-issues.md deleted file mode 100644 index eb930ef8aa..0000000000 --- a/scripts/close-no-repro-issues.md +++ /dev/null @@ -1,9 +0,0 @@ -To align with our new [Open Governance](https://remix.run/blog/rr-governance) model, we are now requiring that all issues have a [**minimal** and **runnable** reproduction](https://github.com/remix-run/react-router/blob/main/GOVERNANCE.md#bugissue-process). To that end, we're doing some housekeeping in the repo to clean up existing issues that do not have a valid reproduction. This should get us down to a more manageable number of issues and allow us to be more responsive to existing and newly filed issues. - -We're using a GitHub actions script to identify issues without a reproduction by looking for a [StackBlitz](https://stackblitz.com/), [CodeSandbox](https://codesandbox.io/), or [GitHub](https://github.com) link in the issue body. This won't be perfect, so if this issue has a reproduction on another platform, please comment back on here, and we can re-open the issue. Similarly, if there's a reproduction buried in a comment, please move the link into the description and comment back. Please tag `@brophdawg11` or `@brookslybrand` in your comment so we get a notification as well 🙂. - -If this issue did not have a reproduction but is still valid, or if you wish to start with a fresh issue, please [create a new issue](https://github.com/remix-run/react-router/issues/new?template=bug_report.yml) with a fresh reproduction against v7 and link to this issue in the new description. - -If this is a feature request, please open a new [Proposal Discussion](https://github.com/remix-run/react-router/discussions/new?category=proposals) in React Router, and if it gets enough community support, it can be considered for implementation. - -If you have any questions, you can always reach out on [Discord](https://rmx.as/discord). Thanks again for providing feedback and helping us make React Router even better! diff --git a/scripts/close-no-repro-issues.ts b/scripts/close-no-repro-issues.ts deleted file mode 100644 index 7d2f562a2e..0000000000 --- a/scripts/close-no-repro-issues.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { execSync } from "node:child_process"; -import { parseArgs } from "node:util"; - -const sleep = (ms: number) => - new Promise((resolve) => setTimeout(resolve, ms)); - -const ignoredIssues = new Set([9991, 12570, 13607, 13659, 11940]); - -const { values: args } = parseArgs({ - options: { - dryRun: { - type: "boolean", - default: false, - }, - }, - strict: true, -}); - -run(); - -async function run() { - let issuesCmd = `gh issue list --search "is:issue state:open label:bug sort:created-asc" --limit 250 --json number,body`; - console.log(`Executing command: ${issuesCmd}`); - let result = execSync(issuesCmd).toString(); - let allIssues = JSON.parse(result) as { number: number; body: string }[]; - let noReproIssues = allIssues.filter(({ number, body }) => { - return ( - !ignoredIssues.has(number) && - !/https?:\/\/stackblitz\.com\//.test(body) && - !/https?:\/\/codesandbox\.io\//.test(body) && - !/https?:\/\/github\.com\//.test( - body - // Remove uploaded image URLs and links to react router source code - // and new issue links before looking for git repo reproductions - .replace("https://github.com/user-attachments/", "") - .replace("https://github.com/remix-run/react-router/", ""), - ) - ); - }); - - console.log( - `Found ${noReproIssues.length} issues without a reproduction:\n` + - noReproIssues.map((i) => i.number).join(","), - ); - - for (let issue of noReproIssues) { - console.log(`--- Processing issue #${issue.number} ---`); - let commentCmd = `gh issue comment ${issue.number} -F ./scripts/close-no-repro-issues.md`; - let commentResult = runCmdIfTokenExists(commentCmd); - console.log(`Commented on issue #${issue.number}: ${commentResult}`); - await sleep(250); - - let closeCmd = `gh issue close ${issue.number} -r "not planned"`; - runCmdIfTokenExists(closeCmd); - // No log here since the GH CLI already logs for issue close - await sleep(250); - } - - console.log("Done!"); -} - -function runCmdIfTokenExists(cmd: string) { - if (args.dryRun) { - console.log(`⚠️ Dry run, skipping command: ${cmd}`); - return ""; - } - - if (process.env.CI !== "true") { - console.log(`⚠️ Local run without CI env var, skipping command: ${cmd}`); - return ""; - } - - return execSync(cmd).toString(); -} diff --git a/scripts/constants.js b/scripts/constants.js deleted file mode 100644 index c2b3ebeea4..0000000000 --- a/scripts/constants.js +++ /dev/null @@ -1,9 +0,0 @@ -const path = require("path"); - -const ROOT_DIR = path.resolve(__dirname, ".."); -const EXAMPLES_DIR = path.resolve(ROOT_DIR, "examples"); - -module.exports = { - ROOT_DIR, - EXAMPLES_DIR, -}; diff --git a/scripts/setup-installable-branch.ts b/scripts/previews/branch.ts similarity index 98% rename from scripts/setup-installable-branch.ts rename to scripts/previews/branch.ts index 0ff48079ea..44653e0403 100644 --- a/scripts/setup-installable-branch.ts +++ b/scripts/previews/branch.ts @@ -1,7 +1,7 @@ import * as fsp from "node:fs/promises"; import * as path from "node:path"; import * as util from "node:util"; -import { logAndExec } from "./utils/process.ts"; +import { logAndExec } from "../utils/process.ts"; /** * This script prepares a base branch (usually `dev`) to be PNPM-installable diff --git a/scripts/pr-preview.ts b/scripts/previews/pr.ts similarity index 94% rename from scripts/pr-preview.ts rename to scripts/previews/pr.ts index 19eab42ec7..c13a868b44 100644 --- a/scripts/pr-preview.ts +++ b/scripts/previews/pr.ts @@ -11,7 +11,7 @@ * - `cleanup `: Deletes the preview branch from the remote repository * and adds a cleanup notification comment to the PR. * - * Usage: `node pr-preview.ts ` + * Usage: `node scripts/previews/pr.ts ` */ import { parseArgs } from "node:util"; @@ -21,8 +21,8 @@ import { deletePrComment, getPrComments, updatePrComment, -} from "./utils/github.ts"; -import { logAndExec } from "./utils/process.ts"; +} from "../utils/github.ts"; +import { logAndExec } from "../utils/process.ts"; const STICKY_MARKER = ""; const CLEANUP_MARKER = ""; @@ -57,7 +57,7 @@ if (commands[command]) { } function printUsage() { - console.error("Usage: node pr-preview.ts "); + console.error("Usage: node scripts/previews/pr.ts "); console.error( " comment - Add preview comment to PR", ); diff --git a/scripts/utils.js b/scripts/utils.js deleted file mode 100644 index 3c5e91cd3d..0000000000 --- a/scripts/utils.js +++ /dev/null @@ -1,96 +0,0 @@ -const fsp = require("fs").promises; -const path = require("path"); -const { execSync } = require("child_process"); -const jsonfile = require("jsonfile"); - -const { ROOT_DIR, EXAMPLES_DIR } = require("./constants"); - -/** - * @param {string} packageName - * @param {string} [directory] - * @returns {string} - */ -function packageJson(packageName, directory) { - return path.join(ROOT_DIR, directory, packageName, "package.json"); -} - -/** - * @param {string} packageName - * @returns {Promise} - */ -async function getPackageVersion(packageName) { - let file = packageJson(packageName, "packages"); - let json = await jsonfile.readFile(file); - return json.version; -} - -/** - * @returns {void} - */ -function ensureCleanWorkingDirectory() { - let status = execSync(`git status --porcelain`).toString().trim(); - let lines = status.split("\n"); - invariant( - lines.every((line) => line === "" || line.startsWith("?")), - "Working directory is not clean. Please commit or stash your changes.", - ); -} - -/** - * @param {string} packageName - * @param {(json: import('type-fest').PackageJson) => any} transform - */ -async function updatePackageConfig(packageName, transform) { - let file = packageJson(packageName, "packages"); - let json = await jsonfile.readFile(file); - transform(json); - await jsonfile.writeFile(file, json, { spaces: 2 }); -} - -/** - * @param {string} example - * @param {(json: import('type-fest').PackageJson) => any} transform - */ -async function updateExamplesPackageConfig(example, transform) { - let file = path.join(EXAMPLES_DIR, example, "package.json"); - if (!(await fileExists(file))) return; - - let json = await jsonfile.readFile(file); - transform(json); - await jsonfile.writeFile(file, json, { spaces: 2 }); -} - -/** - * @param {string} filePath - * @returns {Promise} - */ -async function fileExists(filePath) { - try { - await fsp.stat(filePath); - return true; - } catch ( - // eslint-disable-next-line no-unused-vars - e - ) { - return false; - } -} - -/** - * @param {*} cond - * @param {string} message - * @returns {asserts cond} - */ -function invariant(cond, message) { - if (!cond) throw new Error(message); -} - -module.exports = { - fileExists, - packageJson, - getPackageVersion, - ensureCleanWorkingDirectory, - invariant, - updatePackageConfig, - updateExamplesPackageConfig, -}; From 7180ad5c1687d07d6857d2240fc65c934f1e924c Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Tue, 19 May 2026 11:06:34 -0400 Subject: [PATCH 2/4] Fix preview build typo --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f5ffa293f6..4795344167 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "lint": "eslint --cache .", "playground": "node ./scripts/playground.js", "previews:branch": "node ./scripts/previews/branch.ts", - "previews:pre": "node ./scripts/previews/pr.ts", + "previews:pr": "node ./scripts/previews/pr.ts", "release-comments": "node scripts/release-comments.ts", "test": "jest", "test:inspect": "node --inspect-brk ./node_modules/.bin/jest", From ff4c0712f7b24d38f51f7b967d7c31aeac531bed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 11:09:48 -0400 Subject: [PATCH 3/4] chore(deps): bump actions/upload-artifact from 4 to 7 (#15073) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pr-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index ef7785e294..46d8f5b2a3 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -47,7 +47,7 @@ jobs: run: node scripts/pr.ts check - name: 📤 Upload result - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: pr-checks-result path: pr-checks-result.json From abcabd593fb8abe5c30bd42d95489fd9df2ef243 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 11:10:06 -0400 Subject: [PATCH 4/4] chore(deps): bump actions/download-artifact from 4 to 8 (#15072) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v8) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/pr-actions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-actions.yml b/.github/workflows/pr-actions.yml index 68d24d1e92..724fe3dd8c 100644 --- a/.github/workflows/pr-actions.yml +++ b/.github/workflows/pr-actions.yml @@ -40,7 +40,7 @@ jobs: run: pnpm install --frozen-lockfile - name: 📥 Download result from upstream workflow - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: pr-checks-result github-token: ${{ github.token }}