cli: Handle no npm info
This commit is contained in:
@@ -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
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
Reference in New Issue
Block a user