chore: rework how we do the bumping as a seperate command

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2024-04-16 11:01:28 +02:00
parent 2ccd389166
commit 03f3bb5808
5 changed files with 125 additions and 49 deletions
@@ -45,6 +45,7 @@ export interface BackstagePackageJson {
backstage?: {
role?: PackageRole;
moved?: string;
};
exports?: JsonValue;
@@ -136,8 +137,8 @@ export class PackageGraph extends Map<string, PackageGraphNode> {
* Lists all local packages in a monorepo.
*/
static async listTargetPackages(): Promise<BackstagePackage[]> {
console.log(paths.targetDir);
const { packages } = await getPackages(paths.targetDir);
return packages as BackstagePackage[];
}
+4
View File
@@ -397,6 +397,10 @@ export function registerCommands(program: Command) {
program
.command('versions:migrate')
.option(
'--pattern <glob>',
'Override glob for matching packages to upgrade',
)
.description(
'Migrate any plugins that have been moved to the @backstage-community namespace automatically',
)
+1 -1
View File
@@ -470,7 +470,7 @@ export async function bumpBackstageJsonVersion(version: string) {
);
}
async function runYarnInstall() {
export async function runYarnInstall() {
const spinner = ora({
prefixText: `Running ${chalk.blue('yarn install')} to install new versions`,
spinner: 'arc',
@@ -43,36 +43,9 @@ jest.mock('../../lib/run', () => {
};
});
const mockFetchPackageInfo = jest.fn();
jest.mock('../../lib/versioning/packages', () => {
const actual = jest.requireActual('../../lib/versioning/packages');
return {
...actual,
fetchPackageInfo: (name: string) => mockFetchPackageInfo(name),
};
});
const REGISTRY_VERSIONS: { [name: string]: string } = {
'@backstage/core': '1.0.6',
'@backstage/core-api': '1.0.7',
'@backstage/theme': '2.0.0',
'@backstage-extra/custom': '1.1.0',
'@backstage-extra/custom-two': '2.0.0',
'@backstage/create-app': '1.0.0',
};
describe('versions:migrate', () => {
mockDir = createMockDirectory();
beforeEach(() => {
mockFetchPackageInfo.mockImplementation(async name => ({
name: name,
'dist-tags': {
latest: REGISTRY_VERSIONS[name],
},
}));
});
afterEach(() => {
jest.resetAllMocks();
});
@@ -84,6 +57,28 @@ describe('versions:migrate', () => {
packages: ['packages/*'],
},
}),
node_modules: {
'@backstage': {
custom: {
'package.json': JSON.stringify({
name: '@backstage-extra/custom',
version: '1.0.1',
backstage: {
moved: '@backstage-community/custom',
},
}),
},
'custom-two': {
'package.json': JSON.stringify({
name: '@backstage-extra/custom-two',
version: '1.0.0',
backstage: {
moved: '@backstage-community/custom-two',
},
}),
},
},
},
packages: {
a: {
'package.json': JSON.stringify({
@@ -110,7 +105,6 @@ describe('versions:migrate', () => {
});
jest.spyOn(run, 'run').mockResolvedValue(undefined);
console.log(paths);
await migrate({});
const { log: logs } = await withLogCollector(['log'], async () => {});
@@ -141,10 +135,6 @@ describe('versions:migrate', () => {
// 'Version bump complete!',
// ]);
expect(mockFetchPackageInfo).toHaveBeenCalledTimes(5);
expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/core');
expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/theme');
expect(run.run).toHaveBeenCalledTimes(1);
expect(run.run).toHaveBeenCalledWith(
'yarn',
@@ -155,12 +145,13 @@ describe('versions:migrate', () => {
const packageA = await fs.readJson(
mockDir.resolve('packages/a/package.json'),
);
expect(packageA).toEqual({
name: 'a',
dependencies: {
'@backstage-community/custom': '^1.1.0',
'@backstage-community/custom-two': '^2.0.0',
'@backstage/core': '^1.0.6',
'@backstage-community/custom': '^1.0.1',
'@backstage-community/custom-two': '^1.0.0',
'@backstage/core': '^1.0.5',
},
});
const packageB = await fs.readJson(
@@ -170,9 +161,9 @@ describe('versions:migrate', () => {
name: 'b',
dependencies: {
'@backstage-community/custom': '^1.1.0',
'@backstage-community/custom-two': '^2.0.0',
'@backstage/core': '^1.0.6',
'@backstage/theme': '^2.0.0',
'@backstage-community/custom-two': '^1.0.0',
'@backstage/core': '^1.0.3',
'@backstage/theme': '^1.0.0',
},
});
});
+89 -9
View File
@@ -13,17 +13,97 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PackageGraph } from '@backstage/cli-node';
import { BackstagePackageJson, PackageGraph } from '@backstage/cli-node';
import chalk from 'chalk';
import { resolve as resolvePath } from 'path';
import { OptionValues } from 'commander';
import { readJson, writeJson } from 'fs-extra';
import { minimatch } from 'minimatch';
import { runYarnInstall } from './bump';
export default async (_: OptionValues) => {
const packageMap = PackageGraph.fromPackages(
await PackageGraph.listTargetPackages(),
);
export default async (options: OptionValues) => {
const changed = await migrateMovedPackages({
pattern: options.pattern,
});
const packagesThatHaveMoved = new Map<string, string>();
for (const [name, pkg] of packageMap.entries()) {
console.log(name, pkg);
if (changed) {
await runYarnInstall();
}
};
export async function migrateMovedPackages(options?: { pattern?: string }) {
const packages = await PackageGraph.listTargetPackages();
const thingsThatHaveMoved = new Map<
string,
{
dependencies: { [k: string]: string };
devDependencies: { [k: string]: string };
peerDependencies: { [k: string]: string };
}
>();
let didAnythingChange = false;
for (const pkg of packages) {
const pkgName = pkg.packageJson.name;
thingsThatHaveMoved.set(pkgName, {
dependencies: {},
devDependencies: {},
peerDependencies: {},
});
let didPackageChange = false;
for (const depType of [
'dependencies',
'devDependencies',
'peerDependencies',
] as const) {
const depsObj = pkg.packageJson[depType];
if (!depsObj) {
continue;
}
for (const [depName, depVersion] of Object.entries(depsObj)) {
if (options?.pattern && !minimatch(depName, options.pattern)) {
continue;
}
let packageInfo: BackstagePackageJson;
try {
packageInfo = await readJson(
require.resolve(`${depName}/package.json`, {
paths: [pkg.dir],
}),
);
} catch (ex) {
console.warn(
chalk.yellow`Could not find package.json for ${depName}@${depVersion} in ${pkgName} (${depType})`,
);
continue;
}
const movedPackageName = packageInfo.backstage?.moved;
if (movedPackageName) {
console.log(
chalk.yellow`Found a moved package ${depName}@${depVersion} -> ${movedPackageName} in ${pkgName} (${depType})`,
);
didPackageChange = true;
didAnythingChange = true;
depsObj[movedPackageName] = depsObj[depName];
delete depsObj[depName];
}
}
}
if (didPackageChange) {
await writeJson(resolvePath(pkg.dir, 'package.json'), pkg.packageJson, {
spaces: 2,
});
}
}
return didAnythingChange;
}