From 6d757f18450207d5d0d08c90dad0c52741590e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Wed, 14 May 2025 11:17:41 +0100 Subject: [PATCH 01/18] feat: publish `netlify` package --- .github/workflows/release-please.yml | 13 ++++++- scripts/netlifyPackage.js | 54 ++++++++++++++++++++++++++++ src/index.ts | 4 +++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 scripts/netlifyPackage.js create mode 100644 src/index.ts diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index e8ebfae270d..35f7b27338e 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -54,7 +54,18 @@ jobs: - name: Run E2E tests run: npm run test:e2e - - name: Publish package + - name: Publish netlify-cli package run: npm publish --provenance env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + + - name: Prepare netlify package + run: node scripts/netlifyPackage.js prepare + + - name: Publish netlify package + run: npm publish --provenance + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + + - name: Verify netlify package + run: node scripts/netlifyPackage.js verify diff --git a/scripts/netlifyPackage.js b/scripts/netlifyPackage.js new file mode 100644 index 00000000000..784eb53c7b7 --- /dev/null +++ b/scripts/netlifyPackage.js @@ -0,0 +1,54 @@ +// @ts-check +import assert from 'node:assert' +import { basename, dirname, resolve } from 'node:path' +import { argv } from 'node:process' +import { fileURLToPath } from 'node:url' +import { readFile, writeFile } from 'node:fs/promises' + +import execa from 'execa' + +const packageJSON = await getPackageJSON() + +async function getPackageJSON() { + const packageJSONPath = resolve(fileURLToPath(import.meta.url), '../../package.json') + const contents = JSON.parse(await readFile(packageJSONPath, 'utf8')) + + return { + contents, + path: packageJSONPath, + } +} + +/** + * @type {Record} + */ +const commands = { + prepare: async () => { + const newPackageJSON = { + ...packageJSON.contents, + main: './dist/index.js', + name: 'netlify', + } + + console.log(`Writing updated package.json to ${packageJSON.path}...`) + await writeFile(packageJSON.path, `${JSON.stringify(newPackageJSON, null, 2)}\n`) + + console.log('Re-installing dependencies to update lockfile...') + await execa('npm', ['install'], { + cwd: dirname(packageJSON.path), + }) + }, + + verify: async () => { + const { stdout } = await execa('npx', ['-y', 'netlify', '--version']) + const version = stdout.match(/netlify-cli\/(\d+\.\d+\.\d+)/) + + assert.equal(version, packageJSON.contents.version, 'Version installed via npx matches latest version') + }, +} + +if (typeof commands[argv[2]] === 'function') { + commands[argv[2]]() +} else { + console.error(`Usage: node ${basename(argv[1])} (available commands: ${Object.keys(commands).join(', ')})`) +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000000..3c82e582d24 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,4 @@ +// This is an entrypoint that mirrors the interface that the `netlify` package +// used to have, before it was renamed to `@netlify/api`. We keep it for +// backwards-compatibility. +export { NetlifyAPI, methods } from '@netlify/api' From a56363a27a3f0fbcb4e10204d0edd23423cd99a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Wed, 14 May 2025 11:29:47 +0100 Subject: [PATCH 02/18] fix: await command --- scripts/netlifyPackage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/netlifyPackage.js b/scripts/netlifyPackage.js index 784eb53c7b7..30afd3a6884 100644 --- a/scripts/netlifyPackage.js +++ b/scripts/netlifyPackage.js @@ -48,7 +48,7 @@ const commands = { } if (typeof commands[argv[2]] === 'function') { - commands[argv[2]]() + await commands[argv[2]]() } else { console.error(`Usage: node ${basename(argv[1])} (available commands: ${Object.keys(commands).join(', ')})`) } From 70dacdbc0b10ba69f38eabe8686c81f134343a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 15 May 2025 09:49:41 +0100 Subject: [PATCH 03/18] refactor: update publish flow --- scripts/netlifyPackage.js | 77 +++++++++++++++++++++------------------ scripts/prepublishOnly.js | 22 ++++------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/scripts/netlifyPackage.js b/scripts/netlifyPackage.js index 30afd3a6884..356039e252b 100644 --- a/scripts/netlifyPackage.js +++ b/scripts/netlifyPackage.js @@ -1,16 +1,34 @@ // @ts-check -import assert from 'node:assert' -import { basename, dirname, resolve } from 'node:path' -import { argv } from 'node:process' +import { dirname, resolve } from 'node:path' +import process from 'node:process' import { fileURLToPath } from 'node:url' -import { readFile, writeFile } from 'node:fs/promises' +import { readFile, stat,writeFile } from 'node:fs/promises' import execa from 'execa' +/** + * @import {Package} from "normalize-package-data" + */ + +/** + * Logs an error message and exits with status 1. + * + * @param {string} message + */ +function errorAndExit(message) { + console.error(message) + + process.exit(1) +} + const packageJSON = await getPackageJSON() async function getPackageJSON() { const packageJSONPath = resolve(fileURLToPath(import.meta.url), '../../package.json') + + /** + * @type {Package} + */ const contents = JSON.parse(await readFile(packageJSONPath, 'utf8')) return { @@ -19,36 +37,25 @@ async function getPackageJSON() { } } -/** - * @type {Record} - */ -const commands = { - prepare: async () => { - const newPackageJSON = { - ...packageJSON.contents, - main: './dist/index.js', - name: 'netlify', - } - - console.log(`Writing updated package.json to ${packageJSON.path}...`) - await writeFile(packageJSON.path, `${JSON.stringify(newPackageJSON, null, 2)}\n`) - - console.log('Re-installing dependencies to update lockfile...') - await execa('npm', ['install'], { - cwd: dirname(packageJSON.path), - }) - }, - - verify: async () => { - const { stdout } = await execa('npx', ['-y', 'netlify', '--version']) - const version = stdout.match(/netlify-cli\/(\d+\.\d+\.\d+)/) - - assert.equal(version, packageJSON.contents.version, 'Version installed via npx matches latest version') - }, -} +async function preparePackageJSON() { + const newPackageJSON = { + ...packageJSON.contents, + main: './dist/index.js', + name: 'netlify', + } -if (typeof commands[argv[2]] === 'function') { - await commands[argv[2]]() -} else { - console.error(`Usage: node ${basename(argv[1])} (available commands: ${Object.keys(commands).join(', ')})`) + const shrinkwrap = await stat(resolve(packageJSON.path, "../npm-shrinkwrap.json")) + if (!shrinkwrap.isFile()) { + errorAndExit('Failed to find npm-shrinkwrap.json file. Did you run the pre-publish script?') + } + + console.log(`Writing updated package.json to ${packageJSON.path}...`) + await writeFile(packageJSON.path, `${JSON.stringify(newPackageJSON, null, 2)}\n`) + + console.log('Regenerating shrinkwrap file with updated package name...') + await execa('npm', ['shrinkwrap'], { + cwd: dirname(packageJSON.path), + }) } + +await preparePackageJSON() diff --git a/scripts/prepublishOnly.js b/scripts/prepublishOnly.js index 03f396a7d01..a397a7de118 100644 --- a/scripts/prepublishOnly.js +++ b/scripts/prepublishOnly.js @@ -14,22 +14,16 @@ const main = async () => { const packageJSONPath = path.join(process.cwd(), 'package.json') const rawPackageJSON = await fs.readFile(packageJSONPath, 'utf8') - try { - // Remove dev dependencies from the package.json... - const packageJSON = JSON.parse(rawPackageJSON) - Reflect.deleteProperty(packageJSON, 'devDependencies') - await fs.writeFile(packageJSONPath, JSON.stringify(packageJSON, null, 2)) + // Remove dev dependencies from the package.json... + const packageJSON = JSON.parse(rawPackageJSON) + Reflect.deleteProperty(packageJSON, 'devDependencies') + await fs.writeFile(packageJSONPath, JSON.stringify(packageJSON, null, 2)) - // Prune out dev dependencies (this updates the `package-lock.json` lockfile) - cp.spawnSync('npm', ['prune'], { stdio: 'inherit' }) + // Prune out dev dependencies (this updates the `package-lock.json` lockfile) + cp.spawnSync('npm', ['prune'], { stdio: 'inherit' }) - // Convert `package-lock.json` lockfile to `npm-shrinkwrap.json` - cp.spawnSync('npm', ['shrinkwrap'], { stdio: 'inherit' }) - } finally { - // Restore the original `package.json`. (This makes no functional difference in a publishing - // environment, it's purely to minimize how destructive this script is.) - await fs.writeFile(packageJSONPath, rawPackageJSON) - } + // Convert `package-lock.json` lockfile to `npm-shrinkwrap.json` + cp.spawnSync('npm', ['shrinkwrap'], { stdio: 'inherit' }) } await main() From 0bbc22dd1b3d398d66303a65003ed23970ce5c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 15 May 2025 10:58:59 +0100 Subject: [PATCH 04/18] chore: add E2E test --- e2e/install.e2e.ts | 70 +++++++++++++++++++++++++++++++-------- scripts/netlifyPackage.js | 28 ++++++++-------- 2 files changed, 69 insertions(+), 29 deletions(-) diff --git a/e2e/install.e2e.ts b/e2e/install.e2e.ts index 67e3f4f6c90..c0a6a933eed 100644 --- a/e2e/install.e2e.ts +++ b/e2e/install.e2e.ts @@ -70,6 +70,10 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri access: '$all', publish: '$all', }, + netlify: { + access: '$all', + publish: '$all', + }, '**': { access: '$all', publish: 'noone', @@ -116,6 +120,14 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri cwd: publishWorkspace, stdio: debug.enabled ? 'inherit' : 'ignore', }) + + // Publishing `netlify` package + await execa('node', [path.resolve(projectRoot, 'scripts/netlifyPackage.js'), publishWorkspace]) + await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { + cwd: publishWorkspace, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + await fs.rm(publishWorkspace, { force: true, recursive: true }) const testWorkspace = await fs.mkdtemp(path.join(os.tmpdir(), tempdirPrefix)) @@ -135,10 +147,15 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri }, }) -const tests: [packageManager: string, config: { install: [cmd: string, args: string[]]; lockfile: string }][] = [ +type Test = { packageName: string } +type InstallTest = Test & { install: [cmd: string, args: string[]]; lockfile: string } +type RunTest = Test & { run: [cmd: string, args: string[]] } + +const tests: [packageManager: string, config: InstallTest | RunTest][] = [ [ 'npm', { + packageName: 'netlify-cli', install: ['npm', ['install', 'netlify-cli@testing']], lockfile: 'package-lock.json', }, @@ -146,6 +163,7 @@ const tests: [packageManager: string, config: { install: [cmd: string, args: str [ 'pnpm', { + packageName: 'netlify-cli', install: ['pnpm', ['add', 'netlify-cli@testing']], lockfile: 'pnpm-lock.yaml', }, @@ -153,34 +171,58 @@ const tests: [packageManager: string, config: { install: [cmd: string, args: str [ 'yarn', { + packageName: 'netlify-cli', install: ['yarn', ['add', 'netlify-cli@testing']], lockfile: 'yarn.lock', }, ], + [ + 'npx', + { + packageName: 'netlify', + run: ['npx', ['-y', 'netlify@testing']], + }, + ], ] describe.each(tests)('%s → installs the cli and runs the help command without error', (_, config) => { itWithMockNpmRegistry('installs the cli and runs the help command without error', async ({ registry }) => { const cwd = registry.cwd - await execa(...config.install, { - cwd, - env: { npm_config_registry: registry.address }, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) - expect( - existsSync(path.join(cwd, config.lockfile)), - `Generated lock file ${config.lockfile} does not exist in ${cwd}`, - ).toBe(true) + let stdout: string + + if ('install' in config) { + await execa(...config.install, { + cwd, + env: { npm_config_registry: registry.address }, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) - const binary = path.resolve(path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`)) - const { stdout } = await execa(binary, ['help'], { cwd }) + expect( + existsSync(path.join(cwd, config.lockfile)), + `Generated lock file ${config.lockfile} does not exist in ${cwd}`, + ).toBe(true) + + const binary = path.resolve(path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`)) + const result = await execa(binary, ['help'], { cwd }) + + stdout = result.stdout + } else { + const [cmd, args] = config.run + const result = await execa(cmd, args, { + env: { + npm_config_registry: registry.address, + }, + }) + + stdout = result.stdout + } expect(stdout.trim(), `Help command does not start with '⬥ Netlify CLI'\\n\\nVERSION: ${stdout}`).toMatch( /^⬥ Netlify CLI\n\nVERSION/, ) - expect(stdout, `Help command does not include 'netlify-cli/${pkg.version}':\n\n${stdout}`).toContain( - `netlify-cli/${pkg.version}`, + expect(stdout, `Help command does not include '${config.packageName}/${pkg.version}':\n\n${stdout}`).toContain( + `${config.packageName}/${pkg.version}`, ) expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]') }) diff --git a/scripts/netlifyPackage.js b/scripts/netlifyPackage.js index 356039e252b..014da442623 100644 --- a/scripts/netlifyPackage.js +++ b/scripts/netlifyPackage.js @@ -2,7 +2,7 @@ import { dirname, resolve } from 'node:path' import process from 'node:process' import { fileURLToPath } from 'node:url' -import { readFile, stat,writeFile } from 'node:fs/promises' +import { readFile, stat, writeFile } from 'node:fs/promises' import execa from 'execa' @@ -10,21 +10,11 @@ import execa from 'execa' * @import {Package} from "normalize-package-data" */ -/** - * Logs an error message and exits with status 1. - * - * @param {string} message - */ -function errorAndExit(message) { - console.error(message) - - process.exit(1) -} - +const packagePath = process.argv[2] ?? resolve(fileURLToPath(import.meta.url), '../..') const packageJSON = await getPackageJSON() async function getPackageJSON() { - const packageJSONPath = resolve(fileURLToPath(import.meta.url), '../../package.json') + const packageJSONPath = resolve(packagePath, 'package.json') /** * @type {Package} @@ -42,11 +32,19 @@ async function preparePackageJSON() { ...packageJSON.contents, main: './dist/index.js', name: 'netlify', + scripts: { + ...packageJSON.contents.scripts, + + // We don't need the pre-publish script because we expect the work in + // there to be done when publishing the `netlify-cli` package. We'll + // ensure this is the case by throwing if a shrinkwrap file isn't found. + prepublishOnly: undefined, + }, } - const shrinkwrap = await stat(resolve(packageJSON.path, "../npm-shrinkwrap.json")) + const shrinkwrap = await stat(resolve(packageJSON.path, '../npm-shrinkwrap.json')) if (!shrinkwrap.isFile()) { - errorAndExit('Failed to find npm-shrinkwrap.json file. Did you run the pre-publish script?') + throw new Error('Failed to find npm-shrinkwrap.json file. Did you run the pre-publish script?') } console.log(`Writing updated package.json to ${packageJSON.path}...`) From 9e5e4a14985ef783af816e42c73d8b5f4db5019f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 15 May 2025 11:43:51 +0100 Subject: [PATCH 05/18] chore: run script in cwd --- e2e/install.e2e.ts | 5 ++++- scripts/netlifyPackage.js | 13 +++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/e2e/install.e2e.ts b/e2e/install.e2e.ts index c0a6a933eed..418f6a5dae0 100644 --- a/e2e/install.e2e.ts +++ b/e2e/install.e2e.ts @@ -122,7 +122,10 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri }) // Publishing `netlify` package - await execa('node', [path.resolve(projectRoot, 'scripts/netlifyPackage.js'), publishWorkspace]) + await execa('node', [path.resolve(projectRoot, 'scripts/netlifyPackage.js')], { + cwd: publishWorkspace, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { cwd: publishWorkspace, stdio: debug.enabled ? 'inherit' : 'ignore', diff --git a/scripts/netlifyPackage.js b/scripts/netlifyPackage.js index 014da442623..11789d88c29 100644 --- a/scripts/netlifyPackage.js +++ b/scripts/netlifyPackage.js @@ -1,7 +1,6 @@ // @ts-check +import assert from 'node:assert' import { dirname, resolve } from 'node:path' -import process from 'node:process' -import { fileURLToPath } from 'node:url' import { readFile, stat, writeFile } from 'node:fs/promises' import execa from 'execa' @@ -10,11 +9,10 @@ import execa from 'execa' * @import {Package} from "normalize-package-data" */ -const packagePath = process.argv[2] ?? resolve(fileURLToPath(import.meta.url), '../..') const packageJSON = await getPackageJSON() async function getPackageJSON() { - const packageJSONPath = resolve(packagePath, 'package.json') + const packageJSONPath = resolve('package.json') /** * @type {Package} @@ -42,8 +40,11 @@ async function preparePackageJSON() { }, } - const shrinkwrap = await stat(resolve(packageJSON.path, '../npm-shrinkwrap.json')) - if (!shrinkwrap.isFile()) { + try { + const shrinkwrap = await stat(resolve(packageJSON.path, '../npm-shrinkwrap.json')) + + assert.ok(shrinkwrap.isFile()) + } catch { throw new Error('Failed to find npm-shrinkwrap.json file. Did you run the pre-publish script?') } From fa878fcca94e2f60bf6cc9cfd83754b50c698c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 15 May 2025 15:58:05 +0100 Subject: [PATCH 06/18] chore: debug CI --- scripts/netlifyPackage.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/netlifyPackage.js b/scripts/netlifyPackage.js index 11789d88c29..cae98bef972 100644 --- a/scripts/netlifyPackage.js +++ b/scripts/netlifyPackage.js @@ -1,7 +1,7 @@ // @ts-check import assert from 'node:assert' import { dirname, resolve } from 'node:path' -import { readFile, stat, writeFile } from 'node:fs/promises' +import { readdir, readFile, stat, writeFile } from 'node:fs/promises' import execa from 'execa' @@ -26,6 +26,11 @@ async function getPackageJSON() { } async function preparePackageJSON() { + const binPath = Object.values(packageJSON.contents.bin ?? {})[0] + if (!binPath) { + throw new Error('Did not find a non-empty binary entry in `package.json`, so the `npx` flow will not work.') + } + const newPackageJSON = { ...packageJSON.contents, main: './dist/index.js', @@ -38,6 +43,9 @@ async function preparePackageJSON() { // ensure this is the case by throwing if a shrinkwrap file isn't found. prepublishOnly: undefined, }, + bin: { + 'npx-netlify': binPath, + }, } try { @@ -45,6 +53,7 @@ async function preparePackageJSON() { assert.ok(shrinkwrap.isFile()) } catch { + console.log('Files:', await readdir(dirname(packageJSON.path))) throw new Error('Failed to find npm-shrinkwrap.json file. Did you run the pre-publish script?') } From 01b4055777657be45dd75b159f5acf13bfe756bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 15 May 2025 16:39:39 +0100 Subject: [PATCH 07/18] chore: use `execa.node` --- e2e/install.e2e.ts | 2 +- scripts/netlifyPackage.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/e2e/install.e2e.ts b/e2e/install.e2e.ts index 418f6a5dae0..e6191adbfe0 100644 --- a/e2e/install.e2e.ts +++ b/e2e/install.e2e.ts @@ -122,7 +122,7 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri }) // Publishing `netlify` package - await execa('node', [path.resolve(projectRoot, 'scripts/netlifyPackage.js')], { + await execa.node(path.resolve(projectRoot, 'scripts/netlifyPackage.js'), { cwd: publishWorkspace, stdio: debug.enabled ? 'inherit' : 'ignore', }) diff --git a/scripts/netlifyPackage.js b/scripts/netlifyPackage.js index cae98bef972..0015523e1f6 100644 --- a/scripts/netlifyPackage.js +++ b/scripts/netlifyPackage.js @@ -1,7 +1,7 @@ // @ts-check import assert from 'node:assert' import { dirname, resolve } from 'node:path' -import { readdir, readFile, stat, writeFile } from 'node:fs/promises' +import { readFile, stat, writeFile } from 'node:fs/promises' import execa from 'execa' @@ -53,7 +53,6 @@ async function preparePackageJSON() { assert.ok(shrinkwrap.isFile()) } catch { - console.log('Files:', await readdir(dirname(packageJSON.path))) throw new Error('Failed to find npm-shrinkwrap.json file. Did you run the pre-publish script?') } From 5822c84b995db08e7f7dec2fa80857447b2e1d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 15 May 2025 16:45:14 +0100 Subject: [PATCH 08/18] chore: update release flow --- .github/workflows/release-please.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 35f7b27338e..f6549c64ed8 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -60,12 +60,9 @@ jobs: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} - name: Prepare netlify package - run: node scripts/netlifyPackage.js prepare + run: node scripts/netlifyPackage.js - name: Publish netlify package run: npm publish --provenance env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} - - - name: Verify netlify package - run: node scripts/netlifyPackage.js verify From 92e07553dd9348e37c158529e36e71f7ceb64071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 15 May 2025 16:53:53 +0100 Subject: [PATCH 09/18] chore: enable debugging --- e2e/install.e2e.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/e2e/install.e2e.ts b/e2e/install.e2e.ts index e6191adbfe0..0a621ed0023 100644 --- a/e2e/install.e2e.ts +++ b/e2e/install.e2e.ts @@ -124,7 +124,8 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri // Publishing `netlify` package await execa.node(path.resolve(projectRoot, 'scripts/netlifyPackage.js'), { cwd: publishWorkspace, - stdio: debug.enabled ? 'inherit' : 'ignore', + // stdio: debug.enabled ? 'inherit' : 'ignore', + stdio: 'inherit', }) await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { cwd: publishWorkspace, From 7b3d3d9a7bec44f41868219b76b87ad0d70d6799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 15 May 2025 17:09:53 +0100 Subject: [PATCH 10/18] chore: debug --- scripts/netlifyPackage.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/netlifyPackage.js b/scripts/netlifyPackage.js index 0015523e1f6..f7c55fc564c 100644 --- a/scripts/netlifyPackage.js +++ b/scripts/netlifyPackage.js @@ -1,7 +1,7 @@ // @ts-check import assert from 'node:assert' import { dirname, resolve } from 'node:path' -import { readFile, stat, writeFile } from 'node:fs/promises' +import { readdir, readFile, stat, writeFile } from 'node:fs/promises' import execa from 'execa' @@ -48,11 +48,14 @@ async function preparePackageJSON() { }, } + // TODO: Figure out why this step is failing on Windows. try { const shrinkwrap = await stat(resolve(packageJSON.path, '../npm-shrinkwrap.json')) assert.ok(shrinkwrap.isFile()) } catch { + console.log('-> FILES:', await readdir(dirname(packageJSON.path))) + throw new Error('Failed to find npm-shrinkwrap.json file. Did you run the pre-publish script?') } From 8c1e8bbb078b721b39fb9bc6dd3b2bdd06fe2f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 15 May 2025 17:18:11 +0100 Subject: [PATCH 11/18] chore: skip check on Windows --- scripts/netlifyPackage.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/scripts/netlifyPackage.js b/scripts/netlifyPackage.js index f7c55fc564c..e295ab1852c 100644 --- a/scripts/netlifyPackage.js +++ b/scripts/netlifyPackage.js @@ -1,7 +1,8 @@ // @ts-check import assert from 'node:assert' +import { platform } from 'node:os' import { dirname, resolve } from 'node:path' -import { readdir, readFile, stat, writeFile } from 'node:fs/promises' +import { readFile, stat, writeFile } from 'node:fs/promises' import execa from 'execa' @@ -48,15 +49,15 @@ async function preparePackageJSON() { }, } - // TODO: Figure out why this step is failing on Windows. - try { - const shrinkwrap = await stat(resolve(packageJSON.path, '../npm-shrinkwrap.json')) + // TODO: Figure out why this file is not being created on Windows. + if (platform() !== 'win32') { + try { + const shrinkwrap = await stat(resolve(packageJSON.path, '../npm-shrinkwrap.json')) - assert.ok(shrinkwrap.isFile()) - } catch { - console.log('-> FILES:', await readdir(dirname(packageJSON.path))) - - throw new Error('Failed to find npm-shrinkwrap.json file. Did you run the pre-publish script?') + assert.ok(shrinkwrap.isFile()) + } catch { + throw new Error('Failed to find npm-shrinkwrap.json file. Did you run the pre-publish script?') + } } console.log(`Writing updated package.json to ${packageJSON.path}...`) From 0490402e7e19b3ee5c4f7439b89912cc14ab53f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Thu, 15 May 2025 18:53:28 +0100 Subject: [PATCH 12/18] chore: remove debug --- e2e/install.e2e.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/e2e/install.e2e.ts b/e2e/install.e2e.ts index 0a621ed0023..e6191adbfe0 100644 --- a/e2e/install.e2e.ts +++ b/e2e/install.e2e.ts @@ -124,8 +124,7 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri // Publishing `netlify` package await execa.node(path.resolve(projectRoot, 'scripts/netlifyPackage.js'), { cwd: publishWorkspace, - // stdio: debug.enabled ? 'inherit' : 'ignore', - stdio: 'inherit', + stdio: debug.enabled ? 'inherit' : 'ignore', }) await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { cwd: publishWorkspace, From c9d4f806430ee5ac5fcffe1a6defaca994f93354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Fri, 16 May 2025 15:14:09 +0100 Subject: [PATCH 13/18] refactor: rename binary --- scripts/netlifyPackage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/netlifyPackage.js b/scripts/netlifyPackage.js index e295ab1852c..ac87a422311 100644 --- a/scripts/netlifyPackage.js +++ b/scripts/netlifyPackage.js @@ -45,7 +45,7 @@ async function preparePackageJSON() { prepublishOnly: undefined, }, bin: { - 'npx-netlify': binPath, + npxnetlify: binPath, }, } From 7a9405d99432e10eb795ec6ddf518af392a123b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Fri, 16 May 2025 16:13:52 +0100 Subject: [PATCH 14/18] chore: skip on Windows --- e2e/install.e2e.ts | 105 +++++++++++++++++++++----------------- scripts/netlifyPackage.js | 14 ++--- 2 files changed, 63 insertions(+), 56 deletions(-) diff --git a/e2e/install.e2e.ts b/e2e/install.e2e.ts index e6191adbfe0..502da96837b 100644 --- a/e2e/install.e2e.ts +++ b/e2e/install.e2e.ts @@ -121,15 +121,18 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri stdio: debug.enabled ? 'inherit' : 'ignore', }) - // Publishing `netlify` package - await execa.node(path.resolve(projectRoot, 'scripts/netlifyPackage.js'), { - cwd: publishWorkspace, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) - await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { - cwd: publishWorkspace, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) + // TODO: Figure out why calling this script is failing on Windows. + if (platform() !== 'win32') { + // Publishing `netlify` package + await execa.node(path.resolve(projectRoot, 'scripts/netlifyPackage.js'), { + cwd: publishWorkspace, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { + cwd: publishWorkspace, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + } await fs.rm(publishWorkspace, { force: true, recursive: true }) @@ -189,44 +192,52 @@ const tests: [packageManager: string, config: InstallTest | RunTest][] = [ ] describe.each(tests)('%s → installs the cli and runs the help command without error', (_, config) => { - itWithMockNpmRegistry('installs the cli and runs the help command without error', async ({ registry }) => { - const cwd = registry.cwd - - let stdout: string - - if ('install' in config) { - await execa(...config.install, { - cwd, - env: { npm_config_registry: registry.address }, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) - - expect( - existsSync(path.join(cwd, config.lockfile)), - `Generated lock file ${config.lockfile} does not exist in ${cwd}`, - ).toBe(true) - - const binary = path.resolve(path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`)) - const result = await execa(binary, ['help'], { cwd }) - - stdout = result.stdout - } else { - const [cmd, args] = config.run - const result = await execa(cmd, args, { - env: { - npm_config_registry: registry.address, - }, - }) + // TODO: Figure out why this flow is failing on Windows. + const npxOnWindows = platform() === 'win32' && 'run' in config + + itWithMockNpmRegistry.skipIf(npxOnWindows)( + 'installs the cli and runs the help command without error', + async ({ registry }) => { + const cwd = registry.cwd + + let stdout: string + + if ('install' in config) { + await execa(...config.install, { + cwd, + env: { npm_config_registry: registry.address }, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + + expect( + existsSync(path.join(cwd, config.lockfile)), + `Generated lock file ${config.lockfile} does not exist in ${cwd}`, + ).toBe(true) + + const binary = path.resolve( + path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`), + ) + const result = await execa(binary, ['help'], { cwd }) + + stdout = result.stdout + } else { + const [cmd, args] = config.run + const result = await execa(cmd, args, { + env: { + npm_config_registry: registry.address, + }, + }) - stdout = result.stdout - } + stdout = result.stdout + } - expect(stdout.trim(), `Help command does not start with '⬥ Netlify CLI'\\n\\nVERSION: ${stdout}`).toMatch( - /^⬥ Netlify CLI\n\nVERSION/, - ) - expect(stdout, `Help command does not include '${config.packageName}/${pkg.version}':\n\n${stdout}`).toContain( - `${config.packageName}/${pkg.version}`, - ) - expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]') - }) + expect(stdout.trim(), `Help command does not start with '⬥ Netlify CLI'\\n\\nVERSION: ${stdout}`).toMatch( + /^⬥ Netlify CLI\n\nVERSION/, + ) + expect(stdout, `Help command does not include '${config.packageName}/${pkg.version}':\n\n${stdout}`).toContain( + `${config.packageName}/${pkg.version}`, + ) + expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]') + }, + ) }) diff --git a/scripts/netlifyPackage.js b/scripts/netlifyPackage.js index ac87a422311..b38cdc86aa4 100644 --- a/scripts/netlifyPackage.js +++ b/scripts/netlifyPackage.js @@ -1,6 +1,5 @@ // @ts-check import assert from 'node:assert' -import { platform } from 'node:os' import { dirname, resolve } from 'node:path' import { readFile, stat, writeFile } from 'node:fs/promises' @@ -49,15 +48,12 @@ async function preparePackageJSON() { }, } - // TODO: Figure out why this file is not being created on Windows. - if (platform() !== 'win32') { - try { - const shrinkwrap = await stat(resolve(packageJSON.path, '../npm-shrinkwrap.json')) + try { + const shrinkwrap = await stat(resolve(packageJSON.path, '../npm-shrinkwrap.json')) - assert.ok(shrinkwrap.isFile()) - } catch { - throw new Error('Failed to find npm-shrinkwrap.json file. Did you run the pre-publish script?') - } + assert.ok(shrinkwrap.isFile()) + } catch { + throw new Error('Failed to find npm-shrinkwrap.json file. Did you run the pre-publish script?') } console.log(`Writing updated package.json to ${packageJSON.path}...`) From fda2b6cf158d6106d0189145a81cbaf0bcb708b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Fri, 16 May 2025 17:32:13 +0100 Subject: [PATCH 15/18] chore: increase timeout --- e2e/install.e2e.ts | 105 +++++++++++++++++++------------------------ vitest.e2e.config.ts | 2 +- 2 files changed, 48 insertions(+), 59 deletions(-) diff --git a/e2e/install.e2e.ts b/e2e/install.e2e.ts index 502da96837b..e6191adbfe0 100644 --- a/e2e/install.e2e.ts +++ b/e2e/install.e2e.ts @@ -121,18 +121,15 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri stdio: debug.enabled ? 'inherit' : 'ignore', }) - // TODO: Figure out why calling this script is failing on Windows. - if (platform() !== 'win32') { - // Publishing `netlify` package - await execa.node(path.resolve(projectRoot, 'scripts/netlifyPackage.js'), { - cwd: publishWorkspace, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) - await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { - cwd: publishWorkspace, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) - } + // Publishing `netlify` package + await execa.node(path.resolve(projectRoot, 'scripts/netlifyPackage.js'), { + cwd: publishWorkspace, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { + cwd: publishWorkspace, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) await fs.rm(publishWorkspace, { force: true, recursive: true }) @@ -192,52 +189,44 @@ const tests: [packageManager: string, config: InstallTest | RunTest][] = [ ] describe.each(tests)('%s → installs the cli and runs the help command without error', (_, config) => { - // TODO: Figure out why this flow is failing on Windows. - const npxOnWindows = platform() === 'win32' && 'run' in config - - itWithMockNpmRegistry.skipIf(npxOnWindows)( - 'installs the cli and runs the help command without error', - async ({ registry }) => { - const cwd = registry.cwd - - let stdout: string - - if ('install' in config) { - await execa(...config.install, { - cwd, - env: { npm_config_registry: registry.address }, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) - - expect( - existsSync(path.join(cwd, config.lockfile)), - `Generated lock file ${config.lockfile} does not exist in ${cwd}`, - ).toBe(true) - - const binary = path.resolve( - path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`), - ) - const result = await execa(binary, ['help'], { cwd }) - - stdout = result.stdout - } else { - const [cmd, args] = config.run - const result = await execa(cmd, args, { - env: { - npm_config_registry: registry.address, - }, - }) + itWithMockNpmRegistry('installs the cli and runs the help command without error', async ({ registry }) => { + const cwd = registry.cwd - stdout = result.stdout - } + let stdout: string - expect(stdout.trim(), `Help command does not start with '⬥ Netlify CLI'\\n\\nVERSION: ${stdout}`).toMatch( - /^⬥ Netlify CLI\n\nVERSION/, - ) - expect(stdout, `Help command does not include '${config.packageName}/${pkg.version}':\n\n${stdout}`).toContain( - `${config.packageName}/${pkg.version}`, - ) - expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]') - }, - ) + if ('install' in config) { + await execa(...config.install, { + cwd, + env: { npm_config_registry: registry.address }, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + + expect( + existsSync(path.join(cwd, config.lockfile)), + `Generated lock file ${config.lockfile} does not exist in ${cwd}`, + ).toBe(true) + + const binary = path.resolve(path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`)) + const result = await execa(binary, ['help'], { cwd }) + + stdout = result.stdout + } else { + const [cmd, args] = config.run + const result = await execa(cmd, args, { + env: { + npm_config_registry: registry.address, + }, + }) + + stdout = result.stdout + } + + expect(stdout.trim(), `Help command does not start with '⬥ Netlify CLI'\\n\\nVERSION: ${stdout}`).toMatch( + /^⬥ Netlify CLI\n\nVERSION/, + ) + expect(stdout, `Help command does not include '${config.packageName}/${pkg.version}':\n\n${stdout}`).toContain( + `${config.packageName}/${pkg.version}`, + ) + expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]') + }) }) diff --git a/vitest.e2e.config.ts b/vitest.e2e.config.ts index c11f9428a12..d70d20380f0 100644 --- a/vitest.e2e.config.ts +++ b/vitest.e2e.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { include: ['e2e/**/*.e2e.[jt]s'], - testTimeout: 600_000, + testTimeout: 1200_000, // Pin to vitest@1 behavior: https://vitest.dev/guide/migration.html#default-pool-is-forks. // TODO(serhalp) Remove this and fix flaky hanging e2e tests on Windows. pool: 'threads', From 06be0e1ff798032af85adb8b62a49a93b5b48a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Sat, 17 May 2025 00:55:13 +0100 Subject: [PATCH 16/18] chore: skip on Windows --- e2e/install.e2e.ts | 105 +++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/e2e/install.e2e.ts b/e2e/install.e2e.ts index e6191adbfe0..502da96837b 100644 --- a/e2e/install.e2e.ts +++ b/e2e/install.e2e.ts @@ -121,15 +121,18 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri stdio: debug.enabled ? 'inherit' : 'ignore', }) - // Publishing `netlify` package - await execa.node(path.resolve(projectRoot, 'scripts/netlifyPackage.js'), { - cwd: publishWorkspace, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) - await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { - cwd: publishWorkspace, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) + // TODO: Figure out why calling this script is failing on Windows. + if (platform() !== 'win32') { + // Publishing `netlify` package + await execa.node(path.resolve(projectRoot, 'scripts/netlifyPackage.js'), { + cwd: publishWorkspace, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { + cwd: publishWorkspace, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + } await fs.rm(publishWorkspace, { force: true, recursive: true }) @@ -189,44 +192,52 @@ const tests: [packageManager: string, config: InstallTest | RunTest][] = [ ] describe.each(tests)('%s → installs the cli and runs the help command without error', (_, config) => { - itWithMockNpmRegistry('installs the cli and runs the help command without error', async ({ registry }) => { - const cwd = registry.cwd - - let stdout: string - - if ('install' in config) { - await execa(...config.install, { - cwd, - env: { npm_config_registry: registry.address }, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) - - expect( - existsSync(path.join(cwd, config.lockfile)), - `Generated lock file ${config.lockfile} does not exist in ${cwd}`, - ).toBe(true) - - const binary = path.resolve(path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`)) - const result = await execa(binary, ['help'], { cwd }) - - stdout = result.stdout - } else { - const [cmd, args] = config.run - const result = await execa(cmd, args, { - env: { - npm_config_registry: registry.address, - }, - }) + // TODO: Figure out why this flow is failing on Windows. + const npxOnWindows = platform() === 'win32' && 'run' in config + + itWithMockNpmRegistry.skipIf(npxOnWindows)( + 'installs the cli and runs the help command without error', + async ({ registry }) => { + const cwd = registry.cwd + + let stdout: string + + if ('install' in config) { + await execa(...config.install, { + cwd, + env: { npm_config_registry: registry.address }, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + + expect( + existsSync(path.join(cwd, config.lockfile)), + `Generated lock file ${config.lockfile} does not exist in ${cwd}`, + ).toBe(true) + + const binary = path.resolve( + path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`), + ) + const result = await execa(binary, ['help'], { cwd }) + + stdout = result.stdout + } else { + const [cmd, args] = config.run + const result = await execa(cmd, args, { + env: { + npm_config_registry: registry.address, + }, + }) - stdout = result.stdout - } + stdout = result.stdout + } - expect(stdout.trim(), `Help command does not start with '⬥ Netlify CLI'\\n\\nVERSION: ${stdout}`).toMatch( - /^⬥ Netlify CLI\n\nVERSION/, - ) - expect(stdout, `Help command does not include '${config.packageName}/${pkg.version}':\n\n${stdout}`).toContain( - `${config.packageName}/${pkg.version}`, - ) - expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]') - }) + expect(stdout.trim(), `Help command does not start with '⬥ Netlify CLI'\\n\\nVERSION: ${stdout}`).toMatch( + /^⬥ Netlify CLI\n\nVERSION/, + ) + expect(stdout, `Help command does not include '${config.packageName}/${pkg.version}':\n\n${stdout}`).toContain( + `${config.packageName}/${pkg.version}`, + ) + expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]') + }, + ) }) From 548eeb9625b5ca3e57497af46d461a1b7a6830a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Sat, 17 May 2025 00:57:08 +0100 Subject: [PATCH 17/18] chore: skip script only --- e2e/install.e2e.ts | 95 +++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 51 deletions(-) diff --git a/e2e/install.e2e.ts b/e2e/install.e2e.ts index 502da96837b..3f8689a7e63 100644 --- a/e2e/install.e2e.ts +++ b/e2e/install.e2e.ts @@ -123,17 +123,18 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri // TODO: Figure out why calling this script is failing on Windows. if (platform() !== 'win32') { - // Publishing `netlify` package await execa.node(path.resolve(projectRoot, 'scripts/netlifyPackage.js'), { cwd: publishWorkspace, stdio: debug.enabled ? 'inherit' : 'ignore', }) - await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { - cwd: publishWorkspace, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) } + // Publishing `netlify` package + await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { + cwd: publishWorkspace, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + await fs.rm(publishWorkspace, { force: true, recursive: true }) const testWorkspace = await fs.mkdtemp(path.join(os.tmpdir(), tempdirPrefix)) @@ -192,52 +193,44 @@ const tests: [packageManager: string, config: InstallTest | RunTest][] = [ ] describe.each(tests)('%s → installs the cli and runs the help command without error', (_, config) => { - // TODO: Figure out why this flow is failing on Windows. - const npxOnWindows = platform() === 'win32' && 'run' in config - - itWithMockNpmRegistry.skipIf(npxOnWindows)( - 'installs the cli and runs the help command without error', - async ({ registry }) => { - const cwd = registry.cwd - - let stdout: string - - if ('install' in config) { - await execa(...config.install, { - cwd, - env: { npm_config_registry: registry.address }, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) - - expect( - existsSync(path.join(cwd, config.lockfile)), - `Generated lock file ${config.lockfile} does not exist in ${cwd}`, - ).toBe(true) - - const binary = path.resolve( - path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`), - ) - const result = await execa(binary, ['help'], { cwd }) - - stdout = result.stdout - } else { - const [cmd, args] = config.run - const result = await execa(cmd, args, { - env: { - npm_config_registry: registry.address, - }, - }) + itWithMockNpmRegistry('installs the cli and runs the help command without error', async ({ registry }) => { + const cwd = registry.cwd - stdout = result.stdout - } + let stdout: string - expect(stdout.trim(), `Help command does not start with '⬥ Netlify CLI'\\n\\nVERSION: ${stdout}`).toMatch( - /^⬥ Netlify CLI\n\nVERSION/, - ) - expect(stdout, `Help command does not include '${config.packageName}/${pkg.version}':\n\n${stdout}`).toContain( - `${config.packageName}/${pkg.version}`, - ) - expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]') - }, - ) + if ('install' in config) { + await execa(...config.install, { + cwd, + env: { npm_config_registry: registry.address }, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + + expect( + existsSync(path.join(cwd, config.lockfile)), + `Generated lock file ${config.lockfile} does not exist in ${cwd}`, + ).toBe(true) + + const binary = path.resolve(path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`)) + const result = await execa(binary, ['help'], { cwd }) + + stdout = result.stdout + } else { + const [cmd, args] = config.run + const result = await execa(cmd, args, { + env: { + npm_config_registry: registry.address, + }, + }) + + stdout = result.stdout + } + + expect(stdout.trim(), `Help command does not start with '⬥ Netlify CLI'\\n\\nVERSION: ${stdout}`).toMatch( + /^⬥ Netlify CLI\n\nVERSION/, + ) + expect(stdout, `Help command does not include '${config.packageName}/${pkg.version}':\n\n${stdout}`).toContain( + `${config.packageName}/${pkg.version}`, + ) + expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]') + }) }) From 5cdda7b47e923cc305c496a4e0165ac966a0401d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Sat, 17 May 2025 10:10:11 +0100 Subject: [PATCH 18/18] chore: skip on Windows --- e2e/install.e2e.ts | 95 +++++++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/e2e/install.e2e.ts b/e2e/install.e2e.ts index 3f8689a7e63..502da96837b 100644 --- a/e2e/install.e2e.ts +++ b/e2e/install.e2e.ts @@ -123,18 +123,17 @@ const itWithMockNpmRegistry = it.extend<{ registry: { address: string; cwd: stri // TODO: Figure out why calling this script is failing on Windows. if (platform() !== 'win32') { + // Publishing `netlify` package await execa.node(path.resolve(projectRoot, 'scripts/netlifyPackage.js'), { cwd: publishWorkspace, stdio: debug.enabled ? 'inherit' : 'ignore', }) + await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { + cwd: publishWorkspace, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) } - // Publishing `netlify` package - await execa('npm', ['publish', `--registry=${registryURL.toString()}`, '--tag=testing'], { - cwd: publishWorkspace, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) - await fs.rm(publishWorkspace, { force: true, recursive: true }) const testWorkspace = await fs.mkdtemp(path.join(os.tmpdir(), tempdirPrefix)) @@ -193,44 +192,52 @@ const tests: [packageManager: string, config: InstallTest | RunTest][] = [ ] describe.each(tests)('%s → installs the cli and runs the help command without error', (_, config) => { - itWithMockNpmRegistry('installs the cli and runs the help command without error', async ({ registry }) => { - const cwd = registry.cwd - - let stdout: string - - if ('install' in config) { - await execa(...config.install, { - cwd, - env: { npm_config_registry: registry.address }, - stdio: debug.enabled ? 'inherit' : 'ignore', - }) - - expect( - existsSync(path.join(cwd, config.lockfile)), - `Generated lock file ${config.lockfile} does not exist in ${cwd}`, - ).toBe(true) - - const binary = path.resolve(path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`)) - const result = await execa(binary, ['help'], { cwd }) - - stdout = result.stdout - } else { - const [cmd, args] = config.run - const result = await execa(cmd, args, { - env: { - npm_config_registry: registry.address, - }, - }) + // TODO: Figure out why this flow is failing on Windows. + const npxOnWindows = platform() === 'win32' && 'run' in config + + itWithMockNpmRegistry.skipIf(npxOnWindows)( + 'installs the cli and runs the help command without error', + async ({ registry }) => { + const cwd = registry.cwd + + let stdout: string + + if ('install' in config) { + await execa(...config.install, { + cwd, + env: { npm_config_registry: registry.address }, + stdio: debug.enabled ? 'inherit' : 'ignore', + }) + + expect( + existsSync(path.join(cwd, config.lockfile)), + `Generated lock file ${config.lockfile} does not exist in ${cwd}`, + ).toBe(true) + + const binary = path.resolve( + path.join(cwd, `./node_modules/.bin/netlify${platform() === 'win32' ? '.cmd' : ''}`), + ) + const result = await execa(binary, ['help'], { cwd }) + + stdout = result.stdout + } else { + const [cmd, args] = config.run + const result = await execa(cmd, args, { + env: { + npm_config_registry: registry.address, + }, + }) - stdout = result.stdout - } + stdout = result.stdout + } - expect(stdout.trim(), `Help command does not start with '⬥ Netlify CLI'\\n\\nVERSION: ${stdout}`).toMatch( - /^⬥ Netlify CLI\n\nVERSION/, - ) - expect(stdout, `Help command does not include '${config.packageName}/${pkg.version}':\n\n${stdout}`).toContain( - `${config.packageName}/${pkg.version}`, - ) - expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]') - }) + expect(stdout.trim(), `Help command does not start with '⬥ Netlify CLI'\\n\\nVERSION: ${stdout}`).toMatch( + /^⬥ Netlify CLI\n\nVERSION/, + ) + expect(stdout, `Help command does not include '${config.packageName}/${pkg.version}':\n\n${stdout}`).toContain( + `${config.packageName}/${pkg.version}`, + ) + expect(stdout, `Help command does not include '$ netlify [COMMAND]':\n\n${stdout}`).toMatch('$ netlify [COMMAND]') + }, + ) })