Skip to content

Commit

Permalink
feat(core): support collecting migrations and filter out the already …
Browse files Browse the repository at this point in the history
…applied ones
  • Loading branch information
leosvelperez committed Feb 13, 2023
1 parent 0b7ef8f commit 6c8f7d6
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 12 deletions.
106 changes: 106 additions & 0 deletions packages/nx/src/command-line/migrate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,112 @@ describe('Migration', () => {
},
});
});

it('should generate the correct migrations when "--only-skipped-migrations"', async () => {
const migrator = new Migrator({
packageJson: createPackageJson({
dependencies: {
parent: '1.0.0',
pkg1: '1.0.0',
},
}),
getInstalledPackageVersion: (p, overrides) => overrides?.[p] ?? '1.0.0',
fetch: (p) => {
if (p === 'parent') {
return Promise.resolve({
version: '2.0.0',
packageGroup: [{ package: 'pkg1', version: '*' }],
generators: {
// previous migration
migration1: {
version: '1.0.0',
description: 'migration1 desc',
requires: {
// didn't meet requirements and now meets requirements, should collect it
pkg1: '>=2.0.0',
},
},
// previous migration
migration2: {
version: '1.0.0',
description: 'migration2 desc',
requires: {
// didn't meet requirements and now doesn't meet requirements, should not collect it
pkg1: '>=3.0.0',
},
},
// previous migration, no requirements, should not collect it
migration3: {
version: '1.0.0',
description: 'migration3 desc',
},
// new migration
migration4: {
version: '2.0.0',
description: 'migration4 desc',
requires: {
// meets requirements, should collect it
pkg1: '>=2.0.0',
},
},
// new migration
migration5: {
version: '2.0.0',
description: 'migration5 desc',
requires: {
// doesn't meet requirements, should not collect it
pkg1: '>=3.0.0',
},
},
// new migrationg, no requirements, should collect it
migration6: {
version: '2.0.0',
description: 'migration6 desc',
},
},
});
} else if (p === 'pkg1') {
return Promise.resolve({ version: '2.0.0' });
} else {
return Promise.resolve(null);
}
},
from: { parent: '0.1.0' },
to: {},
onlySkippedMigrations: true,
});

const result = await migrator.updatePackageJson('parent', '2.0.0');

expect(result).toEqual({
migrations: [
{
version: '1.0.0',
name: 'migration1',
package: 'parent',
description: 'migration1 desc',
requires: { pkg1: '>=2.0.0' },
},
{
version: '2.0.0',
name: 'migration4',
package: 'parent',
description: 'migration4 desc',
requires: { pkg1: '>=2.0.0' },
},
{
version: '2.0.0',
name: 'migration6',
package: 'parent',
description: 'migration6 desc',
},
],
packageJson: {
parent: { version: '2.0.0', addToPackageJson: false },
pkg1: { version: '2.0.0', addToPackageJson: false },
},
});
});
});

