UNPKG

23.3 kBJavaScriptView Raw
1/**
2 * This code is closed source and Confidential and Proprietary to
3 * Appcelerator, Inc. All Rights Reserved. This code MUST not be
4 * modified, copied or otherwise redistributed without express
5 * written permission of Appcelerator. This file is licensed as
6 * part of the Appcelerator Platform and governed under the terms
7 * of the Appcelerator license agreement.
8 */
9var fs = require('fs'),
10 path = require('path'),
11 chalk,
12 urllib = require('url'),
13 debug = require('debug')('appc:util'),
14 spinner,
15 spriteIndex = 0,
16 cachedConfig,
17 sprite = '/-\\|';
18
19//NOTE: not using char-spinner because i don't want it to reset to beginning
20//of line each time it starts/stops. i want to spin in place at point of cursor
21
22/**
23 * start the spinner
24 */
25function startSpinner() {
26 stopSpinner();
27 if (!spinner && process.stdout.isTTY && !process.env.TRAVIS) {
28 var count = 0;
29 spinner = setInterval(function() {
30 if (count++ > 0) {
31 // go back one column
32 process.stdout.write('\033[1D');
33 }
34 var s = ++spriteIndex % sprite.length;
35 var c = sprite[s];
36 process.stdout.write(c);
37 },50);
38 }
39}
40
41/**
42 * stop the spinner
43 */
44function stopSpinner() {
45 if (spinner) {
46 clearInterval(spinner);
47 // go back on column
48 process.stdout.write('\033[1D');
49 spinner = null;
50 }
51}
52
53/**
54 * write a wait message and start spinner
55 */
56function waitMessage (msg) {
57 process.stdout.write(msg);
58 startSpinner();
59}
60
61/**
62 * write OK mark and stop spinner
63 */
64function okMessage (msg) {
65 chalk = chalk || require('chalk');
66 stopSpinner();
67 msg = msg || '';
68 process.stdout.write(msg+' '+chalk.green(isWindows()?'OK':'✓')+'\n');
69}
70
71/**
72 * write message and stop spinner
73 */
74function infoMessage (msg) {
75 stopSpinner();
76 console.log(msg);
77}
78
79/**
80 * return the platform specific HOME directory
81 */
82function getHomeDir() {
83 return process.env.HOME || process.env.USERPROFILE;
84}
85
86/**
87 * return our AppC directory
88 */
89function getAppcDir() {
90 return path.join(getHomeDir(), '.appcelerator');
91}
92
93/**
94 * return the AppC install tag file
95 */
96function getInstallTag() {
97 return path.join(getAppcDir(),'.installing');
98}
99
100/**
101 * return the global cache directory in the users home folder
102 */
103function getCacheDir() {
104 return path.join(getAppcDir(), 'cache');
105}
106
107/**
108 * return the platform specific install directory
109 */
110function getInstallDir () {
111 return path.join(getAppcDir(), 'install');
112}
113
114/**
115 * return the version file
116 */
117function getVersionFile() {
118 return path.join(getInstallDir(),'.version');
119}
120
121/**
122 * return the config file
123 */
124function getConfigFile() {
125 return path.join(getAppcDir(), 'appc-cli.json');
126}
127
128/**
129 * return the private npm cache directory
130 */
131function getNpmCacheDirectory() {
132 return path.join(getAppcDir(),'.npm');
133}
134
135/**
136 * write out the current version file
137 */
138function writeVersion(version) {
139 var versionFile = getVersionFile();
140 debug('writing version: %s to %s',version,versionFile);
141 if (fs.existsSync(versionFile)) {
142 fs.unlinkSync(versionFile);
143 }
144 fs.writeFileSync(versionFile,version);
145}
146
147/**
148 * return the active version (if specified) or undefined
149 */
150function getActiveVersion() {
151 var versionFile = getVersionFile();
152 if (fs.existsSync(versionFile)) {
153 return fs.readFileSync(versionFile).toString().trim();
154 }
155}
156
157/**
158 * remove version file
159 */
160function removeVersion() {
161 var versionFile = getVersionFile();
162 debug('remove version %s',versionFile);
163 if (fs.existsSync(versionFile)) {
164 fs.unlinkSync(versionFile);
165 }
166}
167
168/**
169 * list versions installed
170 */
171function listVersions (opts, versions) {
172 chalk = chalk || require('chalk');
173 if (!versions) {
174 console.log(chalk.red('No versions available'));
175 return;
176 }
177 var activeVersion = getActiveVersion();
178 versions.forEach(function(entry){
179 var ver = entry.version ? entry.version : entry,
180 suffix = getInstallBinary(opts, ver) ? 'Installed' : 'Not Installed';
181 if (versions.latest === ver) {
182 suffix += chalk.white.bold(' (Latest)');
183 }
184 if (activeVersion && activeVersion === ver) {
185 suffix += chalk.red(' (Active)');
186 }
187 var date = entry.date ? ' '+chalk.grey(pad(new Date(Date.parse(entry.date)), 15)) : '';
188 console.log(chalk.yellow(pad(ver, 10))+' '+chalk.cyan(pad(suffix, 40))+date);
189 });
190}
191
192/**
193 * return json object of versions
194 */
195function getVersionJson (versions) {
196 var activeVersion = getActiveVersion(),
197 obj = {
198 versions: [],
199 latest: undefined,
200 active: activeVersion
201 };
202 if (Array.isArray(versions)) {
203 if (versions[0].version) {
204 obj.versions = Object.keys(versions).map(function(value, index){ return versions[index].version; });
205 obj.latest = versions[0].version;
206 } else {
207 obj.versions = versions;
208 obj.latest = versions[0];
209 }
210 }
211 return obj;
212}
213
214/**
215 * return the current versions installed
216 */
217function getInstalledVersions (callback) {
218 var installDir = getInstallDir();
219 if (fs.existsSync(installDir)) {
220 // try and resolve the latest
221 try {
222 var dirs = fs.readdirSync(installDir);
223 if (dirs.length) {
224 if (dirs.length > 1) {
225 // attempt to sort by latest version
226 dirs = dirs.filter(function(e){ return e[0]!=='.'; }).sort(function(a,b){
227 var av = parseInt(a.replace(/\./g,'')),
228 bv = parseInt(b.replace(/\./g,''));
229 return bv - av;
230 });
231 }
232 debug('found the following version directories: %j',dirs);
233 return dirs;
234 }
235 }
236 catch (E) {
237 debug('error reading install directory %o',E);
238 if (E.code==='EACCES') {
239 chalk = chalk || require('chalk');
240 var message = process.platform==='win32' ? 'Please make sure you change the permissions and re-try' : 'Please make sure you change the permissions using these commands:\n\n\t'+chalk.yellow('sudo chown -R '+process.env.USER+' '+installDir+'\n\tchmod -R 0700 '+installDir)+'\n';
241 fail('Ooops! Your install directory ('+installDir+') is not writable.\n'+message);
242 }
243 fail(E);
244 }
245 }
246}
247
248/**
249 * return the platform specific install binary path
250 */
251function getInstallBinary (opts, theversion) {
252 opts = opts || {};
253 // first check and see if specified on command line as option
254 var version = theversion || (opts.version!==true ? opts.version : null) || '',
255 installDir = getInstallDir(),
256 bin = path.join(installDir, version, 'package', 'bin', 'appc'),
257 pkg,
258 dirs;
259
260 if (fs.existsSync(bin)) {
261 // check the package.json since we will delete it on an interrupted download attempt
262 pkg = path.join(installDir, version, 'package', 'package.json');
263 return fs.existsSync(pkg) && bin;
264 }
265 else if (theversion) {
266 // if we specified a version and we didn't find it, return null
267 return null;
268 }
269
270 // see if we have a version set
271 theversion = getActiveVersion();
272 if (theversion) {
273 bin = getInstallBinary(opts, theversion);
274 if (!bin) {
275 if (!opts.version) {
276 chalk = chalk || require('chalk');
277 debug("you have specified a version (%s) that isn't found",theversion);
278 // only warn if we're not asking for this version
279 // invalid version specified in version file. remove it and then re-install from latest
280 console.log(chalk.red('version '+theversion+' specified previously is no longer available.'));
281 }
282 removeVersion();
283 }
284 else {
285 return bin;
286 }
287 }
288
289 dirs = getInstalledVersions();
290 if (dirs) {
291 for (var c=0;c<dirs.length;c++) {
292 bin = path.join(installDir, dirs[c], 'package', 'bin', 'appc');
293 if (fs.existsSync(bin)) {
294 // check the package.json since we will delete it on an interrupted download attempt
295 pkg = path.join(installDir, dirs[c], 'package', 'package.json');
296 return fs.existsSync(pkg) && bin;
297 }
298 }
299 }
300}
301
302/**
303 * given a full path, makes sure that the directory exists
304 */
305function ensureDir (dir) {
306 var last = expandPath(dir),
307 parts = [];
308 // find the top of the root that exists
309 do {
310 parts.unshift(path.basename(last));
311 last = path.join(last, '..');
312 } while (!fs.existsSync(last));
313
314 if (!fs.existsSync(last)) {
315 fs.mkdirSync(last);
316 }
317
318 // now create the directories in order
319 for (var c=0;c<parts.length;c++) {
320 var fp = path.join(last, parts[c]);
321 if (!fs.existsSync(fp)) {
322 fs.mkdirSync(fp);
323 }
324 last = fp;
325 }
326
327 return dir;
328}
329
330/**
331 * expand ~ in fn
332 */
333function expandPath (fn) {
334 var home = getHomeDir(),
335 p = fn.replace(/~\/?/g, function(value) {
336 if (/\/$/.test(value)) {
337 return home + '/';
338 }
339 return home;
340 });
341 return p;
342}
343
344/**
345 * fail and properly exit
346 */
347function fail (msg) {
348 stopSpinner();
349 debug('fail %o',msg);
350 if (msg.stack && process.env.DEBUG) {
351 console.error(msg.stack);
352 }
353 chalk = chalk || require('chalk');
354 console.error('\n'+(chalk.red(msg.message || msg)));
355 process.exit(1);
356}
357
358var optionRE = /^-{1,2}([\w-_]+)=?(.*)?$/;
359
360/**
361 * very loose parsing of options
362 */
363function parseOpts() {
364 var args = {};
365 for (var c=2;c<process.argv.length;c++) {
366 var arg = process.argv[c];
367 if (optionRE.test(arg)) {
368 var match = optionRE.exec(arg),
369 name = match[1],
370 value = match.length > 2 && match[2] || (process.argv[c+1] && !/^--/.test(process.argv[c+1]) ? process.argv[c+1] : null) || true;
371 if (value==='true' || value==='false') {
372 value = value==='true';
373 }
374 if (name.indexOf('no-') === 0) {
375 name = name.substring(3);
376 value = false;
377 }
378 args[name] = value;
379 }
380 }
381 return args;
382}
383
384/**
385 * loose parse none options
386 */
387function parseArgs(opts) {
388 if (!opts) throw new Error("missing opts");
389 var args = [];
390 for (var c=2;c<process.argv.length;c++) {
391 var arg = process.argv[c];
392 if (optionRE.test(arg)) {
393 var match = optionRE.exec(arg),
394 name = match[1],
395 value = opts[name];
396 // see if a value was provided and if so, remove it too
397 if (value && String(process.argv[c+1]===String(value))) {
398 c++;
399 }
400 continue;
401 }
402 else {
403 args.push(arg);
404 }
405 }
406 return args;
407}
408
409/**
410 * make a registry url
411 */
412function makeURL (opts, urlpath) {
413 if (typeof(opts)==='string') {
414 urlpath = opts;
415 opts = {};
416 }
417 else {
418 opts = opts || {};
419 }
420 var baseurl;
421 if (opts.registry) {
422 baseurl = opts.registry;
423 }
424 else if (process.env.APPC_REGISTRY_SERVER) {
425 baseurl = process.env.APPC_REGISTRY_SERVER;
426 }
427 else if (process.env.APPC_ENV || process.env.NODE_ENV) {
428 var env = process.env.APPC_ENV || process.env.NODE_ENV;
429 if (env === 'preproduction') {
430 baseurl = DEFAULT_PREPROD_REGISTRY_URL;
431 }
432 else if (env === 'production') {
433 baseurl = DEFAULT_PROD_REGISTRY_URL;
434 }
435 }
436 if (!baseurl) {
437 var config = readConfig();
438 if (config && config.registry) {
439 baseurl = config.registry;
440 }
441 else if (config && (config.defaultEnvironment==='preproduction'||config.environmentName==='preproduction')) {
442 baseurl = DEFAULT_PREPROD_REGISTRY_URL;
443 }
444 else {
445 baseurl = DEFAULT_PROD_REGISTRY_URL;
446 }
447 }
448 return urllib.resolve(baseurl,urlpath);
449}
450
451function makeRequestError(msg, code) {
452 var err = new Error(msg);
453 err.code = code;
454 return err;
455}
456
457var DEFAULT_PREPROD_REGISTRY_URL = 'https://8d2938f67044d8367d468453b5a6c2536185bcea.cloudapp-enterprise-preprod.appctest.com';
458var DEFAULT_PROD_REGISTRY_URL = 'https://software.appcelerator.com';
459
460/**
461 * return the request library
462 */
463function getRequest() {
464 return require('request');
465}
466
467/**
468 * make a request to location url
469 */
470function request (location, callback) {
471 var url = urllib.parse(location),
472 userAgent = 'Appcelerator CLI/'+require('../package').version+' ('+process.platform+')',
473 opts = {
474 url: url,
475 proxy: process.env.APPC_CONFIG_PROXY,
476 headers: {
477 'user-agent': userAgent,
478 host: url.host
479 }
480 },
481 req = getRequest().get(opts);
482
483 debug('request %j',opts);
484
485 // start the request
486 req.on('response', function(res) {
487 debug('request response received');
488 if (req.__err) {
489 debug('request response callback skipped, request error already executed.');
490 } else {
491 callback(null, res, req);
492 }
493 });
494
495 // check the error
496 req.on('error',function(err){
497 req.__err = true;
498 debug('request error',err);
499 if (err.name === 'AppCError') {
500 return callback(err);
501 }
502 if (err.code === 'ECONNREFUSED') {
503 return callback(makeRequestError("Error connecting to download server at "+url.host+". Make sure you are online.",err.code));
504 }
505 else if (err.code === 'ENOTFOUND') {
506 return callback(makeRequestError("Error connecting to download server at "+url.host+" (not found). Make sure you are online.",err.code));
507 }
508 else if (err.code === 'ECONNRESET') {
509 return callback(makeRequestError("Error connecting to download server at "+url.host+" (reset). Make sure you are online.",err.code));
510 }
511 return callback(err);
512 });
513
514 return req;
515}
516
517/**
518 * make a request a return JSON
519 */
520function requestJSON(location, callback, attempts) {
521 return request(location, function(err,res,req){
522 if (err) {
523 if (err.code === 'ECONNREFUSED' || err.code === 'ECONNRESET' || err.message.indexOf('hang up') > 0) {
524 // retry again
525 attempts = attempts || 1;
526 if (attempts > 10) {
527 return callback(err);
528 }
529 return setTimeout(function(){
530 return requestJSON(location, callback, attempts + 1);
531 },500*attempts);
532 }
533 return callback(err);
534 }
535 if (res && req.headers && req.headers['content-type'] && req.headers['content-type'].indexOf('/json') < 0) {
536 // retry again
537 attempts = attempts || 1;
538 if (attempts > 10) {
539 return callback(err);
540 }
541 return setTimeout(function(){
542 return requestJSON(location, callback, attempts + 1);
543 },500*attempts);
544 }
545 if (res.statusCode===200) {
546 var buf = '';
547 res.on('data', function (chunk) {
548 buf+=chunk;
549 });
550 res.on('end', function(){
551 debug('attempting to parse JSON => [%s]',buf);
552 callback(null, JSON.parse(buf));
553 });
554 res.on('error',callback);
555 }
556 else if (res.statusCode===301 || res.statusCode===302) {
557 return requestJSON(res.headers.location, callback);
558 }
559 else if (res && /^(400|404|408|500|502|503|504)$/.test(String(res.statusCode))) {
560 attempts = attempts || 1;
561 if (attempts > 10) {
562 return callback(err);
563 }
564 return setTimeout(function(){
565 return requestJSON(location, callback, attempts + 1);
566 },500*attempts);
567 }
568 else {
569 return callback(new Error("Invalid response code: "+res.statusCode+" received from server."));
570 }
571 });
572}
573
574/**
575 * right pad a string to a specific length
576 */
577function pad(str, len) {
578 chalk = chalk || require('chalk');
579 var slen = chalk.stripColor(str).length;
580 var newstr = str;
581 for (var c=slen;c<len;c++) {
582 newstr+=' ';
583 }
584 return newstr;
585}
586
587/**
588 * returns true if directory is writable by user
589 */
590function canWriteDir(dir) {
591 var del = !fs.existsSync(dir),
592 fn;
593 try {
594 if (del) {
595 ensureDir(dir);
596 }
597 if (fs.statSync(dir).isDirectory()) {
598 // create a temp file -- seems like the best way to handle cross platform
599 fn = path.join(dir, String(+new Date())+(Math.random()*3)+'.txt');
600 // console.log(fn);
601 fs.writeFileSync(fn,'hi');
602 return true;
603 }
604 else {
605 // not a directory but a file, sorry...
606 return false;
607 }
608 }
609 catch (E) {
610 if (E.code==='EACCES') {
611 return false;
612 }
613 console.log(E.stack, E.code);
614 }
615 finally {
616 if (fs.existsSync(fn)) {
617 try { fs.unlinkSync(fn); } catch (ig) {}
618 }
619 if (del) {
620 try { fs.unlinkSync(path); } catch (ig) {}
621 }
622 }
623}
624
625/**
626 * if not writable, returns a message otherwise undefined
627 */
628function checkDirectory(dir, name) {
629 var message;
630 var errorlib = require('./error'),
631 chalk = chalk || require('chalk');
632 if (!canWriteDir(dir)) {
633 message = process.platform==='win32' ? 'Please make sure you change the permissions and re-try' : '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';
634 return errorlib.createError('com.appcelerator.install.preflight.directory.unwritable',name,dir,message);
635 }
636 else {
637 // check the ownership of the directory too
638 if (process.platform!=='win32') {
639 var stat = fs.statSync(dir);
640 if (stat.uid!==process.getuid()) {
641 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';
642 return errorlib.createError('com.appcelerator.install.preflight.directory.ownership',name,dir,process.env.USER,message);
643 }
644 }
645 }
646}
647
648// because you can never get enough opportunities to use emojis ... ✊
649var whyYouDoAbort = isWindows() ? [':<'] : ['😡','😨','😥','😭','😱','😰','😤','😠'];
650function abortMessage (name) {
651 // clear line and reset it
652 if (process.stdout.isTTY) {
653 stopSpinner();
654 process.stdout.clearLine();
655 process.stdout.cursorTo(0);
656 }
657 console.log(name+' aborted ... '+(whyYouDoAbort[Math.floor(Math.round(Math.random()*whyYouDoAbort.length))] || whyYouDoAbort[0]));
658 process.exit(1);
659}
660
661function readConfig () {
662 if (cachedConfig) { return cachedConfig; }
663 var cf = getConfigFile();
664 if (!fs.existsSync(cf)) {
665 return null;
666 }
667 return (cachedConfig=JSON.parse(fs.readFileSync(cf)));
668}
669
670function writeConfig(config) {
671 cachedConfig = config;
672 var cf = getConfigFile();
673 fs.writeFileSync(cf, JSON.stringify(config,null,2));
674}
675
676/**
677 * perform an update check to see if we have a new version
678 *
679 * however, some rules:
680 *
681 * - don't check each time
682 * - if specifying a version, skip
683 * - if specifying --quiet, skip
684 * - if no config, skip
685 * - if any failure in checking, skip
686 * - only do it once per day (or what is configured)
687 *
688 */
689function updateCheck(opts, callback) {
690 // we are specifying a version or we want quiet output, skip
691 if (opts.version || opts.quiet) { return callback(); }
692
693 // check to see if we have a config file and if we don't that's OK, return
694 // since we are in a setup/install
695 var config = readConfig();
696
697 if (!config) {
698 return callback();
699 }
700
701 chalk = chalk || require('chalk');
702
703 try {
704 var check = config.lastUpdateCheck;
705 var checkEveryMS = config.updateCheckInterval || 86400000; // once per day in MS
706 if (!check || check + checkEveryMS < Date.now()) {
707 // do the check below
708 debug('update check skipping, %d, %d', check, checkEveryMS);
709 }
710 else {
711 // don't do the check
712 return callback();
713 }
714 }
715 catch (E){
716 // ignore errors, they will be dealt with otherwise
717 return callback();
718 }
719
720 var url = makeURL(opts, '/api/appc/list');
721 requestJSON(url, function(err,result){
722 // skip failures
723 if (!err && result) {
724 try {
725 debug('update check completed, latest is %s',result.latest);
726 // set the update check timestamp
727 config.lastUpdateCheck = Date.now();
728 // write out our config
729 writeConfig(config);
730 // see if we have it already
731 var found = getInstallBinary(opts, result.latest);
732 // if not, inform the user of the update
733 debug('update check found %s',found);
734 if (!found) {
735 console.log('A new update '+chalk.yellow('('+result.latest+')')+' is available... Download with '+chalk.green('appc use '+result.latest));
736 }
737 }
738 catch (E) {
739 }
740 }
741 callback();
742 });
743}
744
745function isWindows() {
746 return process.platform === 'win32';
747}
748
749/**
750 * if a TTY is connected, clear all text on the line and reset the
751 * cursor to the beginning of the line
752 */
753function resetLine () {
754 if (process.stdout.isTTY) {
755 process.stdout.clearLine();
756 process.stdout.cursorTo(0);
757 }
758}
759
760/**
761 * rmdirSyncRecursive method borrowed from wrench
762 *
763 * The MIT License
764 *
765 * Copyright (c) 2010 Ryan McGrath
766 *
767 * Permission is hereby granted, free of charge, to any person obtaining a copy
768 * of this software and associated documentation files (the "Software"), to deal
769 * in the Software without restriction, including without limitation the rights
770 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
771 * copies of the Software, and to permit persons to whom the Software is
772 * furnished to do so, subject to the following conditions:
773 *
774 * The above copyright notice and this permission notice shall be included in
775 * all copies or substantial portions of the Software.
776 *
777 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
778 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
779 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
780 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
781 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
782 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
783 * THE SOFTWARE.
784 */
785function rmdirSyncRecursive(_path, failSilent) {
786 var files;
787
788 try {
789 files = fs.readdirSync(_path);
790 } catch (err) {
791
792 if(failSilent) return;
793 throw new Error(err.message);
794 }
795
796 /* Loop through and delete everything in the sub-tree after checking it */
797 for(var i = 0; i < files.length; i++) {
798 var file = path.join(_path, files[i]);
799 var currFile = fs.lstatSync(file);
800
801 if(currFile.isDirectory()) {
802 // Recursive function back to the beginning
803 rmdirSyncRecursive(file);
804 } else if(currFile.isSymbolicLink()) {
805 // Unlink symlinks
806 if (isWindows()) {
807 fs.chmodSync(file, 666); // Windows needs this unless joyent/node#3006 is resolved..
808 }
809
810 fs.unlinkSync(file);
811 } else {
812 // Assume it's a file - perhaps a try/catch belongs here?
813 if (isWindows) {
814 fs.chmodSync(file, 666); // Windows needs this unless joyent/node#3006 is resolved..
815 }
816
817 fs.unlinkSync(file);
818 }
819 }
820
821 /* Now that we know everything in the sub-tree has been deleted, we can delete the main
822 directory. Huzzah for the shopkeep. */
823 return fs.rmdirSync(_path);
824}
825
826/**
827 * return an array of arguments appending any subsequent process args
828 */
829function mergeOptsToArgs(args, opts){
830 var argv = [].concat(process.__argv.slice(3));
831 if (argv.length) {
832 for (var c=0;c<argv.length;c++) {
833 var arg = argv[c];
834 args.push(arg);
835 }
836 }
837 return args;
838}
839
840/*
841 * return the proxy setting from config
842 *
843 */
844function getProxyServer() {
845 var config = readConfig(),
846 proxy = '',
847 parsed;
848
849 if (config) {
850 proxy = config.proxyServer || '';
851 parsed = urllib.parse(proxy);
852
853 if ((!/^https?\:$/.test(parsed.protocol)) || (!(parsed.host || ''))) {
854 proxy = '';
855 }
856 }
857
858 return proxy;
859}
860
861exports.getAppcDir = getAppcDir;
862exports.getHomeDir = getHomeDir;
863exports.getCacheDir = getCacheDir;
864exports.getConfigFile = getConfigFile;
865exports.getNpmCacheDirectory = getNpmCacheDirectory;
866exports.ensureDir = ensureDir;
867exports.expandPath = expandPath;
868exports.getInstallDir = getInstallDir;
869exports.listVersions = listVersions;
870exports.getVersionJson = getVersionJson;
871exports.getInstalledVersions = getInstalledVersions;
872exports.getInstallBinary = getInstallBinary;
873exports.fail = fail;
874exports.parseOpts = parseOpts;
875exports.parseArgs = parseArgs;
876exports.writeVersion = writeVersion;
877exports.getActiveVersion = getActiveVersion;
878exports.makeURL = makeURL;
879exports.request = request;
880exports.requestJSON = requestJSON;
881exports.pad = pad;
882exports.waitMessage = waitMessage;
883exports.okMessage = okMessage;
884exports.infoMessage = infoMessage;
885exports.stopSpinner = stopSpinner;
886exports.startSpinner = startSpinner;
887exports.canWriteDir = canWriteDir;
888exports.checkDirectory = checkDirectory;
889exports.abortMessage = abortMessage;
890exports.updateCheck = updateCheck;
891exports.isWindows = isWindows;
892exports.resetLine = resetLine;
893exports.rmdirSyncRecursive = rmdirSyncRecursive;
894exports.getRequest = getRequest;
895exports.mergeOptsToArgs = mergeOptsToArgs;
896exports.getInstallTag = getInstallTag;
897exports.getProxyServer = getProxyServer;