Skip to content

Commit

Permalink
feat: provide major versions
Browse files Browse the repository at this point in the history
  • Loading branch information
sumwatshade committed Aug 4, 2021
1 parent 1cf204a commit 9ac3c71
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 19 deletions.
1 change: 0 additions & 1 deletion .plugin-cli-lazy-cache

This file was deleted.

36 changes: 18 additions & 18 deletions src/commands/use.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import cli from 'cli-ux';
import * as fs from 'fs-extra';
import * as semver from 'semver';
import {
EXTENDED_SEMVER_REGEX,
FUZZY_SEMVER_REGEX,
getMatchingVersions,
} from '../utils/version-calculation';

import UpdateCommand from './update';

const SEMVER_REGEX =
/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?/;

export default class UseCommand extends UpdateCommand {
static description =
'Checks for a previously installed version of the <%= config.bin %> CLI. Throws an error if the version is not found.';
Expand All @@ -27,7 +29,9 @@ export default class UseCommand extends UpdateCommand {

// Check if this command is trying to update the channel. TODO: make this dynamic
const prereleaseChannels = ['alpha', 'beta', 'next'];
const isExplicitVersion = SEMVER_REGEX.test(args.version || '');
const isExplicitVersion =
EXTENDED_SEMVER_REGEX.test(args.version || '') ||
FUZZY_SEMVER_REGEX.test(args.version || '');
const channelUpdateRequested = ['stable', ...prereleaseChannels].some(
(c) => args.version === c,
);
Expand Down Expand Up @@ -58,22 +62,18 @@ export default class UseCommand extends UpdateCommand {
const versions = fs
.readdirSync(this.clientRoot)
.filter((dirOrFile) => dirOrFile !== 'bin' && dirOrFile !== 'current');

// Back out if no local versions are found
if (versions.length === 0)
throw new Error('No locally installed versions found.');
const matchingLocalVersions = versions
.filter((version) => {
// - If the version contains 'partial', ignore it
if (version.includes('partial')) {
return false;
}
// - If we request stable, only provide standard versions...
if (this.channel === 'stable') {
return !prereleaseChannels.some((c) => version.includes(c));
}
// - ... otherwise check if the version is contained
return version.includes(targetVersion);
})
.sort((a, b) => semver.compare(b, a));

// Get matching versions
const matchingLocalVersions = await getMatchingVersions(
versions,
targetVersion,
this.channel,
prereleaseChannels,
);

if (
args.version &&
Expand Down
44 changes: 44 additions & 0 deletions src/utils/version-calculation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as semver from 'semver';

const EXTENDED_SEMVER_REGEX =
/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+)?/;

const SEMVER_REGEX = /^(\d+)\.(\d+)\.(\d+)$/;

const FUZZY_SEMVER_REGEX = /^(\d+)\.?(\d+)?\.?(\d+)?$/;

const getMatchingVersions = async (
versionList: string[],
targetVersion: string,
channel: string,
prereleaseChannels: string[],
): Promise<string[]> => {
const isTargetExplicitSemver = FUZZY_SEMVER_REGEX.test(targetVersion);

return versionList
.filter((version) => {
// - If the version contains 'partial', ignore it
if (version.includes('partial')) {
return false;
}

if (isTargetExplicitSemver && SEMVER_REGEX.test(version)) {
// Ex: 2.1 should capture 2.1.3, 2.1.4, 2.1.5, but not 2.2.0
return version.startsWith(targetVersion);
}
// - If we request stable, only provide standard versions...
if (channel === 'stable') {
return !prereleaseChannels.some((c) => version.includes(c));
}
// - ... otherwise check if the version is contained
return version.includes(targetVersion);
})
.sort((a, b) => semver.compare(b, a));
};

export {
getMatchingVersions,
SEMVER_REGEX,
EXTENDED_SEMVER_REGEX,
FUZZY_SEMVER_REGEX,
};
48 changes: 48 additions & 0 deletions test/commands/use.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,54 @@ describe('Use Command', () => {
);
});

it('will appropriately choose the latest minor from stable channel', async () => {
mockFs.readdirSync.mockReturnValue([
'1.0.0-next.2',
'1.0.0-next.3',
'1.0.1',
'2.1.0',
'2.2.0',
'2.1.5',
'2.1.3',
] as any);

// oclif-example use 2.1
commandInstance = new MockedUseCommand(['2.1'], config);

commandInstance.fetchManifest.mockResolvedValue({
channel: 'stable',
} as IManifest);

await commandInstance.run();

expect(commandInstance.downloadAndExtract).not.toBeCalled();
expect(commandInstance.updatedVersion).toBe('2.1.5');
});

it('will appropriately choose the latest major from stable channel', async () => {
mockFs.readdirSync.mockReturnValue([
'1.0.0-next.2',
'1.0.0-next.3',
'1.0.1',
'2.1.0',
'2.2.0',
'2.1.5',
'2.1.3',
] as any);

// oclif-example use 2.1
commandInstance = new MockedUseCommand(['2'], config);

commandInstance.fetchManifest.mockResolvedValue({
channel: 'stable',
} as IManifest);

await commandInstance.run();

expect(commandInstance.downloadAndExtract).not.toBeCalled();
expect(commandInstance.updatedVersion).toBe('2.2.0');
});

it('will ignore partials when trying to update to stable', async () => {
mockFs.readdirSync.mockReturnValue([
'1.0.0-next.2',
Expand Down
67 changes: 67 additions & 0 deletions test/utils/version-calculation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { getMatchingVersions } from '../../src/utils/version-calculation';

const PRERELEASE_CHANNELS = ['alpha', 'next'];
describe('Version Calculation', () => {
it('fetches a channel-specific set of non-partial channel versions', async () => {
const versionMatch = await getMatchingVersions(
[
'3.0.0-next.0',
'3.0.0-next.1',
'2.4.0',
'3.0.0-next.3',
'3.0.0-next.4',
'3.0.0-alpha.4',
],
'next',
'next',
PRERELEASE_CHANNELS,
);
expect(versionMatch).toStrictEqual([
'3.0.0-next.4',
'3.0.0-next.3',
'3.0.0-next.1',
'3.0.0-next.0',
]);
});

it('only fetches semvers when stable channel is provided', async () => {
const versionMatch = await getMatchingVersions(
['2.4.0', '2.6.0', '2.0.0-next.3', '3.0.0-alpha.5'],
'2.6.0',
'stable',
PRERELEASE_CHANNELS,
);

expect(versionMatch).toStrictEqual(['2.6.0']);
});

it('only fetches semvers when stable channel is targeted', async () => {
const versionMatch = await getMatchingVersions(
['2.4.0', '2.6.0', '2.0.0-next.3', '3.0.0-alpha.5'],
'stable',
'stable',
PRERELEASE_CHANNELS,
);

expect(versionMatch).toStrictEqual(['2.6.0', '2.4.0']);
});

it('Will search for up to the latest minor', async () => {
const versionMatch = await getMatchingVersions(
[
'2.4.0',
'2.4.1',
'2.6.1',
'2.4.2',
'2.6.0',
'2.0.0-next.3',
'3.0.0-alpha.5',
],
'2.4',
'stable',
PRERELEASE_CHANNELS,
);

expect(versionMatch).toStrictEqual(['2.4.2', '2.4.1', '2.4.0']);
});
});

0 comments on commit 9ac3c71

Please sign in to comment.