Skip to content

Commit

Permalink
fix(cli): adjust colors for unstable version updates (#5498)
Browse files Browse the repository at this point in the history
This change special-cases the color indicator for updates to
*unstable* package versions, to more accurately indicate
backwards compatibility.

For 0.x.y versions, increments to the *minor* version number will
be considered a *major* version change, and increments to the
*patch* number, will be considered a *minor* version change.

For 0.0.x versions, increments to *any* version number will be
considered a *major* version change.

This matches the behavior of how yarn & npm treats updates to
packages defined with a caret-range.
  • Loading branch information
simonkberg authored and arcanis committed Mar 24, 2018
1 parent b7956d8 commit f4ae8a6
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 5 deletions.
46 changes: 43 additions & 3 deletions __tests__/util/semver.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* @flow */

import {satisfiesWithPrereleases} from '../../src/util/semver.js';

const semver = require('semver');
import semver from 'semver';
import {satisfiesWithPrereleases, diffWithUnstable} from '../../src/util/semver.js';

describe('satisfiesWithPrereleases', () => {
it('matches the behavior of node-semver for non-prerelease versions', () => {
Expand Down Expand Up @@ -54,3 +53,44 @@ describe('satisfiesWithPrereleases', () => {
expect(satisfiesWithPrereleases('1.0.0', '>1.0.0-rc.1')).toBe(true);
});
});

describe('diffWithUnstable', () => {
it('matches the behavior of node-semver for stable versions', () => {
expect(diffWithUnstable('2.0.0', '1.0.0')).toBe(semver.diff('2.0.0', '1.0.0'));
expect(diffWithUnstable('2.1.0', '2.0.0')).toBe(semver.diff('2.1.0', '2.0.0'));
expect(diffWithUnstable('2.1.1', '2.1.0')).toBe(semver.diff('2.1.1', '2.1.0'));
expect(diffWithUnstable('2.1.1-beta', '2.1.0')).toBe(semver.diff('2.1.1-beta', '2.1.0'));
expect(diffWithUnstable('2.1.1-alpha', '2.1.1')).toBe(semver.diff('2.1.1-alpha', '2.1.1'));
expect(diffWithUnstable('2.1.1', '2.1.1')).toBe(semver.diff('2.1.1', '2.1.1'));
expect(diffWithUnstable('2.1.1-rc', '2.1.1-rc')).toBe(semver.diff('2.1.1-rc', '2.1.1-rc'));
});

it('treats patch and minor releases for pre-major versions as minor & major respectively', () => {
expect(diffWithUnstable('0.2.0', '0.2.1')).toBe('minor');
expect(diffWithUnstable('0.2.0', '0.2.1-rc')).toBe('preminor');
expect(diffWithUnstable('0.1.0', '0.2.0')).toBe('major');
expect(diffWithUnstable('0.1.0', '0.2.0-alpha')).toBe('premajor');
expect(diffWithUnstable('0.2.1', '1.0.0')).toBe('major');
expect(diffWithUnstable('0.2.1-rc', '1.0.0-beta')).toBe('premajor');
expect(diffWithUnstable('0.2.2-rc.0', '0.2.2-rc.1')).toBe('prerelease');
});

it('treats patch and minor releases for pre-minor versions as major', () => {
expect(diffWithUnstable('0.0.1', '0.0.2')).toBe('major');
expect(diffWithUnstable('0.0.1', '0.0.2-rc')).toBe('premajor');
expect(diffWithUnstable('0.0.2', '0.1.0')).toBe('major');
expect(diffWithUnstable('0.0.2', '0.1.0-alpha')).toBe('premajor');
expect(diffWithUnstable('0.0.2', '1.0.0')).toBe('major');
expect(diffWithUnstable('0.0.2-rc', '1.0.0-beta')).toBe('premajor');
expect(diffWithUnstable('0.0.2-rc.1', '0.0.2-rc.2')).toBe('prerelease');
});

it('returns null for equal versions', () => {
expect(diffWithUnstable('1.0.0', '1.0.0')).toBeNull();
expect(diffWithUnstable('2.0.1-beta.0', '2.0.1-beta.0')).toBeNull();
expect(diffWithUnstable('0.2.1', '0.2.1')).toBeNull();
expect(diffWithUnstable('0.2.3-alpha', '0.2.3-alpha')).toBeNull();
expect(diffWithUnstable('0.0.11', '0.0.11')).toBeNull();
expect(diffWithUnstable('0.0.100-beta.2', '0.0.100-beta.2')).toBeNull();
});
});
3 changes: 2 additions & 1 deletion src/util/color-for-versions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* @flow */
import semver from 'semver';
import {diffWithUnstable} from './semver.js';
import {VERSION_COLOR_SCHEME} from '../constants.js';
import type {VersionColor} from '../constants.js';

Expand All @@ -8,7 +9,7 @@ export default function(from: string, to: string): VersionColor {
const validTo = semver.valid(to);
let versionBump = 'unknown';
if (validFrom && validTo) {
versionBump = semver.diff(validFrom, validTo) || 'unchanged';
versionBump = diffWithUnstable(validFrom, validTo) || 'unchanged';
}
return VERSION_COLOR_SCHEME[versionBump];
}
65 changes: 64 additions & 1 deletion src/util/semver.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* @flow */

const semver = require('semver');
import semver, {type Release} from 'semver';

/**
* Returns whether the given semver version satisfies the given range. Notably this supports
Expand Down Expand Up @@ -45,3 +45,66 @@ export function satisfiesWithPrereleases(version: string, range: string, loose?:
return !comparatorSet.some(comparator => !comparator.test(semverVersion));
});
}

const PRE_RELEASES = {
major: 'premajor',
minor: 'preminor',
patch: 'prepatch',
};

/**
* Returns the difference between two versions as a semantic string representation.
* Similar to the `diff` method in node-semver, but it also accounts for unstable versions,
* like 0.x.x or 0.0.x.
*/

export function diffWithUnstable(version1: string, version2: string): Release | null {
if (semver.eq(version1, version2) === false) {
const v1 = semver.parse(version1);
const v2 = semver.parse(version2);

if (v1 != null && v2 != null) {
const isPreRelease = v1.prerelease.length > 0 || v2.prerelease.length > 0;
const preMajor = v1.major === 0 || v2.major === 0;
const preMinor = preMajor && (v1.minor === 0 || v2.minor === 0);

let diff = null;

if (v1.major !== v2.major) {
diff = 'major';
} else if (v1.minor !== v2.minor) {
if (preMajor) {
// If the major version number is zero (0.x.x), treat a change
// of the minor version number as a major change.
diff = 'major';
} else {
diff = 'minor';
}
} else if (v1.patch !== v2.patch) {
if (preMinor) {
// If the major & minor version numbers are zero (0.0.x), treat a change
// of the patch version number as a major change.
diff = 'major';
} else if (preMajor) {
// If the major version number is zero (0.x.x), treat a change
// of the patch version number as a minor change.
diff = 'minor';
} else {
diff = 'patch';
}
}

if (isPreRelease) {
if (diff != null) {
diff = PRE_RELEASES[diff];
} else {
diff = 'prerelease';
}
}

return diff;
}
}

return null;
}

0 comments on commit f4ae8a6

Please sign in to comment.