1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | var fs = require('fs'),
|
11 | path = require('path'),
|
12 | os = require('os'),
|
13 | chalk,
|
14 | urllib = require('url'),
|
15 | PacAgent = require('pac-proxy-agent'),
|
16 | semver = require('semver'),
|
17 | debug = require('debug')('appc:util'),
|
18 | spinner,
|
19 | spriteIndex = 0,
|
20 | cachedConfig,
|
21 | sprite = '/-\\|',
|
22 | execSync = require('child_process').execSync;
|
23 |
|
24 | var MAX_RETRIES = exports.MAX_RETRIES = 5;
|
25 | var CONN_TIMEOUT = 10000;
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | exports.stdout = process.stdout;
|
34 |
|
35 | exports.exit = function (code) {
|
36 | process.exit(code);
|
37 | };
|
38 |
|
39 | exports.setCachedConfig = function (val) {
|
40 | cachedConfig = val;
|
41 | };
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | function startSpinner() {
|
47 | stopSpinner();
|
48 | if (!spinner && exports.stdout.isTTY && !process.env.TRAVIS) {
|
49 | var count = 0;
|
50 | spinner = setInterval(function () {
|
51 | if (count++ > 0) {
|
52 |
|
53 | exports.stdout.write('\u001b[1D');
|
54 | }
|
55 | var s = ++spriteIndex % sprite.length;
|
56 | var c = sprite[s];
|
57 | exports.stdout.write(c);
|
58 | }, 50);
|
59 | }
|
60 | }
|
61 |
|
62 |
|
63 |
|
64 |
|
65 | function stopSpinner() {
|
66 | if (spinner) {
|
67 | clearInterval(spinner);
|
68 |
|
69 | exports.stdout.write('\u001b[1D');
|
70 | spinner = null;
|
71 | }
|
72 | }
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 | function waitMessage(msg) {
|
79 | exports.stdout.write(msg);
|
80 | startSpinner();
|
81 | }
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 | function okMessage(msg) {
|
88 | chalk = chalk || require('chalk');
|
89 | stopSpinner();
|
90 | msg = msg || '';
|
91 | exports.stdout.write(msg + ' ' + chalk.green(isWindows() ? 'OK' : '✓') + '\n');
|
92 | }
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 | function infoMessage(msg) {
|
99 | stopSpinner();
|
100 | exports.stdout.write(msg + '\n');
|
101 | }
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 | function getHomeDir() {
|
108 | return os.homedir();
|
109 | }
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 | function getAppcDir() {
|
116 | return path.join(getHomeDir(), '.appcelerator');
|
117 | }
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 | function getInstallTag() {
|
124 | return path.join(getAppcDir(), '.installing');
|
125 | }
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 | function getCacheDir() {
|
132 | return path.join(getAppcDir(), 'cache');
|
133 | }
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 | function getInstallDir() {
|
140 | return path.join(getAppcDir(), 'install');
|
141 | }
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | function getVersionFile() {
|
148 | return path.join(getInstallDir(), '.version');
|
149 | }
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 | function getConfigFile() {
|
156 | return path.join(getAppcDir(), 'appc-cli.json');
|
157 | }
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 | function getNpmCacheDirectory() {
|
164 | return path.join(getAppcDir(), '.npm');
|
165 | }
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 | function writeVersion(version) {
|
172 | var versionFile = getVersionFile();
|
173 | debug('writing version: %s to %s', version, versionFile);
|
174 | if (fs.existsSync(versionFile)) {
|
175 | fs.unlinkSync(versionFile);
|
176 | }
|
177 | fs.writeFileSync(versionFile, version);
|
178 | }
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 | function getActiveVersion() {
|
185 | var versionFile = getVersionFile();
|
186 | if (fs.existsSync(versionFile)) {
|
187 | return fs.readFileSync(versionFile).toString().trim();
|
188 | }
|
189 | }
|
190 |
|
191 |
|
192 |
|
193 |
|
194 | function removeVersion() {
|
195 | var versionFile = getVersionFile();
|
196 | debug('remove version %s', versionFile);
|
197 | if (fs.existsSync(versionFile)) {
|
198 | fs.unlinkSync(versionFile);
|
199 | }
|
200 | }
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 | function listVersions(opts, versions) {
|
209 | chalk = chalk || require('chalk');
|
210 | if (!versions) {
|
211 | exports.stdout.write(chalk.red('No versions available') + '\n');
|
212 | return;
|
213 | }
|
214 | var activeVersion = getActiveVersion();
|
215 |
|
216 |
|
217 | var versionsList = Object.keys(versions).map(function (value, index) {
|
218 | return versions[index].version || versions[index];
|
219 | });
|
220 |
|
221 | if (opts.latest && versionsList.indexOf(opts.latest) === -1) {
|
222 | versionsList.push(opts.latest);
|
223 | }
|
224 |
|
225 | versions = versionsList.sort(semver.compareLoose);
|
226 |
|
227 | versions.forEach(function (entry) {
|
228 | var ver = entry.version ? entry.version : entry,
|
229 | suffix = getInstallBinary(opts, ver) ? 'Installed' : 'Not Installed';
|
230 | if (opts.latest === ver) {
|
231 | suffix += chalk.white.bold(' (Latest)');
|
232 | }
|
233 | if (activeVersion && activeVersion === ver) {
|
234 | suffix += chalk.red(' (Active)');
|
235 | }
|
236 | var date = entry.date ? ' ' + chalk.grey(pad(new Date(Date.parse(entry.date)), 15)) : '';
|
237 | exports.stdout.write(chalk.yellow(pad(ver, 10)) + ' ' + chalk.cyan(pad(suffix, 40)) + date + '\n');
|
238 | });
|
239 | }
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 | function getVersionJson(opts, versions) {
|
248 | var activeVersion = getActiveVersion(),
|
249 | obj = {
|
250 | versions: [],
|
251 | installed: getInstalledVersions(),
|
252 | latest: opts.latest,
|
253 | active: activeVersion
|
254 | };
|
255 | if (Array.isArray(versions)) {
|
256 | if (versions[0] && versions[0].version) {
|
257 | obj.versions = Object.keys(versions).map(function (value, index) {
|
258 | return versions[index].version;
|
259 | });
|
260 | } else {
|
261 | obj.versions = versions;
|
262 | }
|
263 | }
|
264 | return obj;
|
265 | }
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 | function getInstalledVersions() {
|
272 | var installDir = getInstallDir();
|
273 | if (fs.existsSync(installDir)) {
|
274 |
|
275 | try {
|
276 | var dirs = fs.readdirSync(installDir);
|
277 | if (dirs.length) {
|
278 | if (dirs.length > 1) {
|
279 |
|
280 | dirs = dirs
|
281 | .filter(function (e) {
|
282 | return e[0] !== '.';
|
283 | })
|
284 | .sort(function (a, b) {
|
285 | var av = parseInt(a.replace(/\./g, '')),
|
286 | bv = parseInt(b.replace(/\./g, ''));
|
287 | return bv - av;
|
288 | });
|
289 | }
|
290 | debug('found the following version directories: %j', dirs);
|
291 | return dirs;
|
292 | }
|
293 | } catch (E) {
|
294 | debug('error reading install directory %o', E);
|
295 | if (E.code === 'EACCES') {
|
296 | chalk = chalk || require('chalk');
|
297 | var chPer = 'Please make sure you change the permissions and re-try';
|
298 | var chPerWithCmds = 'Please make sure you change the permissions using these commands:\n\n\t';
|
299 | chPerWithCmds += chalk.yellow('sudo chown -R ' + process.env.USER + ' ' + installDir + '\n\tchmod -R 0700 ' + installDir);
|
300 | var message = process.platform === 'win32' ? chPer : chPerWithCmds + '\n';
|
301 | fail('Ooops! Your install directory (' + installDir + ') is not writable.\n' + message);
|
302 | }
|
303 | fail(E);
|
304 | }
|
305 | }
|
306 | }
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 | function getInstallBinary(opts, theversion) {
|
315 | opts = opts || {};
|
316 |
|
317 | var version = theversion || (opts.version !== true ? opts.version : null) || '',
|
318 | installDir = getInstallDir(),
|
319 | bin = path.join(installDir, version, 'package', 'bin', 'appc'),
|
320 | pkg,
|
321 | dirs;
|
322 |
|
323 | if (fs.existsSync(bin)) {
|
324 |
|
325 | pkg = path.join(installDir, version, 'package', 'package.json');
|
326 | return fs.existsSync(pkg) && bin;
|
327 | } else if (theversion) {
|
328 |
|
329 | return null;
|
330 | }
|
331 |
|
332 |
|
333 | theversion = getActiveVersion();
|
334 | if (theversion) {
|
335 | bin = getInstallBinary(opts, theversion);
|
336 | if (!bin) {
|
337 | if (!opts.version) {
|
338 | chalk = chalk || require('chalk');
|
339 | debug('you have specified a version (%s) that isn\'t found', theversion);
|
340 |
|
341 |
|
342 | exports.stdout.write(chalk.red('version ' + theversion + ' specified previously is no longer available.') + '\n');
|
343 | }
|
344 | removeVersion();
|
345 | } else {
|
346 | return bin;
|
347 | }
|
348 | }
|
349 |
|
350 | dirs = getInstalledVersions();
|
351 | if (dirs) {
|
352 | for (var c = 0; c < dirs.length; c++) {
|
353 | bin = path.join(installDir, dirs[c], 'package', 'bin', 'appc');
|
354 | if (fs.existsSync(bin)) {
|
355 |
|
356 | pkg = path.join(installDir, dirs[c], 'package', 'package.json');
|
357 | return fs.existsSync(pkg) && bin;
|
358 | }
|
359 | }
|
360 | }
|
361 | }
|
362 |
|
363 |
|
364 |
|
365 |
|
366 |
|
367 |
|
368 | function ensureDir(dir) {
|
369 | var last = expandPath(dir),
|
370 | parts = [];
|
371 |
|
372 | do {
|
373 | parts.unshift(path.basename(last));
|
374 | last = path.join(last, '..');
|
375 | } while (!fs.existsSync(last));
|
376 |
|
377 | if (!fs.existsSync(last)) {
|
378 | fs.mkdirSync(last);
|
379 | }
|
380 |
|
381 |
|
382 | for (var c = 0; c < parts.length; c++) {
|
383 | var fp = path.join(last, parts[c]);
|
384 | if (!fs.existsSync(fp)) {
|
385 | fs.mkdirSync(fp);
|
386 | }
|
387 | last = fp;
|
388 | }
|
389 |
|
390 | return dir;
|
391 | }
|
392 |
|
393 |
|
394 |
|
395 |
|
396 |
|
397 |
|
398 | function expandPath(fn) {
|
399 | var home = getHomeDir(),
|
400 | p = fn.replace(/~\/?/g, function (value) {
|
401 | if (/\/$/.test(value)) {
|
402 | return home + '/';
|
403 | }
|
404 | return home;
|
405 | });
|
406 | return p;
|
407 | }
|
408 |
|
409 |
|
410 |
|
411 |
|
412 |
|
413 | function fail(msg) {
|
414 | stopSpinner();
|
415 | debug('fail %o', msg);
|
416 | if (msg.stack && process.env.DEBUG) {
|
417 | console.error(msg.stack);
|
418 | }
|
419 | chalk = chalk || require('chalk');
|
420 | console.error('\n' + (chalk.red(msg.message || msg)));
|
421 | exports.exit(1);
|
422 | }
|
423 |
|
424 | var optionRE = /^-{1,2}([\w-_]+)=?(.*)?$/;
|
425 |
|
426 |
|
427 |
|
428 |
|
429 |
|
430 | function parseOpts() {
|
431 | var args = {};
|
432 | for (var c = 2; c < process.argv.length; c++) {
|
433 | var arg = process.argv[c];
|
434 | if (optionRE.test(arg)) {
|
435 | var match = optionRE.exec(arg),
|
436 | name = match[1],
|
437 | value = match.length > 2 && match[2] || (process.argv[c + 1] && !/^-{1,2}/.test(process.argv[c + 1]) ? process.argv[c + 1] : null) || true;
|
438 | if (value === 'true' || value === 'false') {
|
439 | value = value === 'true';
|
440 | }
|
441 | if (name.indexOf('no-') === 0) {
|
442 | name = name.substring(3);
|
443 | value = false;
|
444 | }
|
445 | args[name] = value;
|
446 | }
|
447 | }
|
448 | return args;
|
449 | }
|
450 |
|
451 |
|
452 |
|
453 |
|
454 |
|
455 |
|
456 | function parseArgs(opts) {
|
457 | if (!opts) {
|
458 | throw new Error('missing opts');
|
459 | }
|
460 | var args = [];
|
461 | for (var c = 2; c < process.argv.length; c++) {
|
462 | var arg = process.argv[c];
|
463 | var previous = process.argv[c - 1];
|
464 | if (optionRE.test(previous)) {
|
465 | var previousMatch = optionRE.exec(previous);
|
466 | previous = previousMatch[1];
|
467 | }
|
468 | if (optionRE.test(arg)) {
|
469 | var match = optionRE.exec(arg),
|
470 | name = match[1],
|
471 | value = opts[name];
|
472 |
|
473 | if (value && String(process.argv[c + 1] === String(value))) {
|
474 | c++;
|
475 | }
|
476 | continue;
|
477 | } else if (opts[previous] === undefined) {
|
478 | args.push(arg);
|
479 | }
|
480 | }
|
481 | return args;
|
482 | }
|
483 |
|
484 |
|
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 | function makeURL(opts, urlpath) {
|
491 | if (typeof (opts) === 'string') {
|
492 | urlpath = opts;
|
493 | opts = {};
|
494 | } else {
|
495 | opts = opts || {};
|
496 | }
|
497 | var baseurl;
|
498 | if (opts.registry) {
|
499 | baseurl = opts.registry;
|
500 | } else if (process.env.APPC_REGISTRY_SERVER) {
|
501 | baseurl = process.env.APPC_REGISTRY_SERVER;
|
502 | } else if (process.env.APPC_ENV || process.env.NODE_ENV) {
|
503 | var env = process.env.APPC_ENV || process.env.NODE_ENV;
|
504 | if (env === 'preproduction') {
|
505 | baseurl = DEFAULT_PREPROD_REGISTRY_URL;
|
506 | } else if (env === 'preprodonprod') {
|
507 | baseurl = DEFAULT_PREPRODONPROD_REGISTRY_URL;
|
508 | } else if (env === 'production') {
|
509 | baseurl = DEFAULT_PROD_REGISTRY_URL;
|
510 | }
|
511 | }
|
512 | if (!baseurl) {
|
513 | var config = exports.readConfig();
|
514 | if (config && config.registry) {
|
515 | baseurl = config.registry;
|
516 | } else if (config && (config.defaultEnvironment === 'preproduction' || config.environmentName === 'preproduction')) {
|
517 | baseurl = DEFAULT_PREPROD_REGISTRY_URL;
|
518 | } else if (config && (config.defaultEnvironment === 'preprodonprod' || config.environmentName === 'preprodonprod')) {
|
519 | baseurl = DEFAULT_PREPRODONPROD_REGISTRY_URL;
|
520 | } else {
|
521 | baseurl = DEFAULT_PROD_REGISTRY_URL;
|
522 | }
|
523 | }
|
524 | return urllib.resolve(baseurl, urlpath);
|
525 | }
|
526 |
|
527 | function makeRequestError(msg, code) {
|
528 | var err = new Error(msg);
|
529 | err.code = code;
|
530 | return err;
|
531 | }
|
532 |
|
533 |
|
534 | var DEFAULT_PREPROD_REGISTRY_URL = 'https://registry.axwaytest.net';
|
535 | var DEFAULT_PROD_REGISTRY_URL = 'https://registry.platform.axway.com';
|
536 | var DEFAULT_PREPRODONPROD_REGISTRY_URL = 'https://software-preprodonprod.appcelerator.com';
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 | function getRequest() {
|
543 | return require('request');
|
544 | }
|
545 |
|
546 |
|
547 |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 | function request(location, callback) {
|
553 | var options;
|
554 | if (typeof (location) === 'object') {
|
555 | options = location;
|
556 | location = options.url;
|
557 | }
|
558 |
|
559 | var url = urllib.parse(location),
|
560 | config = readConfig(),
|
561 | userAgent = 'Appcelerator CLI/' + require('../package').version + ' (' + process.platform + ')',
|
562 | opts = {
|
563 | url: url,
|
564 | headers: {
|
565 | 'user-agent': userAgent,
|
566 | host: url.host,
|
567 | 'appc-token': config && config.sid
|
568 | }
|
569 | };
|
570 |
|
571 | if (options) {
|
572 | opts.timeout = CONN_TIMEOUT * (options.attempts || 1);
|
573 | }
|
574 |
|
575 | if (process.env.APPC_CONFIG_PAC_FILE) {
|
576 | opts.agent = new PacAgent('pac+' + process.env.APPC_CONFIG_PAC_FILE);
|
577 | } else if (process.env.APPC_CONFIG_PROXY !== '') {
|
578 | opts.proxy = process.env.APPC_CONFIG_PROXY;
|
579 | }
|
580 |
|
581 | if (process.env.APPC_CONFIG_CAFILE) {
|
582 | opts.ca = fs.readFileSync(process.env.APPC_CONFIG_CAFILE, 'utf8');
|
583 | }
|
584 |
|
585 | if (process.env.APPC_CONFIG_STRICTSSL === 'false') {
|
586 | opts.strictSSL = false;
|
587 | }
|
588 |
|
589 | var req = getRequest().get(opts);
|
590 |
|
591 | debug('request %j', opts);
|
592 |
|
593 |
|
594 | req.on('response', function (res) {
|
595 | debug('request response received');
|
596 | if (req.__err) {
|
597 | debug('request response callback skipped, request error already executed.');
|
598 | } else {
|
599 | callback(null, res, req);
|
600 | }
|
601 | });
|
602 |
|
603 |
|
604 | req.on('error', function (err) {
|
605 | req.__err = true;
|
606 | debug('request error', err);
|
607 | if (err.name === 'AppCError') {
|
608 | return callback(err);
|
609 | }
|
610 | if (err.code === 'ECONNREFUSED') {
|
611 | return callback(makeRequestError('Error connecting to download server at ' + url.host + '. Make sure you are online.', err.code));
|
612 | } else if (err.code === 'ENOTFOUND') {
|
613 | return callback(makeRequestError('Error connecting to download server at ' + url.host + ' (not found). Make sure you are online.', err.code));
|
614 | } else if (err.code === 'ECONNRESET') {
|
615 | return callback(makeRequestError('Error connecting to download server at ' + url.host + ' (reset). Make sure you are online.', err.code));
|
616 | }
|
617 | return callback(err);
|
618 | });
|
619 |
|
620 | return req;
|
621 | }
|
622 |
|
623 |
|
624 |
|
625 |
|
626 |
|
627 |
|
628 |
|
629 |
|
630 | function requestJSON(location, callback, attempts) {
|
631 | return request(location, function (err, res, req) {
|
632 | attempts = attempts || 1;
|
633 | if (typeof (location) === 'object') {
|
634 | location.attempts = attempts + 1;
|
635 | }
|
636 |
|
637 | debug('connection attempt %d of %d', attempts, MAX_RETRIES);
|
638 | if (err) {
|
639 | if (err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT' || err.message.indexOf('hang up') > 0) {
|
640 | debug('connection error %s with message %s', err.code, err.message);
|
641 |
|
642 | if (attempts >= MAX_RETRIES) {
|
643 | return callback(err);
|
644 | }
|
645 | return setTimeout(function () {
|
646 | return requestJSON(location, callback, attempts + 1);
|
647 | }, 500 * attempts);
|
648 | }
|
649 | return callback(err);
|
650 | }
|
651 | if (res && req.headers && req.headers['content-type'] && req.headers['content-type'].indexOf('/json') < 0) {
|
652 | debug('response status code: %d with headers: %j', res.statusCode, res.headers);
|
653 |
|
654 | if (attempts >= MAX_RETRIES) {
|
655 | return callback(err);
|
656 | }
|
657 | return setTimeout(function () {
|
658 | return requestJSON(location, callback, attempts + 1);
|
659 | }, 500 * attempts);
|
660 | }
|
661 | if (res.statusCode === 200) {
|
662 | debug('response status code: %d with headers: %j', res.statusCode, res.headers);
|
663 | var buf = '';
|
664 | res.on('data', function (chunk) {
|
665 | buf += chunk;
|
666 | });
|
667 | res.on('end', function () {
|
668 | debug('attempting to parse JSON => [%s]', buf);
|
669 | callback(null, JSON.parse(buf));
|
670 | });
|
671 | res.on('error', callback);
|
672 | } else if (res.statusCode === 301 || res.statusCode === 302) {
|
673 | debug('response status code: %d with headers: %j', res.statusCode, res.headers);
|
674 | return requestJSON(res.headers.location, callback);
|
675 | } else if (res && /^(400|404|408|500|502|503|504)$/.test(String(res.statusCode))) {
|
676 | debug('response status code: %d with headers: %j', res.statusCode, res.headers);
|
677 | attempts = attempts || 1;
|
678 | if (attempts >= MAX_RETRIES) {
|
679 | return callback(err);
|
680 | }
|
681 | return setTimeout(function () {
|
682 | return requestJSON(location, callback, attempts + 1);
|
683 | }, 500 * attempts);
|
684 | } else {
|
685 | return callback(new Error('Invalid response code: ' + res.statusCode + ' received from server.'));
|
686 | }
|
687 | });
|
688 | }
|
689 |
|
690 |
|
691 |
|
692 |
|
693 |
|
694 |
|
695 |
|
696 | function pad(str, len) {
|
697 | chalk = chalk || require('chalk');
|
698 | var slen = chalk.stripColor(str).length;
|
699 | var newstr = str;
|
700 | for (var c = slen; c < len; c++) {
|
701 | newstr += ' ';
|
702 | }
|
703 | return newstr;
|
704 | }
|
705 |
|
706 |
|
707 |
|
708 |
|
709 |
|
710 |
|
711 | function canWriteDir(dir) {
|
712 | var del = !fs.existsSync(dir),
|
713 | fn;
|
714 | try {
|
715 | if (del) {
|
716 | ensureDir(dir);
|
717 | }
|
718 | if (fs.statSync(dir).isDirectory()) {
|
719 |
|
720 | fn = path.join(dir, String(+new Date()) + (Math.random() * 3) + '.txt');
|
721 |
|
722 | fs.writeFileSync(fn, 'hi');
|
723 | return true;
|
724 | } else {
|
725 |
|
726 | return false;
|
727 | }
|
728 | } catch (E) {
|
729 | if (E.code === 'EACCES') {
|
730 | return false;
|
731 | }
|
732 | console.log(E.stack, E.code);
|
733 | } finally {
|
734 | if (fs.existsSync(fn)) {
|
735 | try {
|
736 | fs.unlinkSync(fn);
|
737 | } catch (ig) {
|
738 |
|
739 | }
|
740 | }
|
741 | if (del) {
|
742 | try {
|
743 | fs.unlinkSync(path);
|
744 | } catch (ig) {
|
745 |
|
746 | }
|
747 | }
|
748 | }
|
749 | }
|
750 |
|
751 |
|
752 |
|
753 |
|
754 |
|
755 |
|
756 |
|
757 | function checkDirectory(dir, name) {
|
758 | var message;
|
759 | var errorlib = require('./error'),
|
760 | chalk = chalk || require('chalk');
|
761 | if (!canWriteDir(dir)) {
|
762 | var chPer = 'Please make sure you change the permissions and re-try';
|
763 | var chPerWithCmd = 'Please make sure you change the permissions using these commands:\n\n\t';
|
764 | chPerWithCmd += chalk.yellow('sudo chown -R ' + process.env.USER + ' ' + dir + '\n\tchmod -R 0700 ' + dir);
|
765 | message = process.platform === 'win32' ? chPer : chPerWithCmd + '\n';
|
766 | return errorlib.createError('com.appcelerator.install.preflight.directory.unwritable', name, dir, message);
|
767 | } else if (process.platform !== 'win32') {
|
768 |
|
769 | var stat = fs.statSync(dir);
|
770 | if (stat.uid !== process.getuid()) {
|
771 | message = 'Please make sure you change the permissions using these commands:\n\n\t' + chalk.yellow('sudo chown -R ' + process.env.USER + ' ' + dir + '\n\tchmod -R 0700 ' + dir) + '\n';
|
772 | return errorlib.createError('com.appcelerator.install.preflight.directory.ownership', name, dir, process.env.USER, message);
|
773 | }
|
774 | }
|
775 | }
|
776 |
|
777 | function abortMessage(name) {
|
778 |
|
779 | if (exports.stdout.isTTY) {
|
780 | stopSpinner();
|
781 | exports.stdout.clearLine();
|
782 | exports.stdout.cursorTo(0);
|
783 | }
|
784 | exports.stdout.write(name + ' aborted.\n');
|
785 | exports.exit(1);
|
786 | }
|
787 |
|
788 | function readConfig() {
|
789 | if (cachedConfig) {
|
790 | return cachedConfig;
|
791 | }
|
792 | var cf = getConfigFile();
|
793 | if (!fs.existsSync(cf)) {
|
794 | return null;
|
795 | }
|
796 | return (cachedConfig = JSON.parse(fs.readFileSync(cf)));
|
797 | }
|
798 |
|
799 | function writeConfig(config) {
|
800 | cachedConfig = config;
|
801 | var cf = getConfigFile();
|
802 | fs.writeFileSync(cf, JSON.stringify(config, null, 2));
|
803 | }
|
804 |
|
805 |
|
806 |
|
807 |
|
808 |
|
809 |
|
810 |
|
811 |
|
812 |
|
813 |
|
814 |
|
815 |
|
816 |
|
817 |
|
818 |
|
819 |
|
820 |
|
821 | function updateCheck(opts, callback) {
|
822 |
|
823 | if (opts.version || opts.quiet || opts.output === 'json') {
|
824 | return callback();
|
825 | }
|
826 |
|
827 |
|
828 |
|
829 | var config = readConfig();
|
830 |
|
831 | if (!config) {
|
832 | return callback();
|
833 | }
|
834 |
|
835 | chalk = chalk || require('chalk');
|
836 |
|
837 | try {
|
838 | var check = config.lastUpdateCheck;
|
839 | var checkEveryMS = config.updateCheckInterval || 86400000;
|
840 | if (!check || check + checkEveryMS < Date.now()) {
|
841 |
|
842 | debug('update check skipping, %d, %d', check, checkEveryMS);
|
843 | } else {
|
844 |
|
845 | return callback();
|
846 | }
|
847 | } catch (E) {
|
848 |
|
849 | return callback();
|
850 | }
|
851 |
|
852 | var url = makeURL(opts, '/api/appc/list');
|
853 | exports.requestJSON(url, function (err, result) {
|
854 |
|
855 | if (!err && result) {
|
856 | try {
|
857 | var activeVersion = exports.getActiveVersion(),
|
858 | resultList = result.key && result[result.key],
|
859 | latest = result.latest || (resultList && (resultList.length > 0) && resultList[0].version);
|
860 |
|
861 | debug('update check completed, latest is %s', latest);
|
862 |
|
863 | config.lastUpdateCheck = Date.now();
|
864 |
|
865 |
|
866 | writeConfig(config);
|
867 |
|
868 |
|
869 | var found = exports.getInstallBinary(opts, latest);
|
870 |
|
871 |
|
872 | debug('update check found %s', found);
|
873 | if (!found && semver.lt(activeVersion, latest)) {
|
874 | exports.stdout.write('A new update ' + chalk.yellow('(' + latest + ')') + ' is available... Download with ' + chalk.green('appc use ' + latest) + '\n');
|
875 | }
|
876 | } catch (E) {
|
877 |
|
878 | }
|
879 | }
|
880 | callback();
|
881 | });
|
882 | }
|
883 |
|
884 | function isWindows() {
|
885 | return process.platform === 'win32';
|
886 | }
|
887 |
|
888 |
|
889 |
|
890 |
|
891 |
|
892 | function resetLine() {
|
893 | if (exports.stdout.isTTY) {
|
894 | exports.stdout.clearLine();
|
895 | exports.stdout.cursorTo(0);
|
896 | }
|
897 | }
|
898 |
|
899 |
|
900 |
|
901 |
|
902 |
|
903 |
|
904 |
|
905 |
|
906 |
|
907 |
|
908 |
|
909 |
|
910 |
|
911 |
|
912 |
|
913 |
|
914 |
|
915 |
|
916 |
|
917 |
|
918 |
|
919 |
|
920 |
|
921 |
|
922 |
|
923 |
|
924 |
|
925 |
|
926 |
|
927 |
|
928 | function rmdirSyncRecursive(_path, failSilent) {
|
929 | var files;
|
930 |
|
931 | try {
|
932 | files = fs.readdirSync(_path);
|
933 | } catch (err) {
|
934 |
|
935 | if (failSilent) {
|
936 | return;
|
937 | }
|
938 | throw new Error(err.message);
|
939 | }
|
940 |
|
941 |
|
942 | for (var i = 0; i < files.length; i++) {
|
943 | var file = path.join(_path, files[i]);
|
944 | var currFile = fs.lstatSync(file);
|
945 |
|
946 | if (currFile.isDirectory()) {
|
947 |
|
948 | rmdirSyncRecursive(file);
|
949 | } else if (currFile.isSymbolicLink()) {
|
950 |
|
951 | if (isWindows()) {
|
952 | fs.chmodSync(file, 666);
|
953 | }
|
954 |
|
955 | fs.unlinkSync(file);
|
956 | } else {
|
957 |
|
958 | if (isWindows) {
|
959 | fs.chmodSync(file, 666);
|
960 | }
|
961 |
|
962 | fs.unlinkSync(file);
|
963 | }
|
964 | }
|
965 |
|
966 | |
967 |
|
968 | return fs.rmdirSync(_path);
|
969 | }
|
970 |
|
971 |
|
972 |
|
973 |
|
974 |
|
975 |
|
976 |
|
977 | function mergeOptsToArgs(args, _opts) {
|
978 | var argv = [].concat(process.__argv.slice(3));
|
979 | if (argv.length) {
|
980 | for (var c = 0; c < argv.length; c++) {
|
981 | var arg = argv[c];
|
982 | args.push(arg);
|
983 | }
|
984 | }
|
985 | return args;
|
986 | }
|
987 |
|
988 |
|
989 |
|
990 |
|
991 |
|
992 |
|
993 |
|
994 |
|
995 |
|
996 |
|
997 |
|
998 |
|
999 | function getProxyServer(config) {
|
1000 | var proxy = null,
|
1001 | parsed;
|
1002 |
|
1003 | if (config && config.proxyServer) {
|
1004 | parsed = urllib.parse(config.proxyServer);
|
1005 | if (/^https?:$/.test(parsed.protocol) && parsed.hostname && parsed.hostname !== 'null') {
|
1006 | proxy = config.proxyServer;
|
1007 | }
|
1008 | }
|
1009 |
|
1010 | return proxy
|
1011 | || process.env.HTTP_PROXY
|
1012 | || process.env.http_proxy
|
1013 | || process.env.HTTPS_PROXY
|
1014 | || process.env.https_proxy
|
1015 | || '';
|
1016 | }
|
1017 |
|
1018 |
|
1019 |
|
1020 |
|
1021 |
|
1022 |
|
1023 |
|
1024 |
|
1025 |
|
1026 |
|
1027 | function getStrictSSL(config) {
|
1028 | return config ? config.strictSSL : null;
|
1029 | }
|
1030 |
|
1031 |
|
1032 |
|
1033 |
|
1034 |
|
1035 |
|
1036 |
|
1037 |
|
1038 |
|
1039 |
|
1040 | function getCAfile(config) {
|
1041 | if (config && config.cafile && fs.existsSync(config.cafile)) {
|
1042 | return config.cafile;
|
1043 | }
|
1044 |
|
1045 | return null;
|
1046 | }
|
1047 |
|
1048 |
|
1049 |
|
1050 |
|
1051 |
|
1052 |
|
1053 | function writeVersions(pkgDir) {
|
1054 | var versionsFile = path.join(pkgDir, '.nodeversions'),
|
1055 | versions = process.versions,
|
1056 | versionsStr = JSON.stringify(versions);
|
1057 |
|
1058 | debug('writing node version: %s to %s', versionsStr, versionsFile);
|
1059 | if (fs.existsSync(versionsFile)) {
|
1060 | fs.unlinkSync(versionsFile);
|
1061 | }
|
1062 | fs.writeFileSync(versionsFile, versionsStr);
|
1063 |
|
1064 |
|
1065 | var oldVersionFile = path.join(pkgDir, '.nodeversion');
|
1066 | if (fs.existsSync(oldVersionFile)) {
|
1067 | fs.unlinkSync(oldVersionFile);
|
1068 | }
|
1069 | }
|
1070 |
|
1071 |
|
1072 |
|
1073 |
|
1074 |
|
1075 |
|
1076 |
|
1077 | function readVersions(installBin) {
|
1078 | var versionFile = path.join(installBin, '..', '..', '..', '.nodeversions'),
|
1079 | versions;
|
1080 |
|
1081 | if (fs.existsSync(versionFile)) {
|
1082 | try {
|
1083 | versions = JSON.parse(fs.readFileSync(versionFile));
|
1084 | } catch (e) {
|
1085 | debug('unable to read versions file.');
|
1086 | }
|
1087 | return versions;
|
1088 | }
|
1089 | }
|
1090 |
|
1091 |
|
1092 |
|
1093 |
|
1094 |
|
1095 |
|
1096 |
|
1097 | function isNodeVersionChanged(installBin) {
|
1098 | var version = getPackageNodeVersion(installBin),
|
1099 | usedNode = version && version.split('.'),
|
1100 | currentNode = process.version.split('.'),
|
1101 | result = false;
|
1102 |
|
1103 | if (usedNode && usedNode.length >= 2 && currentNode.length >= 2) {
|
1104 | result = !(usedNode[0] === currentNode[0] && usedNode[1] === currentNode[1]);
|
1105 | }
|
1106 |
|
1107 | debug('node used %s, current version %s, result: %s', usedNode, currentNode, result);
|
1108 | return result;
|
1109 | }
|
1110 |
|
1111 |
|
1112 |
|
1113 |
|
1114 |
|
1115 |
|
1116 |
|
1117 | function isModuleVersionChanged(installBin) {
|
1118 | var versions = readVersions(installBin),
|
1119 | usedVersion = versions && versions.modules,
|
1120 | currentModuleVersion = process.versions && process.versions.modules,
|
1121 | result = false;
|
1122 |
|
1123 | if (usedVersion && currentModuleVersion) {
|
1124 | result = (usedVersion !== currentModuleVersion);
|
1125 | debug('modules version used %s, current version %s, result: %s', usedVersion, currentModuleVersion, result);
|
1126 | } else {
|
1127 | result = isNodeVersionChanged(installBin);
|
1128 | }
|
1129 |
|
1130 | return result;
|
1131 | }
|
1132 |
|
1133 |
|
1134 |
|
1135 |
|
1136 |
|
1137 |
|
1138 |
|
1139 | function getPackageNodeVersion(installBin) {
|
1140 | var versions = readVersions(installBin),
|
1141 | usedNodeVersion = versions && versions.node;
|
1142 |
|
1143 | if (usedNodeVersion) {
|
1144 | return usedNodeVersion;
|
1145 | }
|
1146 |
|
1147 | var versionFile = path.join(installBin, '..', '..', '..', '.nodeversion');
|
1148 | if (fs.existsSync(versionFile)) {
|
1149 | return fs.readFileSync(versionFile).toString().trim();
|
1150 | }
|
1151 | }
|
1152 |
|
1153 | function outputInfo(msg, isJSON) {
|
1154 | if (isJSON) {
|
1155 | return;
|
1156 | }
|
1157 |
|
1158 | exports.stdout.write(msg);
|
1159 | }
|
1160 |
|
1161 | function killDaemon(version, installBin) {
|
1162 | var pkgFile = path.join(getInstallDir(), version, 'package', 'package.json');
|
1163 | var pkg = fs.existsSync(pkgFile) && require(pkgFile);
|
1164 | if (isWindows()) {
|
1165 | installBin = '"' + process.execPath + '" "' + installBin + '"';
|
1166 | }
|
1167 | if (pkg && 'appcd' in pkg.dependencies) {
|
1168 | debug('stop appcd');
|
1169 | try {
|
1170 | execSync(installBin + ' appcd restart');
|
1171 | } catch (error) {
|
1172 |
|
1173 | debug('error killing the daemon');
|
1174 | debug(error);
|
1175 | }
|
1176 | }
|
1177 | }
|
1178 |
|
1179 | function checkNodeVersion (supportedNodeRange) {
|
1180 | if (!semver.satisfies(process.version, supportedNodeRange)) {
|
1181 | console.log(chalk.cyan('Appcelerator Command-Line Interface'));
|
1182 | console.log('Copyright (c) 2014-' + (new Date().getFullYear()) + ', Appcelerator, Inc. All Rights Reserved.');
|
1183 | console.log('');
|
1184 | console.log(chalk.red('ERROR: appc requires Node.js ' + semver.validRange(supportedNodeRange)));
|
1185 | console.log('Visit ' + chalk.cyan('http://nodejs.org/') + ' to download a newer version.');
|
1186 | console.log('');
|
1187 |
|
1188 | exports.exit(1);
|
1189 | }
|
1190 | }
|
1191 |
|
1192 | exports.checkNodeVersion = checkNodeVersion;
|
1193 | exports.getAppcDir = getAppcDir;
|
1194 | exports.getHomeDir = getHomeDir;
|
1195 | exports.getCacheDir = getCacheDir;
|
1196 | exports.getConfigFile = getConfigFile;
|
1197 | exports.getNpmCacheDirectory = getNpmCacheDirectory;
|
1198 | exports.ensureDir = ensureDir;
|
1199 | exports.expandPath = expandPath;
|
1200 | exports.getInstallDir = getInstallDir;
|
1201 | exports.listVersions = listVersions;
|
1202 | exports.getVersionJson = getVersionJson;
|
1203 | exports.getInstalledVersions = getInstalledVersions;
|
1204 | exports.getInstallBinary = getInstallBinary;
|
1205 | exports.fail = fail;
|
1206 | exports.parseOpts = parseOpts;
|
1207 | exports.parseArgs = parseArgs;
|
1208 | exports.writeVersion = writeVersion;
|
1209 | exports.getActiveVersion = getActiveVersion;
|
1210 | exports.makeURL = makeURL;
|
1211 | exports.request = request;
|
1212 | exports.requestJSON = requestJSON;
|
1213 | exports.pad = pad;
|
1214 | exports.waitMessage = waitMessage;
|
1215 | exports.okMessage = okMessage;
|
1216 | exports.infoMessage = infoMessage;
|
1217 | exports.stopSpinner = stopSpinner;
|
1218 | exports.startSpinner = startSpinner;
|
1219 | exports.canWriteDir = canWriteDir;
|
1220 | exports.checkDirectory = checkDirectory;
|
1221 | exports.abortMessage = abortMessage;
|
1222 | exports.updateCheck = updateCheck;
|
1223 | exports.isWindows = isWindows;
|
1224 | exports.resetLine = resetLine;
|
1225 | exports.rmdirSyncRecursive = rmdirSyncRecursive;
|
1226 | exports.getRequest = getRequest;
|
1227 | exports.mergeOptsToArgs = mergeOptsToArgs;
|
1228 | exports.getInstallTag = getInstallTag;
|
1229 | exports.getProxyServer = getProxyServer;
|
1230 | exports.getStrictSSL = getStrictSSL;
|
1231 | exports.getCAfile = getCAfile;
|
1232 | exports.readConfig = readConfig;
|
1233 | exports.writeVersions = writeVersions;
|
1234 | exports.isModuleVersionChanged = isModuleVersionChanged;
|
1235 | exports.getPackageNodeVersion = getPackageNodeVersion;
|
1236 | exports.outputInfo = outputInfo;
|
1237 | exports.killDaemon = killDaemon;
|