From 2b6c1ea0beb1b8edee71ec72146e37aff346ed39 Mon Sep 17 00:00:00 2001 From: MT Lewis Date: Sun, 24 Nov 2024 20:21:56 +0000 Subject: [PATCH] backstage-cli: automatically update the yarn plugin during versions:bump Signed-off-by: MT Lewis --- .changeset/brown-monkeys-ring.md | 5 + .../cli/src/commands/versions/bump.test.ts | 118 ++++++++++++++++++ packages/cli/src/commands/versions/bump.ts | 15 +++ 3 files changed, 138 insertions(+) create mode 100644 .changeset/brown-monkeys-ring.md diff --git a/.changeset/brown-monkeys-ring.md b/.changeset/brown-monkeys-ring.md new file mode 100644 index 0000000000..5aaf738d38 --- /dev/null +++ b/.changeset/brown-monkeys-ring.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +If the Backstage yarn plugin is installed, it will now be automatically updated as part of `versions:bump`. diff --git a/packages/cli/src/commands/versions/bump.test.ts b/packages/cli/src/commands/versions/bump.test.ts index cc48b91fd8..96e8de517b 100644 --- a/packages/cli/src/commands/versions/bump.test.ts +++ b/packages/cli/src/commands/versions/bump.test.ts @@ -33,6 +33,7 @@ jest.mock('global-agent/bootstrap', () => {}); // Remove log coloring to simplify log matching jest.mock('chalk', () => ({ + bold: (str: string) => str, red: (str: string) => str, blue: (str: string) => str, cyan: (str: string) => str, @@ -90,6 +91,12 @@ const REGISTRY_VERSIONS: { [name: string]: string } = { '@backstage/create-app': '1.0.0', }; +const yarnRcMock = `plugins: + - checksum: cafedead + path: .yarn/plugins/@yarnpkg/plugin-backstage.cjs + spec: 'https://versions.backstage.io/v1/releases/0.0.0/yarn-plugin' +`; + const HEADER = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 @@ -435,6 +442,117 @@ describe('bump', () => { }); }); + it('should use backstage:^ versions for packages in the release manifest when the yarn plugin is installed', async () => { + mockDir.setContent({ + '.yarnrc.yml': yarnRcMock, + 'yarn.lock': lockfileMock, + 'package.json': JSON.stringify({ + workspaces: { + packages: ['packages/*'], + }, + }), + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + dependencies: { + '@backstage/core': '^1.0.5', + }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + dependencies: { + '@backstage/core': '^1.0.3', + '@backstage/theme': '^1.0.0', + }, + }), + }, + }, + }); + + jest.spyOn(runObj, 'run').mockResolvedValue(undefined); + worker.use( + rest.get( + 'https://versions.backstage.io/v1/tags/main/manifest.json', + (_, res, ctx) => + res( + ctx.status(200), + ctx.json({ + releaseVersion: '0.0.1', + packages: [ + { + name: '@backstage/theme', + version: '5.0.0', + }, + { + name: '@backstage/create-app', + version: '3.0.0', + }, + ], + }), + ), + ), + ); + const { log: logs } = await withLogCollector(['log', 'warn'], async () => { + await bump({ pattern: null, release: 'main' } as unknown as Command); + }); + expectLogsToMatch(logs, [ + 'Using default pattern glob @backstage/*', + 'Checking for updates of @backstage/core', + 'Checking for updates of @backstage/theme', + 'NOTE: this bump used backstage:^ versions in package.json files, since the Backstage yarn plugin was detected in the repository. To migrate back to explicit npm versions, remove the plugin by running "yarn plugin remove @yarnpkg/plugin-backstage", then repeat this command.', + 'Some packages are outdated, updating', + 'bumping @backstage/theme in b to ^5.0.0', + 'bumping @backstage/core in b to ^1.0.6', + 'bumping @backstage/core in a to ^1.0.6', + 'Updating yarn plugin to v0.0.1...', + 'Your project is now at version 0.0.1, which has been written to backstage.json', + 'Running yarn install to install new versions', + 'Checking for moved packages to the @backstage-community namespace...', + '⚠️ The following packages may have breaking changes:', + ' @backstage/theme : 1.0.0 ~> 5.0.0', + ' https://github.com/backstage/backstage/blob/master/packages/theme/CHANGELOG.md', + 'Version bump complete!', + ]); + + expect(mockFetchPackageInfo).toHaveBeenCalledTimes(1); + expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/core'); + + expect(runObj.run).toHaveBeenCalledTimes(2); + expect(runObj.run).toHaveBeenCalledWith('yarn', [ + 'plugin', + 'import', + 'https://versions.backstage.io/v1/releases/0.0.1/yarn-plugin', + ]); + expect(runObj.run).toHaveBeenCalledWith( + 'yarn', + ['install'], + expect.any(Object), + ); + + const packageA = await fs.readJson( + mockDir.resolve('packages/a/package.json'), + ); + expect(packageA).toEqual({ + name: 'a', + dependencies: { + '@backstage/core': '^1.0.6', + }, + }); + const packageB = await fs.readJson( + mockDir.resolve('packages/b/package.json'), + ); + expect(packageB).toEqual({ + name: 'b', + dependencies: { + '@backstage/core': '^1.0.6', + '@backstage/theme': 'backstage:^', + }, + }); + }); + it('should only bump packages in the manifest when a specific release is specified', async () => { mockDir.setContent({ 'yarn.lock': lockfileMock, diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts index eec7f6b20a..76e6092140 100644 --- a/packages/cli/src/commands/versions/bump.ts +++ b/packages/cli/src/commands/versions/bump.ts @@ -44,6 +44,7 @@ import { } from '@backstage/release-manifests'; import { migrateMovedPackages } from './migrate'; import { runYarnInstall } from './utils'; +import { run } from '../../lib/run'; function shouldUseGlobalAgent(): boolean { // see https://www.npmjs.com/package/global-agent @@ -120,6 +121,20 @@ export default async (opts: OptionValues) => { }); } + if (hasYarnPlugin) { + console.log(); + console.log( + `Updating yarn plugin to v${releaseManifest.releaseVersion}...`, + ); + console.log(); + await run('yarn', [ + 'plugin', + 'import', + `https://versions.backstage.io/v1/releases/${releaseManifest.releaseVersion}/yarn-plugin`, + ]); + console.log(); + } + // First we discover all Backstage dependencies within our own repo const dependencyMap = await mapDependencies(paths.targetDir, pattern);