feat: starting work on migration tooling
Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -120,6 +120,7 @@
|
||||
"react-dev-utils": "^12.0.0-next.60",
|
||||
"react-refresh": "^0.14.0",
|
||||
"recursive-readdir": "^2.2.2",
|
||||
"replace-in-file": "^7.1.0",
|
||||
"rollup": "^4.0.0",
|
||||
"rollup-plugin-dts": "^6.1.0",
|
||||
"rollup-plugin-esbuild": "^6.1.1",
|
||||
|
||||
@@ -402,6 +402,10 @@ export function registerCommands(program: Command) {
|
||||
'--pattern <glob>',
|
||||
'Override glob for matching packages to upgrade',
|
||||
)
|
||||
.option(
|
||||
'--skip-code-changes',
|
||||
'Skip code changes and only update package.json files',
|
||||
)
|
||||
.description(
|
||||
'Migrate any plugins that have been moved to the @backstage-community namespace automatically',
|
||||
)
|
||||
|
||||
@@ -216,6 +216,7 @@ describe('bump', () => {
|
||||
'bumping @backstage/core in b to ^1.0.6',
|
||||
'bumping @backstage/theme in b to ^2.0.0',
|
||||
'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 ~> 2.0.0',
|
||||
' https://github.com/backstage/backstage/blob/master/packages/theme/CHANGELOG.md',
|
||||
@@ -323,6 +324,7 @@ describe('bump', () => {
|
||||
'bumping @backstage/core in b to ^1.0.6',
|
||||
'bumping @backstage/theme in b to ^2.0.0',
|
||||
'Skipping yarn install',
|
||||
'Checking for moved packages to the @backstage-community namespace...',
|
||||
'⚠️ The following packages may have breaking changes:',
|
||||
' @backstage/theme : 1.0.0 ~> 2.0.0',
|
||||
' https://github.com/backstage/backstage/blob/master/packages/theme/CHANGELOG.md',
|
||||
@@ -437,6 +439,7 @@ describe('bump', () => {
|
||||
'bumping @backstage/core in a to ^1.0.6',
|
||||
'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',
|
||||
@@ -640,6 +643,7 @@ describe('bump', () => {
|
||||
'bumping @backstage/core in a to ^1.0.6',
|
||||
'Your project is now at version 1.0.0, 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',
|
||||
@@ -745,6 +749,7 @@ describe('bump', () => {
|
||||
'bumping @backstage/theme in b to ^2.0.0',
|
||||
'Skipping backstage.json update as custom pattern is used',
|
||||
'Running yarn install to install new versions',
|
||||
'Checking for moved packages to the @backstage-community namespace...',
|
||||
'⚠️ The following packages may have breaking changes:',
|
||||
' @backstage-extra/custom-two : 1.0.0 ~> 2.0.0',
|
||||
' @backstage/theme : 1.0.0 ~> 2.0.0',
|
||||
@@ -968,6 +973,7 @@ describe('bump', () => {
|
||||
'bumping @backstage/core in b to ^1.0.6',
|
||||
'bumping @backstage/theme in b to ^2.0.0',
|
||||
'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 ~> 2.0.0',
|
||||
' https://github.com/backstage/backstage/blob/master/packages/theme/CHANGELOG.md',
|
||||
|
||||
@@ -266,9 +266,12 @@ export default async (opts: OptionValues) => {
|
||||
}
|
||||
|
||||
if (!opts.skipMigrate) {
|
||||
console.log();
|
||||
|
||||
const changed = await migrateMovedPackages({
|
||||
pattern: opts.pattern,
|
||||
});
|
||||
|
||||
if (changed && !opts.skipInstall) {
|
||||
await runYarnInstall();
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ describe('versions:migrate', () => {
|
||||
});
|
||||
|
||||
expectLogsToMatch(logs, [
|
||||
'Checking for moved packages to the @backstage-community namespace...',
|
||||
'Found a moved package @backstage/custom@^1.0.1 -> @backstage-community/custom in a (dependencies)',
|
||||
'Found a moved package @backstage/custom-two@^1.0.0 -> @backstage-community/custom-two in a (dependencies)',
|
||||
'Found a moved package @backstage/custom@^1.1.0 -> @backstage-community/custom in b (dependencies)',
|
||||
@@ -164,4 +165,96 @@ describe('versions:migrate', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should replace the occurences of the moved package in files inside the correct package', async () => {
|
||||
mockDir.setContent({
|
||||
'package.json': JSON.stringify({
|
||||
workspaces: {
|
||||
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({
|
||||
name: 'a',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.5',
|
||||
'@backstage/custom': '^1.0.1',
|
||||
'@backstage/custom-two': '^1.0.0',
|
||||
},
|
||||
}),
|
||||
src: {
|
||||
'index.ts': "import { myThing } from '@backstage/custom';",
|
||||
'index.test.ts': "import { myThing } from '@backstage/custom-two';",
|
||||
},
|
||||
},
|
||||
b: {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'b',
|
||||
dependencies: {
|
||||
'@backstage/core': '^1.0.3',
|
||||
'@backstage/theme': '^1.0.0',
|
||||
'@backstage/custom': '^1.1.0',
|
||||
'@backstage/custom-two': '^1.0.0',
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
jest.spyOn(run, 'run').mockResolvedValue(undefined);
|
||||
|
||||
const s = await withLogCollector(async () => {
|
||||
await migrate({});
|
||||
});
|
||||
|
||||
console.log(s);
|
||||
|
||||
expect(run.run).toHaveBeenCalledTimes(1);
|
||||
expect(run.run).toHaveBeenCalledWith(
|
||||
'yarn',
|
||||
['install'],
|
||||
expect.any(Object),
|
||||
);
|
||||
|
||||
const indexA = await fs.readFile(
|
||||
mockDir.resolve('packages/a/src/index.ts'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
expect(indexA).toEqual(
|
||||
"import { myThing } from '@backstage-community/custom';",
|
||||
);
|
||||
|
||||
const indexTestA = await fs.readFile(
|
||||
mockDir.resolve('packages/a/src/index.test.ts'),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
expect(indexTestA).toEqual(
|
||||
"import { myThing } from '@backstage-community/custom-two';",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,15 +15,33 @@
|
||||
*/
|
||||
import { BackstagePackageJson, PackageGraph } from '@backstage/cli-node';
|
||||
import chalk from 'chalk';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { resolve as resolvePath, join } from 'path';
|
||||
import { OptionValues } from 'commander';
|
||||
import { readJson, writeJson } from 'fs-extra';
|
||||
import { minimatch } from 'minimatch';
|
||||
import { runYarnInstall } from './bump';
|
||||
import replace from 'replace-in-file';
|
||||
|
||||
declare module 'replace-in-file' {
|
||||
export default function (config: {
|
||||
files: string | string[];
|
||||
processor: (content: string, file: string) => string;
|
||||
ignore?: string | string[];
|
||||
allowEmptyPaths?: boolean;
|
||||
}): Promise<
|
||||
{
|
||||
file: string;
|
||||
hasChanged: boolean;
|
||||
numMatches?: number;
|
||||
numReplacements?: number;
|
||||
}[]
|
||||
>;
|
||||
}
|
||||
|
||||
export default async (options: OptionValues) => {
|
||||
const changed = await migrateMovedPackages({
|
||||
pattern: options.pattern,
|
||||
skipCodeChanges: options.skipCodeChanges,
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
@@ -31,29 +49,20 @@ export default async (options: OptionValues) => {
|
||||
}
|
||||
};
|
||||
|
||||
export async function migrateMovedPackages(options?: { pattern?: string }) {
|
||||
export async function migrateMovedPackages(options?: {
|
||||
pattern?: string;
|
||||
skipCodeChanges?: boolean;
|
||||
}) {
|
||||
console.log(
|
||||
'Checking for moved packages to the @backstage-community namespace...',
|
||||
);
|
||||
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;
|
||||
const movedPackages = new Map<string, string>();
|
||||
|
||||
for (const depType of [
|
||||
'dependencies',
|
||||
@@ -87,6 +96,7 @@ export async function migrateMovedPackages(options?: { pattern?: string }) {
|
||||
const movedPackageName = packageInfo.backstage?.moved;
|
||||
|
||||
if (movedPackageName) {
|
||||
movedPackages.set(depName, movedPackageName);
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
`Found a moved package ${depName}@${depVersion} -> ${movedPackageName} in ${pkgName} (${depType})`,
|
||||
@@ -106,6 +116,33 @@ export async function migrateMovedPackages(options?: { pattern?: string }) {
|
||||
await writeJson(resolvePath(pkg.dir, 'package.json'), pkg.packageJson, {
|
||||
spaces: 2,
|
||||
});
|
||||
|
||||
if (!options?.skipCodeChanges) {
|
||||
// Replace all occurrences of the old package names in the code.
|
||||
const files = await replace({
|
||||
files: join(pkg.dir, '**', '*.{ts,tsx,js,jsx}'),
|
||||
ignore: ['/node_modules/', '/yarn.lock/'],
|
||||
allowEmptyPaths: true,
|
||||
processor: content => {
|
||||
return Array.from(movedPackages.entries()).reduce(
|
||||
(newContent, [oldName, newName]) => {
|
||||
return newContent
|
||||
.replace(new RegExp(`'${oldName}'`, 'g'), `'${newName}'`)
|
||||
.replace(new RegExp(`"${oldName}"`, 'g'), `"${newName}"`);
|
||||
},
|
||||
content,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
if (files.length > 0) {
|
||||
console.log(
|
||||
chalk.green(
|
||||
`Updated ${files.length} files in ${pkgName} to use the new package names`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3729,6 +3729,7 @@ __metadata:
|
||||
react-dev-utils: ^12.0.0-next.60
|
||||
react-refresh: ^0.14.0
|
||||
recursive-readdir: ^2.2.2
|
||||
replace-in-file: ^7.1.0
|
||||
rollup: ^4.0.0
|
||||
rollup-plugin-dts: ^6.1.0
|
||||
rollup-plugin-esbuild: ^6.1.1
|
||||
@@ -29082,7 +29083,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"glob@npm:^8.0.0, glob@npm:^8.0.1, glob@npm:^8.0.3":
|
||||
"glob@npm:^8.0.0, glob@npm:^8.0.1, glob@npm:^8.0.3, glob@npm:^8.1.0":
|
||||
version: 8.1.0
|
||||
resolution: "glob@npm:8.1.0"
|
||||
dependencies:
|
||||
@@ -40432,6 +40433,19 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"replace-in-file@npm:^7.1.0":
|
||||
version: 7.1.0
|
||||
resolution: "replace-in-file@npm:7.1.0"
|
||||
dependencies:
|
||||
chalk: ^4.1.2
|
||||
glob: ^8.1.0
|
||||
yargs: ^17.7.2
|
||||
bin:
|
||||
replace-in-file: bin/cli.js
|
||||
checksum: 2ed61bd0cf0752b18775b52342ad36f4ee6c806f7eca0b0d085c23bafe0cb4828e4ec8f59058bde6b67d2ed5ac51e1681284f586089b58b966e2489712830db0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"request@npm:^2.88.0":
|
||||
version: 2.88.2
|
||||
resolution: "request@npm:2.88.2"
|
||||
|
||||
Reference in New Issue
Block a user