Skip to content

Commit

Permalink
fix(core): migrate should parse versions that don't follow semver
Browse files Browse the repository at this point in the history
  • Loading branch information
vsavkin committed Jan 16, 2020
1 parent 1df2cd6 commit 3940a1f
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 41 deletions.
94 changes: 93 additions & 1 deletion packages/tao/src/commands/migrate.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Migrator } from './migrate';
import { Migrator, normalizeVersion, parseMigrationsOptions } from './migrate';

describe('Migration', () => {
describe('packageJson patch', () => {
Expand Down Expand Up @@ -430,4 +430,96 @@ describe('Migration', () => {
});
});
});

describe('normalizeVersions', () => {
it('should return version when it meets semver requirements', () => {
expect(normalizeVersion('1.2.3')).toEqual('1.2.3');
expect(normalizeVersion('1.2.3-beta.1')).toEqual('1.2.3-beta.1');
});

it('should handle versions missing a patch or a minor', () => {
expect(normalizeVersion('1.2')).toEqual('1.2.0');
expect(normalizeVersion('1')).toEqual('1.0.0');
expect(normalizeVersion('1-beta.1')).toEqual('1.0.0-beta.1');
});

it('should handle incorrect versions', () => {
expect(normalizeVersion('1-invalid-version')).toEqual('1.0.0-invalid');
expect(normalizeVersion('1.invalid-version')).toEqual('1.0.0');
expect(normalizeVersion('invalid-version')).toEqual('0.0.0');
});
});

describe('parseMigrationsOptions', () => {
it('should work', () => {
const r = parseMigrationsOptions([
'8.12.0',
'--from',
'@myscope/a@12.3,@myscope/b@1.1.1',
'--to',
'@myscope/c@12.3.1'
]);
expect(r).toEqual({
type: 'generateMigrations',
targetPackage: '@nrwl/workspace',
targetVersion: '8.12.0',
from: {
'@myscope/a': '12.3.0',
'@myscope/b': '1.1.1'
},
to: {
'@myscope/c': '12.3.1'
}
});
});

it('should handle different variations of the target package', () => {
expect(parseMigrationsOptions(['8.12'])).toMatchObject({
targetPackage: '@nrwl/workspace',
targetVersion: '8.12.0'
});
expect(parseMigrationsOptions(['8'])).toMatchObject({
targetPackage: '@nrwl/workspace',
targetVersion: '8.0.0'
});
expect(parseMigrationsOptions(['@nrwl/workspace@8.12'])).toMatchObject({
targetPackage: '@nrwl/workspace',
targetVersion: '8.12.0'
});
expect(parseMigrationsOptions(['mypackage@8.12'])).toMatchObject({
targetPackage: 'mypackage',
targetVersion: '8.12.0'
});
expect(() => parseMigrationsOptions(['mypackage@latest'])).toThrowError(
`Incorrect version "latest". You must use a semver version (cannot use "latest" or "next").`
);
expect(() => parseMigrationsOptions(['@nrwl/workspace'])).toThrowError(
`Provide the correct package name and version. E.g., @nrwl/workspace@9.0.0.`
);
});

it('should handle incorrect from', () => {
expect(() =>
parseMigrationsOptions(['8.12.0', '--from', '@myscope/a@'])
).toThrowError(`Incorrect 'from' section. Use --from="package@version"`);
expect(() =>
parseMigrationsOptions(['8.12.0', '--from', '@myscope/a'])
).toThrowError(`Incorrect 'from' section. Use --from="package@version"`);
expect(() =>
parseMigrationsOptions(['8.12.0', '--from', 'myscope'])
).toThrowError(`Incorrect 'from' section. Use --from="package@version"`);
});

it('should handle incorrect from', () => {
expect(() =>
parseMigrationsOptions(['8.12.0', '--to', '@myscope/a@'])
).toThrowError(`Incorrect 'to' section. Use --to="package@version"`);
expect(() =>
parseMigrationsOptions(['8.12.0', '--to', '@myscope/a'])
).toThrowError(`Incorrect 'to' section. Use --to="package@version"`);
expect(() =>
parseMigrationsOptions(['8.12.0', '--to', 'myscope'])
).toThrowError(`Incorrect 'to' section. Use --to="package@version"`);
});
});
});
136 changes: 96 additions & 40 deletions packages/tao/src/commands/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import * as stripJsonComments from 'strip-json-comments';
import { dirSync } from 'tmp';
import { getLogger } from '../shared/logger';
import { convertToCamelCase, handleErrors } from '../shared/params';
import { commandName } from '../shared/print-help';
import minimist = require('minimist');

export type MigrationsJson = {
Expand Down Expand Up @@ -231,19 +230,54 @@ export class Migrator {
}

private gt(v1: string, v2: string) {
return gt(this.normalizeVersion(v1), this.normalizeVersion(v2));
return gt(normalizeVersion(v1), normalizeVersion(v2));
}

private lte(v1: string, v2: string) {
return lte(this.normalizeVersion(v1), this.normalizeVersion(v2));
return lte(normalizeVersion(v1), normalizeVersion(v2));
}
}

