diff --git a/packages/cli/src/commands/versions/bump.test.ts b/packages/cli/src/commands/versions/bump.test.ts index 1b9e0aa7cc..ce37181b72 100644 --- a/packages/cli/src/commands/versions/bump.test.ts +++ b/packages/cli/src/commands/versions/bump.test.ts @@ -44,6 +44,7 @@ const REGISTRY_VERSIONS: { [name: string]: string } = { '@backstage/theme': '2.0.0', '@backstage-extra/custom': '1.1.0', '@backstage-extra/custom-two': '2.0.0', + '@backstage/create-app': '1.0.0', }; const HEADER = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. @@ -154,6 +155,8 @@ describe('bump', () => { 'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7', 'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7', 'bumping @backstage/theme in b to ^2.0.0', + 'Checking for updates of @backstage/create-app', + 'Creating backstage.json', 'Running yarn install to install new versions', '⚠️ The following packages may have breaking changes:', ' @backstage/theme : 1.0.0 ~> 2.0.0', @@ -243,7 +246,16 @@ describe('bump', () => { res( ctx.status(200), ctx.json({ - packages: [{ name: '@backstage/theme', version: '5.0.0' }], + packages: [ + { + name: '@backstage/theme', + version: '5.0.0', + }, + { + name: '@backstage/create-app', + version: '3.0.0', + }, + ], }), ), ), @@ -262,6 +274,8 @@ describe('bump', () => { 'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7', 'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7', 'bumping @backstage/theme in b to ^5.0.0', + 'Checking for updates of @backstage/create-app', + 'Creating backstage.json', 'Running yarn install to install new versions', '⚠️ The following packages may have breaking changes:', ' @backstage/theme : 1.0.0 ~> 5.0.0', @@ -269,7 +283,7 @@ describe('bump', () => { 'Version bump complete!', ]); - expect(runObj.runPlain).toHaveBeenCalledTimes(3); + expect(runObj.runPlain).toHaveBeenCalledTimes(2); expect(runObj.runPlain).toHaveBeenCalledWith( 'yarn', 'info', @@ -306,6 +320,107 @@ describe('bump', () => { }); }); + it('should only bump packages in the manifest when a specific release is specified', async () => { + mockFs({ + '/yarn.lock': lockfileMock, + '/package.json': JSON.stringify({ + workspaces: { + 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': '^1.0.0', + }, + }), + }); + jest.spyOn(runObj, 'runPlain').mockImplementation(async (...[, , , name]) => + JSON.stringify({ + type: 'inspect', + data: { + name: name, + 'dist-tags': { + latest: REGISTRY_VERSIONS[name], + }, + }, + }), + ); + jest + .spyOn(paths, 'resolveTargetRoot') + .mockImplementation((...path) => resolvePath('/', ...path)); + + jest.spyOn(runObj, 'run').mockResolvedValue(undefined); + worker.use( + rest.get( + 'https://versions.backstage.io/v1/releases/1.0.0/manifest.json', + (_, res, ctx) => + res( + ctx.status(200), + ctx.json({ + packages: [ + { name: '@backstage/core', version: '5.0.0' }, + { name: '@backstage/core-api', version: '5.0.0' }, + { name: '@backstage/create-app', version: '2.0.0' }, + ], + }), + ), + ), + ); + const { log: logs } = await withLogCollector(['log'], async () => { + await expect( + bump({ pattern: null, release: '1.0.0' } as unknown as Command), + ).rejects.toThrow('Duplicate versions present after package bump'); + }); + expect(logs.filter(Boolean)).toEqual([ + 'Using default pattern glob @backstage/*', + 'Checking for updates of @backstage/theme', + 'Checking for updates of @backstage/core', + 'Package @backstage/theme not found in release manifest, skipping', + 'Checking for updates of @backstage/core-api', + 'Checking for updates of @backstage/theme', + 'Checking for updates of @backstage/core', + 'Package @backstage/theme not found in release manifest, skipping', + 'Some packages are outdated, updating', + 'bumping @backstage/core in b to ^5.0.0', + 'bumping @backstage/core in a to ^5.0.0', + 'Checking for updates of @backstage/create-app', + 'Creating backstage.json', + 'Running yarn install to install new versions', + '⚠️ The following packages may have breaking changes:', + ' @backstage/core : 1.0.6 ~> 5.0.0', + ' https://github.com/backstage/backstage/blob/master/packages/core/CHANGELOG.md', + 'Version bump complete!', + ]); + + expect(runObj.run).toHaveBeenCalledTimes(1); + expect(runObj.run).toHaveBeenCalledWith('yarn', ['install']); + + const packageA = await fs.readJson('/packages/a/package.json'); + expect(packageA).toEqual({ + name: 'a', + dependencies: { + '@backstage/core': '^5.0.0', + }, + }); + const packageB = await fs.readJson('/packages/b/package.json'); + expect(packageB).toEqual({ + name: 'b', + dependencies: { + '@backstage/core': '^5.0.0', + '@backstage/theme': '^1.0.0', + }, + }); + expect(await fs.readJson('/backstage.json')).toEqual({ version: '2.0.0' }); + }); + it('should bump backstage dependencies and dependencies matching pattern glob', async () => { const customLockfileMock = `${lockfileMock} "@backstage-extra/custom@^1.1.0": @@ -414,7 +529,7 @@ describe('bump', () => { 'Version bump complete!', ]); - expect(runObj.runPlain).toHaveBeenCalledTimes(6); + expect(runObj.runPlain).toHaveBeenCalledTimes(5); expect(runObj.runPlain).toHaveBeenCalledWith( 'yarn', 'info', @@ -545,27 +660,12 @@ describe('bumpBackstageJsonVersion', () => { '/backstage.json': JSON.stringify({ version: '0.0.1' }), }); paths.targetDir = '/'; - const latest = '1.4.1'; jest .spyOn(paths, 'resolveTargetRoot') .mockImplementation((...path) => resolvePath('/', ...path)); - jest.spyOn(runObj, 'runPlain').mockImplementation(async (...[, , , name]) => - JSON.stringify({ - type: 'inspect', - data: { - name, - 'dist-tags': { - latest, - }, - }, - }), - ); - jest.spyOn(runObj, 'run').mockResolvedValue(undefined); - await bumpBackstageJsonVersion(); - - const json = await fs.readJson('/backstage.json'); - expect(json).toEqual({ version: '1.4.1' }); + await bumpBackstageJsonVersion('1.4.1'); + expect(await fs.readJson('/backstage.json')).toEqual({ version: '1.4.1' }); }); it("should create backstage.json if doesn't exist", async () => { @@ -575,23 +675,9 @@ describe('bumpBackstageJsonVersion', () => { jest .spyOn(paths, 'resolveTargetRoot') .mockImplementation((...path) => resolvePath('/', ...path)); - jest.spyOn(runObj, 'runPlain').mockImplementation(async (...[, , , name]) => - JSON.stringify({ - type: 'inspect', - data: { - name, - 'dist-tags': { - latest, - }, - }, - }), - ); - jest.spyOn(runObj, 'run').mockResolvedValue(undefined); - await bumpBackstageJsonVersion(); - - const json = await fs.readJson('/backstage.json'); - expect(json).toEqual({ version: '1.4.1' }); + await bumpBackstageJsonVersion(latest); + expect(await fs.readJson('/backstage.json')).toEqual({ version: latest }); }); }); diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts index 46044bcca1..6e602c726c 100644 --- a/packages/cli/src/commands/versions/bump.ts +++ b/packages/cli/src/commands/versions/bump.ts @@ -231,8 +231,12 @@ export default async (cmd: Command) => { console.log(); - await bumpBackstageJsonVersion(); - + // Do not update backstage.json when upgrade patterns are used. + if (pattern === DEFAULT_PATTERN_GLOB) { + await bumpBackstageJsonVersion( + await findTargetVersion('@backstage/create-app'), + ); + } console.log(); console.log( `Running ${chalk.blue('yarn install')} to install new versions`, @@ -384,7 +388,7 @@ export function createVersionFinder(options: { }; } -export async function bumpBackstageJsonVersion() { +export async function bumpBackstageJsonVersion(createAppVersion: string) { const backstageJsonPath = paths.resolveTargetRoot(BACKSTAGE_JSON); const backstageJson = await fs.readJSON(backstageJsonPath).catch(e => { if (e.code === 'ENOENT') { @@ -394,10 +398,7 @@ export async function bumpBackstageJsonVersion() { throw e; }); - const info = await fetchPackageInfo('@backstage/create-app'); - const { latest } = info['dist-tags']; - - if (backstageJson?.version === latest) { + if (backstageJson?.version === createAppVersion) { return; } @@ -411,7 +412,7 @@ export async function bumpBackstageJsonVersion() { await fs.writeJson( backstageJsonPath, - { ...backstageJson, version: latest }, + { ...backstageJson, version: createAppVersion }, { spaces: 2, encoding: 'utf8',