From 08dd08ec7216cf6ba6651621fb8503b94e512a9b Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Tue, 23 Aug 2022 22:53:05 -0500 Subject: [PATCH] Skip auto-install for missing deps in CI (#39882) Follow-up to https://github.com/vercel/next.js/pull/39838 this skips auto-installing missing TypeScript dependencies in CI environments as this creates a side-effect which we want to avoid. x-ref: [slack thread](https://vercel.slack.com/archives/C03KAR5DCKC/p1661301586792559?thread_ts=1661299706.559769&cid=C03KAR5DCKC) ## Bug - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` --- .../lib/typescript/missingDependencyError.ts | 43 +++++++++++++++++++ packages/next/lib/verifyTypeScriptSetup.ts | 10 ++++- .../typescript-auto-install/index.test.ts | 10 +++++ .../ci-missing-typescript-deps/index.test.ts | 33 ++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 packages/next/lib/typescript/missingDependencyError.ts create mode 100644 test/production/ci-missing-typescript-deps/index.test.ts diff --git a/packages/next/lib/typescript/missingDependencyError.ts b/packages/next/lib/typescript/missingDependencyError.ts new file mode 100644 index 000000000000000..49c21d99d07a05f --- /dev/null +++ b/packages/next/lib/typescript/missingDependencyError.ts @@ -0,0 +1,43 @@ +import chalk from 'next/dist/compiled/chalk' + +import { getOxfordCommaList } from '../oxford-comma-list' +import { MissingDependency } from '../has-necessary-dependencies' +import { FatalError } from '../fatal-error' +import { getPkgManager } from '../helpers/get-pkg-manager' + +export async function missingDepsError( + dir: string, + missingPackages: MissingDependency[] +) { + const packagesHuman = getOxfordCommaList(missingPackages.map((p) => p.pkg)) + const packagesCli = missingPackages.map((p) => p.pkg).join(' ') + const packageManager = getPkgManager(dir) + + const removalMsg = + '\n\n' + + chalk.bold( + 'If you are not trying to use TypeScript, please remove the ' + + chalk.cyan('tsconfig.json') + + ' file from your package root (and any TypeScript files in your pages directory).' + ) + + throw new FatalError( + chalk.bold.red( + `It looks like you're trying to use TypeScript but do not have the required package(s) installed.` + ) + + '\n\n' + + chalk.bold(`Please install ${chalk.bold(packagesHuman)} by running:`) + + '\n\n' + + `\t${chalk.bold.cyan( + (packageManager === 'yarn' + ? 'yarn add --dev' + : packageManager === 'pnpm' + ? 'pnpm install --save-dev' + : 'npm install --save-dev') + + ' ' + + packagesCli + )}` + + removalMsg + + '\n' + ) +} diff --git a/packages/next/lib/verifyTypeScriptSetup.ts b/packages/next/lib/verifyTypeScriptSetup.ts index 9f06333166a319d..dd632d27b3dbb35 100644 --- a/packages/next/lib/verifyTypeScriptSetup.ts +++ b/packages/next/lib/verifyTypeScriptSetup.ts @@ -14,6 +14,8 @@ import { TypeCheckResult } from './typescript/runTypeCheck' import { writeAppTypeDeclarations } from './typescript/writeAppTypeDeclarations' import { writeConfigurationDefaults } from './typescript/writeConfigurationDefaults' import { installDependencies } from './install-dependencies' +import { isCI } from '../telemetry/ci-info' +import { missingDepsError } from './typescript/missingDependencyError' const requiredPackages = [ { @@ -64,6 +66,11 @@ export async function verifyTypeScriptSetup({ ) if (deps.missing?.length > 0) { + if (isCI) { + // we don't attempt auto install in CI to avoid side-effects + // and instead log the error for installing needed packages + await missingDepsError(dir, deps.missing) + } console.log( chalk.bold.yellow( `It looks like you're trying to use TypeScript but do not have the required package(s) installed.` @@ -82,7 +89,8 @@ export async function verifyTypeScriptSetup({ if (err && typeof err === 'object' && 'command' in err) { console.error( `Failed to install required TypeScript dependencies, please install them manually to continue:\n` + - (err as any).command + (err as any).command + + '\n' ) } throw err diff --git a/test/development/typescript-auto-install/index.test.ts b/test/development/typescript-auto-install/index.test.ts index 4f5cca72d8eabb3..ec794f3a4aeb2a0 100644 --- a/test/development/typescript-auto-install/index.test.ts +++ b/test/development/typescript-auto-install/index.test.ts @@ -17,6 +17,16 @@ describe('typescript-auto-install', () => { } `, }, + env: { + // unset CI env as this skips the auto-install behavior + // being tested + CI: '', + CIRCLECI: '', + GITHUB_ACTIONS: '', + CONTINUOUS_INTEGRATION: '', + RUN_ID: '', + BUILD_NUMBER: '', + }, startCommand: 'yarn next dev', installCommand: 'yarn', dependencies: {}, diff --git a/test/production/ci-missing-typescript-deps/index.test.ts b/test/production/ci-missing-typescript-deps/index.test.ts new file mode 100644 index 000000000000000..217f7c524045003 --- /dev/null +++ b/test/production/ci-missing-typescript-deps/index.test.ts @@ -0,0 +1,33 @@ +import { createNext } from 'e2e-utils' + +describe('ci-missing-typescript-deps', () => { + it('should show missing TypeScript dependencies error in CI', async () => { + const next = await createNext({ + files: { + 'pages/index.tsx': ` + export default function Page() { + return

hello world

+ } + `, + }, + env: { + CI: '1', + }, + skipStart: true, + }) + try { + let error + await next.start().catch((err) => { + error = err + }) + + expect(error).toBeDefined() + expect(next.cliOutput).toContain( + `It looks like you're trying to use TypeScript but do not have the required package(s) installed.` + ) + expect(next.cliOutput).toContain(`Please install`) + } finally { + await next.destroy() + } + }) +})