Commit Graph

488 Commits

Author SHA1 Message Date
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
Tim Neutkens cdd77ac4f3 Add deployable tarballs to pack-next (#93963)
### What?

Adds a `--deployable-tar` option to `pack-next` that writes generated
tarballs into the target project and patches package references to use
relative `file:` paths.

### Why?

Projects that need to deploy with locally packed Next.js packages should
be able to include those tarballs inside the project directory instead
of referencing tarballs from the Next.js checkout.

### How?

Resolves the target project package.json location, writes tarballs to a
sibling `tarballs/` directory, and maps package override paths to
project-relative references when patching package.json. Existing `--tar`
behavior continues to write to the repository tarballs directory.

### Verification

- `git diff --check`
- `git diff --cached --check`
- `pnpm prettier --with-node-modules --ignore-path .prettierignore
--write scripts/pack-next.ts scripts/pack-utils/patch-package-json.ts
contributing/core/testing.md contributing/core/developing.md`
- `npx eslint --config eslint.config.mjs --fix scripts/pack-next.ts
scripts/pack-utils/patch-package-json.ts contributing/core/testing.md
contributing/core/developing.md` (Markdown files were ignored because no
matching ESLint config was supplied)
- `pnpm pack-next --project ../sandbox/nextjs-duplicated-html-repro/
--deployable-tar`

<!-- NEXT_JS_LLM_PR -->
2026-05-20 08:00:56 -07:00
Tim Neutkens 8141dcf12e Convert test/integration to isolated tests (#93247)
### What?

Converts every test under `test/integration/` to an isolated test
running through `nextTestSetup` (under `test/e2e/`, `test/production/`,
`test/development/`, or `test/unit/`), then deletes `test/integration/`
along with the legacy CI orchestration that was specific to it.

- `test/integration/` removed entirely (~327 test suites)
- New isolated suites added across the existing folders:
  - `test/e2e/` — 175
  - `test/production/` — 130
  - `test/development/` — 43
  - `test/unit/` — 1
- `.github/workflows/build_and_test.yml` and `run-tests.js` no longer
have any `integration` branches
- `nextTestSetup` gained a `baseUrl` option on `next.browser()` so a
small number of tests that drive their own proxy/static-export server
can keep using `next.browser(...)` instead of importing `next-webdriver`
directly

### Why?

`test/integration/` predated `nextTestSetup` and ran tests directly
against the source checkout via custom helpers (`launchApp`,
`nextBuild`, `nextStart`, `runNextCommand`, `webdriver`, `fetchViaHTTP`,
…). Each suite hand-rolled its own dev/start/build orchestration,
fixture mutation, and process management.

The isolated test model used by the rest of the repo gives each suite an
isolated working directory containing a packed `next.tgz` install, a
uniform `next.start()` / `next.build()` / `next.fetch()` /
`next.browser()` API, and the same lifecycle for dev, start, and deploy
modes — so a single set of assertions covers all three. Deploy-mode
skips and per-feature gates are expressed declaratively
(`skipDeployment`, `disableAutoSkewProtection`, `if (skipped) return`)
instead of branching on `process.env`.

Removing `test/integration/` lets us:

- Delete the bespoke orchestration code in the CI workflow and
`run-tests.js`
- Run every converted suite consistently in dev, start, and deploy modes
(where applicable)
- Reproduce every test locally with the same `pnpm
test-{dev,start}-{turbo,webpack}` commands; no separate `integration`
path
- Open the door to running `test/production` against deployments in the
future (the converted suites already declare `skipDeployment` so they
can be flipped on)

### How?

Mechanical conversion per suite, with targeted clean-ups:

1. **Per-suite conversion.** Each
`test/integration/<name>/test/index.test.{js,ts}` was rewritten into a
single `<name>.test.ts` under the right folder based on what the
original exercised:
   - `launchApp` / dev-only assertions → `test/development/`
- `nextBuild` + `nextStart` / start-only assertions → `test/production/`
   - Both → `test/e2e/`
- The one pure jsdom render check (`link-without-router`) → `test/unit/`

2. **API mapping.** Custom helpers were replaced by `nextTestSetup`
equivalents: `launchApp` → `next.start()`, `nextBuild` → `next.build()`,
`runNextCommand` → `next.runCommand`, `fetchViaHTTP` → `next.fetch`,
`webdriver(...)` → `next.browser(...)`. Fixture mutations switched from
raw `fs.writeFile`/`fs.rename` to `next.patchFile` (with the 3-arg
`runWithTempContent` callback when the change has a defined scope) and
`next.deleteFile`.

3. **Deploy-mode handling.** Suites that can't run in deploy mode (use
`patchFile` / `next.build()` / depend on local CLI output) declare
`skipDeployment: true` and early-return on the `skipped` boolean. Suites
where Vercel's edge mutates URLs (`&dpl=`, immutable assets) declare
`disableAutoSkewProtection: true`.

4. **`next.browser({ baseUrl })`.** A handful of tests
(`prerender-export`, `cdn-cache-busting`, `preload-viewport`, both
`react-virtualized` suites) need to drive a separate server (a
static-export server or an `http-proxy` instance) rather than the
Next.js process. Instead of importing `next-webdriver` directly, those
tests now pass `{ baseUrl: <port|url> }` to `next.browser()`. For the
proxy cases, the proxy was moved into `server.js` inside the fixture and
`http-proxy` declared via the `dependencies` option of `nextTestSetup`,
so the test runs with a fully isolated dependency graph.

5. **CI clean-up.** With `test/integration` gone, the `test
integration*` jobs and `integration-tests-manifest`-related logic in
`.github/workflows/build_and_test.yml` were removed, and `run-tests.js`
no longer has the `integration` test-folder branch.

6. **Validation.** The PR was iterated against multiple full CI runs;
the remaining failures on the latest run are pre-existing flakes
(segment-cache 60s `act` timeouts in turbopack-prod) or transient
infrastructure issues unrelated to the conversion.
2026-05-11 13:55:47 +02:00
Niklas Mischkulnig ead8013f88 Turbopack: more strict vergen setup (#93541)
Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com>
Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com>
2026-05-07 08:50:02 +00:00
Sebastian "Sebbie" Silbermann 04294cb47a [ci] Clarify new preview build flow (#93540) 2026-05-06 14:04:19 +00:00
Sebastian "Sebbie" Silbermann 7240c74b23 [ci] Create unique preview build versions for canary previews (#93490) 2026-05-05 15:39:45 +02:00
Sebastian "Sebbie" Silbermann eaad55c518 [ci] Migrate React sync off of RELEASE_BOT_GITHUB_TOKEN (#93456) 2026-05-05 08:50:55 +00:00
Sebastian "Sebbie" Silbermann a9fe66ca2e [ci] Move Turbopack, Rspack and font data workflows off of RELEASE_BOT_GITHUB_TOKEN (#93455)
Co-authored-by: Benjamin Woodruff <github@benjam.info>
2026-05-05 10:23:01 +02:00
Sebastian "Sebbie" Silbermann 818aa24505 [ci] Allow configuring the base URL for preview builds (#93464) 2026-05-04 19:47:07 +02:00