diff --git a/core/server/lib/ghost-version.js b/core/server/lib/ghost-version.js index ad56987bd1..562c4a2dcf 100644 --- a/core/server/lib/ghost-version.js +++ b/core/server/lib/ghost-version.js @@ -1,8 +1,27 @@ -var packageInfo = require('../../../package.json'), - version = packageInfo.version; +'use strict'; -module.exports = { - full: version, - safe: version.match(/^(\d+\.)?(\d+)/)[0] +const semver = require('semver'), + packageInfo = require('../../../package.json'), + version = packageInfo.version, + plainVersion = version.match(/^(\d+\.)?(\d+\.)?(\d+)/)[0]; + +let _private = {}; + +_private.compose = function compose(type) { + switch (type) { + case 'pre': + return plainVersion + '-' + semver.prerelease(version)[0] + (semver.prerelease(version)[1] ? '.' + semver.prerelease(version)[1] : ''); + default: + return version; + } }; +// major.minor +module.exports.safe = version.match(/^(\d+\.)?(\d+)/)[0]; + +// major.minor.patch-{prerelease} +module.exports.full = semver.prerelease(version) ? _private.compose('pre') : plainVersion; + +// original string in package.json (can contain pre-release and build suffix) +module.exports.original = version; + diff --git a/core/server/web/middleware/api/version-match.js b/core/server/web/middleware/api/version-match.js index 05f8194c05..eeda5ceb5a 100644 --- a/core/server/web/middleware/api/version-match.js +++ b/core/server/web/middleware/api/version-match.js @@ -3,7 +3,8 @@ var semver = require('semver'), function checkVersionMatch(req, res, next) { var clientVersion = req.get('X-Ghost-Version'), - serverVersion = res.locals.version, + // can contain pre-release suffix, you should be able to use e.g. 1.19.0-pre [server] with 1.18.0 [client] + serverVersion = res.locals.version.match(/^(\d+\.)?(\d+\.)?(\d+)/)[0], constraint = '^' + clientVersion + '.0'; // no error when client is on an earlier minor version than server diff --git a/core/test/unit/lib/ghost-version_spec.js b/core/test/unit/lib/ghost-version_spec.js new file mode 100644 index 0000000000..421b6e6348 --- /dev/null +++ b/core/test/unit/lib/ghost-version_spec.js @@ -0,0 +1,75 @@ +'use strict'; + +// jshint unused: false +const should = require('should'), + rewire = require('rewire'), + testUtils = require('../../utils'); + +let ghostVersionUtils, + version; + +describe('Utils: Ghost Version', function () { + const beforeEachIt = function be() { + testUtils.mockNotExistingModule(/package\.json/, {version: version}); + + ghostVersionUtils = rewire('../../../server/lib/ghost-version'); + }; + + afterEach(function () { + testUtils.unmockNotExistingModule(/package\.json/); + }); + + it('default', function () { + version = '1.10.0'; + beforeEachIt(); + + ghostVersionUtils.full.should.eql(version); + ghostVersionUtils.original.should.eql(version); + ghostVersionUtils.safe.should.eql('1.10'); + }); + + it('pre-release', function () { + version = '1.11.1-beta'; + beforeEachIt(); + + ghostVersionUtils.full.should.eql(version); + ghostVersionUtils.original.should.eql(version); + ghostVersionUtils.safe.should.eql('1.11'); + }); + + it('pre-release .1', function () { + version = '1.11.1-alpha.1'; + beforeEachIt(); + + ghostVersionUtils.full.should.eql(version); + ghostVersionUtils.original.should.eql(version); + ghostVersionUtils.safe.should.eql('1.11'); + }); + + it('build', function () { + version = '1.11.1+build'; + beforeEachIt(); + + ghostVersionUtils.full.should.eql('1.11.1'); + ghostVersionUtils.original.should.eql(version); + ghostVersionUtils.safe.should.eql('1.11'); + }); + + it('mixed', function () { + version = '1.11.1-pre+build.1'; + beforeEachIt(); + + ghostVersionUtils.full.should.eql('1.11.1-pre'); + ghostVersionUtils.original.should.eql(version); + ghostVersionUtils.safe.should.eql('1.11'); + }); + + it('mixed 1', function () { + version = '1.11.1-beta.12+build.2'; + beforeEachIt(); + + ghostVersionUtils.full.should.eql('1.11.1-beta.12'); + ghostVersionUtils.original.should.eql(version); + ghostVersionUtils.safe.should.eql('1.11'); + }); +}); diff --git a/core/test/unit/web/middleware/api/version-match_spec.js b/core/test/unit/web/middleware/api/version-match_spec.js index 34371e0606..df52775c26 100644 --- a/core/test/unit/web/middleware/api/version-match_spec.js +++ b/core/test/unit/web/middleware/api/version-match_spec.js @@ -99,4 +99,26 @@ describe('Version Mismatch', function () { nextStub.firstCall.args[0].should.have.property('errorType', 'VersionMismatchError'); nextStub.firstCall.args[0].should.have.property('statusCode', 400); }); + + it('should call next if pre-release is allowed', function () { + var server = '1.5.0-pre', + client = '1.4'; + + testVersionMatch(server, client); + + nextStub.calledOnce.should.be.true(); + nextStub.firstCall.args.should.be.empty(); + }); + + it('throws error if server is a pre-release, but later by major version', function () { + var server = '2.0.0-alpha', + client = '1.5'; + + testVersionMatch(server, client); + + nextStub.calledOnce.should.be.true(); + nextStub.firstCall.args.should.have.lengthOf(1); + nextStub.firstCall.args[0].should.have.property('errorType', 'VersionMismatchError'); + nextStub.firstCall.args[0].should.have.property('statusCode', 400); + }); });