Skip to content

Commit 5da2252

Browse files
committed
feat(version): add handling for deprecated Ghost versions
1 parent 9ddcf0b commit 5da2252

File tree

4 files changed

+78
-29
lines changed

4 files changed

+78
-29
lines changed

lib/commands/install.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class InstallCommand extends Command {
8080
const semver = require('semver');
8181
const {SystemError} = require('../errors');
8282
const {resolveVersion, versionFromZip} = require('../utils/version');
83-
let {version, zip, v1, fromExport} = ctx.argv;
83+
let {version, zip, v1, fromExport, force} = ctx.argv;
8484
let exportVersion = null;
8585

8686
if (fromExport) {
@@ -105,7 +105,7 @@ class InstallCommand extends Command {
105105
if (zip) {
106106
resolvedVersion = await versionFromZip(zip);
107107
} else {
108-
resolvedVersion = await resolveVersion(version, null, {v1});
108+
resolvedVersion = await resolveVersion(version, null, {v1, force});
109109
}
110110

111111
if (exportVersion && semver.lt(resolvedVersion, exportVersion)) {
@@ -174,6 +174,10 @@ InstallCommand.options = {
174174
description: 'Check for empty directory before installing',
175175
type: 'boolean',
176176
default: true
177+
},
178+
force: {
179+
description: 'Force installing a particular version',
180+
type: 'boolean'
177181
}
178182
};
179183
InstallCommand.runPreChecks = true;

lib/utils/version.js

+27-7
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,39 @@ const utils = {
2020
.filter(v => semver.satisfies(v, MIN_RELEASE))
2121
.sort(semver.rcompare);
2222

23+
const deprecations = Object.keys(result.versions)
24+
.reduce((obj, v) => {
25+
if (result.versions[v].deprecated) {
26+
obj[v] = result.versions[v].deprecated;
27+
}
28+
29+
return obj;
30+
}, {});
31+
2332
if (!versions.length) {
2433
return {
2534
latest: null,
2635
latestMajor: {},
27-
all: []
36+
all: [],
37+
deprecations: {}
2838
};
2939
}
3040

31-
const latestMajor = versions.reduce((result, v) => {
41+
const latestMajor = versions.reduce((majors, v) => {
3242
const key = `v${semver.major(v)}`;
3343

34-
if (!result[key]) {
35-
return {...result, [key]: v};
44+
if (!majors[key] && !deprecations[v]) {
45+
majors[key] = v;
3646
}
3747

38-
return result;
48+
return majors;
3949
}, {});
4050

4151
return {
42-
latest: versions[0],
52+
latest: versions.find(v => !deprecations[v]),
4353
latestMajor,
44-
all: versions
54+
all: versions,
55+
deprecations
4556
};
4657
},
4758

@@ -131,6 +142,15 @@ const utils = {
131142
if (customVersion) {
132143
const normalizedVersion = versions.latestMajor[customVersion.replace(/^v?/, 'v')] || customVersion;
133144
version = utils.checkCustomVersion(normalizedVersion, versions.all, activeVersion, opts);
145+
146+
if (versions.deprecations[version] && !opts.force) {
147+
const deprecation = versions.deprecations[version];
148+
149+
throw new CliError({
150+
message: `You are trying to install Ghost v${version}, which has been deprecated with the following notice: "${deprecation}"`,
151+
help: `Either pick a different version to install, or re-run with --force to install anyways.`
152+
});
153+
}
134154
}
135155
const latest = version || latestVersion;
136156

test/unit/commands/install-spec.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -275,11 +275,11 @@ describe('Unit: Commands > Install', function () {
275275
});
276276

277277
const testInstance = new InstallCommand({}, {});
278-
const context = {argv: {version: '1.0.0', v1: false}};
278+
const context = {argv: {version: '1.0.0', v1: false, force: false}};
279279

280280
await testInstance.version(context);
281281
expect(resolveVersion.calledOnce).to.be.true;
282-
expect(resolveVersion.calledWithExactly('1.0.0', null, {v1: false})).to.be.true;
282+
expect(resolveVersion.calledWithExactly('1.0.0', null, {v1: false, force: false})).to.be.true;
283283
expect(context.version).to.equal('1.5.0');
284284
expect(context.installPath).to.equal(path.join(process.cwd(), 'versions/1.5.0'));
285285
});
@@ -337,7 +337,7 @@ describe('Unit: Commands > Install', function () {
337337
const context = {argv: {version: '2.0.0', fromExport: 'test-export.json'}, ui: {log}};
338338

339339
await testInstance.version(context);
340-
expect(resolveVersion.calledOnceWithExactly('v1', null, {v1: undefined})).to.be.true;
340+
expect(resolveVersion.calledOnceWithExactly('v1', null, {v1: undefined, force: undefined})).to.be.true;
341341
expect(parseExport.calledOnceWithExactly('test-export.json')).to.be.true;
342342
expect(context.version).to.equal('1.5.0');
343343
expect(context.installPath).to.equal(path.join(process.cwd(), 'versions/1.5.0'));
@@ -357,7 +357,7 @@ describe('Unit: Commands > Install', function () {
357357
const context = {argv: {fromExport: 'test-export.json'}, ui: {log}};
358358

359359
await testInstance.version(context);
360-
expect(resolveVersion.calledOnceWithExactly('2.0.0', null, {v1: undefined})).to.be.true;
360+
expect(resolveVersion.calledOnceWithExactly('2.0.0', null, {v1: undefined, force: undefined})).to.be.true;
361361
expect(parseExport.calledOnceWithExactly('test-export.json')).to.be.true;
362362
expect(context.version).to.equal('2.0.0');
363363
expect(context.installPath).to.equal(path.join(process.cwd(), 'versions/2.0.0'));
@@ -381,7 +381,7 @@ describe('Unit: Commands > Install', function () {
381381
} catch (error) {
382382
expect(error).to.be.an.instanceof(errors.SystemError);
383383
expect(error.message).to.include('v3.0.0 into v2.0.0');
384-
expect(resolveVersion.calledOnceWithExactly('v2', null, {v1: undefined})).to.be.true;
384+
expect(resolveVersion.calledOnceWithExactly('v2', null, {v1: undefined, force: undefined})).to.be.true;
385385
expect(parseExport.calledOnceWithExactly('test-export.json')).to.be.true;
386386
expect(log.called).to.be.false;
387387
return;

test/unit/utils/version-spec.js

+40-15
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,17 @@ const {
1414

1515
describe('Unit: Utils: version', function () {
1616
describe('loadVersions', function () {
17-
const stub = (versionList = []) => {
18-
const versions = versionList.reduce((obj, v) => ({...obj, [v]: true}), {});
17+
const stub = (versionList = [], deprecated = []) => {
18+
const versions = versionList.reduce((obj, v) => {
19+
if (deprecated.includes(v)) {
20+
return {
21+
...obj,
22+
[v]: {deprecated: 'test deprecation notice'}
23+
};
24+
}
25+
26+
return {...obj, [v]: true};
27+
}, {});
1928
const {loadVersions} = proxyquire(modulePath, {
2029
'package-json': async () => ({versions})
2130
});
@@ -30,22 +39,31 @@ describe('Unit: Utils: version', function () {
3039
expect(result).to.deep.equal({
3140
latest: null,
3241
latestMajor: {},
33-
all: []
42+
all: [],
43+
deprecations: {}
3444
});
3545
});
3646

3747
it('returns correct all versions/latest versions, sorted desc', async function () {
38-
const loadVersions = stub(['0.11.0', '1.0.0', '1.0.1', '1.0.2', '1.0.3', '1.0.4', '2.0.0', '2.1.0', '2.22.0', '3.0.0']);
48+
const loadVersions = stub(
49+
['0.11.0', '1.0.0', '1.0.1', '1.0.2', '1.0.3', '1.0.4', '2.0.0', '2.1.0', '2.22.0', '3.0.0', '3.1.0'],
50+
['1.0.4', '2.22.0', '3.1.0']
51+
);
3952
const result = await loadVersions();
4053

4154
expect(result).to.deep.equal({
4255
latest: '3.0.0',
4356
latestMajor: {
44-
v1: '1.0.4',
45-
v2: '2.22.0',
57+
v1: '1.0.3',
58+
v2: '2.1.0',
4659
v3: '3.0.0'
4760
},
48-
all: ['3.0.0', '2.22.0', '2.1.0', '2.0.0', '1.0.4', '1.0.3', '1.0.2', '1.0.1', '1.0.0']
61+
all: ['3.1.0', '3.0.0', '2.22.0', '2.1.0', '2.0.0', '1.0.4', '1.0.3', '1.0.2', '1.0.1', '1.0.0'],
62+
deprecations: {
63+
'1.0.4': 'test deprecation notice',
64+
'2.22.0': 'test deprecation notice',
65+
'3.1.0': 'test deprecation notice'
66+
}
4967
});
5068
});
5169
});
@@ -185,7 +203,7 @@ describe('Unit: Utils: version', function () {
185203
});
186204

187205
it('returns null if no versions found', async function () {
188-
loadVersions.resolves({all: []});
206+
loadVersions.resolves({all: [], deprecations: {}});
189207
const result = await resolveVersion();
190208

191209
expect(result).to.be.null;
@@ -198,7 +216,8 @@ describe('Unit: Utils: version', function () {
198216
v1: '1.0.0',
199217
v2: '2.0.0'
200218
},
201-
all: ['2.0.0', '1.0.0']
219+
all: ['2.0.0', '1.0.0'],
220+
deprecations: {}
202221
});
203222

204223
const result = await resolveVersion(null, null, {v1: true});
@@ -212,7 +231,8 @@ describe('Unit: Utils: version', function () {
212231
v1: '1.0.0',
213232
v2: '2.0.0'
214233
},
215-
all: ['2.0.0', '1.0.0']
234+
all: ['2.0.0', '1.0.0'],
235+
deprecations: {}
216236
});
217237

218238
const result = await resolveVersion();
@@ -226,7 +246,8 @@ describe('Unit: Utils: version', function () {
226246
v1: '1.0.0',
227247
v2: '2.2.0'
228248
},
229-
all: ['2.2.0', '2.1.0', '2.0.0', '1.0.0']
249+
all: ['2.2.0', '2.1.0', '2.0.0', '1.0.0'],
250+
deprecations: {}
230251
});
231252

232253
const result = await resolveVersion('2.0.0');
@@ -240,7 +261,8 @@ describe('Unit: Utils: version', function () {
240261
v1: '1.1.0',
241262
v2: '2.2.0'
242263
},
243-
all: ['2.2.0', '2.1.0', '2.0.0', '1.1.0', '1.0.0']
264+
all: ['2.2.0', '2.1.0', '2.0.0', '1.1.0', '1.0.0'],
265+
deprecations: {}
244266
});
245267

246268
const result = await resolveVersion('1');
@@ -254,7 +276,8 @@ describe('Unit: Utils: version', function () {
254276
v1: '1.1.0',
255277
v2: '2.2.0'
256278
},
257-
all: ['2.2.0', '2.1.0', '2.0.0', '1.1.0', '1.0.0']
279+
all: ['2.2.0', '2.1.0', '2.0.0', '1.1.0', '1.0.0'],
280+
deprecations: {}
258281
});
259282

260283
const result = await resolveVersion('v2');
@@ -268,7 +291,8 @@ describe('Unit: Utils: version', function () {
268291
v1: '1.0.0',
269292
v2: '2.2.0'
270293
},
271-
all: ['2.2.0', '2.1.0', '2.0.0', '1.0.0']
294+
all: ['2.2.0', '2.1.0', '2.0.0', '1.0.0'],
295+
deprecations: {}
272296
});
273297

274298
const result = await resolveVersion(null, '2.0.0');
@@ -282,7 +306,8 @@ describe('Unit: Utils: version', function () {
282306
v1: '1.0.0',
283307
v2: '2.2.0'
284308
},
285-
all: ['2.2.0', '2.1.0', '2.0.0', '1.0.0']
309+
all: ['2.2.0', '2.1.0', '2.0.0', '1.0.0'],
310+
deprecations: {}
286311
});
287312

288313
const result = await resolveVersion('2.1.0', '2.0.0');

0 commit comments

Comments
 (0)