Commit Graph

496 Commits

Author SHA1 Message Date
Benjamin Woodruff ‮ 1ae491021d agents: Add a --comments-only flag to pr-status script, trim down context bloat in AGENTS.md (#98781)
- The `pr-status` can be very slow if you have a lot of test failures on
a PR. If the user asks a targeted question about PR review comments,
just fetch that information, it's much faster.
- Context bloat: Rely on the agent to pull in the `pr-status-triage`
skill if they need it, don't bloat the AGENTS.md, except to mention the
skill.
2026-09-17 14:01:27 -07:00
Tobias Koppers e648495f95 Replace lzzzz with lz4_flex (#98799)
## What?

Replace the direct `lzzzz` dependency used by Turbopack persistence with
`lz4_flex` 0.14. The migration covers persisted blocks, the SST
inspection utility, and optional compressed-size diagnostics in the task
backend.

## Why?

This removes the direct C/liblz4-backed codec dependency from these
paths in favor of a Rust implementation. Persistence caches are scoped
to the Next.js and Turbopack version, so this does not require a
compatibility path for caches written by earlier versions.

## How?

Persistence continues to use raw LZ4 blocks and the existing compression
selection and checksum layout.

A large thread-local `CompressTable` is reused across blocks. Starting
with the large table costs about 8 KiB more per compression thread than
the small table, but improves compression speed and produces LZ4 streams
that are faster for `lz4_flex` to decode.

The compressor retains the existing caller-owned buffer API. It clears
and reserves the output vector, lets `lz4_flex` write directly into the
uninitialized backing allocation, and exposes only the initialized byte
count returned by the compressor. This avoids both per-call output
allocations and pre-zeroing the maximum output size. Zstandard behavior
is unchanged.

### Benchmark vs `lzzzz`

The final PR configuration was benchmarked against `lzzzz` 2.0.0 using
the repository-pinned Rust nightly. The benchmark used source-code
blocks sampled from this repository, pinned each process to one physical
CPU, measured thread CPU time, and alternated codec order within every
paired round. The table reports the median across four physical cores
and 20 paired rounds per process (480 paired rounds total).

Each decoder read the output produced by its own encoder, matching how a
same-version persistence cache is written and later read.

| Source corpus | Block size | Compression | Decompression | Compressed
size |
| --- | ---: | ---: | ---: | ---: |
| Mixed | 12 KiB | **7.1% faster** | 9.9% slower | 0.08% larger |
| Rust | 12 KiB | **5.5% faster** | 10.4% slower | 0.09% larger |
| TypeScript | 12 KiB | **7.4% faster** | 7.4% slower | 0.23% larger |
| Mixed | 16 KiB | **5.9% faster** | 8.8% slower | 0.17% smaller |
| Rust | 16 KiB | **4.6% faster** | 10.0% slower | 0.12% smaller |
| TypeScript | 16 KiB | **6.7% faster** | 6.1% slower | 0.02% smaller |

For these 12–16 KiB source blocks, `lz4_flex` compression was **4.6–7.4%
faster**, decompression was **6.1–10.4% slower**, and compressed size
changed by **-0.17% to +0.23%**.

The benchmark was stable enough to support those codec-level ranges:
wall time divided by thread CPU time was 1.000073 at the median and
1.001883 at p99. The median difference between the two codec orders was
0.38 percentage points for compression and 1.45 points for
decompression.

A separate decoder-only comparison fed identical `lzzzz`-encoded source
blocks to both decoders; there, `lz4_flex` was 17–21% slower. The
smaller regression in the table above comes from the large Flex
compression table producing a different valid LZ4 token stream. The PR
already uses `lz4_flex`'s supported unsafe pointer implementation with
validation; a local experiment that removed validation recovered only
5–6% and remained slower than liblz4, so this PR does not weaken decoder
safety.

These are codec microbenchmarks, not an end-to-end Turbopack speedup
claim.

## Verification

- `cargo fmt --all -- --check`
- `cargo clippy -p turbo-persistence --all-targets -- -D warnings`
- `cargo test -p turbo-persistence -- --test-threads=1` (110 passed)
- `cargo check -p turbo-tasks-backend --features
print_cache_item_size_with_compressed`
- Pinned-nightly paired codec benchmark: 24 processes / 480 paired
rounds

<!-- NEXT_JS_LLM -->

<!-- fleet 8f69f876-cb41-4fa2-ab60-dc8ccea88055 -->

---------

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Benjamin Woodruff ‮ <180404+bgw@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-17 18:37:51 +00:00
Sebastian "Sebbie" Silbermann 1329212686 [ci] Cleanup orphaned next-stats-action (#98348)
Became orphaned in https://github.com/vercel/next.js/pull/97792. Another
script and the test harness needed some utils from `next-stats-action`
so I moved that into `test/lib/link-packed-packages`.
2026-09-10 13:50:36 +02:00
Sebastian "Sebbie" Silbermann 76a0bdbdf4 [scripts] Move scripts (and benchmarks) off of node-fetch (#98347)
We're using Node.js versions with a built-in `fetch` implementation in
all of these scripts.
2026-09-10 13:50:35 +02:00
Sebastian "Sebbie" Silbermann 59b76e42ba [cd] Update peerDependencies in prereleases to accept the current prerelease (#98411)
https://github.com/vercel/next.js/pull/98330 revealed theat
`@next/third-parties` never worked with prereleases after 16.0. Now we
[update `peerDependencies` similar to what we do for preview builds
already](https://github.com/vercel/next.js/blob/27f679224e2660971377f855ced51093462734d8/scripts/create-preview-tarballs.js#L130-L132)
(we should unify both eventually to go through the same flow).

Fixes
```
  npm error code ERESOLVE
  npm error ERESOLVE unable to resolve dependency tree
  npm error
  npm error While resolving: undefined@undefined
  npm error Found: next@16.4.0-canary.22
  npm error node_modules/next
  npm error   next@"16.4.0-canary.22" from the root project
  npm error
  npm error Could not resolve dependency:
  npm error peer next@"^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0-beta.0" from @next/third-parties@16.4.0-canary.22
  npm error node_modules/@next/third-parties
  npm error   @next/third-parties@"16.4.0-canary.22" from the root project
```
-- https://github.com/vercel/next.js/actions/runs/34291905918/attempts/2
2026-09-09 13:39:15 +00:00
Tobias Koppers e44c5f192c ci: check that Turbopack compiles for wasm (#97588)
### What?

Adds a CI job that checks Turbopack compiles for
`wasm32-wasip1-threads`, so wasm portability
regressions are caught rather than rediscovered.

### Why?

Everything under `#[cfg(target_family = "wasm")]` is invisible to host
builds **and** to host clippy —
it is only checked when you deliberately build for the target. Four
separate defects in this stack were
caught only that way (a wrong build-script condition, two bad imports,
and a value that compiled but was
wrong). Without a gate, the next one lands unnoticed.

This also re-enables coverage that had been off for ~2 years: the old
`test-next-napi-bindings-wasi` job
was disabled with `if: false` pending napi-rs/napi-rs#2009, which closed
in April 2024.

### How?

Modelled on `rust-check` via `build_reusable.yml` (`needsRust`,
`skipInstallBuild`, `skipNativeBuild`),
so it does not pay for a JS build. Beyond `rustup target add` it needs
two things:

- **a WASI C toolchain**, because `lzzzz` (LZ4, via `turbo-persistence`)
and `zstd-sys` have C build
scripts. The SDK build is selected from `$RUNNER_ARCH` —
`build_reusable.yml` defaults to an arm64
runner, and an x86_64 clang fails there with `Exec format error` — with
a pinned sha256 per arch,
  unpacked under `$RUNNER_TEMP` so the workspace stays clean.
- **emnapi**, because `next-napi-bindings`' build script calls
`napi_build::setup()`, whose wasi path
panics without `EMNAPI_LINK_DIR` — so it is required even for `cargo
check`. It is installed into a
scratch directory rather than the root `package.json`, because this job
runs with `skipInstallBuild`
  and therefore never runs `pnpm install`.

Two things worth recording, both of which cost a CI round trip to find:

- pnpm is invoked from the repo root with `--dir`, not by `cd`-ing into
the scratch directory: corepack
resolves the pnpm version from the nearest `package.json`, and outside
the repo it picks the latest
  pnpm (11.x), which cannot run on the pinned Node 20
  (`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`).
- the job is added to `tests-pass`, whose `needs:` list is what actually
blocks a PR — a job that runs
but is absent from that list looks like coverage while blocking nothing.

The `emnapi@2.0.0-alpha.4` pin is deliberate and is the one fragility
here: the archive must define
`emnapi_create_env` / `emnapi_delete_env`, which exists only in emnapi
v2, still a prerelease. Move to
the stable release once it ships.


<!-- fleet b6d0486f-97c7-42a7-bdaf-3490774cdec3 -->

Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
2026-09-09 10:25:45 +02:00
Sebastian "Sebbie" Silbermann 662b0355eb [test] Resolve workspace:* packages to the version under test (#98330)
Test fixtures that depend on monorepo packages (for example
`@next/third-parties` and `@next/mdx`) declared them at the `canary`
dist-tag, which installs the published npm canary instead of the build
under test. In deploy tests this also broke the remote install entirely:
the published canary's `peerDependencies` ranges (for example
`^16.0.0-beta.0`) reject the prerelease preview versions that
`NEXT_TEST_VERSION` installs for `next` (for example
`16.4.0-preview-<sha>-<date>`), so `npm install` failed with ERESOLVE.

Test dependencies can now be declared as `workspace:*`, which resolves
to the build from the current checkout in every test mode:

- dev/start: the locally packed tarball from `pack-for-isolated-tests`,
with a descriptive error when the package has no pack task or is not
part of the repository.
- deploy: the preview tarball of the tested commit when
`NEXT_TEST_VERSION` points at a preview build (preview tarballs already
rewrite their monorepo peer dependencies to the same preview URLs, so
peer resolution succeeds), falling back to the version in the worktree
otherwise. Private packages without published preview tarballs fail with
a descriptive error.

All existing tests that used `canary` for monorepo packages are migrated
to `workspace:*`.
2026-09-08 18:17:29 +02:00
Sebastian "Sebbie" Silbermann 665d3458b6 [ci] Align build-and-deploy triggers with build-and-test (#98331)
Change detection (`git diff HEAD~1`) only covers the whole change set
when the checkout is the PR merge commit. `build-and-test` runs on
`pull_request` while `build-and-deploy` ran on `push` for upstream PR
branches, so a multi-commit push whose last commit was docs-only made
`build-and-deploy` skip the preview build while deploy tests in
`build-and-test` waited up to 30 minutes for a preview tarball that
never got published.

`build-and-deploy` now triggers on the same events as `build-and-test`
(`push` to the default branch and `pull_request`), while still accepting
release tags and workflow dispatch. The `deploy-target` fork guard is
removed since upstream PRs are no longer covered by a push run, and
`create-release-branch.js` now rewrites the workflow's branch list
alongside the existing `refs/heads/canary` replacement so release
branches keep their staging deploys.

Preview tarball creation now uses the PR head sha instead of falling
back to the merge-commit sha on `pull_request` `opened` events
(`github.event.after` only exists on `synchronize`), matching the sha
that `upload_preview_tarballs.yml` publishes under and that tests poll
for. This fallback was a pre-existing bug that only affected fork PRs;
it would have started affecting every upstream PR's first run once
previews moved to the `pull_request` event.
2026-09-08 10:55:44 +02:00
Sebastian "Sebbie" Silbermann 28195e9b75 [ci] Remove dormant code_freeze workflow (#97756)
Code freezes are now managed by toggling the existing `code freeze`
repository ruleset in the repository settings, so the `code_freeze`
workflow is no longer needed. It was also broken beyond repair: it
targeted the legacy branch-protection API with a `CODE_FREEZE_TOKEN`
secret that no longer exists instead of the rulesets the repository uses
now, and it referenced a `releaseType` input that was never declared.
This PR deletes the workflow and `scripts/code-freeze.js`, which had no
other consumer.

Note on the investigated alternative: resurrecting the workflow on top
of the rulesets API with just `GITHUB_TOKEN` is not possible. Creating
or updating repository rulesets requires the "Administration" repository
permission, which the workflow token cannot be granted (`permissions:
administration: write` is rejected as an invalid workflow, and even
`write-all` gets `403 Resource not accessible by integration`). Verified
empirically in
https://github.com/eps1lon/github-actions-repository-dispatch-admin-permissions.

Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
2026-08-28 19:11:37 +02:00
Sebastian "Sebbie" Silbermann a18361694e [ci] Use presigned URLs to upload preview builds (#97922)
Follow-up to #97256, pairing with
[vercel/vercel-packages#107](https://github.com/vercel/vercel-packages/pull/107).
The token exchange endpoint no longer returns a client-upload token
derived from a store read-write token; it presigns the upload URL with
the deployment's own Vercel OIDC identity and returns the URL, pinning
the pathname, expiry, size cap, and access and overwrite policy
server-side. No store credential exists anywhere after this lands, and
no Blob SDK is needed here for the new path — the script PUTs the
tarball bytes to the URL directly.

The client only uses presigned-URL. The server still supports client
tokens until we landed this and synced the mirror.


Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
2026-08-28 17:29:16 +02:00
Sebastian "Sebbie" Silbermann 027205875b [ci] Run flake detection and new deploy tests when merged and on backport branches (#97991)
The previous detection mechanism special-cased `canary` which lead to
large diffs when backport branches ran CI.

We stop special casing `canary` reducing complexity and giving us
another flake-detecting attempt when the change is merged.

That way we can safely enable flake detection on backport branches
without having to test large diffs when PRs are merged targetting
backport branches. New backport branches will no longer have flake
detecton and new deploy test runs disabled.
2026-08-28 09:09:32 +00:00
Aurora Scharff 49d3043e61 ci: skip framework tests for agent evals and skills (#97998)
Agent evals, skills, and the eval runner do not affect the framework
runtime, but they currently make `Determine changes` schedule the full
Rust, native, deployment, and framework test matrices.

Classify changes confined to `evals/`, `skills/`, and `run-evals.js`
with the existing lightweight documentation path. The always-on build
and lint checks still run.

Validated with Node syntax checking, Prettier, ESLint, and `git diff
--check`.
2026-08-27 22:05:19 +00:00
Niklas Mischkulnig d6e5741682 ci: remove pull_request_stats workflow (#97792)
This was quite useless (very flaky) and furthermore also slow
2026-08-27 09:43:29 +02:00
Andrew Clark 94327e8ade Port React's @gate test directive to the e2e harness (#96228)
The test suite has accumulated a bunch of patterns for disabling tests
that are known to fail under some configuration: `it.skip`, `if
(isNextDev) { test('skipped in dev mode', () => {}); return }`, whole
describes toggled off by checking `process.env.__NEXT_CACHE_COMPONENTS`.
These all have the same flaw: nothing tells you when the thing you
skipped starts working. The test stays disabled forever, and the
workaround it was guarding rots along with it.

React solves this with the `@gate` pragma, and this PR ports it to the
Next.js e2e harness:

```ts
// Blocked on the optimization that marks a route as fully static when
// no dynamic params are referenced in Server Components.
// @gate !cacheComponents
it('navigates to a page with a lazily-generated static param', async () => {
  // body unchanged
})
```

The test still runs. If the condition is false and the test fails, the
failure is absorbed and the suite stays green. If it _passes_, the suite
fails: the gate is stale, delete it. So instead of a skip that hides a
fixed bug indefinitely, you get a CI failure the day the fix lands.

When the condition is static, the inversion is Jest's own `test.failing`
under the hood. A lazy condition isn't known until the fixture's
resolved config is read inside the body, so those tests invert at
runtime instead.

`// @force-gate <condition>` skips for real — for tests that can't even
be attempted (prefetching is disabled in dev, deploy has no local build
output, the fixture won't build under the condition), and for tests of a
new API, where the disabled state can only throw and running it proves
nothing:

```ts
// Prefetching is disabled in dev, so this suite has nothing to test.
// @force-gate prefetching
describe('segment cache prefetch scheduling', () => {
  // ...
})
```

There's no staleness check in that case, so this is a judgment call:
prefer `@gate` when the off state fails for a meaningful reason — the
flag changes behavior that already exists — and `@force-gate` when the
body can only throw because the API doesn't exist. A static condition
(mode, bundler) resolves at collection time into a normal Jest skip. A
lazy condition resolves at runtime, and when a lazy force-gate on a
describe is false, we skip the fixture build entirely — that's what
makes it usable for suites whose fixtures are build-incompatible with
the condition. (One caveat: Jest has no way to skip a test that's
already running, so these report as passing with a warning in the log,
not as skipped.)

Conditions live in a hand-written registry. I considered deriving the
lazy ones from the config schema automatically, but a gate is a claim
about which dimension of the test matrix explains a failure, and I'd
rather each of those claims be spelled out with a description.
Referencing an undeclared name fails the suite at collection time, so a
typo can't silently disable a gate.

The important design decision for lazy conditions is that they read the
fixture's _resolved_ config, never `process.env`. The env var isn't the
truth: `__NEXT_CACHE_COMPONENTS=true` only applies when the fixture
doesn't set `cacheComponents` itself, and config resolution implies
flags the fixture never mentions (`cacheComponents: true` alone turns on
`experimental.ppr`). Resolution happens in a child process, because
in-process `loadConfig` would leak the fixture's `.env` files into the
Jest worker. Suites with no lazy gate never pay for any of this.

The condition expression is parsed using a small grammar (also ported
from the React repo). An expression that doesn't parse fails the suite:

```ts
// @gate mode === 'start' && !cacheComponents
// @gate !(turbopack || rspack)
```

There's also a runtime version, mirroring React's `gate(flags =>
flags.enableFoo)`, for tests that run under both states but assert
differently (and for `it.each`, where the pragma can't attach):

```ts
import { gate } from 'next-test-utils'

it('renders the fallback', async () => {
  if (await gate((conditions) => conditions.cacheComponents)) {
    // PPR: the fallback is part of the static shell
  } else {
    // fully dynamic: the fallback streams in
  }
})
```

It also accepts the pragma expression language as a string: `await
gate('cacheComponents && !dev')`.

Docs are in `test/lib/gate/README.md`; `test/unit/gate/` covers the
transform, the expression language, and the runtime.
2026-08-26 10:22:11 -04:00
Sebastian "Sebbie" Silbermann 7dcf6c0a68 [ci] Use short-lived access tokens for preview-build uploads instead of static token (#97256)
Replaces the static `PREVIEW_BUILDS_BLOB_READ_WRITE_TOKEN` secret with a
short-lived access token to upload to Vercel Blob in exchange for a
GitHub OIDC (endpoint side:
https://github.com/vercel/vercel-packages/pull/98):

- the job mints GitHub Actions OIDC tokens for the
`https://vercel-packages.vercel.app` audience itself (the workflow gains
`id-token: write`) and re-mints shortly before expiry since a token
lives about five minutes while a package batch can take longer
- `upload-preview-tarballs.js` exchanges the token at vercel-packages
for scoped client-upload tokens via POST and uploads the tarball bytes
directly to Blob storage with `@vercel/blob/client`'s `put`, so the blob
read-write token never leaves vercel-packages
- the `preview-builds` environment is dropped from the job: it only
existed to selectively expose the static token and restrict uploads to
canary, and the canary binding is now enforced by the `job_workflow_ref`
match at vercel-packages.


---------

Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
2026-08-25 20:13:01 +02:00
Jiwon Choi d7aa66c345 Remove generated error codes (#97687)
### Why?

Should come up with better solution that does not block PRs with git
conflict

x-ref:
https://vercel.slack.com/archives/C02CDC2ALJH/p1785263902728189?thread_ts=1785263687.502649&cid=C02CDC2ALJH

### How?

- Delete `errors.json`, the error-code SWC plugin, generated WASM, merge
driver, and validation/build tooling.
- Stop attaching error codes to server-rendering digests, redboxes, and
telemetry; native `Error.code` and `Error.name` remain available where
applicable.
- Remove the development-overlay error feedback UI, middleware, and
telemetry event that depended on stable codes.
- Update fixtures, snapshots, and guidance for code-free errors and
numeric-only digests.

<!-- NEXT_JS_LLM -->
2026-08-21 22:45:12 +02:00
Sebastian "Sebbie" Silbermann 8c7ed006b8 [react-sync] Check assignability before assigning the actor (#97638)
On scheduled runs of the Update React workflow, `github.actor` often
resolves to `github-actions[bot]`, which cannot be assigned to pull
requests in `vercel/next.js`. Since the sync switched to the GitHub App
token, adding a non-assignable assignee fails the request with 403 e.g.
https://github.com/vercel/next.js/actions/runs/32392135342/job/96500520936#step:8:781
(user tokens silently ignored it instead), which failed the entire sync
run and raced the in-flight reviewer request, leaving the created pull
request without a reviewer.

The script now checks assignability and skips assignment with a warning
when the actor cannot be assigned, and the finalize requests (assign,
request reviewers, add labels) run through `Promise.allSettled`, with
any failures rethrown together as an `AggregateError` so the run still
fails on real errors.

Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
2026-08-21 09:53:32 +02:00
Sebastian "Sebbie" Silbermann 9e1a290955 Add a script for adopting fork pull requests (#97252)
Fork PRs run without repository secrets, so deploy tests never run on
external contributions. The workaround is to re-push the contributor's
commits to a branch in `vercel/next.js` and open a replacement PR from
there, which is fiddly by hand and carries a real security risk. This
adds a `pr-adopt` PNPM script to encode the procedure.

```bash
pnpm pr-adopt <pr-number>            # adopt
node pr-adopt <pr-number> --dry-run  # report without pushing
```

We copy over the title and body as-is and prepend a `Adopts #N. Closes
#N.` line so that the underlying PR automatically closes and we create
the proper backlinks.

Commits are signed

The description is copied rather than rewritten for two reasons: it is
the contributor's own text, and any `Fixes #123` inside it has to
survive, since the original PR closes unmerged and so never fires its
own.

### The confirmation gate

Adoption runs the contributor's code on a branch where CI has repository
secrets, which is exactly what a fork PR is denied. Anything executing
during install, build, or test can read and exfiltrate them, so before
touching git the script prints the PR link, status, author handle and
association, diff size, and every file touched. Confirming requires
retyping the author's handle, and a non-interactive stdin is refused
rather than silently proceeding.

Two details worth a reviewer's attention:

- **The file list is deliberately unranked.** An earlier revision
flagged "high-risk" paths (`.github/`, `package.json`, lockfiles). That
was removed: a payload can sit in any fixture or source file, and
printing "no high-risk paths touched" manufactures false assurance.
- **The list comes from the paginated REST endpoint.** `gh pr view
--json files` silently caps at 100 entries. #96550 has 106 changed files
and returns 100, which would present a truncated list as the complete
surface. If the count still falls short of `changedFiles` (GitHub stops
at 3000), the shortfall is reported rather than hidden.

Draft and closed PRs are both adoptable, since a contributor may still
be iterating or may have abandoned an unreviewed change; the status is
surfaced rather than enforced. Merged PRs are refused because their
commits are already in `canary`.
2026-08-13 15:52:09 +00:00
Jimmy Miller e928f2395f Migrate napi-rs bindings from v2 to v3 (#95412)
Draft PR for testing

<!-- NEXT_JS_LLM_PR -->
2026-08-13 11:35:52 -04:00
Sebastian "Sebbie" Silbermann 10725074a4 [ci] Use OIDC tokens to read private preview builds (#96919)
Replaces the shared, static `PREVIEW_BUILDS_READ_TOKEN` secret with OIDC
tokens for reading auth-protected preview builds from vercel-packages
(see https://github.com/vercel/vercel-packages/pull/96):

- CI jobs mint a GH oidc token for the
`https://vercel-packages.vercel.app` audience
- deploys have a `.npmrc` written that uses the Vercel OIDC token

Tarball polling now also fails fast on 401/403 (retrying can't change
the authorization outcome) and failure messages include response headers
so request IDs are available for debugging.



Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 18:21:05 +02:00
Mayur 705cb0614d fix(scripts): correct typo in rm.mjs error message (#87015)
This pull request makes a minor correction to the error message in
`scripts/rm.mjs` to fix a typo.

* The error message now correctly reads "requires at least one
parameter" instead of "requires a least one parameter".

Co-authored-by: Marcos Hernanz <96699542+marcoshernanz@users.noreply.github.com>
2026-08-10 14:55:57 -07:00
Sam Poder b87f7b4a21 [turbopack] Don't run Webpack tests on Turbopack-only changes (#96656)
Hopefully this saves us money + makes it easier to merge Turbopack PRs
due to flaky Webpack tests
2026-08-07 13:49:12 -07:00
Sebastian "Sebbie" Silbermann d470d18941 [ci] Reset the turbopack deploy test project in the weekly cron (#96822)
The weekly `test-e2e-project-reset-cron` workflow never defined
`VERCEL_TURBOPACK_TEST_TEAM` or `VERCEL_TURBOPACK_TEST_TOKEN`, even
though `run-e2e-test-project-reset.mjs` iterates over all three deploy
test teams. Because `resetProject` defaulted `teamId` and `token` to the
base team, and a destructuring default fires on an explicit `undefined`,
the turbopack iteration silently resolved to `vtest314-next-e2e-tests`.

The cron has therefore been deleting and recreating the base team's
project twice per run while never resetting the turbopack team's
project, and reporting success throughout. This dates back to #89458,
which wired the env pair into `build_reusable.yml` and
`test_e2e_deploy_release.yml` but missed the cron.

This drops the defaults in favor of explicitly passing the team.
2026-08-07 11:57:11 +02:00
Will Binns-Smith 286169a9e7 Remove turbopack/packages and relocate devlow to packages/ (#96860)
## Summary

`@vercel/devlow-bench` hasn't published since December 2024. The job
that published it was removed in #93064; it was gated on the head commit
message starting with `chore: release turbopack npm packages`, which
squash-merge rewrites, so it couldn't fire through normal review —
#93951 bumped the version correctly and still published nothing.

Rather than rebuild that machinery for `turbopack/packages/*`, this
moves the one remaining package to `packages/`, where lerna and the
existing recursive publish already handle everything.
**`publish-release.js` is unchanged.**

The tradeoff is that the package joins the Next.js lockstep version,
going from `0.4.0` to the current canary, and ships nightly instead of
on manual bumps. At ~10k downloads/month, essentially all CI, nobody is
reading its version for compatibility — not worth ~300 lines of bespoke
release code in the script that gates Next.js releases.

`packages/` already holds unscoped, non-`@next/` packages
(`create-next-app`, `eslint-config-next`, `next-rspack`), all lockstep.

The root eslint config covers `packages/` but never applied to
`turbopack/`, so this exempts the three rules the source doesn't satisfy
and prefixes six unused parameters, rather than reformatting the package
as part of a move.

## Deletions

Three nft packages, last published in 2022, none with a build script or
any consumer:

- `@vercel/experimental-nft` — a stub containing only a `package.json`
- `@vercel/webpack-nft` — resolved binaries built from a
`node-file-trace` crate that no longer exists, so its core codepath
couldn't work
- `@vercel/experimental-nft-next-plugin` — depends on the above

`turbopack/xtask/src/publish.rs` goes with them: its only caller was the
workflow removed in #93064, its `npm` subcommand targeted that same
missing crate, and nothing invokes `cargo xtask`.

`turbopack/packages/` no longer exists.

## Verification

- `publish-release.js --dry-run` — publishes `@vercel/devlow-bench` via
the untouched recursive publish
- `lerna list` and `turbo run build` both resolve it at the new path
- `pnpm run --filter=devlow-bench test` — 3/3, and the CI jobs reference
it by name, not path
- Root eslint and the package's own `lint` script both clean
- `cargo check`/`clippy`/`fmt -p xtask`
- Not run: a real publish, which only CI can exercise

`pnpm-lock.yaml` is hand-edited to repoint the workspace entry and drop
the three removed ones. A full `pnpm install` rewrites ~1800 unrelated
lines, and `--frozen-lockfile` fails identically on unmodified `canary`
from pre-existing `apps/docs` drift.

<!-- NEXT_JS_LLM -->
2026-08-06 20:39:38 -07:00
Sebastian "Sebbie" Silbermann 4f37c39616 [ci] Open automated update pull requests with nextjs-bot (#96683)
Gets rid of of all usages of the static `GH_TOKEN_PULL_REQUESTS` token
that was issued for `vercel-release-bot`.

We already create commits with `nextjs-bot`. `nextjs-bot` already had
permissions to open PRs which is already being used by React sync.
2026-08-06 16:32:54 +02:00
Sebastian "Sebbie" Silbermann 2c04735944 [fragment-scroll] Remove config.experimental.appNewScrollHandler (#95602) 2026-08-06 09:53:09 +00:00
Sebastian "Sebbie" Silbermann 50eacb5b22 [react-sync] Open pull requests as the bot that authors the commits (#96682)
Noticed when auto-merge didn't work. `vercel-release-bot` isn't in the
exemption list (for good reason).

Since `vercel-release-bot` still uses a static token, it's time to use
the app fully.

The rest of the usage I'll do in a follow-up.

## test plan

It'll fail for now when enabling auto-merge. Everything else works
though. I'll fix the perms for auto-merge in a follow-up.

- [x] [sync from this
branch](https://github.com/vercel/next.js/actions/runs/30954178134/job/92143210455)
-> https://github.com/vercel/next.js/pull/96688
2026-08-05 07:24:09 +02:00
Luke Sandberg fca0fea594 [ci] Fix create_release_branch for new repo permissions (#96641)
### What

Fixes the **Create Release Branch** workflow, which currently fails on
every run.

1. Drop `permission-environments: write` from the GitHub App token
request.
- The release App installation no longer grants it, and
`actions/create-github-app-token` fails hard on an ungranted permission.
2. Drop the runtime `deployment-branch-policies` API call (and its 5s
sleep) from `scripts/create-release-branch.js`.
- No longer needed: the `release-stable` environment now statically
allows branches conforming to `next-NN` / `next-NN-*`. This is what the
permission above existed for.
3. Create the setup commit as a GitHub-signed commit via
`scripts/github-utils/signed-commit.js` instead of `git push`.
- Branch protection requires signed commits; `git push` pushed an
unsigned one. Uses the same `createSignedCommit` + `upsertBranchRef`
pattern as `scripts/automated-update-workflow.js`.

### Why

The most recent run
([30923742579](https://github.com/vercel/next.js/actions/runs/30923742579/job/92040655448))
dies at the first step, `Create GitHub App token`, with a 422 — so the
job never even clones the repo.

### Note for review

Release tags are **annotated** tag objects, so `git rev-parse v16.0.0`
returns the tag object SHA, not the commit. `createSignedCommit` passes
`baseSha` straight into `parents: [...]`, so the base is dereferenced
with `^{commit}`. Its internal `^{tree}` lookup happens to resolve
either way, so this would otherwise only have failed at commit-creation
time during a real release.

The static patterns cover `next-16-2`-style names but not a single-digit
major like `next-9-5`. Not a concern for current or future releases, but
noting it since the previous code created the policy for any branch
name.

### Verification

The workflow is `workflow_dispatch`-only and writes to the real repo, so
it can't be run end-to-end from a PR. Verified locally against the real
`v16.0.0` tag in a throwaway clone:

- `git reset --hard v16.0.0` leaves HEAD exactly at `v16.0.0^{commit}`,
confirming the base SHA is right.
- Replaying the lerna.json / `build_and_deploy.yml` /
`build_and_test.yml` mutations produces exactly those 3 changed files
and nothing else.
- Driving `createTreeFromLocalCommit` with a mocked `request` (no
network) uploads 3 blobs with correct `100644` modes and a `base_tree`
that matches the tag's tree.
- `node --check`, prettier, and eslint all clean.

Still needs a real `workflow_dispatch` run after merge to confirm the
token step succeeds and the resulting commit shows as **Verified**.
2026-08-04 17:18:15 +00:00
Hendrik Liebau c00449d52b [ci] Wait for the preview tarballs before running the deploy tests (#96527)
The "Test new and changed tests when deployed" jobs install Next.js from
the preview tarballs built for the commit under test, but nothing made
them wait for those tarballs to exist. Until #96438 the job was ordered
behind `test-prod` and the other new-test jobs, which delayed it long
enough that the tarballs had usually landed by the time it looked for
them. Now that it starts immediately it frequently fails with "Artifacts
not found for commit ...".

The ordering cannot be expressed as a `needs:` entry, because the
tarballs are published by a different workflow and, for pull requests
from branches in this repository, a different event than the
`build-and-test` run that consumes them. We therefore add a
`wait-for-preview-tarball` job that polls for the tarball instead, and
make both deploy test jobs depend on it. Doing the waiting in one cheap
job keeps the deploy test matrices off their 16-core runners until there
is something for them to install.

The new job is also listed in `tests-pass`. A dependency failure skips
the jobs that need it, and a skipped job does not count as a failure, so
without that entry a tarball that never arrives would have let the
required check pass with the deploy tests never having run.

`test-new-tests-deploy-cache-components` gets the same treatment,
dropping the incidental ordering it still inherited from
`test-cache-components-prod` and picking up the docs-only guard that
`test-new-tests-deploy` already had.
2026-08-03 16:27:51 +02:00
Sebastian "Sebbie" Silbermann e89118a0ab [react-sync] Enable auto-merge on PRs (#96442)
Saves a click. These PRs still require approval as per repo settings.

The commit description is empty so that the React PRs aren't mentioned
again in commits. That'd be pretty noisy for the React PRs.
2026-07-31 21:03:23 +00:00
Sebastian "Sebbie" Silbermann 34299ad5d0 [ci] Match the test job summary style to the PR test report comment (#95882) 2026-07-30 08:41:25 -04:00
Sebastian "Sebbie" Silbermann 6ab0dd6fcd [ci] Install Node.js from the official tarball in the native-builder image (#96298)
Fixes e.g.
https://github.com/vercel/next.js/actions/runs/30314671467/job/90137708228#step:12:603

The `native-builder` image (`scripts/native-builder.Dockerfile`), used
by the `build-native` job of `build_and_deploy.yml` to cross-compile
`next-swc`, installed Node.js 20 via the NodeSource setup script. That
script downloads and imports a signing key before configuring the apt
repository. When the key download returns an HTTP 403 from CI runners
the script still exits successfully, so `apt-get install nodejs` falls
back to Ubuntu's stock `nodejs` package, which does **not** bundle
`npm`. The failure only surfaced two layers later when the image ran
`npm i -g @napi-rs/cli` (`npm: not found`, exit 127), breaking the
native build.

This change installs Node.js 20 from the official `nodejs.org` static
tarball instead. The tarball bundles `npm` and `corepack`, is
glibc-linked (matching the previous intent), and does not depend on
NodeSource's apt repository or key server, removing the flaky external
dependency. The install step also asserts `npm --version`, so a future
regression fails at the install layer rather than several layers later.


## test plan

- [x] [full build_and_deploy on this
branch](https://github.com/vercel/next.js/actions/runs/30372888336)
2026-07-28 15:21:20 -04:00
Niklas Mischkulnig b3278b7573 test: Use Turbopack for 'Test new and changed tests when deployed' (#95552) 2026-07-07 16:03:00 +02:00
Sebastian "Sebbie" Silbermann cd3476c3c8 [ci] Add stats comment with partial stats (#95401)
We used to not post a comment when a part of the bundler matrix got
cancelled. Conceptually, a cancelled job is just like a failed on in the
sense of not being able to produce stats.

Now we always aggregate stats and post stats from the bundlers that did
produce them.

The risk here is a race between a run that was cancelled because another
run superseded it and the run that superseded it. Though practically,
aggregating the stats isn't slower than running the full workflow. Not
impossible (especially given how slow the Webpack job is) but incredibly
rare.
2026-07-06 19:13:28 +00:00
Sebastian "Sebbie" Silbermann f656c597c6 [fragment-scroll] Enable new scroll handler by default (#95378) 2026-07-06 18:39:07 +00:00
Sebastian "Sebbie" Silbermann d3169de7e5 Collect modules with browser variants statically (#95200)
Instead of hardcoding the list, we now automatically detect any
`.browser` sibling and create a compiler alias for that module.

Together with a linting rule, we'll ensure no future regressions are
handled automatically (detection of conditional require and missing
.browser sibling).

This isn't the smoothest DX yet. Ideally we'd generate the list at
Next.js compile time (i.e. before publishing) to avoid having to rerun a
script (I'll follow-up). Generating the list during build time (next dev
or next build) is just another tiny cost users would have to pay.
2026-07-03 08:45:23 +02:00
Sebastian "Sebbie" Silbermann 7443e117dc [cd] Replace the release package with our own GitHub release creation (#95352) 2026-07-02 09:07:26 +00:00
Sebastian "Sebbie" Silbermann dcb662c7fb [ci] Add support for auth based preview build registries (#95204)
Follow-up to https://github.com/vercel/next.js/pull/93464 which didn't
consider auth based preview build registries.
2026-06-30 20:59:15 +02:00
Sebastian "Sebbie" Silbermann 1d9b56dad1 [ci] Display correct test commands when Cache Component test fail (#95267) 2026-06-29 12:16:05 +00:00
Sebastian "Sebbie" Silbermann 69acc33f03 [ci] Make automatic native binding install opt-in (#95114)
A `pnpm install` installs native bindings for Next.js by default. This
makes sense for local development where half of the team doesn't work on
native code and just needs the most recent bindings. However, CI almost
exclusively wants to test with bindings that are built from the current
commit. The rest doesn't need native bindings at all.

The only exception is `test_examples` which is likely not functional.
Though we still support it by allowing to opt-into automatically
installing native bindings in CI.

## test plan

- [x] fetch test timings still skips:
https://github.com/vercel/next.js/actions/runs/28087707205/job/83157705446?pr=95114#step:7:228
2026-06-24 22:39:36 +02:00
Sebastian "Sebbie" Silbermann 681940523a [cd] add ad-hoc preview release cut from canary (#95086)
Add a `preview` release type so `@preview` npm releases can be cut
**ad-hoc from `canary`** via the `Trigger Release` workflow, retiring
the dedicated long-lived preview branch. The GitHub environment has been
updated accordingly.

A preview cut produces **two commits**, pushed in a single update:

1. **Bump-to-preview** — all packages set to the computed preview
version, tagged `v…-preview.N`. This tag's build publishes `@preview`
(relies on #95085).
2. **Revert-to-canary** — restores the `-canary.N` versions so `canary`
keeps advancing its own line and isn't stranded on a preview version.

```
B  canary HEAD before cut (e.g. 16.3.0-canary.61)
│
P  v16.3.0-preview.N   ← tag here; tag build publishes @preview
│
R  canary HEAD after cut (versions restored to 16.3.0-canary.61)
```

### Preview version numbering

`base = max(canary major.minor.patch, npm @preview major.minor.patch)`:
- base still on the published preview line → continue it (`n + 1`)
- canary advanced to a higher base (or no `@preview` published) → reset
to `.0`

So if `canary` jumps to `18.0.0-canary.x` since the last
`16.3.0-preview.x`, the next cut is `18.0.0-preview.0`, not a
continuation.

## Test plan

The dry mode of `start-release.js` was extended to intercept the GH
requests and log the relevant data.

```console
$ node ./scripts/start-release.js --release-type preview --dry-run
Dry run: keeping commits locally, skipping git push and GitHub release creation
Running pnpm release-preview...
[...]
> lerna version 16.3.0-preview.4 --force-publish -y --no-push --allow-branch '**'
[...]
Creating GitHub-signed release commit(s) for v16.3.0-preview.4 from local commits 0f5c1e865cc34edb257a16823ed6525d9cd58bfc..0062348c411776271fe7eac64e97b8bad2775b77
[dry-run] GitHub API POST /repos/vercel/next.js/git/blobs [...]
[dry-run] GitHub API POST /repos/vercel/next.js/git/commits {"message":"v16.3.0-preview.4","tree":"0000000000000000000000000000000000000016","parents":["0f5c1e865cc34edb257a16823ed6525d9cd58bfc"]}
[dry-run] GitHub API POST /repos/vercel/next.js/git/commits {"message":"Restore canary version 16.3.0-canary.61 after v16.3.0-preview.4 preview release","tree":"000000000000000000000000000000000000002d","parents":["0000000000000000000000000000000000000017"]}
[dry-run] GitHub API POST /repos/vercel/next.js/git/refs {"ref":"refs/tags/v16.3.0-preview.4","sha":"0000000000000000000000000000000000000017"}
[dry-run] GitHub API PATCH /repos/vercel/next.js/git/refs/heads/sebbie/preview-release-from-canary {"sha":"000000000000000000000000000000000000002e","force":false}
Dry run: skipping local branch sync; would set sebbie/preview-release-from-canary to 000000000000000000000000000000000000002e and tag v16.3.0-preview.4 at 0000000000000000000000000000000000000017
Created GitHub-signed release tag v16.3.0-preview.4 at 0000000000000000000000000000000000000017; branch sebbie/preview-release-from-canary now at 000000000000000000000000000000000000002e
Dry run: skipping GitHub release creation
Release process is finished
```
2026-06-24 16:33:37 +02:00
Sebastian "Sebbie" Silbermann 2aba93f005 [scripts] Write pnpm overrides for pnpm projects in pack/patch helpers (#94747) 2026-06-22 17:12:14 +02:00
Steven b229f6400c [cd] reduce size of npm metadata on publish (#94953) 2026-06-19 01:02:53 +02:00
Sebastian "Sebbie" Silbermann 47c8c6fc92 [cd] Continue release train if @next/swc-wasm-* fails to publish (#94912)
https://github.com/vercel/next.js/pull/94622 tried to be more strict
with when a release is considered healthy. However, `@next/swc-wasm-web`
is not enrolled in Trusted Publishing and blocks the release train.

This PR restores the behavior for `@next/swc-wasm-*` packages prior to
https://github.com/vercel/next.js/pull/94622 i.e. we continue the
release train even if any `@next/swc-wasm-*` fails. Other NAPI bindings
will continue to block the release train if they fail to publish.

The bail vs no-bail behavior is controlled with a repository variable so
that we can mor easily test if `@next/swc-wasm-web` is enrolled in
Trusted Publishing.
2026-06-17 20:49:57 +02:00
Sebastian "Sebbie" Silbermann 618957c166 [cd] More robust publish pipeline (#94622) 2026-06-17 19:44:59 +02:00
Benjamin Woodruff 3caee03f2a [ci] Reduce usage of turborepo cache in build_and_deploy.yml (#94319)
We're consolidating on `actions/cache` now that we got rid of
self-hosted runners. This doesn't remove all of the uses of turborepo's
cache (notably sccache), but this does eliminate the remote turborepo
cache for the native build cache and the docker image.

These build steps use docker so that we're building against an old
pinned glibc (2.31), and so that we can have access to both `aarch64`
and `x86_64` musl sysroots.

## Testing

**Local Build:** `node scripts/docker-native-build.js` still works (this
script just exists for being able to debug these builds locally)

**CI Build:**
- Run an automated-preview CI job, but cancel it before the native build
succeeds to test writing to the docker cache but not the native cache:
https://github.com/vercel/next.js/actions/runs/26986955366/job/79638633136?pr=94319
- Re-run the job with only the docker image cache, see that we get a
cache hit for the docker image:
https://github.com/vercel/next.js/actions/runs/26986955366/job/79639365126?pr=94319
- Re-run the job again and see that we hit the native build cache hit
and don't touch docker at all:
https://github.com/vercel/next.js/actions/runs/26986955366/job/79647324952?pr=94319
- Manually trigger the job to do a full preview build:
https://github.com/vercel/next.js/actions/runs/26989861549
2026-06-10 14:23:18 -07:00
Joseph 867227352b chore: lint-language output only warnings and errors (#94261)
The lint-language script is too verbose, it prints "no issues found" for
all files it touches.
2026-06-02 10:07:28 +02:00
Sebastian "Sebbie" Silbermann c1c33c92f9 [ci] Reserve comments for stats and tests (#93240) 2026-05-26 18:00:46 +02:00
Tobias Koppers a80e0d5668 Fix errors.json merge driver to handle gaps in error codes (#93829)
### What?

Fixes the `errors.json` git merge driver
(`scripts/merge-errors-json/merge.mjs`) so that it allocates new error
ids based on the largest existing numeric key rather than the entry
count. When the current file has gaps in its numeric keys (e.g. retired
error codes), the previous logic produced ids that collided with
existing entries and silently overwrote them during a rebase/merge.

### Why?

The driver computed the next id with:

```js
let nextKey = Object.keys(result).length + 1
```

This only works when ids are dense (`1..N` with no holes).
`packages/next/errors.json` has accumulated gaps over time — for example
missing ids around 1232–1234 and 1246 on `canary`. When a branch was
rebased onto `canary` and the driver replayed a commit that added new
entries, the computed `nextKey` (`length + 1`) pointed at an id that was
already in use higher up in the map. The replayed entries then clobbered
existing canary messages (e.g. "Route encountered %s during the initial
render" and "in a Client Component" at 1244/1245), corrupting
`errors.json` without raising a conflict.

Because the driver writes the result directly and exits 0, this kind of
corruption is invisible at rebase time and only surfaces later when the
displaced messages start producing wrong/duplicate codes at runtime.

### How?

- Introduce a `getMaxNumericKey(map)` helper that scans the keys of the
merged map, parses each as an integer, and returns the largest value (or
`0` if there are no numeric keys).
- Use `getMaxNumericKey(result) + 1` as the starting `nextKey` in
`mergeErrors`, so newly added messages are always appended above the
highest currently-used id, regardless of gaps.

Behavior for the dense / no-gap case is unchanged: `max + 1 === length +
1` when ids are `1..N` contiguous, so all three existing scenarios in
`test/scripts/merge-errors-json/merge-errors-json.test.ts` (basic
conflict, empty base, stacked PRs) continue to pass.

No changes to `errors.json` itself are included here; this PR only fixes
the driver going forward.

<!-- NEXT_JS_LLM_PR -->

Co-authored-by: v-work-app[bot] <262237222+v-work-app[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
2026-05-25 20:29:01 +02:00
Tim Neutkens 685d5b9e93 Fix pr-status job pagination (#94049)
### What?

Lower the GitHub Actions jobs page size used by `scripts/pr-status.js`
and keep walking pages until all jobs are fetched. Reuse the paginated
job fetch for flaky-test detection.

### Why?

The previous `per_page=100&page=1` request for run `26093602659` returns
HTTP 502 from GitHub because the query is too slow.

### How?

Add a shared `JOBS_PAGE_SIZE = 30`, update the pagination stop condition
to match it, and route flaky-test failed job lookup through
`getFailedJobs()`.

### Verification

- `node --check scripts/pr-status.js`
- `pnpm prettier --with-node-modules --ignore-path .prettierignore
--write scripts/pr-status.js`
- `npx eslint --config eslint.config.mjs --fix scripts/pr-status.js`
- `gh api
"repos/vercel/next.js/actions/runs/26093602659/jobs?per_page=30&page=<n>"
--jq ".jobs | length"` across pages 1-5 captured 137 jobs, matching
`total_count: 137`
- Confirmed the old `per_page=100&page=1` request still returns HTTP 502
- Not run: full Next.js test suite (scripts-only helper change)

<!-- NEXT_JS_LLM_PR -->
2026-05-22 15:37:23 +00:00