private normalizeVersion(v: string) {
if (v.startsWith('8-')) return '8.0.0-beta.1';
if (v.startsWith('9-')) return '9.0.0-beta.1';
if (v.startsWith('10-')) return '9.0.0-beta.1';
if (v.startsWith('11-')) return '9.0.0-beta.1';
return v;
export function normalizeVersionWithTagCheck(version: string) {
if (version[0].match(/[0-9]/)) {
return normalizeVersion(version);
} else {
throw new Error(
`Incorrect version "${version}". You must use a semver version (cannot use "latest" or "next").`
);
}
}

export function normalizeVersion(version: string) {
const [v, t] = version.split('-');
const [major, minor, patch] = v.split('.');
const newV = `${major || 0}.${minor || 0}.${patch || 0}`;
const newVersion = t ? `${newV}-${t}` : newV;

try {
gt(newVersion, '0.0.0');
return newVersion;
} catch (e) {
try {
gt(newV, '0.0.0');
return newV;
} catch (e) {
const withoutPatch = `${major || 0}.${minor || 0}.0`;
try {
if (gt(withoutPatch, '0.0.0')) {
return withoutPatch;
}
} catch (e) {
const withoutPatchAndMinor = `${major || 0}.0.0`;
try {
if (gt(withoutPatchAndMinor, '0.0.0')) {
return withoutPatchAndMinor;
}
} catch (e) {
return '0.0.0';
}
}
}
}
}

Expand All @@ -256,7 +290,7 @@ type GenerateMigrations = {
};
type RunMigrations = { type: 'runMigrations'; runMigrations: string };

function parseMigrationsOptions(
export function parseMigrationsOptions(
args: string[]
): GenerateMigrations | RunMigrations {
const options = convertToCamelCase(
Expand All @@ -268,36 +302,11 @@ function parseMigrationsOptions(
})
);
if (!options.runMigrations) {
let from = {};
if (options.from) {
options.from.split(',').forEach(p => {
const split = p.lastIndexOf('@');
from[p.substring(0, split)] = p.substring(split + 1);
});
}

let to = {};
if (options.to) {
options.to.split(',').forEach(p => {
const split = p.lastIndexOf('@');
to[p.substring(0, split)] = p.substring(split + 1);
});
}

let targetPackage;
let targetVersion;
if (args[0] && args[0].indexOf('@') > 1) {
const i = args[0].lastIndexOf('@');
targetPackage = args[0].substring(0, i);
targetVersion = args[0].substring(i + 1);
} else if (args[0]) {
targetPackage = '@nrwl/workspace';
targetVersion = args[0];
} else {
targetPackage = '@nrwl/workspace';
targetVersion = 'latest';
}

const from = options.from ? versionOverrides(options.from, 'from') : {};
const to = options.to ? versionOverrides(options.to, 'to') : {};
const { targetPackage, targetVersion } = parseTargetPackageAndVersion(
args[0]
);
return {
type: 'generateMigrations',
targetPackage,
Expand All @@ -310,6 +319,53 @@ function parseMigrationsOptions(
}
}

function parseTargetPackageAndVersion(args: string) {
if (!args) {
throw new Error(
`Provide the correct package name and version. E.g., @nrwl/workspace@9.0.0.`
);
}

if (args.indexOf('@') > -1) {
const i = args.lastIndexOf('@');
const targetPackage = args.substring(0, i);
const maybeVersion = args.substring(i + 1);
if (!targetPackage || !maybeVersion) {
throw new Error(
`Provide the correct package name and version. E.g., @nrwl/workspace@9.0.0.`
);
}
const targetVersion = normalizeVersionWithTagCheck(maybeVersion);
return { targetPackage, targetVersion };
} else {
return {
targetPackage: '@nrwl/workspace',
targetVersion: normalizeVersionWithTagCheck(args)
};
}
}

function versionOverrides(overrides: string, param: string) {
const res = {};
overrides.split(',').forEach(p => {
const split = p.lastIndexOf('@');
if (split === -1 || split === 0) {
throw new Error(
`Incorrect '${param}' section. Use --${param}="package@version"`
);
}
const selectedPackage = p.substring(0, split).trim();
const selectedVersion = p.substring(split + 1).trim();
if (!selectedPackage || !selectedVersion) {
throw new Error(
`Incorrect '${param}' section. Use --${param}="package@version"`
);
}
res[selectedPackage] = normalizeVersionWithTagCheck(selectedVersion);
});
return res;
}

function versions(root: string, from: { [p: string]: string }) {
return (packageName: string) => {
try {
Expand Down

0 comments on commit 3940a1f

Please sign in to comment.