Skip to content

Commit 18465eb

Browse files
authored
Merge pull request #1 from bandhavya/master
Included cordova cli requirements validations
2 parents 892c957 + 2cde922 commit 18465eb

6 files changed

Lines changed: 200 additions & 34 deletions

File tree

index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
const {
33
build
44
} = require('./src/command');
5-
const { endWith } = require('./src/utils');
65

76
const args = require('yargs')
87
.command('build <platform> [src] [dest] [options]', 'build for target platform', yargs => {

src/android.js

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ const {
55
} = require('./exec');
66

77
const logger = require('./logger');
8+
const {
9+
validate,
10+
hasValidNodeVersion,
11+
hasValidJavaVersion,
12+
checkForGradleAvailability,
13+
isGitInstalled,
14+
checkForAndroidStudioAvailability
15+
} = require('./requirements');
816
const loggerLabel = 'android-build';
917

1018
const buildSigningFile = (path, keyStore, storePassword, keyAlias, keyPassword, buildType) => {
@@ -21,23 +29,6 @@ const buildSigningFile = (path, keyStore, storePassword, keyAlias, keyPassword,
2129
fs.writeFileSync(path, JSON.stringify(settings, null, 2));
2230
};
2331

24-
const validate = (keyStore, storePassword, keyAlias, keyPassword) => {
25-
let errors = [];
26-
if (!(keyStore && fs.existsSync(keyStore))) {
27-
errors.push(`keystore is required (valid file): ${keyStore}`);
28-
}
29-
if (!keyAlias) {
30-
errors.push('keyAlias is required.');
31-
}
32-
if (!keyPassword) {
33-
errors.push('keyPassword is required.');
34-
}
35-
if (!storePassword) {
36-
errors.push('storePassword is required.');
37-
}
38-
return errors;
39-
};
40-
4132
module.exports = {
4233
build: async (args) => {
4334
let {
@@ -56,20 +47,32 @@ module.exports = {
5647
keyPassword = 'android';
5748
storePassword = 'android';
5849
}
50+
if (!await hasValidNodeVersion() || !await hasValidJavaVersion() ||
51+
!await checkForGradleAvailability() || !await isGitInstalled() ||
52+
!await checkForAndroidStudioAvailability()) {
53+
return {
54+
success: false
55+
}
56+
}
5957
const errors = validate(keyStore, storePassword, keyAlias, keyPassword);
6058
if (errors.length > 0) {
6159
return {
6260
success: false,
6361
errors: errors
6462
}
6563
}
64+
6665
await exec(cordova, ['platform', 'add', `android@${cordovaAndroidVersion}`, '--verbose'], {
6766
cwd: projectDir
6867
});
6968
logger.info({
7069
label: loggerLabel,
7170
message: 'Added cordova android'
7271
});
72+
73+
// await exec('cordova', ['requirements'], {
74+
// cwd: projectDir
75+
// });
7376
await exec(cordova, ['prepare', 'android', '--verbose'], {
7477
cwd: projectDir
7578
});

src/command.js

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,26 @@ const {
1212
const et = require('elementtree');
1313
const path = require('path');
1414
const npmCache = require('./npm-cache');
15-
15+
const { showConfirmation } = require('./requirements');
1616
const loggerLabel = 'wm-cordova-cli';
17-
1817
const PHONEGAP_CLI = {
1918
'cli-9.0.0' : ['9.0.0', '8.0.0', '5.1.1'],
2019
'cli-8.1.1' : ['8.1.1', '7.1.2', '4.5.5'],
2120
'cli-8.0.0' : ['8.0.0', '7.0.0', '4.5.4']
2221
};
2322

24-
25-
function setupBuildDirectory(src, dest) {
23+
async function setupBuildDirectory(src, dest) {
2624
const target = dest;
2725
if (fs.existsSync(target)) {
28-
fs.rmdirSync(target, {
29-
recursive: true
30-
});
26+
if (fs.readdirSync(target).length) {
27+
const response = await showConfirmation('Would you like to empty the dest folder (i.e. ' + dest + ') ?');
28+
if (response !== 'y' && response !== 'yes') {
29+
return false;
30+
}
31+
fs.removeSync(target);
32+
}
3133
}
32-
fs.mkdirSync(target);
34+
fs.mkdirsSync(target);
3335
fs.copySync(src, dest);
3436
}
3537

@@ -90,9 +92,16 @@ function setPreferences(projectDir, args) {
9092
module.exports = {
9193
build: async function (args) {
9294
try {
95+
let folderName = args.src.split('/').pop();
96+
const isZipFile = folderName.endsWith('.zip');
97+
98+
folderName = isZipFile ? folderName.replace('.zip', '') : folderName;
99+
100+
const tmp = `${require('os').homedir()}/.wm-cordova-cli/build/${folderName}/${Date.now()}`;
101+
93102
if (args.src.endsWith('.zip')) {
94103
const zipFile = args.src;
95-
args.src = path.dirname(args.src) + '/build_' + Date.now();
104+
args.src = tmp + '/src';
96105
await exec('unzip', [
97106
'-o',
98107
zipFile,
@@ -101,8 +110,8 @@ module.exports = {
101110
])
102111
}
103112
args.src = path.resolve(args.src) + '/';
104-
args.dest = path.resolve(args.dest || (args.src) + `../build-${args.platform}`) + '/';
105-
setupBuildDirectory(args.src, args.dest);
113+
args.dest = path.resolve(args.dest || `${tmp}/build-${args.platform}`) + '/';
114+
await setupBuildDirectory(args.src, args.dest);
106115
setPreferences(args.dest, args);
107116
await updatePackageJson(args.dest, args.cordovaVersion, args.cordovaIosVersion, args.cordovaAndroidVersion);
108117
config.src = args.dest;

src/exec.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ class OutputPipe {
1010
this.logOutput = (log !== false);
1111
this.loggerLabel = loggerLabel;
1212
}
13-
log(str) {
13+
log(str, isErrorType) {
1414
let reminder = '';
1515
str.split('\n').forEach((v, i, splits) => {
1616
if (i < splits.length - 1) {
17-
v && this.logOutput && logger.debug({label: this.loggerLabel, message: v});
17+
v && (this.logOutput || isErrorType) && logger.debug({label: this.loggerLabel, message: v});
1818
if (this.content.length > this.bufferSize) {
1919
this.content.shift();
2020
}
@@ -25,9 +25,9 @@ class OutputPipe {
2525
});
2626
return reminder;
2727
}
28-
push(str) {
28+
push(str, isErrorType) {
2929
if (str) {
30-
this.output = this.log(this.output + str) || '';
30+
this.output = this.log(this.output + str, isErrorType) || '';
3131
}
3232
}
3333
flush() {
@@ -44,7 +44,7 @@ module.exports = {
4444
outputPipe.push(String.fromCharCode.apply(null, new Uint16Array(data)));
4545
});
4646
spawn.stderr.on('data', (data) => {
47-
outputPipe.push(String.fromCharCode.apply(null, new Uint16Array(data)));
47+
outputPipe.push(String.fromCharCode.apply(null, new Uint16Array(data)), true);
4848
});
4949
return new Promise((resolve, reject) => {
5050
spawn.on('close', code => {

src/ios.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ module.exports = {
124124
label: loggerLabel,
125125
message: 'Added cordova ios'
126126
});
127+
// await exec('cordova', ['requirements'], {
128+
// cwd: config.src
129+
// });
127130
await exec(cordova, ['prepare', 'ios', '--verbose'], {
128131
cwd: projectDir
129132
});

src/requirements.js

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
const fs = require('fs');
2+
const logger = require('./logger');
3+
const {
4+
exec
5+
} = require('./exec');
6+
const loggerLabel = 'cordova-cli-requirements';
7+
const semver = require('semver');
8+
const prompt = require('prompt');
9+
const VERSIONS = {
10+
'NODE': '12.0.0',
11+
'JAVA': '1.8.0',
12+
'GRADLE': '6.0.1'
13+
}
14+
15+
async function checkAvailability(cmd) {
16+
try {
17+
let version = (await exec(cmd, ['--version'])).join('');
18+
19+
// to just return version in x.x.x format
20+
version = version.match(/[0-9\.]+/)[0];
21+
22+
logger.info({
23+
'label': loggerLabel,
24+
'message': cmd + ' version available is ' + version
25+
})
26+
const requiredVersion = VERSIONS[cmd.toUpperCase()];
27+
if (requiredVersion && semver.lt(version, requiredVersion)) {
28+
logger.error('Minimum ' + cmd + ' version required is ' + requiredVersion + '. Please update the version.');
29+
return false;
30+
}
31+
return version;
32+
} catch(e) {
33+
console.error(e);
34+
return false;
35+
}
36+
}
37+
38+
module.exports = {
39+
showConfirmation: async (message) => {
40+
return new Promise((resolve, reject) => {
41+
prompt.get({
42+
properties: {
43+
confirm: {
44+
pattern: /^(yes|no|y|n)$/gi,
45+
description: message,
46+
message: 'Type yes/no',
47+
required: true,
48+
default: 'no'
49+
}
50+
}
51+
}, function (err, result) {
52+
if (err) {
53+
reject();
54+
}
55+
resolve(result.confirm.toLowerCase());
56+
});
57+
});
58+
},
59+
checkForGradleAvailability: async () => {
60+
return await checkAvailability('gradle');
61+
},
62+
checkForAndroidStudioAvailability: async () => {
63+
// ANDROID_HOME environment variable is set or not. If it is set checking if its a valid path or no.
64+
const ANDROID_HOME = process.env['ANDROID_HOME'];
65+
const ANDROID_SDK_ROOT = process.env['ANDROID_SDK_ROOT']
66+
if (ANDROID_HOME && !ANDROID_SDK_ROOT) {
67+
logger.warn({
68+
'label': loggerLabel,
69+
'message': 'ANDROID_HOME is deprecated. Recommended to set ANDROID_SDK_ROOT'
70+
});
71+
}
72+
envVariable = ANDROID_SDK_ROOT || ANDROID_HOME;
73+
if (!envVariable) {
74+
logger.error({
75+
'label': loggerLabel,
76+
'message': 'Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
77+
'Try update your \'PATH\' to include path to valid SDK directory.'});
78+
return false;
79+
}
80+
if (!fs.existsSync(envVariable)) {
81+
logger.error({
82+
'label': loggerLabel,
83+
'message': '\'ANDROID_HOME\' environment variable is set to non-existent path: ' + process.env['ANDROID_HOME'] +
84+
'\nTry update it manually to point to valid SDK directory.'});
85+
return false;
86+
}
87+
const sdkPath = envVariable + '/tools/bin/sdkmanager';
88+
89+
if (!fs.existsSync(sdkPath)) {
90+
logger.error({
91+
'label': loggerLabel,
92+
'message': 'Failed to find \'android-sdk\' in your \'PATH\'. Install Android-Studio before proceeding to build.'});
93+
return false;
94+
}
95+
logger.info({
96+
'label': loggerLabel,
97+
'message': 'Found Android SDK manager at ' + sdkPath
98+
})
99+
100+
try {
101+
await exec('sdkmanager', ['--list']);
102+
} catch(e) {
103+
console.error(e);
104+
return false;
105+
}
106+
return true;
107+
},
108+
hasValidJavaVersion: async () => {
109+
const javaVersion = (await exec('java', ['-version'])).join('').match(/[0-9\.]+/)[0];
110+
111+
if (semver.lt(javaVersion, VERSIONS.JAVA)) {
112+
logger.error('Minimum java version required is' + VERSIONS.JAVA + '. Please update the java version.');
113+
return false;
114+
}
115+
116+
const envVariable = process.env['JAVA_HOME'];
117+
118+
if (!envVariable) {
119+
logger.error({
120+
'label': loggerLabel,
121+
'message': 'Failed to find \'JAVA_HOME\' environment variable. Try setting it manually.\n' +
122+
'Try update your \'PATH\' to include path to valid directory.'});
123+
return false;
124+
}
125+
return true;
126+
},
127+
hasValidNodeVersion: async () => {
128+
return await checkAvailability('node');
129+
},
130+
isGitInstalled: async () => {
131+
return await checkAvailability('git');
132+
},
133+
134+
// TODO: cocoapod for ios
135+
136+
validate: (keyStore, storePassword, keyAlias, keyPassword) => {
137+
let errors = [];
138+
if (!(keyStore && fs.existsSync(keyStore))) {
139+
errors.push(`keystore is required (valid file): ${keyStore}`);
140+
}
141+
if (!keyAlias) {
142+
errors.push('keyAlias is required.');
143+
}
144+
if (!keyPassword) {
145+
errors.push('keyPassword is required.');
146+
}
147+
if (!storePassword) {
148+
errors.push('storePassword is required.');
149+
}
150+
return errors;
151+
}
152+
}

0 commit comments

Comments
 (0)