Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforces global dependencies are available. #920

Merged
merged 3 commits into from
Mar 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/ignite-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@
"gluegun": "^0.16.0",
"gluegun-patching": "^0.3.0",
"minimist": "^1.2.0",
"pretty-error": "^2.0.2",
"ramda": "^0.23.0",
"ramdasauce": "^1.2.0",
"shelljs": "^0.7.6",
"pretty-error": "^2.0.2"
"semver": "^5.3.0",
"shelljs": "^0.7.6"
},
"devDependencies": {
"ava": "^0.18.1",
Expand Down
52 changes: 35 additions & 17 deletions packages/ignite-cli/src/cli/check.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,52 @@
// NOTE: this file is intentionally written with es3

// check the node version
var Sniff = require('gluegun/sniff')
var Shell = require('shelljs')

// check the node version
if (!Sniff.isNewEnough) {
console.log('Node.js 7.6+ is required to run. You have ' + Sniff.nodeVersion + '. Womp, womp.')
console.log(
'Node.js 7.6+ is required to run. You have ' +
Sniff.nodeVersion +
'. Womp, womp.'
)
process.exit(1)
}

// check for async and await
if (!Sniff.hasAsyncAwait) {
console.log('The async feature is not available. Please ensure your Node is up to date.')
console.log(
'The async feature is not available. Please ensure your Node is up to date.'
)
process.exit(2)
}

// check the yarn version is >= 0.20
function getYarnVersion () {
return Shell.exec('yarn --version', {silent: true}).stdout.replace('\n', '').split('.')
}
// lets check our global dependencies
var enforceGlobalDependency = require('./enforceGlobalDependency')
var exitCodes = require('../lib/exitCodes')

function checkYarn (yarnVersion) {
var major = parseInt(yarnVersion[0], 10)
var minor = parseInt(yarnVersion[1], 10)
return (major >= 1) || (minor >= 20)
}
// react-native-cli
var rnCli = enforceGlobalDependency({
optional: false,
range: '>=2.0.0',
which: 'react-native',
versionCommand: 'react-native --version',
installMessage: 'To install: npm i -g react-native-cli'
})

var hasYarn = Shell.which('yarn')
if (hasYarn && !checkYarn(getYarnVersion())) {
console.log('You have yarn installed but it\'s version ' + getYarnVersion().join('.') + '. Ignite requires yarn 20.0+.')
console.log('Run `brew upgrade yarn` if you use Homebrew or visit https://yarnpkg.com/en/docs/install.')
process.exit(3)
if (!rnCli) {
process.exit(exitCodes.INVALID_GLOBAL_DEPENDENCY)
}

// yarn
var yarn = enforceGlobalDependency({
optional: true,
range: '>=0.21.0',
which: 'yarn',
versionCommand: 'yarn --version',
installMessage: 'See https://yarnpkg.com/en/docs/install on how to upgrade for your OS.'
})

if (!yarn) {
process.exit(exitCodes.INVALID_GLOBAL_DEPENDENCY)
}
125 changes: 125 additions & 0 deletions packages/ignite-cli/src/cli/enforceGlobalDependency.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// NOTE: this file is intentionally written with es3
var shell = require('shelljs')
var semver = require('semver')
var ramda = require('ramda')

/**
* Extracts the version number from a somewhere within a string.
*
* This looks for things that look like semver (e.g. 0.0.0) and picks the first one.
*
* @param {string} raw - The raw text which came from running the version command.
*/
function defaultVersionMatcher (raw) {
// sanity check
if (ramda.isNil(raw) || ramda.isEmpty(raw)) return null

try {
// look for something that looks like semver
var rx = /([0-9]+\.[0-9]+\.[0-9]+)/
var match = ramda.match(rx, raw)
if (match.length > 0) {
return match[0]
} else {
return null
}
} catch (err) {
// something tragic happened
return false
}
}

/**
* Verifies the dependency which is installed is compatible with ignite.
*
* @param {any} opts The options to enforce.
* @param {boolean} opts.optional Is this an optional dependency?
* @param {string} opts.range The semver range to test against.
* @param {string} opts.which The command to run `which` on.
* @param {string} opts.versionCommand The command to run which returns text containing the version number.
* @param {string} opts.installMessage What to print should we fail.
* @param {function} opts.versionMatcher A way to override the method to discover the version number.
* @return {boolean} `true` if we meet the requirements; otherwise `false`.
*/
function enforce (opts = {}) {
// opts to pass in
var optional = opts.optional || false
var range = opts.range
var which = opts.which
var versionCommand = opts.versionCommand
var installMessage = opts.installMessage
var versionMatcher = opts.versionMatcher || defaultVersionMatcher

/**
* Prints a friendly message that they don't meet the requirement.
*
* @param {string} installedVersion - current version if installed.
*/
function printNotMetMessage (installedVersion) {
console.log('Ignite requires ' + which + ' ' + range + ' to be installed.')
if (installedVersion) {
console.log('')
console.log('You currently have ' + installedVersion + ' installed.')
}
console.log('')
console.log(installMessage)
}

/**
* Gets the version from the global dependency.
*
* @return {string} The version number or null.
*/
function getVersion () {
// parse the version number
try {
// grab the raw output
var result = shell.exec(versionCommand, { silent: true })
var rawOut = ramda.trim(result.stdout || '')
var rawErr = ramda.trim(result.stderr || '') // java -version does this... grr

// assign the "right" one to raw
var raw = rawOut
if (ramda.isEmpty(raw)) {
raw = rawErr
}
if (ramda.isEmpty(raw)) {
raw = null
}

// and run it by the version matcher
return versionMatcher(raw)
} catch (err) {
return null
}
}

// are we installed?
var isInstalled = Boolean(shell.which(which))

if (!isInstalled) {
if (optional) {
return true
} else {
printNotMetMessage()
return false
}
}

// which version is installed?
try {
var installedVersion = getVersion()
var isMet = semver.satisfies(installedVersion, range)

// dependency has minimum met, we're good.
if (isMet) return true
} catch (err) {
// can't parse? just catch and we'll fallback to an error.
}

// o snap, time to upgrade
printNotMetMessage(installedVersion)
return false
}

module.exports = enforce
12 changes: 0 additions & 12 deletions packages/ignite-cli/src/extensions/reactNative.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,6 @@ function attach (plugin, command, context) {
rnOptions.push('--skip-jest')
}

// install React Native CLI if it isn't found
const rncliSpinner = print.spin(`checking react-native-cli`)
const cliInstalled = await system.which('react-native')
if (cliInstalled) {
rncliSpinner.stop()
} else {
// No React Native installed, let's get it
rncliSpinner.text = 'installing react-native-cli'
await system.run('npm install -g react-native-cli')
rncliSpinner.succeed(`installed react-native-cli`)
}

// react-native init
const cmd = trim(`react-native init ${name} ${rnOptions.join(' ')}`)
log('initializing react native')
Expand Down
7 changes: 6 additions & 1 deletion packages/ignite-cli/src/lib/exitCodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,10 @@ module.exports = {
/**
* node_modules/react-native already exists.
*/
EXISTING_REACT_NATIVE: 11
EXISTING_REACT_NATIVE: 11,

/**
* One of the global dependencies are not met.
*/
INVALID_GLOBAL_DEPENDENCY: 12
}