mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-06 12:57:46 +00:00
[cli] Switch to npm and add more context to install errors during Builder import (#8545)
Before: <img width="1012" alt="Screen Shot 2022-09-09 at 7 03 01 PM" src="https://user-images.githubusercontent.com/71256/189464732-8cf6398a-9432-423b-8509-a52bd714333e.png"> After: <img width="579" alt="Screen Shot 2022-09-12 at 12 27 31 PM" src="https://user-images.githubusercontent.com/71256/189739091-86399428-d9b8-4d03-b0e6-7d27a1037bce.png"> This is a precursor to https://github.com/vercel/vercel/pull/8485, since the last remaining test failure there is related to specialized messaging we had for these same cases.
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { URL } from 'url';
|
||||
import plural from 'pluralize';
|
||||
import npa from 'npm-package-arg';
|
||||
import { satisfies } from 'semver';
|
||||
import { dirname, join } from 'path';
|
||||
@@ -13,6 +15,9 @@ import { VERCEL_DIR } from '../projects/link';
|
||||
import { Output } from '../output';
|
||||
import readJSONFile from '../read-json-file';
|
||||
import { CantParseJSONFile } from '../errors-ts';
|
||||
import { errorToString, isErrnoException, isError } from '../is-error';
|
||||
import cmd from '../output/cmd';
|
||||
import code from '../output/code';
|
||||
|
||||
export interface BuilderWithPkg {
|
||||
path: string;
|
||||
@@ -201,15 +206,54 @@ async function installBuilders(
|
||||
if (err.code !== 'EEXIST') throw err;
|
||||
}
|
||||
|
||||
output.debug(`Installing Builders: ${Array.from(buildersToAdd).join(', ')}`);
|
||||
await spawnAsync('yarn', ['add', '@vercel/build-utils', ...buildersToAdd], {
|
||||
cwd: buildersDir,
|
||||
});
|
||||
output.log(
|
||||
`Installing ${plural('Builder', buildersToAdd.size)}: ${Array.from(
|
||||
buildersToAdd
|
||||
).join(', ')}`
|
||||
);
|
||||
try {
|
||||
await spawnAsync(
|
||||
'npm',
|
||||
['install', '@vercel/build-utils', ...buildersToAdd],
|
||||
{
|
||||
cwd: buildersDir,
|
||||
stdio: 'pipe',
|
||||
}
|
||||
);
|
||||
} catch (err: unknown) {
|
||||
if (isError(err)) {
|
||||
(err as any).link =
|
||||
'https://vercel.link/builder-dependencies-install-failed';
|
||||
if (isErrnoException(err) && err.code === 'ENOENT') {
|
||||
// `npm` is not installed
|
||||
err.message = `Please install ${cmd('npm')} before continuing`;
|
||||
} else {
|
||||
const message = errorToString(err);
|
||||
const notFound = /GET (.*) - Not found/.exec(message);
|
||||
if (notFound) {
|
||||
const url = new URL(notFound[1]);
|
||||
const packageName = decodeURIComponent(url.pathname.slice(1));
|
||||
err.message = `The package ${code(
|
||||
packageName
|
||||
)} is not published on the npm registry`;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Symlink `@now/build-utils` -> `@vercel/build-utils` to support legacy Builders
|
||||
const nowScopePath = join(buildersDir, 'node_modules/@now');
|
||||
await mkdirp(nowScopePath);
|
||||
await symlink('../@vercel/build-utils', join(nowScopePath, 'build-utils'));
|
||||
|
||||
try {
|
||||
await symlink('../@vercel/build-utils', join(nowScopePath, 'build-utils'));
|
||||
} catch (err: unknown) {
|
||||
if (!isErrnoException(err) || err.code !== 'EEXIST') {
|
||||
// Throw unless the error is due to the symlink already existing
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Cross-reference any builderSpecs from the saved `package.json` file,
|
||||
// in case they were installed from a URL
|
||||
|
||||
@@ -37,20 +37,56 @@ describe('importBuilders()', () => {
|
||||
const cwd = await getWriteableDirectory();
|
||||
try {
|
||||
const spec = 'vercel-deno@2.0.1';
|
||||
const specs = new Set([spec]);
|
||||
const tarballSpec = 'https://test2020-h5hdll5dz-tootallnate.vercel.app';
|
||||
const specs = new Set([spec, tarballSpec]);
|
||||
const builders = await importBuilders(specs, cwd, client.output);
|
||||
expect(builders.size).toEqual(1);
|
||||
expect(builders.size).toEqual(2);
|
||||
expect(builders.get(spec)?.pkg.name).toEqual('vercel-deno');
|
||||
expect(builders.get(spec)?.pkg.version).toEqual('2.0.1');
|
||||
expect(builders.get(spec)?.pkgPath).toEqual(
|
||||
join(cwd, '.vercel/builders/node_modules/vercel-deno/package.json')
|
||||
);
|
||||
expect(typeof builders.get(spec)?.builder.build).toEqual('function');
|
||||
expect(builders.get(tarballSpec)?.pkg.name).toEqual('vercel-bash');
|
||||
expect(builders.get(tarballSpec)?.pkg.version).toEqual('4.1.0');
|
||||
expect(builders.get(tarballSpec)?.pkgPath).toEqual(
|
||||
join(cwd, '.vercel/builders/node_modules/vercel-bash/package.json')
|
||||
);
|
||||
expect(typeof builders.get(tarballSpec)?.builder.build).toEqual(
|
||||
'function'
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
'> Installing Builders: vercel-deno@2.0.1, https://test2020-h5hdll5dz-tootallnate.vercel.app'
|
||||
);
|
||||
} finally {
|
||||
await remove(cwd);
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw when importing a Builder that is not on npm registry', async () => {
|
||||
let err: Error | undefined;
|
||||
const cwd = await getWriteableDirectory();
|
||||
try {
|
||||
const spec = '@vercel/does-not-exist@0.0.1';
|
||||
const specs = new Set([spec]);
|
||||
await importBuilders(specs, cwd, client.output);
|
||||
} catch (_err) {
|
||||
err = _err;
|
||||
} finally {
|
||||
await remove(cwd);
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
throw new Error('Expected `err` to be defined');
|
||||
}
|
||||
|
||||
expect(err.message).toEqual(
|
||||
'The package `@vercel/does-not-exist` is not published on the npm registry'
|
||||
);
|
||||
expect((err as any).link).toEqual(
|
||||
'https://vercel.link/builder-dependencies-install-failed'
|
||||
);
|
||||
});
|
||||
|
||||
it('should import legacy `@now/build-utils` Builders', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
// this test creates symlinks which require admin by default on Windows
|
||||
|
||||
Reference in New Issue
Block a user