From f72c707270622d5f7a39e5a2208e4bd05f2ff5ff Mon Sep 17 00:00:00 2001 From: Jimmy Lai Date: Mon, 5 Jan 2026 09:57:11 +0100 Subject: [PATCH 1/3] chore: warn when running tests against stale build --- jest.config.js | 1 + packages/next/taskfile.js | 10 ++++++ run-tests.js | 4 +++ test/jest-global-setup.ts | 5 +++ test/lib/check-build-freshness.js | 59 +++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+) create mode 100644 test/jest-global-setup.ts create mode 100644 test/lib/check-build-freshness.js diff --git a/jest.config.js b/jest.config.js index b1219cbd5819ca..9f81261a7eff83 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,6 +7,7 @@ const createJestConfig = nextJest() const customJestConfig = { displayName: process.env.IS_WEBPACK_TEST ? 'webpack' : 'Turbopack', testMatch: ['**/*.test.js', '**/*.test.ts', '**/*.test.jsx', '**/*.test.tsx'], + globalSetup: '/jest-global-setup.ts', setupFilesAfterEnv: ['/jest-setup-after-env.ts'], verbose: true, rootDir: 'test', diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index 225fb3b1f69ae2..203b2494299f2f 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -2684,6 +2684,16 @@ export async function build(task, opts) { ['precompile', 'compile', 'check_error_codes', 'generate_types'], opts ) + // Write git commit hash to dist for stale build detection during tests + try { + const { stdout: commitHash } = await execa('git', ['rev-parse', 'HEAD']) + await fs.writeFile( + join(__dirname, 'dist', '.build-commit'), + commitHash.trim() + ) + } catch (err) { + // Ignore errors (e.g., not in a git repo) + } } export async function generate_types(task, opts) { diff --git a/run-tests.js b/run-tests.js index 6c776d119ffcbf..da96dd179fbd16 100644 --- a/run-tests.js +++ b/run-tests.js @@ -13,6 +13,7 @@ const glob = promisify(_glob) const exec = promisify(execOrig) const core = require('@actions/core') const { getTestFilter } = require('./test/get-test-filter') +const { checkBuildFreshness } = require('./test/lib/check-build-freshness') // Do not rename or format. sync-react script relies on this line. // prettier-ignore @@ -219,6 +220,9 @@ async function main() { // Ensure we have the arguments awaited from yargs. argv = await argv + // Check for stale or missing build + await checkBuildFreshness() + // `.github/workflows/build_reusable.yml` sets this, we should use it unless // it's overridden by an explicit `--concurrency` argument. const envConcurrency = diff --git a/test/jest-global-setup.ts b/test/jest-global-setup.ts new file mode 100644 index 00000000000000..5b668053cca28b --- /dev/null +++ b/test/jest-global-setup.ts @@ -0,0 +1,5 @@ +import { checkBuildFreshness } from './lib/check-build-freshness' + +export default async function globalSetup() { + await checkBuildFreshness() +} diff --git a/test/lib/check-build-freshness.js b/test/lib/check-build-freshness.js new file mode 100644 index 00000000000000..6b100e4d64c4b4 --- /dev/null +++ b/test/lib/check-build-freshness.js @@ -0,0 +1,59 @@ +const { existsSync } = require('fs') +const fsp = require('fs/promises') +const path = require('path') +const { execSync } = require('child_process') + +const YELLOW = '\x1b[33m' +const RESET = '\x1b[0m' + +/** + * Check if the Next.js build is fresh (matches current git HEAD). + * Prints warnings to console if build is missing or stale. + * @returns {Promise} + */ +async function checkBuildFreshness() { + const distPath = path.join(__dirname, '../../packages/next/dist') + const buildCommitPath = path.join(distPath, '.build-commit') + + if (!existsSync(distPath)) { + console.warn(`${YELLOW}⚠️ WARNING: No build found!${RESET}`) + console.warn( + `${YELLOW} The packages/next/dist directory does not exist.${RESET}` + ) + console.warn( + `${YELLOW} Run \`pnpm build\` before running tests.\n${RESET}` + ) + return + } + + if (!existsSync(buildCommitPath)) { + console.warn(`${YELLOW}⚠️ WARNING: Build may be stale!${RESET}`) + console.warn( + `${YELLOW} Unable to verify build freshness (no .build-commit marker).${RESET}` + ) + console.warn(`${YELLOW} Run \`pnpm build\` to rebuild.\n${RESET}`) + return + } + + try { + const buildCommit = (await fsp.readFile(buildCommitPath, 'utf8')).trim() + const currentCommit = execSync('git rev-parse HEAD', { + encoding: 'utf8', + }).trim() + + if (buildCommit !== currentCommit) { + console.warn(`${YELLOW}⚠️ WARNING: Build is stale!${RESET}`) + console.warn( + `${YELLOW} Build was compiled at commit: ${buildCommit.slice(0, 8)}${RESET}` + ) + console.warn( + `${YELLOW} Current HEAD is at commit: ${currentCommit.slice(0, 8)}${RESET}` + ) + console.warn(`${YELLOW} Run \`pnpm build\` to rebuild.\n${RESET}`) + } + } catch (err) { + // Ignore errors (e.g., git not available) + } +} + +module.exports = { checkBuildFreshness } From 4762d1c0172ea1caad9f4762738c09d85cb60eb7 Mon Sep 17 00:00:00 2001 From: Jimmy Lai Date: Mon, 5 Jan 2026 12:44:12 +0100 Subject: [PATCH 2/3] fix: throw error instead of ignoring when writing build commit hash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/next/taskfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index 203b2494299f2f..d002407f51f34f 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -2692,7 +2692,7 @@ export async function build(task, opts) { commitHash.trim() ) } catch (err) { - // Ignore errors (e.g., not in a git repo) + throw new Error(`Failed to write build commit hash: ${err.message}`) } } From ab028539406a90b90385c18acdbe8f013079fe5f Mon Sep 17 00:00:00 2001 From: Jimmy Lai Date: Mon, 5 Jan 2026 12:52:48 +0100 Subject: [PATCH 3/3] Update packages/next/taskfile.js Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> --- packages/next/taskfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index d002407f51f34f..01ac089ed46043 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -2692,7 +2692,7 @@ export async function build(task, opts) { commitHash.trim() ) } catch (err) { - throw new Error(`Failed to write build commit hash: ${err.message}`) + console.warn(`Warning: Could not write build commit hash: ${err.message}`) } }