Skip to content

Commit 702e953

Browse files
kirrg001acburdine
authored andcommitted
feat(v2): avoid direct upgrade to v2 (#770)
refs #759 - extended version resolver - detected major version jumps - optimised code around the force flag - added unit tests
1 parent eb315c3 commit 702e953

File tree

6 files changed

+123
-14
lines changed

6 files changed

+123
-14
lines changed

lib/commands/update.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,9 @@ class UpdateCommand extends Command {
178178
return Promise.resolve(true);
179179
}
180180

181-
const updateVersion = force ? null : activeVersion;
182181
const resolveVersion = zip ?
183-
() => require('../utils/version-from-zip')(zip, updateVersion) :
184-
() => require('../utils/resolve-version')(version, updateVersion, v1);
182+
() => require('../utils/version-from-zip')(zip, activeVersion, force) :
183+
() => require('../utils/resolve-version')(version, activeVersion, v1, force);
185184

186185
return resolveVersion().then((version) => {
187186
context.version = version;

lib/utils/resolve-version.js

+30-4
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ const MIN_RELEASE = '>= 1.0.0';
1111
* and/or a passed version & any locally installed versions
1212
*
1313
* @param {String} version Any version supplied manually by the user
14-
* @param {String} update Current version if we are updating
14+
* @param {String} activeVersion Current version if we are updating
1515
* @param {Boolean} v1 Whether or not to limit versions to 1.x release versions
1616
* @return Promise<string> Promise that resolves with the version to install
1717
*/
18-
module.exports = function resolveVersion(version, update, v1 = false) {
18+
module.exports = function resolveVersion(version, activeVersion, v1 = false, force = false) {
1919
// If version contains a leading v, remove it
2020
if (version && version.match(/^v[0-9]/)) {
2121
version = version.slice(1);
@@ -29,7 +29,7 @@ module.exports = function resolveVersion(version, update, v1 = false) {
2929
}
3030

3131
return yarn(['info', 'ghost', 'versions', '--json']).then((result) => {
32-
let comparator = update ? `>${update}` : MIN_RELEASE;
32+
let comparator = !force && activeVersion ? `>${activeVersion}` : MIN_RELEASE;
3333

3434
if (v1) {
3535
comparator += ' <2.0.0';
@@ -53,7 +53,33 @@ module.exports = function resolveVersion(version, update, v1 = false) {
5353
}));
5454
}
5555

56-
return version || versions.pop();
56+
let versionToReturn = version || versions.pop();
57+
58+
if (v1 && activeVersion && semver.satisfies(activeVersion, '^2.0.0')) {
59+
return Promise.reject(new errors.CliError({
60+
message: 'You can\'t downgrade from v2 to v1 using these options.',
61+
help: 'Please run "ghost update --rollback".'
62+
}));
63+
}
64+
65+
// CASE: you haven't passed `--v1` and you are not about to install a fresh blog
66+
if (!v1 && activeVersion) {
67+
const majorVersionJump = semver.major(activeVersion) !== semver.major(versionToReturn);
68+
69+
// CASE: use latest v1 release
70+
if (majorVersionJump && force) {
71+
while (!semver.satisfies(versionToReturn, '^1.0.0')) {
72+
versionToReturn = versions.pop();
73+
}
74+
} else if (majorVersionJump && versions.length) {
75+
return Promise.reject(new errors.CliError({
76+
message: 'You are about to migrate to Ghost 2.0. Your blog is not on the latest Ghost 1.0 version.',
77+
help: 'Please run "ghost update --v1".'
78+
}));
79+
}
80+
}
81+
82+
return versionToReturn;
5783
} catch (e) {
5884
return Promise.reject(new errors.CliError({
5985
message: 'Ghost-CLI was unable to load versions from Yarn.',

lib/utils/version-from-zip.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const semver = require('semver');
66
const AdmZip = require('adm-zip');
77
const cliPackage = require('../../package.json');
88

9-
module.exports = function versionFromZip(zipPath, currentVersion) {
9+
module.exports = function versionFromZip(zipPath, currentVersion, force = false) {
1010
if (!path.isAbsolute(zipPath)) {
1111
zipPath = path.join(process.cwd(), zipPath);
1212
}
@@ -47,7 +47,7 @@ module.exports = function versionFromZip(zipPath, currentVersion) {
4747
}));
4848
}
4949

50-
if (currentVersion && semver.lt(pkg.version, currentVersion)) {
50+
if (force && semver.lt(pkg.version, currentVersion)) {
5151
return Promise.reject(
5252
new errors.SystemError('Zip file contains an older release version than what is currently installed.')
5353
);

test/unit/commands/update-spec.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ describe('Unit: Commands > Update', function () {
712712
return instance.version(context).then((result) => {
713713
expect(result).to.be.true;
714714
expect(resolveVersion.calledOnce).to.be.true;
715-
expect(resolveVersion.calledWithExactly(null, '1.0.0', true)).to.be.true;
715+
expect(resolveVersion.calledWithExactly(null, '1.0.0', true, false)).to.be.true;
716716
expect(context.version).to.equal('1.0.1');
717717
expect(context.installPath).to.equal('/var/www/ghost/versions/1.0.1');
718718
});
@@ -740,7 +740,7 @@ describe('Unit: Commands > Update', function () {
740740
expect(result).to.be.true;
741741
expect(resolveVersion.called).to.be.false;
742742
expect(zipVersion.calledOnce).to.be.true;
743-
expect(zipVersion.calledWithExactly('/some/zip/file.zip', '1.0.0')).to.be.true;
743+
expect(zipVersion.calledWithExactly('/some/zip/file.zip', '1.0.0', false)).to.be.true;
744744
expect(context.version).to.equal('1.1.0');
745745
expect(context.installPath).to.equal('/var/www/ghost/versions/1.1.0');
746746
});
@@ -763,7 +763,7 @@ describe('Unit: Commands > Update', function () {
763763
return instance.version(context).then((result) => {
764764
expect(result).to.be.false;
765765
expect(resolveVersion.calledOnce).to.be.true;
766-
expect(resolveVersion.calledWithExactly(null, null, false)).to.be.true;
766+
expect(resolveVersion.calledWithExactly(null, '1.0.0', false, true)).to.be.true;
767767
});
768768
});
769769

@@ -791,7 +791,7 @@ describe('Unit: Commands > Update', function () {
791791
expect(error).to.be.an.instanceof(Error);
792792
expect(error.message).to.equal('something bad');
793793
expect(resolveVersion.calledOnce).to.be.true;
794-
expect(resolveVersion.calledWithExactly(null, null, false)).to.be.true;
794+
expect(resolveVersion.calledWithExactly(null, '1.0.0', false, true)).to.be.true;
795795
});
796796
});
797797
});

test/unit/utils/resolve-version-spec.js

+84
Original file line numberDiff line numberDiff line change
@@ -135,5 +135,89 @@ describe('Unit: resolveVersion', function () {
135135
expect(error.message).to.equal('No valid versions found.');
136136
});
137137
});
138+
139+
describe('jump to next major', function () {
140+
it('throws error if you aren\'t on the latest v1', function () {
141+
stubYarn('{"data": ["1.23.0", "1.24.0", "1.25.0", "2.0.0"]}');
142+
143+
return resolveVersion(null, '1.24.0', false).then(function () {
144+
throw new Error('Version finder should not have resolved');
145+
}).catch(function (error) {
146+
expect(error).to.be.an.instanceOf(Error);
147+
expect(error.message).to.equal('You are about to migrate to Ghost 2.0. Your blog is not on the latest Ghost 1.0 version.');
148+
});
149+
});
150+
151+
it('resolves if you are on the latest v1', function () {
152+
stubYarn('{"data": ["1.23.0", "1.25.1", "1.25.2", "2.0.0"]}');
153+
154+
return resolveVersion(null, '1.25.2', false)
155+
.then(function (version) {
156+
expect(version).to.eql('2.0.0');
157+
});
158+
});
159+
160+
it('resolves using `--v1` and you are\'t on the latest v1', function () {
161+
stubYarn('{"data": ["1.23.0", "1.25.1", "1.25.2", "2.0.0"]}');
162+
163+
return resolveVersion(null, '1.25.1', true)
164+
.then(function (version) {
165+
expect(version).to.eql('1.25.2');
166+
});
167+
});
168+
169+
it('force updating', function () {
170+
stubYarn('{"data": ["1.23.0", "1.25.1", "1.25.2", "2.0.0"]}');
171+
172+
return resolveVersion(null, '1.25.2', false, true)
173+
.then(function (version) {
174+
expect(version).to.eql('1.25.2');
175+
});
176+
});
177+
178+
it('force updating with `--v1`', function () {
179+
stubYarn('{"data": ["1.23.0", "1.25.1", "1.25.2", "2.0.0"]}');
180+
181+
return resolveVersion(null, '1.25.1', true, true)
182+
.then(function (version) {
183+
expect(version).to.eql('1.25.2');
184+
});
185+
});
186+
187+
it('force updating with many v2 releases', function () {
188+
stubYarn('{"data": ["1.23.0", "1.25.1", "1.25.2", "2.0.0", "2.1.0", "2.2.0"]}');
189+
190+
return resolveVersion(null, '1.25.1', false, true)
191+
.then(function (version) {
192+
expect(version).to.eql('1.25.2');
193+
});
194+
});
195+
196+
it('throws error if you want to force updating to a previous major', function () {
197+
stubYarn('{"data": ["1.23.0", "1.25.1", "1.25.2", "2.0.0"]}');
198+
199+
return resolveVersion(null, '2.0.0', true, true)
200+
.then(function () {
201+
throw new Error('Version finder should not have resolved');
202+
})
203+
.catch(function (error) {
204+
expect(error).to.be.an.instanceOf(Error);
205+
expect(error.message).to.equal('You can\'t downgrade from v2 to v1 using these options.');
206+
});
207+
});
208+
209+
it('throws error if you want to update to a previous major', function () {
210+
stubYarn('{"data": ["1.23.0", "1.25.1", "1.25.2", "2.0.0"]}');
211+
212+
return resolveVersion(null, '2.0.0', true, false)
213+
.then(function () {
214+
throw new Error('Version finder should not have resolved');
215+
})
216+
.catch(function (error) {
217+
expect(error).to.be.an.instanceOf(Error);
218+
expect(error.message).to.equal('No valid versions found.');
219+
});
220+
});
221+
});
138222
});
139223
});

test/unit/utils/version-from-zip-spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ describe('Unit: Utils > versionFromZip', function () {
114114
it('rejects if update version passed and zip version < update version', function () {
115115
const versionFromZip = require(modulePath);
116116

117-
return versionFromZip(path.join(__dirname, '../../fixtures/ghostold.zip'), '1.5.0').then(() => {
117+
return versionFromZip(path.join(__dirname, '../../fixtures/ghostold.zip'), '1.5.0', true).then(() => {
118118
expect(false, 'error should have been thrown').to.be.true;
119119
}).catch((error) => {
120120
expect(error).to.be.an.instanceof(errors.SystemError);

0 commit comments

Comments
 (0)