describe('normalizeVersions', () => {
Expand Down
80 changes: 69 additions & 11 deletions packages/nx/src/command-line/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import { promisify } from 'util';
import {
MigrationsJson,
MigrationsJsonEntry,
PackageJsonUpdateForPackage,
PackageJsonUpdates,
} from '../config/misc-interfaces';
Expand All @@ -31,7 +32,6 @@ import { logger } from '../utils/logger';
import {
ArrayPackageGroup,
NxMigrationsConfiguration,
PackageGroup,
PackageJson,
readModulePackageJson,
readNxMigrateConfig,
Expand Down Expand Up @@ -99,7 +99,7 @@ export interface MigratorOptions {
packageJson: PackageJson;
getInstalledPackageVersion: (
pkg: string,
overrides: Record<string, string>
overrides?: Record<string, string>
) => string;
fetch: (
pkg: string,
Expand All @@ -108,6 +108,7 @@ export interface MigratorOptions {
from: { [pkg: string]: string };
to: { [pkg: string]: string };
interactive?: boolean;
onlySkippedMigrations?: boolean;
}

export class Migrator {
Expand All @@ -117,6 +118,7 @@ export class Migrator {
private readonly installedPkgVersionOverrides: MigratorOptions['from'];
private readonly to: MigratorOptions['to'];
private readonly interactive: MigratorOptions['interactive'];
private readonly onlySkippedMigrations: MigratorOptions['onlySkippedMigrations'];
private readonly packageJsonUpdates: Record<
string,
PackageJsonUpdateForPackage
Expand All @@ -130,6 +132,7 @@ export class Migrator {
this.installedPkgVersionOverrides = opts.from;
this.to = opts.to;
this.interactive = opts.interactive;
this.onlySkippedMigrations = opts.onlySkippedMigrations;
}

async updatePackageJson(targetPackage: string, targetVersion: string) {
Expand Down Expand Up @@ -159,7 +162,7 @@ export class Migrator {
migration.version &&
this.gt(migration.version, currentVersion) &&
this.lte(migration.version, version) &&
this.areRequirementsMet(migration.requires)
this.areMigrationRequirementsMet(packageName, migration)
)
.map(([migrationName, migration]) => ({
...migration,
Expand Down Expand Up @@ -249,17 +252,21 @@ export class Migrator {
}
this.collectedVersions[targetPackage] = targetVersion;

this.addPackageJsonUpdate(targetPackage, {
version: migrationConfig.version,
addToPackageJson: target.addToPackageJson || false,
});

const { packageJsonUpdates, packageGroupOrder } =
this.getPackageJsonUpdatesFromMigrationConfig(
targetPackage,
targetVersion,
migrationConfig
);

this.addPackageJsonUpdate(targetPackage, {
version: migrationConfig.version,
addToPackageJson: target.addToPackageJson || false,
});
if (!packageJsonUpdates.length) {
return [];
}

const shouldCheckUpdates = packageJsonUpdates.some(
(packageJsonUpdate) =>
Expand Down Expand Up @@ -465,6 +472,55 @@ export class Migrator {
);
}

private areMigrationRequirementsMet(
packageName: string,
migration: MigrationsJsonEntry
): boolean {
if (!this.onlySkippedMigrations) {
return this.areRequirementsMet(migration.requires);
}

return (
(this.wasMigrationSkipped(migration.requires) ||
this.isMigrationForHigherVersionThanWhatIsInstalled(
packageName,
migration
)) &&
this.areRequirementsMet(migration.requires)
);
}

private isMigrationForHigherVersionThanWhatIsInstalled(
packageName: string,
migration: MigrationsJsonEntry
): boolean {
const installedVersion = this.getInstalledPackageVersion(packageName);

return (
migration.version &&
(!installedVersion || this.gt(migration.version, installedVersion)) &&
this.lte(migration.version, this.packageJsonUpdates[packageName].version)
);
}

private wasMigrationSkipped(
requirements: PackageJsonUpdates[string]['requires']
): boolean {
// no requiremets, so it ran before
if (!requirements || !Object.keys(requirements).length) {
return false;
}

// at least a requirement was not met, it was skipped
return Object.entries(requirements).some(
([pkgName, versionRange]) =>
!this.getInstalledPackageVersion(pkgName) ||
!satisfies(this.getInstalledPackageVersion(pkgName), versionRange, {
includePrerelease: true,
})
);
}

private async runPackageJsonUpdatesConfirmationPrompt(
confirmationPrompt: string
): Promise<boolean> {
Expand Down Expand Up @@ -606,6 +662,7 @@ type GenerateMigrations = {
from: { [k: string]: string };
to: { [k: string]: string };
interactive?: boolean;
onlySkippedMigrations?: boolean;
};

type RunMigrations = { type: 'runMigrations'; runMigrations: string };
Expand All @@ -625,14 +682,14 @@ export function parseMigrationsOptions(options: {
const { targetPackage, targetVersion } = parseTargetPackageAndVersion(
options['packageAndVersion']
);
const interactive = options.interactive;
return {
type: 'generateMigrations',
targetPackage: normalizeSlashes(targetPackage),
targetVersion,
from,
to,
interactive,
interactive: options.interactive,
onlySkippedMigrations: options.onlySkippedMigrations,
};
} else {
return {
Expand All @@ -649,10 +706,10 @@ function createInstalledPackageVersionsResolver(

function getInstalledPackageVersion(
packageName: string,
overrides: Record<string, string>
overrides?: Record<string, string>
): string | null {
try {
if (overrides[packageName]) {
if (overrides?.[packageName]) {
return overrides[packageName];
}

Expand Down Expand Up @@ -1018,6 +1075,7 @@ async function generateMigrationsJsonAndUpdatePackageJson(
from: opts.from,
to: opts.to,
interactive: opts.interactive,
onlySkippedMigrations: opts.onlySkippedMigrations,
});

const { migrations, packageJson } = await migrator.updatePackageJson(
Expand Down
15 changes: 14 additions & 1 deletion packages/nx/src/command-line/nx-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -961,12 +961,25 @@ function withMigrationOptions(yargs: yargs.Argv) {
// TODO(leo): make it visible when we publish the docs about it
hidden: true,
})
.check(({ createCommits, commitPrefix }) => {
.option('onlySkippedMigrations', {
describe:
'Collect only migrations that were previously skipped. To be used with --from',
type: 'boolean',
default: false,
// TODO(leo): make it visible when we publish the docs about it
hidden: true,
})
.check(({ createCommits, commitPrefix, from, onlySkippedMigrations }) => {
if (!createCommits && commitPrefix !== defaultCommitPrefix) {
throw new Error(
'Error: Providing a custom commit prefix requires --create-commits to be enabled'
);
}
if (onlySkippedMigrations && !from) {
throw new Error(
'Error: Collecting only migrations that were previously skipped requires --from to be set'
);
}
return true;
});
}
Expand Down

0 comments on commit 6c8f7d6

Please sign in to comment.