yarn-plugin: handle packing step
This commit introduces a beforeWorkspacePacking hook which converts `backstage:` versions back into `npm:`` versions when packages are packed for publish. This allows the `backstage:` protocol to be used even for packages that are intended to themselves be published (such as packages in third-party Backstage plugin monorepos outside core). Signed-off-by: MT Lewis <mtlewis@users.noreply.github.com>
This commit is contained in:
@@ -1,42 +1,43 @@
|
||||
{
|
||||
"name": "yarn-plugin-backstage",
|
||||
"description": "Yarn plugin for working with Backstage monorepos",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"description": "Yarn plugin for working with Backstage monorepos",
|
||||
"backstage": {
|
||||
"role": "node-library"
|
||||
},
|
||||
"private": true,
|
||||
"keywords": [
|
||||
"backstage"
|
||||
],
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
"directory": "packages/yarn-plugin"
|
||||
},
|
||||
"keywords": [
|
||||
"backstage"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"main": "./src/index.ts",
|
||||
"scripts": {
|
||||
"build": "builder build plugin",
|
||||
"lint": "backstage-cli package lint",
|
||||
"test": "backstage-cli package test",
|
||||
"clean": "backstage-cli package clean",
|
||||
"start": "nodemon --"
|
||||
"lint": "backstage-cli package lint",
|
||||
"start": "nodemon --",
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"exec": "builder build plugin",
|
||||
"ext": "ts",
|
||||
"watch": "./src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/release-manifests": "workspace:^",
|
||||
"@yarnpkg/core": "^4.0.3",
|
||||
"@yarnpkg/fslib": "^3.0.2"
|
||||
"@yarnpkg/fslib": "^3.0.2",
|
||||
"semver": "^7.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@yarnpkg/builder": "^4.0.0",
|
||||
"nodemon": "^3.0.1"
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"watch": "./src",
|
||||
"exec": "builder build plugin",
|
||||
"ext": "ts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 const PROTOCOL = 'backstage:';
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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, Workspace, structUtils } from '@yarnpkg/core';
|
||||
import { inferPackageVersion } from '../util';
|
||||
import { PROTOCOL } from '../constants';
|
||||
|
||||
const getFinalDependencyType = (
|
||||
dependencyType: string,
|
||||
descriptor: Descriptor,
|
||||
workspace: Workspace,
|
||||
) => {
|
||||
if (dependencyType !== 'dependencies') {
|
||||
return dependencyType;
|
||||
}
|
||||
|
||||
return workspace.manifest.ensureDependencyMeta(
|
||||
structUtils.makeDescriptor(descriptor, 'unknown'),
|
||||
).optional
|
||||
? 'optionalDependencies'
|
||||
: dependencyType;
|
||||
};
|
||||
|
||||
export const beforeWorkspacePacking = async (
|
||||
workspace: Workspace,
|
||||
rawManifest: any,
|
||||
) => {
|
||||
for (const dependencyType of [
|
||||
'dependencies',
|
||||
'devDependencies',
|
||||
'peerDependencies',
|
||||
] as const) {
|
||||
const entries = Array.from(
|
||||
workspace.manifest.getForScope(dependencyType).values(),
|
||||
).filter(
|
||||
descriptor =>
|
||||
structUtils.parseRange(descriptor.range).protocol === PROTOCOL,
|
||||
);
|
||||
|
||||
for (const descriptor of entries) {
|
||||
const finalDependencyType = getFinalDependencyType(
|
||||
dependencyType,
|
||||
descriptor,
|
||||
workspace,
|
||||
);
|
||||
const ident = structUtils.stringifyIdent(descriptor);
|
||||
|
||||
rawManifest[finalDependencyType][ident] = await inferPackageVersion(
|
||||
descriptor,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -22,12 +22,16 @@
|
||||
*/
|
||||
|
||||
import { Plugin } from '@yarnpkg/core';
|
||||
import { beforeWorkspacePacking } from './handlers/beforeWorkspacePacking';
|
||||
import { BackstageResolver } from './resolver/BackstageResolver';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
const plugin: Plugin = {
|
||||
hooks: {
|
||||
beforeWorkspacePacking,
|
||||
},
|
||||
resolvers: [BackstageResolver],
|
||||
};
|
||||
|
||||
|
||||
@@ -21,11 +21,11 @@ import {
|
||||
Package,
|
||||
Resolver,
|
||||
} from '@yarnpkg/core';
|
||||
import { xfs, npath } from '@yarnpkg/fslib';
|
||||
import { getManifestByVersion } from '@backstage/release-manifests';
|
||||
import { PROTOCOL } from '../constants';
|
||||
import { inferBackstageVersion, inferPackageVersion } from '../util';
|
||||
|
||||
export class BackstageResolver implements Resolver {
|
||||
static protocol = `backstage:`;
|
||||
static protocol = PROTOCOL;
|
||||
|
||||
supportsDescriptor = (descriptor: Descriptor) =>
|
||||
descriptor.range.startsWith(BackstageResolver.protocol);
|
||||
@@ -33,39 +33,18 @@ export class BackstageResolver implements Resolver {
|
||||
shouldPersistResolution = () => true;
|
||||
|
||||
bindDescriptor(descriptor: Descriptor): Descriptor {
|
||||
if (descriptor.range === `${BackstageResolver.protocol}*`) {
|
||||
const backstageJson = xfs.readJsonSync(
|
||||
npath.toPortablePath('./backstage.json'),
|
||||
);
|
||||
|
||||
return structUtils.makeDescriptor(
|
||||
descriptor,
|
||||
`backstage:${backstageJson.version}`,
|
||||
);
|
||||
}
|
||||
|
||||
return descriptor;
|
||||
return structUtils.makeDescriptor(
|
||||
descriptor,
|
||||
`backstage:${inferBackstageVersion(descriptor)}`,
|
||||
);
|
||||
}
|
||||
|
||||
async getCandidates(descriptor: Descriptor): Promise<Locator[]> {
|
||||
const backstageVersion = descriptor.range.replace(
|
||||
BackstageResolver.protocol,
|
||||
'',
|
||||
);
|
||||
|
||||
const manifest = await getManifestByVersion({ version: backstageVersion });
|
||||
const ident = structUtils.stringifyIdent(descriptor);
|
||||
|
||||
const manifestEntry = manifest.packages.find(
|
||||
candidate => candidate.name === ident,
|
||||
);
|
||||
|
||||
if (!manifestEntry) {
|
||||
throw new Error(`Package ${ident} not found in manifest`);
|
||||
}
|
||||
|
||||
return [
|
||||
structUtils.makeLocator(descriptor, `npm:${manifestEntry.version}`),
|
||||
structUtils.makeLocator(
|
||||
descriptor,
|
||||
`npm:${await inferPackageVersion(descriptor)}`,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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, structUtils } from '@yarnpkg/core';
|
||||
import { npath, xfs } from '@yarnpkg/fslib';
|
||||
import { valid as semverValid } from 'semver';
|
||||
import { getManifestByVersion } from '@backstage/release-manifests';
|
||||
import { PROTOCOL } from './constants';
|
||||
|
||||
export const inferBackstageVersion = (descriptor: Descriptor) => {
|
||||
const range = structUtils.parseRange(descriptor.range);
|
||||
|
||||
if (range.protocol !== PROTOCOL) {
|
||||
throw new Error(
|
||||
`inferBackstageVersion called with unexpected protocol ${range.protocol}`,
|
||||
);
|
||||
}
|
||||
|
||||
let selector = range.selector;
|
||||
|
||||
// For backstage:* we look up the version from backstage.json
|
||||
if (selector === `*`) {
|
||||
const backstageJson = xfs.readJsonSync(
|
||||
npath.toPortablePath('./backstage.json'),
|
||||
);
|
||||
|
||||
selector = backstageJson.version;
|
||||
}
|
||||
|
||||
if (!semverValid(selector)) {
|
||||
throw new Error(
|
||||
`Invalid "backstage:" version string found for ${structUtils.stringifyIdent(
|
||||
descriptor,
|
||||
)}. Version must be either "backstage:*" or "backstage:<version>", where version is a single Backstage release version.`,
|
||||
);
|
||||
}
|
||||
|
||||
return selector;
|
||||
};
|
||||
|
||||
export const inferPackageVersion = async (descriptor: Descriptor) => {
|
||||
const ident = structUtils.stringifyIdent(descriptor);
|
||||
const backstageVersion = inferBackstageVersion(descriptor);
|
||||
|
||||
const manifest = await getManifestByVersion({
|
||||
version: backstageVersion,
|
||||
});
|
||||
|
||||
const manifestEntry = manifest.packages.find(
|
||||
candidate => candidate.name === ident,
|
||||
);
|
||||
|
||||
if (!manifestEntry) {
|
||||
throw new Error(`Package ${ident} not found in manifest`);
|
||||
}
|
||||
|
||||
return manifestEntry.version;
|
||||
};
|
||||
Reference in New Issue
Block a user