From 13f417d66988140fe16c782fdea92465f50cb0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tyll=20Wei=C3=9F?= Date: Mon, 11 Dec 2017 13:01:06 +0100 Subject: [PATCH 1/3] FEATURE: Integrate a update prompt if the globally installed CLI is outdated - solves #33 --- .../.bin/create-react-microservice | 55 ++++++++++++------- .../create-react-microservice/package.json | 10 +++- yarn.lock | 8 ++- 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/packages/create-react-microservice/.bin/create-react-microservice b/packages/create-react-microservice/.bin/create-react-microservice index 22edb9606..789870520 100755 --- a/packages/create-react-microservice/.bin/create-react-microservice +++ b/packages/create-react-microservice/.bin/create-react-microservice @@ -6,31 +6,48 @@ var path = require('path'); var pkg = require(path.join(__dirname, './../package.json')); require('check-engines')(pkg, function (err) { + var ora = require('ora'); + var spinner = ora().start(`Checking for updates and validating the system context.`); + if (err) { console.log(err); - require('ora')().fail(`System binaries do not meet constraints: ${JSON.stringify(pkg.engines)}.`); + spinner.fail(`System binaries do not meet constraints: ${JSON.stringify(pkg.engines)}.`); process.exit(1); return; } - const meow = require('meow'); - const ora = require('ora'); - const cli = meow({pkg, autoHelp: false}); - const commandsByName = { - default: require('./../dist/commands/default.js') - }; - const commandName = cli.input.join(' '); - const Command = commandsByName[commandName] || commandsByName.default; - const instance = new Command({ - pkg, - input: cli.input, - flags: cli.flags - }); - - instance.exec().catch(e => { - console.error(e); + (async function() { + const meow = require('meow'); + const latestVersion = require('latest-version'); + const version = await latestVersion(pkg.name); - process.exit(1); - }); + if (version !== pkg.version && pkg.version !== '0.0.0-development') { + spinner.fail(`Oudated version of "${pkg.name}" found. Please update your global installation by executing "yarn global upgrade ${pkg.name}@${version}".`); + + return process.exit(1); + } + + spinner.stop(); + + const cli = meow({pkg, autoHelp: false}); + const commandsByName = { + default: require('./../dist/commands/default.js') + }; + const commandName = cli.input.join(' '); + const Command = commandsByName[commandName] || commandsByName.default; + const instance = new Command({ + pkg, + input: cli.input, + flags: cli.flags + }); + + try { + await instance.exec() + } catch (e) { + console.error(e); + + process.exit(1); + } + })() }); diff --git a/packages/create-react-microservice/package.json b/packages/create-react-microservice/package.json index 082ae15b1..f65ea7c06 100644 --- a/packages/create-react-microservice/package.json +++ b/packages/create-react-microservice/package.json @@ -4,7 +4,8 @@ "private": false, "author": "Immonet dev team (https://immonet.de)", "license": "MIT", - "description": "Create highly scalable and universal React microservices/applications within seconds.", + "description": + "Create highly scalable and universal React microservices/applications within seconds.", "bin": { "create-react-microservice": "./.bin/create-react-microservice" }, @@ -19,14 +20,16 @@ "jest:watch": "yarn run jest -- --watch", "jest:coverage": "yarn run jest -- --coverage", "build:clean": "rimraf dist", - "build:babel": "NODE_ENV=production babel src --out-dir dist --ignore spec.js", + "build:babel": + "NODE_ENV=production babel src --out-dir dist --ignore spec.js", "build:flow": "flow-copy-source -v src dist --ignore='*.spec.js'", "build:watch": "yarn run build:babel -- -w", "prebuild": "yarn run build:clean", "build": "yarn run build:babel && yarn run build:flow", "flow": "flow", "flow-typed": "flow-typed", - "flow-typed-install": "flow-typed install --ignoreDeps=bundled peer --overwrite" + "flow-typed-install": + "flow-typed install --ignoreDeps=bundled peer --overwrite" }, "devDependencies": { "@immowelt/babel-preset-immowelt-node": "1.1.0", @@ -50,6 +53,7 @@ "create-react-microservice-scaffold": "0.0.0-development", "execa": "0.8.0", "find-node-modules": "1.0.4", + "latest-version": "^3.1.0", "meow": "4.0.0", "node-emoji": "1.8.1", "ora": "1.3.0", diff --git a/yarn.lock b/yarn.lock index bc4c22be9..3347b8fbf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3581,6 +3581,12 @@ kind-of@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.1.tgz#4948e6263553ac3712fc44d305b77851d9e40ea4" +latest-version@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" + dependencies: + package-json "^4.0.0" + lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" @@ -4508,7 +4514,7 @@ p-timeout@^1.1.1: dependencies: p-finally "^1.0.0" -package-json@^4.0.1: +package-json@^4.0.0, package-json@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" dependencies: From 46ab7ab48558806726b1dd2ed799cf964e964673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tyll=20Wei=C3=9F?= Date: Mon, 11 Dec 2017 13:03:37 +0100 Subject: [PATCH 2/3] TASK: Force the usage of regular string literals to avoid the process from crashing in older node versions --- .../create-react-microservice/.bin/create-react-microservice | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/create-react-microservice/.bin/create-react-microservice b/packages/create-react-microservice/.bin/create-react-microservice index 789870520..2ba2f90ec 100755 --- a/packages/create-react-microservice/.bin/create-react-microservice +++ b/packages/create-react-microservice/.bin/create-react-microservice @@ -7,11 +7,11 @@ var pkg = require(path.join(__dirname, './../package.json')); require('check-engines')(pkg, function (err) { var ora = require('ora'); - var spinner = ora().start(`Checking for updates and validating the system context.`); + var spinner = ora().start('Checking for updates and validating the system context.'); if (err) { console.log(err); - spinner.fail(`System binaries do not meet constraints: ${JSON.stringify(pkg.engines)}.`); + spinner.fail('System binaries do not meet constraints: ' + JSON.stringify(pkg.engines)}); process.exit(1); return; From d99a53d4d90ea6f6953c49bc17eb36791a7f8793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tyll=20Wei=C3=9F?= Date: Mon, 11 Dec 2017 13:27:10 +0100 Subject: [PATCH 3/3] TASK: Clean up the binary and move the version validation of the CLI into the Command class to be able to test it properly --- .../.bin/create-react-microservice | 58 +++++++------------ .../src/commands/default.js | 2 + .../src/commands/default.spec.js | 5 ++ .../src/lib/command.js | 15 +++++ .../src/lib/command.spec.js | 43 ++++++++++++++ .../create-react-microservice/src/lib/npm.js | 5 ++ .../src/lib/npm.spec.js | 9 +++ 7 files changed, 99 insertions(+), 38 deletions(-) create mode 100644 packages/create-react-microservice/src/lib/npm.js create mode 100644 packages/create-react-microservice/src/lib/npm.spec.js diff --git a/packages/create-react-microservice/.bin/create-react-microservice b/packages/create-react-microservice/.bin/create-react-microservice index 2ba2f90ec..09ff0f581 100755 --- a/packages/create-react-microservice/.bin/create-react-microservice +++ b/packages/create-react-microservice/.bin/create-react-microservice @@ -7,47 +7,29 @@ var pkg = require(path.join(__dirname, './../package.json')); require('check-engines')(pkg, function (err) { var ora = require('ora'); - var spinner = ora().start('Checking for updates and validating the system context.'); if (err) { - console.log(err); - spinner.fail('System binaries do not meet constraints: ' + JSON.stringify(pkg.engines)}); - process.exit(1); + ora().fail(err.message); + ora().fail('System binaries do not meet constraints: ' + JSON.stringify(pkg.engines)); - return; + return process.exit(1); } - (async function() { - const meow = require('meow'); - const latestVersion = require('latest-version'); - const version = await latestVersion(pkg.name); - - if (version !== pkg.version && pkg.version !== '0.0.0-development') { - spinner.fail(`Oudated version of "${pkg.name}" found. Please update your global installation by executing "yarn global upgrade ${pkg.name}@${version}".`); - - return process.exit(1); - } - - spinner.stop(); - - const cli = meow({pkg, autoHelp: false}); - const commandsByName = { - default: require('./../dist/commands/default.js') - }; - const commandName = cli.input.join(' '); - const Command = commandsByName[commandName] || commandsByName.default; - const instance = new Command({ - pkg, - input: cli.input, - flags: cli.flags - }); - - try { - await instance.exec() - } catch (e) { - console.error(e); - - process.exit(1); - } - })() + var cli = require('meow')({pkg, autoHelp: false}); + var commandsByName = { + default: require('./../dist/commands/default.js') + }; + var commandName = cli.input.join(' '); + var Command = commandsByName[commandName] || commandsByName.default; + var instance = new Command({ + pkg, + input: cli.input, + flags: cli.flags + }); + + instance.exec().catch(function (e) { + ora().fail(e.message); + console.error(e); + process.exit(1); + }); }); diff --git a/packages/create-react-microservice/src/commands/default.js b/packages/create-react-microservice/src/commands/default.js index 8cb2eaa6e..734cecc7d 100644 --- a/packages/create-react-microservice/src/commands/default.js +++ b/packages/create-react-microservice/src/commands/default.js @@ -25,6 +25,8 @@ class CreateReactMicroService extends Command { return this.printHelp(); } + await this.validateInstallation(); + const name = await this.resolveAppName(); const src = await this.resolveScaffold(); const dist = await this.resolveDistFolder(); diff --git a/packages/create-react-microservice/src/commands/default.spec.js b/packages/create-react-microservice/src/commands/default.spec.js index 4ffb59f58..b1cd4c338 100644 --- a/packages/create-react-microservice/src/commands/default.spec.js +++ b/packages/create-react-microservice/src/commands/default.spec.js @@ -21,6 +21,7 @@ describe('DefaultCommand', () => { describe('new Command().exec()', () => { let instance; + let validateInstallation; let processTemplateAndCreate; let resolveAndPromptTemplateArgs; let resolveDistFolder; @@ -32,6 +33,9 @@ describe('new Command().exec()', () => { beforeEach(() => { instance = new DefaultCommand({input: [], flags: {}, pkg: {}}); + validateInstallation = jest + .spyOn(instance, 'validateInstallation') + .mockImplementation(jest.fn()); processTemplateAndCreate = jest .spyOn(create, 'processTemplateAndCreate') .mockImplementation(jest.fn()); @@ -86,6 +90,7 @@ describe('new Command().exec()', () => { it('should resolve the dist folder, the app name and template args', async () => { await instance.exec(); + expect(validateInstallation).toHaveBeenCalledTimes(1); expect(resolveAppName).toHaveBeenCalledTimes(1); expect(resolveDistFolder).toHaveBeenCalledTimes(1); expect(resolveAndPromptTemplateArgs).toHaveBeenCalledTimes(1); diff --git a/packages/create-react-microservice/src/lib/command.js b/packages/create-react-microservice/src/lib/command.js index 1160c3816..2a2f0dc41 100644 --- a/packages/create-react-microservice/src/lib/command.js +++ b/packages/create-react-microservice/src/lib/command.js @@ -3,6 +3,7 @@ const exec = require('execa'); const chalk = require('chalk'); const ora = require('ora'); +const npm = require('./npm.js'); class Command { static exec = exec; @@ -22,6 +23,20 @@ class Command { this.flags = args.flags; } + /** + * Validates the global CLI installation so we can be sure that the newest version is always installed. + * + * @return {Promise} The Promise that resolves if everything is installed properly / rejects if something is wrong. + */ + async validateInstallation() { + const {name, version} = this.pkg; + const latestPublishedVersion = await npm.latestVersion(name); + + if (latestPublishedVersion !== version && version !== '0.0.0-development') { + throw new Error(`Oudated version of "${name}" found. Please update your global installation by executing "yarn global upgrade ${name}@${latestPublishedVersion}".`); + } + } + /** * Logs a message to the users console. * diff --git a/packages/create-react-microservice/src/lib/command.spec.js b/packages/create-react-microservice/src/lib/command.spec.js index a6998e2c5..64bc8c086 100644 --- a/packages/create-react-microservice/src/lib/command.spec.js +++ b/packages/create-react-microservice/src/lib/command.spec.js @@ -1,5 +1,8 @@ // @flow +jest.mock('./npm.js'); + +const npm: any = require('./npm.js'); const Command = require('./command.js'); describe('Command()', () => { @@ -8,6 +11,46 @@ describe('Command()', () => { }); }); +describe('new Command().validateInstallation()', () => { + let instance; + + beforeEach(() => { + instance = new Command({input: [], flags: {}, pkg: {name: 'foo-name', version: '1.0.0'}}); + }); + + afterEach(() => { + // $FlowFixMe: Ignore errors since the jest type-def is out of date. + jest.restoreAllMocks(); + jest.clearAllMocks(); + }); + + it('should be a function', () => { + expect(typeof instance.validateInstallation).toBe('function'); + }); + + it('should query the latest published version and should not throw an erorr if it matches the initialized version number in the package.json.', async () => { + npm.latestVersion.mockReturnValue('1.0.0'); + + await instance.validateInstallation(); + + expect(npm.latestVersion).toHaveBeenCalledWith('foo-name'); + }); + + it('should not throw an erorr if the initialized version number in the package.json matches "0.0.0-development".', async () => { + instance.pkg.version = '0.0.0-development'; + + npm.latestVersion.mockReturnValue('1.2.0'); + + await expect(instance.validateInstallation()).resolves; + }); + + it('should throw an erorr if the queried version does not match the initialized version number in the package.json.', async () => { + npm.latestVersion.mockReturnValue('1.2.0'); + + await expect(instance.validateInstallation()).rejects; + }); +}); + describe('new Command().log()', () => { let instance; let createLogMsg; diff --git a/packages/create-react-microservice/src/lib/npm.js b/packages/create-react-microservice/src/lib/npm.js new file mode 100644 index 000000000..7ad3dd3f9 --- /dev/null +++ b/packages/create-react-microservice/src/lib/npm.js @@ -0,0 +1,5 @@ +const latestVersion = require('latest-version'); + +module.exports = { + latestVersion +}; diff --git a/packages/create-react-microservice/src/lib/npm.spec.js b/packages/create-react-microservice/src/lib/npm.spec.js new file mode 100644 index 000000000..4491f7835 --- /dev/null +++ b/packages/create-react-microservice/src/lib/npm.spec.js @@ -0,0 +1,9 @@ +// @flow + +const npm = require('./npm.js'); + +describe('npm.latestVersion()', () => { + it('should be a function', () => { + expect(typeof npm.latestVersion).toBe('function'); + }); +});