diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 5aa190c9d..b5908ec7f 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -40,7 +40,7 @@ Paste log file here This is automatically output by Ghost-CLI if an error occurs, please copy & paste: -* OS (add version): +* OS: * Node Version: * Ghost-CLI Version: * Environment: diff --git a/lib/commands/doctor/checks/system-stack.js b/lib/commands/doctor/checks/system-stack.js index 00ef0dd39..3002d9c6d 100644 --- a/lib/commands/doctor/checks/system-stack.js +++ b/lib/commands/doctor/checks/system-stack.js @@ -12,6 +12,7 @@ function systemStack(ctx, task) { if (!ctx.system.platform.linux) { promise = Promise.reject({message: 'Operating system is not Linux'}); } else { + // TODO: refactor to use ctx.system.operatingSystem promise = execa.shell('lsb_release -a').catch( () => Promise.reject({message: 'Linux version is not Ubuntu 16'}) ).then((result) => { diff --git a/lib/system.js b/lib/system.js index 897ac6f7c..93611de3a 100644 --- a/lib/system.js +++ b/lib/system.js @@ -73,6 +73,24 @@ class System { return this._globalConfig; } + /** + * Returns the running operating system + * + * @property operatingSystem + * @type Object + * @public + */ + get operatingSystem() { + if (!this._operatingSystem) { + const getOS = require('./utils/get-os'); + this._operatingSystem = getOS(this.platform); + + return this._operatingSystem; + } + + return this._operatingSystem; + } + /** * Constructs the System class * diff --git a/lib/ui/index.js b/lib/ui/index.js index 92802a5c7..5dc9bf156 100644 --- a/lib/ui/index.js +++ b/lib/ui/index.js @@ -400,7 +400,7 @@ class UI { /** * Helper method to format the debug information whenever an error occurs * - * @param {Syste} system System instance + * @param {System} system System instance * @return string Formated debug info * * @method _formatString @@ -408,6 +408,7 @@ class UI { */ _formatDebug(system) { return 'Debug Information:\n' + + ` OS: ${system.operatingSystem.os}, v${system.operatingSystem.version}\n` + ` Node Version: ${process.version}\n` + ` Ghost-CLI Version: ${system.cliVersion}\n` + ` Environment: ${system.environment}\n` + diff --git a/lib/utils/get-os.js b/lib/utils/get-os.js new file mode 100644 index 000000000..90c2821d0 --- /dev/null +++ b/lib/utils/get-os.js @@ -0,0 +1,41 @@ +'use strict'; + +const os = require('os'); +const execa = require('execa'); + +module.exports = function getOS(platform) { + const osInfo = { + os: os.platform(), + version: os.release() + }; + + if (platform.linux) { + try { + osInfo.os = execa.shellSync('lsb_release -i -s').stdout; + osInfo.version = execa.shellSync('lsb_release -r -s').stdout; + } catch (e) { + return osInfo; + } + } else if (platform.macos) { + // Darwin is Mac OS, use `sw_vers` + try { + osInfo.os = execa.shellSync('sw_vers -productName').stdout; + osInfo.version = execa.shellSync('sw_vers -productVersion').stdout; + } catch (e) { + return osInfo; + } + } else if (platform.windows) { + // for windows run `ver` + // should output something like this: Microsoft Windows XP [Version 5.1.2600] + try { + const winOutput = execa.shellSync('ver').stdout.split(/\[/i); + + osInfo.os = winOutput[0].trim(); + osInfo.version = /[0-9]+\.[0-9]+\.[0-9]+/.exec(winOutput[1])[0]; + } catch (e) { + return osInfo; + } + } + + return osInfo; +}; diff --git a/test/unit/system-spec.js b/test/unit/system-spec.js index 5157724f8..bc02645d4 100644 --- a/test/unit/system-spec.js +++ b/test/unit/system-spec.js @@ -2,7 +2,6 @@ const expect = require('chai').expect; const sinon = require('sinon'); const proxyquire = require('proxyquire').noCallThru(); - const os = require('os'); const modulePath = '../../lib/system'; const Instance = require('../../lib/instance'); @@ -116,6 +115,42 @@ describe('Unit: System', function () { }); }); + describe('operatingSystem getter', function () { + const sandbox = sinon.sandbox.create(); + + afterEach(function () { + sandbox.restore(); + }); + + it('caches and returns the correct OS', function () { + const getOsStub = sinon.stub().returns({ + os: 'Ubuntu', + version: '16' + }); + const System = proxyquire(modulePath, { + './utils/get-os': getOsStub + }); + + const instance = new System({}, []); + const platformStub = sandbox.stub(os, 'platform').returns('linux'); + const operatingSystem = instance.operatingSystem; + + expect(platformStub.calledOnce).to.be.true; + expect(getOsStub.calledOnce).to.be.true; + expect(operatingSystem).to.be.an('object'); + expect(operatingSystem.os).to.equal('Ubuntu'); + expect(operatingSystem.version).to.equal('16'); + + // do the second call to see that it gets cached + const newOperatingSystem = instance.operatingSystem; + expect(newOperatingSystem).to.be.an('object'); + expect(newOperatingSystem.os).to.equal('Ubuntu'); + expect(newOperatingSystem.version).to.equal('16'); + expect(newOperatingSystem).to.deep.equal(operatingSystem); + expect(getOsStub.calledOnce).to.be.true; + }); + }); + describe('setEnvironment', function () { it('sets things correctly in development', function () { const System = require(modulePath); diff --git a/test/unit/ui/index-spec.js b/test/unit/ui/index-spec.js index 5f66741ee..63b50703c 100644 --- a/test/unit/ui/index-spec.js +++ b/test/unit/ui/index-spec.js @@ -678,12 +678,17 @@ describe('Unit: UI', function () { it('#_formatDebug returns a properly formatted value', function (done) { const system = { cliVersion: '0.9.1.8', - environment: 'Earth' + environment: 'Earth', + operatingSystem: { + os: 'Ubuntu', + version: '16' + } }; const SPACES = ' '; const UI = require(modulePath); const ui = new UI(); const expected = ['Debug Information:', + `${SPACES}OS: Ubuntu, v16`, `${SPACES}Node Version: ${process.version}`, `${SPACES}Ghost-CLI Version: 0.9.1.8`, `${SPACES}Environment: Earth`, diff --git a/test/unit/utils/get-os-spec.js b/test/unit/utils/get-os-spec.js new file mode 100644 index 000000000..481746766 --- /dev/null +++ b/test/unit/utils/get-os-spec.js @@ -0,0 +1,66 @@ +'use strict'; +const expect = require('chai').expect; +const sinon = require('sinon'); +const execa = require('execa'); +const os = require('os'); +const getOS = require('../../../lib/utils/get-os'); + +describe('Unit: Utils > getOS', function () { + const sandbox = sinon.sandbox.create(); + let platformStub, versionStub, execaStub; + + beforeEach(function () { + versionStub = sandbox.stub(os, 'release'); + execaStub = sandbox.stub(execa, 'shellSync'); + platformStub = sandbox.stub(os, 'platform'); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('and returns correct Linux OS', function () { + platformStub.returns('linux'); + execaStub.withArgs('lsb_release -i -s').returns({stdout: 'Ubuntu'}); + execaStub.withArgs('lsb_release -r -s').returns({stdout: '16'}); + + const osResult = getOS({linux: true}); + expect(osResult.os).to.equal('Ubuntu'); + expect(osResult.version).to.equal('16'); + expect(execaStub.calledTwice).to.be.true; + }); + + it('and returns correct mac OS', function () { + platformStub.returns('darwin'); + execaStub.withArgs('sw_vers -productName').returns({stdout: 'Mac OS X'}); + execaStub.withArgs('sw_vers -productVersion').returns({stdout: '10.13.3'}); + + const osResult = getOS({macos: true}); + expect(osResult.os).to.equal('Mac OS X'); + expect(osResult.version).to.equal('10.13.3'); + expect(execaStub.calledTwice).to.be.true; + }); + + it('and returns correct Windows OS', function () { + platformStub.returns('win32'); + execaStub.withArgs('ver').returns({stdout: 'Microsoft Windows XP [Version 5.1.2600]'}); + + const osResult = getOS({windows: true}); + expect(osResult.os).to.equal('Microsoft Windows XP'); + expect(osResult.version).to.equal('5.1.2600'); + expect(execaStub.calledOnce).to.be.true; + }); + + it('and returns default os.platform if OS is not Mac, Linux, or Windows', function () { + platformStub.returns('freebsd'); + versionStub.returns('1.0.0') + const osResult = getOS({ + linux: false, + macos: false, + windows: false + }); + expect(osResult.os).to.equal('freebsd'); + expect(osResult.version).to.equal('1.0.0'); + expect(execaStub.calledOnce).to.be.false; + }); +});