From 1dc7b64e3eaf6b163a5db0fb62722ee82d6d5c7e Mon Sep 17 00:00:00 2001 From: Pan Date: Mon, 25 Mar 2019 10:03:11 +0800 Subject: [PATCH] init --- bin/index.js | 52 ++++++++++++++++ lib/checkSystem.js | 122 ++++++++++++++++++++++++++++++++++++ lib/promiseHelpers.js | 24 +++++++ lib/validatorRules.js | 141 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 339 insertions(+) create mode 100644 bin/index.js create mode 100644 lib/checkSystem.js create mode 100644 lib/promiseHelpers.js create mode 100644 lib/validatorRules.js diff --git a/bin/index.js b/bin/index.js new file mode 100644 index 00000000..d868532a --- /dev/null +++ b/bin/index.js @@ -0,0 +1,52 @@ +#!/usr/bin/env node + +'use strict' + +const checker = require('../lib/checkSystem.js') +const colors = require('colors') + +// set color theme +colors.setTheme({ + success: 'green', + info: 'grey', + warn: 'yellow', + error: 'red', + boldWarn: ['bold', 'yellow'], + boldUnderlineSuccess: ['bold', 'underline', 'green'], + boldUnderlineError: ['bold', 'underline', 'red'] +}) + +console.log('Checking versions...'.info, '\n') + +checker(process.argv[2]).then((result) => { + // check if the process should exit prematurely + if (result.status != 0) { + console.log(colors[result.message.type](result.message.text)) + process.exit(result.status) + } + + // print out results for each package + result.packages.forEach((p) => { + if (p.type === 'success') { + console.log(('✔ ' + colors.bold(p.name) + ' was validated with ' + p.expectedVersion + '.').success) + } else if (p.type === 'warn') { + console.log((colors.bold(p.name) + ' was expected, but no validator found!').warn) + } else if (p.type === 'error' && p.commandError) { + console.log(('✘ Error validating ' + colors.bold(p.name) + ': ' + p.commandError).error) + } else if (p.type === 'error' && !p.commandError) { + console.log(( + '✘ ' + colors.bold(p.name) + + ' version is incorrect! Expected ' + + p.expectedVersion + ' but was ' + p.foundVersion + '.' + ).error) + } + }) + + // print out a summary message + if (result.message.type === 'success') { + console.log('\n', result.message.text.boldUnderlineSuccess) + } else { + console.log('\n', result.message.text.boldUnderlineError) + process.exit(1) + } +}) diff --git a/lib/checkSystem.js b/lib/checkSystem.js new file mode 100644 index 00000000..0d8917be --- /dev/null +++ b/lib/checkSystem.js @@ -0,0 +1,122 @@ +'use strict' + +const check = function(pathToPackage) { + const path = require('path') + const Promise = require('bluebird') + const exec = Promise.promisify(require('child_process').exec) + const _ = require('lodash') + const fs = require('fs') + const jsonfile = require('jsonfile') + const validaterRules = require('./validatorRules') + const promiseHelpers = require('./promiseHelpers') + + let engines + + const checkerResult = { + status: 0, + message: '', + packages: [] + } + + const packageJsonPath = pathToPackage || path.join(process.cwd(), 'package.json') + try { + fs.accessSync(packageJsonPath) + engines = jsonfile.readFileSync(packageJsonPath).engines + } catch (ex) { + checkerResult.message = { + text: '✘ No package.json found in the current directory so I can\'t validate what you need!', + type: 'error' + } + checkerResult.status = -1 + + return Promise.resolve(checkerResult) + } + + if (!engines) { + checkerResult.message = { + text: '✘ No engines found in package.json so I can\'t validate what you need!', + type: 'error' + } + checkerResult.status = -1 + + return Promise.resolve(checkerResult) + } + + const thingsToCheck = Object.getOwnPropertyNames(engines) + const validatorPromises = thingsToCheck.map(validate) // run the function over all items. + + return promiseHelpers.allSettled(validatorPromises) + .then((inspections) => { + const environmentIsValid = _.every(inspections, + (inspection) => inspection.isFulfilled() && inspection.value()) + + if (environmentIsValid) { + checkerResult.message = { + text: 'Environment looks good!', + type: 'success' + } + } else { + checkerResult.message = { + text: 'Environment is invalid!', + type: 'error' + } + } + return checkerResult + }) + + function validate(name) { + // find it in the validators + const validator = validaterRules[name] + + if (validator === undefined) { + checkerResult.packages.push({ + name: name, + validatorFound: false, + type: 'warn' + }) + return Promise.resolve(false) + } + + // call the validator and pass in the version we expect + return execAndCheck(validator, engines[name]).then((results) => { + if (results.result) { + checkerResult.packages.push({ + name: name, + validatorFound: true, + expectedVersion: engines[name], + foundVersion: engines[name], + type: 'success' + }) + } else { + checkerResult.packages.push({ + name: name, + validatorFound: true, + expectedVersion: engines[name], + foundVersion: results.reason.trim() || 'missing', + type: 'error' + }) + } + + return Promise.resolve(results.result) + }).catch((error) => { + checkerResult.packages.push({ + name: name, + validatorFound: true, + expectedVersion: engines[name], + commandError: error, + type: 'error' + }) + return Promise.reject() + }) + } + + function execAndCheck(validator, expectedVersion) { + return exec(validator.versionCheck).then((result) => { + return { + result: validator.versionValidate(result, expectedVersion), + reason: result + } + }).catch((e) => { throw e }) + } +} +module.exports = check diff --git a/lib/promiseHelpers.js b/lib/promiseHelpers.js new file mode 100644 index 00000000..90ec5583 --- /dev/null +++ b/lib/promiseHelpers.js @@ -0,0 +1,24 @@ +const Promise = require('bluebird') + +/** + * Creates a promise that is resolved when all input promises have been + * settled. The returned Promise is resolved with an array of + * Promise.Inspection objects. + * + * This is the commonly accepted way of implementing allSettled() in Bluebird. + * See: http://bluebirdjs.com/docs/api/reflect.html + * + * @param promises - The array of input promises. + * @returns {Promise} A promise that will be resolved once + * all input Promises have settled. The returned Promise will be resolved with a + * corresponding array of Promise.Inspection objects. + */ +function allSettled(promises) { + 'use strict' + const wrappedPromises = promises.map((curPromise) => curPromise.reflect()) + return Promise.all(wrappedPromises) +} + +module.exports = { + allSettled: allSettled +} diff --git a/lib/validatorRules.js b/lib/validatorRules.js new file mode 100644 index 00000000..4d8e4f2e --- /dev/null +++ b/lib/validatorRules.js @@ -0,0 +1,141 @@ +'use strict' + +const semver = require('semver') + +module.exports = { + osx: { + versionCheck: 'sw_vers -productVersion', + versionValidate: + (detectedVersion, expectedVersion) => expectedVersion === detectedVersion.trim() + }, + node: { + versionCheck: 'node -v', + versionValidate: + (detectedVersion, expectedVersion) => semver.satisfies(detectedVersion, expectedVersion) + }, + npm: { + versionCheck: 'npm -v', + versionValidate: + (detectedVersion, expectedVersion) => semver.satisfies(detectedVersion, expectedVersion) + }, + jx: { + versionCheck: 'jx -jxv', + versionValidate: + (result, version) => 'v' + version === result.trim() + }, + cordova: { + versionCheck: 'cordova -v', + versionValidate: + (result, version) => version === result.trim() + }, + appium: { + versionCheck: 'appium -v', + versionValidate: + (result, version) => version === result.trim() + }, + 'ios-deploy': { + versionCheck: 'ios-deploy -V', + versionValidate: + (result, version) => version === result.trim() + }, + 'ios-sim': { + versionCheck: 'ios-sim --version', + versionValidate: + (result, version) => version === result.trim() + }, + bower: { + versionCheck: 'bower -v', + versionValidate: + (result, version) => semver.satisfies(result, version) + }, + 'ios-webkit-debug-proxy': { + versionCheck: 'brew list ios-webkit-debug-proxy --versions', + versionValidate: + (result, version) => result.includes(version) + + }, + 'ideviceinstaller': { + versionCheck: 'brew list ideviceinstaller --versions', + versionValidate: + (result, version) => result.includes(version) + }, + java: { + versionCheck: 'javac -version 2>&1', + versionValidate: + (result, version) => result.includes(version) + }, + ant: { + versionCheck: 'ant -version', + versionValidate: + (result, version) => result.includes(version) + }, + adb: { + versionCheck: 'adb version', + versionValidate: + (result, version) => result.includes(version) + }, + git: { + versionCheck: 'git --version', + versionValidate: + (result, version) => { + // http://stackoverflow.com/questions/82064/a-regex-for-version-number-parsing + const found = result.match(/(\d+\.)?(\d+\.)?(\d+)/i) + return found[0] === version + } + }, + windows: { + versionCheck: 'ver', + versionValidate: + (result, version) => result.includes(version) + }, + 'gulp-cli': { + versionCheck: 'npm list --depth=0 -g |grep gulp-cli', + versionValidate: + (result, version) => result.includes(version) + }, + cocoapods: { + versionCheck: 'pod --version', + versionValidate: + (result, version) => version === result.trim() + }, + xcodebuild: { + versionCheck: 'xcodebuild -version', + versionValidate: + (result, version) => result.includes(version) + }, + carthage: { + versionCheck: 'carthage version', + versionValidate: + (result, version) => version === result.trim() + }, + xcpretty: { + versionCheck: 'xcpretty -v', + versionValidate: + (result, version) => version === result.trim() + }, + libimobiledevice: { + versionCheck: 'brew list --versions |grep libimobiledevice', + versionValidate: + (result, version) => result.includes(version) + }, + 'deviceconsole': { + versionCheck: 'npm list --depth=0 -g |grep deviceconsole', + versionValidate: + (result, version) => result.includes(version) + }, + 'check-engine': { + versionCheck: 'npm list --depth=0 -g |grep check-engine', + versionValidate: + (result, version) => result.includes(version) + }, + yarn: { + versionCheck: 'yarn -v', + versionValidate: + (result, version) => semver.satisfies(result, version) + }, + nsp: { + versionCheck: 'nsp --version', + versionValidate: + (result, version) => version === result.trim() + } +}