diff --git a/.circleci/config.yml b/.circleci/config.yml index a76ddba69a..73b36c9e10 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -325,7 +325,7 @@ jobs: name: Execute E2E Tests command: | if (Test-Path env:CIRCLE_PULL_REQUEST) { - node tests\legacy-cli\run_e2e.js "--glob={tests/basic/**,tests/i18n/extract-ivy*.ts,tests/build/profile.ts,tests/test/test-sourcemap.ts}" --nb-shards=$env:CIRCLE_NODE_TOTAL --shard=$env:CIRCLE_NODE_INDEX + node tests\legacy-cli\run_e2e.js "--glob={tests/basic/**,tests/i18n/extract-ivy*.ts,tests/build/profile.ts,tests/test/test-sourcemap.ts,tests/misc/check-postinstalls.ts}" --nb-shards=$env:CIRCLE_NODE_TOTAL --shard=$env:CIRCLE_NODE_INDEX } else { node tests\legacy-cli\run_e2e.js --nb-shards=$env:CIRCLE_NODE_TOTAL --shard=$env:CIRCLE_NODE_INDEX } diff --git a/goldens/public-api/angular_devkit/schematics/tasks/index.md b/goldens/public-api/angular_devkit/schematics/tasks/index.md index 8cdf3b28a0..8b25367a82 100644 --- a/goldens/public-api/angular_devkit/schematics/tasks/index.md +++ b/goldens/public-api/angular_devkit/schematics/tasks/index.md @@ -11,6 +11,8 @@ export class NodePackageInstallTask implements TaskConfigurationGenerator { quiet = true; hideOutput = true; + allowScripts = false; workingDirectory?: string; packageManager?: string; packageName?: string; @@ -45,6 +47,9 @@ export class NodePackageInstallTask implements TaskConfigurationGenerator { + return (tree, context) => { + tree.create('/install-test/package.json', JSON.stringify({ + name: 'install-test', + version: '0.0.0', + scripts: { + postinstall: `node run-post.js`, + } + })); + tree.create('/install-test/.npmrc', `ignore-scripts=${ignoreScripts}`); + tree.create('/install-test/run-post.js', 'require("fs").writeFileSync(__dirname + "/post-script-ran", "12345");') + context.addTask(new tasks.NodePackageInstallTask({ workingDirectory: 'install-test', allowScripts })); + }; +}; diff --git a/tests/legacy-cli/e2e/assets/schematic-allow-scripts/package.json b/tests/legacy-cli/e2e/assets/schematic-allow-scripts/package.json new file mode 100644 index 0000000000..ebb363159d --- /dev/null +++ b/tests/legacy-cli/e2e/assets/schematic-allow-scripts/package.json @@ -0,0 +1,5 @@ +{ + "name": "allow-scripts", + "version": "0.0.1", + "schematics": "./collection.json" +} diff --git a/tests/legacy-cli/e2e/assets/schematic-allow-scripts/schema.json b/tests/legacy-cli/e2e/assets/schematic-allow-scripts/schema.json new file mode 100644 index 0000000000..099432e406 --- /dev/null +++ b/tests/legacy-cli/e2e/assets/schematic-allow-scripts/schema.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "type": "object", + "additionalProperties": false, + "properties": { + "allowScripts": { + "type": "boolean" + }, + "ignoreScripts": { + "type": "boolean" + } + } +} + \ No newline at end of file diff --git a/tests/legacy-cli/e2e/tests/generate/install-allow-scripts.ts b/tests/legacy-cli/e2e/tests/generate/install-allow-scripts.ts new file mode 100644 index 0000000000..f41a49935d --- /dev/null +++ b/tests/legacy-cli/e2e/tests/generate/install-allow-scripts.ts @@ -0,0 +1,31 @@ +import { copyAssets } from '../../utils/assets'; +import { expectFileNotToExist, expectFileToExist, rimraf } from '../../utils/fs'; +import { ng } from '../../utils/process'; + +export default async function () { + // Copy test schematic into test project to ensure schematic dependencies are available + await copyAssets('schematic-allow-scripts', 'schematic-allow-scripts'); + + // By default should not run the postinstall from the added package.json in the schematic + await ng('generate', './schematic-allow-scripts:test'); + await expectFileToExist('install-test/package.json'); + await expectFileNotToExist('install-test/post-script-ran'); + + // Cleanup for next test case + await rimraf('install-test'); + + // Should run the postinstall if the allowScripts task option is enabled + // For testing purposes, this schematic exposes the task option via a schematic option + await ng('generate', './schematic-allow-scripts:test', '--allow-scripts'); + await expectFileToExist('install-test/package.json'); + await expectFileToExist('install-test/post-script-ran'); + + // Cleanup for next test case + await rimraf('install-test'); + + // Package manager configuration should take priority + // The `ignoreScripts` schematic option sets the value of the `ignore-scripts` option in a test project `.npmrc` + await ng('generate', './schematic-allow-scripts:test', '--allow-scripts', '--ignore-scripts'); + await expectFileToExist('install-test/package.json'); + await expectFileNotToExist('install-test/post-script-ran'); +} diff --git a/tests/legacy-cli/e2e/tests/misc/check-postinstalls.ts b/tests/legacy-cli/e2e/tests/misc/check-postinstalls.ts new file mode 100644 index 0000000000..18fefcc907 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/misc/check-postinstalls.ts @@ -0,0 +1,58 @@ +import glob from 'glob'; +import { promisify } from 'util'; +import { readFile } from '../../utils/fs'; + +const globAsync = promisify(glob); + +const CURRENT_SCRIPT_PACKAGES: ReadonlySet = new Set([ + 'esbuild (postinstall)', + 'nice-napi (install)', +]); + +const POTENTIAL_SCRIPTS: ReadonlyArray = ['preinstall', 'install', 'postinstall']; + +// Some packages include test and/or example code that causes false positives +const FALSE_POSITIVE_PATHS: ReadonlySet = new Set([ + 'node_modules/jasmine-spec-reporter/examples/protractor/package.json', + 'node_modules/resolve/test/resolver/multirepo/package.json', +]); + +export default async function () { + const manifestPaths = await globAsync('node_modules/**/package.json'); + const newPackages: string[] = []; + + for (const manifestPath of manifestPaths) { + if (FALSE_POSITIVE_PATHS.has(manifestPath)) { + continue; + } + + let manifest; + try { + manifest = JSON.parse(await readFile(manifestPath)); + } catch { + continue; + } + + if (!manifest.scripts) { + continue; + } + + for (const script of POTENTIAL_SCRIPTS) { + if (!manifest.scripts[script]) { + continue; + } + + const packageScript = `${manifest.name} (${script})`; + + if (!CURRENT_SCRIPT_PACKAGES.has(packageScript)) { + newPackages.push(packageScript + `[${manifestPath}]`); + } + } + } + + if (newPackages.length) { + throw new Error( + 'New install script package(s) detected:\n' + JSON.stringify(newPackages, null, 2), + ); + } +}