cli: Handle no npm info

This commit is contained in:
ahrberg
2021-01-11 16:51:14 +01:00
parent 9ea0aa9bb4
commit 08e9893d26
6 changed files with 110 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Handle no npm info
@@ -177,4 +177,71 @@ describe('bump', () => {
},
});
});
it('should ignore not found packages', async () => {
// Make sure all modules involved in package discovery are in the module cache before we mock fs
await mapDependencies(paths.targetDir);
mockFs({
'/yarn.lock': lockfileMockResult,
'/lerna.json': JSON.stringify({
packages: ['packages/*'],
}),
'/packages/a/package.json': JSON.stringify({
name: 'a',
dependencies: {
'@backstage/core': '^1.0.5',
},
}),
'/packages/b/package.json': JSON.stringify({
name: 'b',
dependencies: {
'@backstage/core': '^1.0.3',
'@backstage/theme': '^2.0.0',
},
}),
});
paths.targetDir = '/';
jest
.spyOn(paths, 'resolveTargetRoot')
.mockImplementation((...paths) => resolvePath('/', ...paths));
jest.spyOn(runObj, 'runPlain').mockImplementation(async () => '');
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
const { log: logs } = await withLogCollector(['log'], async () => {
await bump();
});
expect(logs.filter(Boolean)).toEqual([
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/core',
'Package info not found, ignoring package @backstage/theme',
'Package info not found, ignoring package @backstage/core',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/core',
'Package info not found, ignoring package @backstage/theme',
'Package info not found, ignoring package @backstage/core',
'All Backstage packages are up to date!',
]);
expect(runObj.run).toHaveBeenCalledTimes(0);
const lockfileContents = await fs.readFile('/yarn.lock', 'utf8');
expect(lockfileContents).toBe(lockfileMockResult);
const packageA = await fs.readJson('/packages/a/package.json');
expect(packageA).toEqual({
name: 'a',
dependencies: {
'@backstage/core': '^1.0.5', // not bumped
},
});
const packageB = await fs.readJson('/packages/b/package.json');
expect(packageB).toEqual({
name: 'b',
dependencies: {
'@backstage/core': '^1.0.3', // not bumped
'@backstage/theme': '^2.0.0', // not bumped
},
});
});
});
+21 -2
View File
@@ -26,6 +26,7 @@ import {
Lockfile,
} from '../../lib/versioning';
import { includedFilter, forbiddenDuplicatesFilter } from './lint';
import { NotFoundError } from '../../lib/errors';
const DEP_TYPES = [
'dependencies',
@@ -55,7 +56,16 @@ export default async () => {
// Track package versions that we want to remove from yarn.lock in order to trigger a bump
const unlocked = Array<{ name: string; range: string; target: string }>();
await workerThreads(16, dependencyMap.entries(), async ([name, pkgs]) => {
const target = await findTargetVersion(name);
let target: string;
try {
target = await findTargetVersion(name);
} catch (error) {
if (error instanceof NotFoundError) {
console.log(`Package info not found, ignoring package ${name}`);
return;
}
throw error;
}
for (const pkg of pkgs) {
if (semver.satisfies(target, pkg.range)) {
@@ -84,7 +94,16 @@ export default async () => {
return;
}
const target = await findTargetVersion(name);
let target: string;
try {
target = await findTargetVersion(name);
} catch (error) {
if (error instanceof NotFoundError) {
console.log(`Package info not found, ignoring package ${name}`);
return;
}
throw error;
}
for (const entry of lockfile.get(name) ?? []) {
// Ignore lockfile entries that don't satisfy the version range, since
+2
View File
@@ -44,3 +44,5 @@ export function exitWithError(error: Error): never {
process.exit(1);
}
}
export class NotFoundError extends CustomError {}
@@ -19,6 +19,7 @@ import path from 'path';
import * as runObj from '../run';
import { paths } from '../paths';
import { fetchPackageInfo, mapDependencies } from './packages';
import { NotFoundError } from '@backstage/backend-common';
describe('fetchPackageInfo', () => {
afterEach(() => {
@@ -40,6 +41,14 @@ describe('fetchPackageInfo', () => {
'my-package',
);
});
it('should throw if no info', async () => {
jest.spyOn(runObj, 'runPlain').mockResolvedValue('');
await expect(fetchPackageInfo('my-package')).rejects.toThrow(
new NotFoundError(`No package information found for package my-package`),
);
});
});
describe('mapDependencies', () => {
@@ -15,6 +15,7 @@
*/
import { runPlain } from '../../lib/run';
import { NotFoundError } from '../errors';
const PREFIX = '@backstage';
@@ -49,6 +50,11 @@ export async function fetchPackageInfo(
name: string,
): Promise<YarnInfoInspectData> {
const output = await runPlain('yarn', 'info', '--json', name);
if (!output) {
throw new NotFoundError(`No package information found for package ${name}`);
}
const info = JSON.parse(output) as YarnInfo;
if (info.type !== 'inspect') {
throw new Error(`Received unknown yarn info for ${name}, ${output}`);