diff --git a/.changeset/nice-kangaroos-rush.md b/.changeset/nice-kangaroos-rush.md new file mode 100644 index 0000000000..942fc5b4e6 --- /dev/null +++ b/.changeset/nice-kangaroos-rush.md @@ -0,0 +1,6 @@ +--- +'yarn-plugin-backstage': patch +--- + +Switch to using `reduceDependency` hook to replace `backstage:^` versions. This +makes the same yarn.lock file valid whether or not the plugin is installed. diff --git a/packages/yarn-plugin/package.json b/packages/yarn-plugin/package.json index bca6fed42f..b327fb494e 100644 --- a/packages/yarn-plugin/package.json +++ b/packages/yarn-plugin/package.json @@ -41,6 +41,7 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@yarnpkg/builder": "^4.0.0", + "fs-extra": "^11.2.0", "nodemon": "^3.0.1" } } diff --git a/packages/yarn-plugin/report.api.md b/packages/yarn-plugin/report.api.md index 928703ed3e..620bbbb567 100644 --- a/packages/yarn-plugin/report.api.md +++ b/packages/yarn-plugin/report.api.md @@ -3,10 +3,11 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { Hooks } from '@yarnpkg/plugin-pack'; +import { Hooks } from '@yarnpkg/core'; +import { Hooks as Hooks_2 } from '@yarnpkg/plugin-pack'; import { Plugin as Plugin_2 } from '@yarnpkg/core'; // @public (undocumented) -const plugin: Plugin_2; +const plugin: Plugin_2; export default plugin; ``` diff --git a/packages/yarn-plugin/src/handlers/beforeWorkspacePacking.ts b/packages/yarn-plugin/src/handlers/beforeWorkspacePacking.ts index e5823607e3..436c949b1e 100644 --- a/packages/yarn-plugin/src/handlers/beforeWorkspacePacking.ts +++ b/packages/yarn-plugin/src/handlers/beforeWorkspacePacking.ts @@ -15,11 +15,7 @@ */ import { Descriptor, Workspace, structUtils } from '@yarnpkg/core'; -import { - bindBackstageVersion, - getCurrentBackstageVersion, - getPackageVersion, -} from '../util'; +import { getPackageVersion } from '../util'; import { PROTOCOL } from '../constants'; const hasBackstageVersion = (range: string) => @@ -45,8 +41,6 @@ export const beforeWorkspacePacking = async ( workspace: Workspace, rawManifest: any, ) => { - const backstageVersion = getCurrentBackstageVersion(); - for (const dependencyType of ['dependencies', 'devDependencies'] as const) { const entries = Array.from( workspace.manifest.getForScope(dependencyType).values(), @@ -69,7 +63,7 @@ export const beforeWorkspacePacking = async ( ); rawManifest[finalDependencyType][ident] = `^${await getPackageVersion( - bindBackstageVersion(descriptor, backstageVersion), + descriptor, workspace.project.configuration, )}`; } diff --git a/packages/yarn-plugin/src/handlers/index.ts b/packages/yarn-plugin/src/handlers/index.ts new file mode 100644 index 0000000000..baa144b36f --- /dev/null +++ b/packages/yarn-plugin/src/handlers/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { beforeWorkspacePacking } from './beforeWorkspacePacking'; +export { reduceDependency } from './reduceDependency'; diff --git a/packages/yarn-plugin/src/handlers/reduceDependency.test.ts b/packages/yarn-plugin/src/handlers/reduceDependency.test.ts new file mode 100644 index 0000000000..568f39653a --- /dev/null +++ b/packages/yarn-plugin/src/handlers/reduceDependency.test.ts @@ -0,0 +1,209 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { join as joinPath } from 'path'; +import fs from 'fs-extra'; +import { Configuration, Project, structUtils, httpUtils } from '@yarnpkg/core'; +import { npath, ppath } from '@yarnpkg/fslib'; +import { createMockDirectory } from '@backstage/backend-test-utils'; +import { reduceDependency } from './reduceDependency'; + +describe('reduceDependency', () => { + const mockDir = createMockDirectory(); + let project: Project; + let getMock: jest.SpyInstance< + ReturnType<(typeof httpUtils)['get']>, + Parameters<(typeof httpUtils)['get']> + >; + + beforeEach(() => { + project = new Project(ppath.cwd(), { + configuration: Configuration.create(ppath.cwd()), + }); + + jest + .spyOn(ppath, 'cwd') + .mockReturnValue(npath.toPortablePath(mockDir.path)); + + jest + .spyOn(process, 'cwd') + .mockReturnValue(npath.toPortablePath(mockDir.path)); + + getMock = jest.spyOn(httpUtils, 'get').mockResolvedValue({ + releaseVersion: '1.23.45', + packages: [ + { + name: '@backstage/core', + version: '6.7.8', + }, + ], + }); + + mockDir.setContent({ + 'backstage.json': JSON.stringify({ + version: '1.23.45', + }), + 'package.json': JSON.stringify({ + workspaces: { + packages: ['packages/*'], + }, + }), + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + dependencies: { + '@backstage/core': 'backstage:^', + }, + }), + }, + }, + }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it.each` + range + ${'npm:1.2.3'} + ${'link:../foo/bar'} + ${'workspace:^'} + `('returns non-backstage range "$range" unchanged', async ({ range }) => { + const descriptor = structUtils.makeDescriptor( + structUtils.makeIdent('backstage', 'core'), + range, + ); + + await expect(reduceDependency(descriptor, project)).resolves.toEqual( + descriptor, + ); + }); + + it.each` + selector + ${'*'} + ${'latest'} + `( + 'rejects backstage: ranges with invalid selector "$selector"', + async ({ selector }) => { + await expect( + reduceDependency( + structUtils.makeDescriptor( + structUtils.makeIdent('backstage', 'core'), + `backstage:${selector}`, + ), + project, + ), + ).rejects.toThrow(/unexpected version selector/i); + }, + ); + + describe('with range "backstage:^"', () => { + it('loads the manifest for the current Backstage version', async () => { + await reduceDependency( + structUtils.makeDescriptor( + structUtils.makeIdent('backstage', 'core'), + 'backstage:^', + ), + project, + ); + + expect(getMock).toHaveBeenCalledWith( + 'https://versions.backstage.io/v1/releases/1.23.45/manifest.json', + expect.anything(), + ); + }); + + it('replaces the range with the corresponding npm package range', async () => { + await expect( + reduceDependency( + structUtils.makeDescriptor( + structUtils.makeIdent('backstage', 'core'), + 'backstage:^', + ), + project, + ), + ).resolves.toEqual( + structUtils.makeDescriptor( + structUtils.makeIdent('backstage', 'core'), + 'npm:^6.7.8', + ), + ); + }); + + it('throws if backstage.json is missing', async () => { + await fs.remove(joinPath(mockDir.path, 'backstage.json')); + + await expect( + reduceDependency( + structUtils.makeDescriptor( + structUtils.makeIdent('backstage', 'other'), + 'backstage:^', + ), + project, + ), + ).rejects.toThrow( + expect.objectContaining({ + message: expect.stringContaining( + 'Valid version string not found in backstage.json', + ), + }), + ); + }); + + it(`throws if backstage.json doesn't contain a valid version`, async () => { + mockDir.addContent({ + 'backstage.json': JSON.stringify({}), + }); + + await expect( + reduceDependency( + structUtils.makeDescriptor( + structUtils.makeIdent('backstage', 'other'), + 'backstage:^', + ), + project, + ), + ).rejects.toThrow( + expect.objectContaining({ + message: expect.stringContaining( + 'Valid version string not found in backstage.json', + ), + }), + ); + }); + + it(`throws for packages that don't appear in the manifest`, async () => { + await expect( + reduceDependency( + structUtils.makeDescriptor( + structUtils.makeIdent('backstage', 'other'), + 'backstage:^', + ), + project, + ), + ).rejects.toThrow( + expect.objectContaining({ + message: expect.stringContaining( + 'Package @backstage/other not found in manifest for Backstage v1.23.45', + ), + }), + ); + }); + }); +}); diff --git a/packages/yarn-plugin/src/handlers/reduceDependency.ts b/packages/yarn-plugin/src/handlers/reduceDependency.ts new file mode 100644 index 0000000000..e00a39c27c --- /dev/null +++ b/packages/yarn-plugin/src/handlers/reduceDependency.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Descriptor, Project, structUtils } from '@yarnpkg/core'; +import { getPackageVersion } from '../util'; +import { PROTOCOL } from '../constants'; + +export const reduceDependency = async ( + dependency: Descriptor, + project: Project, +) => { + const range = structUtils.parseRange(dependency.range); + + if (range.protocol === PROTOCOL) { + return structUtils.makeDescriptor( + dependency, + `npm:^${await getPackageVersion(dependency, project.configuration)}`, + ); + } + + return dependency; +}; diff --git a/packages/yarn-plugin/src/index.ts b/packages/yarn-plugin/src/index.ts index 9c20e9a45c..dcf2fe6943 100644 --- a/packages/yarn-plugin/src/index.ts +++ b/packages/yarn-plugin/src/index.ts @@ -21,10 +21,9 @@ * @packageDocumentation */ -import { Plugin, semverUtils, YarnVersion } from '@yarnpkg/core'; -import { Hooks } from '@yarnpkg/plugin-pack'; -import { beforeWorkspacePacking } from './handlers/beforeWorkspacePacking'; -import { BackstageResolver } from './resolver/BackstageResolver'; +import { Plugin, Hooks, semverUtils, YarnVersion } from '@yarnpkg/core'; +import { Hooks as PackHooks } from '@yarnpkg/plugin-pack'; +import { beforeWorkspacePacking, reduceDependency } from './handlers'; // All dependencies of the yarn plugin are bundled during the build. Chalk // triples the size of the plugin bundle when included, so we avoid the @@ -44,11 +43,11 @@ if (!semverUtils.satisfiesWithPrereleases(YarnVersion, '^4.1.1')) { /** * @public */ -const plugin: Plugin = { +const plugin: Plugin = { hooks: { + reduceDependency, beforeWorkspacePacking, }, - resolvers: [BackstageResolver], }; export default plugin; diff --git a/packages/yarn-plugin/src/resolver/BackstageResolver.test.ts b/packages/yarn-plugin/src/resolver/BackstageResolver.test.ts deleted file mode 100644 index 8cba189cb8..0000000000 --- a/packages/yarn-plugin/src/resolver/BackstageResolver.test.ts +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - Configuration, - Project, - ResolveOptions, - structUtils, - ThrowReport, - httpUtils, -} from '@yarnpkg/core'; -import { npath, ppath } from '@yarnpkg/fslib'; -import { BackstageResolver } from './BackstageResolver'; -import { createMockDirectory } from '@backstage/backend-test-utils'; - -describe('BackstageResolver', () => { - const mockDir = createMockDirectory(); - let backstageResolver: BackstageResolver; - let resolveOptions: ResolveOptions; - - beforeEach(() => { - jest - .spyOn(ppath, 'cwd') - .mockReturnValue(npath.toPortablePath(mockDir.path)); - - jest - .spyOn(process, 'cwd') - .mockReturnValue(npath.toPortablePath(mockDir.path)); - - jest.spyOn(httpUtils, 'get').mockResolvedValue({ - releaseVersion: '1.23.45', - packages: [ - { - name: '@backstage/core', - version: '6.7.8', - }, - ], - }); - - mockDir.setContent({ - 'backstage.json': JSON.stringify({ - version: '1.23.45', - }), - 'package.json': JSON.stringify({ - workspaces: { - packages: ['packages/*'], - }, - }), - packages: { - a: { - 'package.json': JSON.stringify({ - name: 'a', - dependencies: { - '@backstage/core': 'backstage:^', - }, - }), - }, - }, - }); - - backstageResolver = new BackstageResolver(); - - resolveOptions = { - resolver: backstageResolver, - project: new Project(ppath.cwd(), { - configuration: Configuration.create(ppath.cwd()), - }), - report: new ThrowReport(), - }; - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - describe('supportsDescriptor', () => { - it.each([ - ['backstage:^'], - ['backstage:1.26.0-next.3'], - ['backstage:anything'], - ])('returns true for range "%s"', range => { - expect( - backstageResolver.supportsDescriptor( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - range, - ), - ), - ).toEqual(true); - }); - }); - - describe('bindDescriptor', () => { - describe('with range "backstage:^"', () => { - it('returns a descriptor with a version range for the current Backstage version', () => { - expect( - backstageResolver.bindDescriptor( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - 'backstage:^', - ), - ), - ).toEqual( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - 'backstage:^::v=1.23.45', - ), - ); - }); - }); - - describe('with range "backstage:^::v=1.23.45"', () => { - it('returns the correct descriptor', () => { - expect( - backstageResolver.bindDescriptor( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - 'backstage:^::v=1.23.45', - ), - ), - ).toEqual( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - 'backstage:^::v=1.23.45', - ), - ); - }); - }); - }); - - describe('getCandidates', () => { - it('returns an npm: descriptor based on the manifest for the appropriate backstage version', async () => { - const descriptor = structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - 'backstage:^::v=1.23.45', - ); - - await expect( - backstageResolver.getCandidates(descriptor, {}, resolveOptions), - ).resolves.toEqual([structUtils.makeLocator(descriptor, 'npm:6.7.8')]); - }); - - it('rejects descriptors not using the backstage: protocol', async () => { - await expect( - backstageResolver.getCandidates( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - 'npm:1.2.3', - ), - {}, - resolveOptions, - ), - ).rejects.toThrow(/unsupported version protocol/i); - }); - - it('rejects backstage: ranges missing a version parameter', async () => { - await expect( - backstageResolver.getCandidates( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - 'backstage:^', - ), - {}, - resolveOptions, - ), - ).rejects.toThrow(/missing Backstage version/i); - }); - - it('rejects backstage: ranges with multiple version parameters', async () => { - await expect( - backstageResolver.getCandidates( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - 'backstage:^::v=1&v=2', - ), - {}, - resolveOptions, - ), - ).rejects.toThrow(/multiple Backstage versions/i); - }); - - it.each` - selector - ${'*'} - ${'latest'} - `( - 'rejects backstage: ranges with invalid selector "$selector"', - async ({ selector }) => { - await expect( - backstageResolver.getCandidates( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - `backstage:${selector}`, - ), - {}, - resolveOptions, - ), - ).rejects.toThrow(/unexpected version selector/i); - }, - ); - }); - - describe('getSatisfying', () => { - it('filters out locators for other packages', async () => { - await expect( - backstageResolver.getSatisfying( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - 'backstage:^::v=1.23.45', - ), - {}, - [ - structUtils.makeLocator( - structUtils.makeIdent('backstage', 'foo'), - 'npm:1.2.3', - ), - structUtils.makeLocator( - structUtils.makeIdent('backstage', 'core'), - 'npm:6.7.8', - ), - structUtils.makeLocator( - structUtils.makeIdent('backstage', 'bar'), - 'npm:1.2.3', - ), - ], - resolveOptions, - ), - ).resolves.toEqual({ - locators: [ - structUtils.makeLocator( - structUtils.makeIdent('backstage', 'core'), - 'npm:6.7.8', - ), - ], - sorted: true, - }); - }); - - it('filters out locators for other package versions', async () => { - await expect( - backstageResolver.getSatisfying( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - 'backstage:^::v=1.23.45', - ), - {}, - [ - structUtils.makeLocator( - structUtils.makeIdent('backstage', 'core'), - 'npm:5.6.7', - ), - structUtils.makeLocator( - structUtils.makeIdent('backstage', 'core'), - 'npm:6.7.8', - ), - structUtils.makeLocator( - structUtils.makeIdent('backstage', 'bar'), - 'npm:7.8.9', - ), - ], - resolveOptions, - ), - ).resolves.toEqual({ - locators: [ - structUtils.makeLocator( - structUtils.makeIdent('backstage', 'core'), - 'npm:6.7.8', - ), - ], - sorted: true, - }); - }); - - it('throws for non `backstage:` descriptors', async () => { - await expect( - backstageResolver.getSatisfying( - structUtils.makeDescriptor( - structUtils.makeIdent('backstage', 'core'), - 'npm:1.2.3', - ), - {}, - [], - resolveOptions, - ), - ).rejects.toThrow(/unsupported version protocol/i); - }); - }); -}); diff --git a/packages/yarn-plugin/src/resolver/BackstageResolver.ts b/packages/yarn-plugin/src/resolver/BackstageResolver.ts deleted file mode 100644 index d2d0c98d1e..0000000000 --- a/packages/yarn-plugin/src/resolver/BackstageResolver.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - structUtils, - Descriptor, - Locator, - Package, - Resolver, - ResolveOptions, -} from '@yarnpkg/core'; -import { PROTOCOL } from '../constants'; -import { - bindBackstageVersion, - getCurrentBackstageVersion, - getPackageVersion, -} from '../util'; - -export class BackstageResolver implements Resolver { - static protocol = PROTOCOL; - - supportsDescriptor = (descriptor: Descriptor) => - descriptor.range.startsWith(BackstageResolver.protocol); - - shouldPersistResolution = () => true; - - /** - * Called for each dependency present in the dependency list of a package - * definition. If it returns a new descriptor, this new descriptor will be - * used. - * - * In this plugin, we convert the specific range "backstage:^" to - * "backstage:". This new range will be the one - * stored in lockfile entries, ensuring that we re-resolve the package when - * the version in backstage.json changes. - */ - bindDescriptor(descriptor: Descriptor): Descriptor { - if (descriptor.range === 'backstage:^') { - return bindBackstageVersion(descriptor, getCurrentBackstageVersion()); - } - - return descriptor; - } - - /** - * Given a descriptor, return the list of locators that potentially satisfy - * it. The implementation in this plugin converts a `backstage:` range with a - * concrete version into the appropriate concrete npm version for that - * backstage release. - */ - async getCandidates( - descriptor: Descriptor, - _dependencies: Record, - opts: ResolveOptions, - ): Promise { - return [ - structUtils.makeLocator( - descriptor, - `npm:${await getPackageVersion( - descriptor, - opts.project.configuration, - )}`, - ), - ]; - } - - /** - * Given a descriptor and a list of possible locators, return a filtered list - * containing only locators that satisfy the descriptor. Since each Backstage - * release version corresponds to a single version for each package, we just - * need to filter that array for locators with that exact version. - */ - async getSatisfying( - descriptor: Descriptor, - _dependencies: Record, - locators: Array, - opts: ResolveOptions, - ): Promise<{ locators: Locator[]; sorted: boolean }> { - const packageVersion = await getPackageVersion( - descriptor, - opts.project.configuration, - ); - - return { - locators: locators.filter( - locator => - structUtils.areIdentsEqual(descriptor, locator) && - locator.reference === `npm:${packageVersion}`, - ), - sorted: true, - }; - } - - /** - * This plugin does not need to support any locators itself, since the - * `getCandidates` method will always convert `backstage:` versions into - * `npm:` versions which can be handled as usual. - */ - supportsLocator = () => false; - - /** - * This resolver never transforms the packages that are actually depended on, - * only replaces versions. As such there's never a need to add additional - * dependencies. - */ - getResolutionDependencies = () => ({}); - - /** - * Once transformed into locators (through getCandidates), the versions are - * resolved by the NpmSemverResolver - */ - async resolve(): Promise { - throw new Error(`Unreachable`); - } -} diff --git a/packages/yarn-plugin/src/util.ts b/packages/yarn-plugin/src/util.ts index 23ce55ac80..939ce807e5 100644 --- a/packages/yarn-plugin/src/util.ts +++ b/packages/yarn-plugin/src/util.ts @@ -31,6 +31,11 @@ export const getCurrentBackstageVersion = () => { const workspaceRoot = npath.toPortablePath( findPaths(npath.fromPortablePath(ppath.cwd())).targetRoot, ); + const backstageJsonPath = ppath.join(workspaceRoot, BACKSTAGE_JSON); + + if (!xfs.existsSync(backstageJsonPath)) { + throw new Error('Valid version string not found in backstage.json'); + } const backstageJson = xfs.readJsonSync( ppath.join(workspaceRoot, BACKSTAGE_JSON), @@ -45,13 +50,6 @@ export const getCurrentBackstageVersion = () => { return backstageVersion; }; -export const bindBackstageVersion = ( - descriptor: Descriptor, - backstageVersion: string, -) => { - return structUtils.bindDescriptor(descriptor, { v: backstageVersion }); -}; - export const getPackageVersion = async ( descriptor: Descriptor, configuration: Configuration, @@ -71,20 +69,10 @@ export const getPackageVersion = async ( ); } - if (!range.params?.v) { - throw new Error( - `Missing Backstage version parameter in range "${descriptor.range}" for package ${ident}`, - ); - } - - if (Array.isArray(range.params.v)) { - throw new Error( - `Multiple Backstage versions specified in range "${descriptor.range}" for package ${ident}`, - ); - } + const backstageVersion = getCurrentBackstageVersion(); const manifest = await getManifestByVersion({ - version: range.params.v, + version: backstageVersion, // We override the fetch function used inside getManifestByVersion with a // custom implementation that calls yarn's built-in `httpUtils` method // instead. This has a couple of benefits: @@ -123,7 +111,7 @@ export const getPackageVersion = async ( if (!manifestEntry) { throw new Error( - `Package ${ident} not found in manifest for Backstage v${range.selector}. ` + + `Package ${ident} not found in manifest for Backstage v${backstageVersion}. ` + `This means the specified package is not included in this Backstage ` + `release. This may imply the package has been replaced with an alternative - ` + `please review the documentation for the package. If you need to continue ` + diff --git a/yarn.lock b/yarn.lock index afc08ad8a0..b556c34400 100644 --- a/yarn.lock +++ b/yarn.lock @@ -48206,6 +48206,7 @@ __metadata: "@yarnpkg/core": ^4.0.3 "@yarnpkg/fslib": ^3.0.2 "@yarnpkg/plugin-pack": ^4.0.0 + fs-extra: ^11.2.0 nodemon: ^3.0.1 semver: ^7.6.0 languageName: unknown