UNPKG

8.88 kBPlain TextView Raw
1#!/usr/bin/env node
2/**
3 * This code is closed source and Confidential and Proprietary to
4 * Appcelerator, Inc. All Rights Reserved. This code MUST not be
5 * modified, copied or otherwise redistributed without express
6 * written permission of Appcelerator. This file is licensed as
7 * part of the Appcelerator Platform and governed under the terms
8 * of the Appcelerator license agreement.
9 */
10var chalk = require('chalk'),
11 semver = require('semver'),
12 pkg = require('../package');
13
14// check Node.js version
15try {
16 if (!semver.satisfies(process.version, pkg.engines.node)) {
17 console.log(chalk.cyan('Appcelerator Command-Line Interface'));
18 console.log('Copyright (c) 2014-' + (new Date().getFullYear()) + ', Appcelerator, Inc. All Rights Reserved.');
19 console.log('');
20 console.log(chalk.red('ERROR: appc requires Node.js ' + semver.validRange(pkg.engines.node)));
21 console.log('Visit ' + chalk.cyan('http://nodejs.org/') + ' to download a newer version.');
22 console.log('');
23
24 process.exit(1);
25 }
26} catch (e) {}
27
28var fs = require('fs'),
29 path = require('path'),
30 util = require('../lib/util'),
31 error = require('../lib/error'),
32 lib = require('../lib/index'),
33 debug = require('debug')('appc:bin'),
34 opts = util.parseOpts(),
35 args = util.parseArgs(opts),
36 installBin = util.getInstallBinary(opts),
37 dirError;
38
39// backup our process.argv
40process.__argv = [].concat(process.argv);
41
42// set appc npm version
43process.env.NPM_APPC_VERSION = pkg.version;
44
45// set our npm cache directory to point to our own directory which we know is
46// writable and doesn't conflict with other npms installed by the user
47// we set this here so that all child processes will automatically pick them up
48var npmCacheDir = util.getNpmCacheDirectory();
49if (fs.existsSync(npmCacheDir)) {
50 dirError = util.checkDirectory(npmCacheDir,'npm');
51}
52else {
53 // make sure we write the directory
54 util.ensureDir(npmCacheDir);
55 dirError = util.checkDirectory(npmCacheDir,'npm');
56}
57
58// check if we have an error and if so, fail
59if (dirError) {
60 util.fail(dirError);
61}
62
63// if we get here, we're OK, set our environment. this means anything we now
64// do with npm should be in our user writable directory and not a different directory
65process.env.npm_config_cache = npmCacheDir;
66process.env.npm_config_prefix = npmCacheDir;
67
68// we need to make sure our install directory is writable at all times and if not, give
69// error up front
70var installDir = util.getInstallDir();
71if (fs.existsSync(installDir)) {
72 dirError = util.checkDirectory(installDir,'install');
73}
74else {
75 // make sure we write the directory
76 util.ensureDir(installDir);
77 dirError = util.checkDirectory(installDir,'install');
78}
79
80// check if we have an error and if so, fail
81if (dirError) {
82 util.fail(dirError);
83}
84
85// cleanup stale install tag
86var installTag = util.getInstallTag();
87if (fs.existsSync(installTag)) {
88 fs.unlinkSync(installTag);
89}
90
91// set an environment to the location of our install directory and bin directory
92process.env.APPC_INSTALL_DIR = installDir;
93process.env.APPC_INSTALL_BIN_DIR = installBin;
94
95debug('install bin is %o',installBin);
96
97process.env.APPC_CONFIG_PROXY = util.getProxyServer();
98
99function after(err, installDir, version, installBin) {
100 debug('after called with %o',arguments);
101
102 if (opts.setup) {
103 // save our version
104 util.writeVersion(version);
105
106 var appcli = util.getConfigFile();
107
108 // if we don't have this file, we need to prompt for login for first-run
109 if (!fs.existsSync(appcli)) {
110 var chalk = require('../vendor/chalk');
111 console.log();
112 console.log(chalk.yellow('Appcelerator login required. Please login now...'));
113 console.log(chalk.yellow(chalk.dim('If you do not yet have an account, please visit '+chalk.underline('http://www.appcelerator.com/signup'))));
114 console.log();
115 var args = program.argv.splice(2);
116 // run our login
117 process.argv[2] = 'login';
118 process.argv[3] = '--no-banner';
119 process.argv.length = 4;
120 util.mergeOptsToArgs(progress.argv,opts);
121 debug('calling lib run %s',installBin);
122 lib.run(installBin);
123 }
124 else {
125 // no need to re-run login. we're already setup
126 process.exit(0);
127 }
128 }
129}
130
131// any of these subcommands will be passed
132var bundledSubcommands = /^(alloy|acs|cloud)$/;
133
134/**
135 * support delegating to these bundled binaries in the case
136 * they are not already installed as standalone binaries.
137 * this is also a nice compromise for all the functionality we
138 * aren't going to include directly in the appc CLI such that
139 * we can still provide more advanced capabilities directly
140 * through these underlying CLIs
141 */
142function subprocess(cmd, installBin) {
143 var which = require('which'),
144 spawn = require('child_process').spawn,
145 pkg = path.join(installBin, '..','..'),
146 version = path.basename(path.dirname(pkg)),
147 argv = [],
148 bin,
149 env = process.env,
150 argopts = { env: env, stdio:'inherit' };
151
152 // re-create args exactly as passed in but remove our subcommand
153 var _args = process.argv.splice(2);
154 for (var c=0;c<_args.length;c++) {
155 var arg = _args[c];
156 if (arg!==cmd) {
157 argv.push(arg);
158 }
159 }
160
161 // set some special environment variables that can be used inside this scripts eventually if necessary
162 env.APPC_VERSION = version;
163 env.APPC_VERSION_DIR = pkg;
164 env.APPC_SUBPROCESS = 1;
165
166 // use our bundled version
167 switch(cmd) {
168 case 'alloy': {
169 bin = path.resolve(path.join(pkg, 'node_modules','appc-cli-titanium','node_modules','alloy','bin','alloy'));
170 break;
171 }
172 case 'cloud':
173 case 'acs': {
174 bin = path.resolve(path.join(pkg, 'node_modules','arrow','node_modules','acs','bin','acs'));
175 cmd = 'acs';
176 break;
177 }
178 }
179
180 if (!fs.existsSync(bin)) {
181 error.failWithError('com.appcelerator.install.binary.missing',bin,pkg,version);
182 }
183
184 debug('calling spawn with %s, bin=%s, argv=%j, argopts=%j',process.execPath,bin,argv,argopts);
185
186 // for windows, we need to actually run from node since these binaries aren't setup with cmd.exe
187 var childArgs = (path.extname(bin).toLowerCase() === '.cmd') ? [bin, argv, argopts] :
188 [process.execPath, [bin].concat(argv), argopts];
189 var child = spawn.apply(null, childArgs);
190
191 child.on('error', function(err){
192 error.failWithError('com.appcelerator.install.binary.error',bin,err.message||String(err));
193 });
194}
195
196function main() {
197
198 var subcommand = args[0];
199 debug('main subcommand %s',subcommand);
200
201 // see if this is a use command
202 if (process.argv.length > 2) {
203 if (subcommand==='use') {
204 return lib.use(opts, function(err) {
205 if (err) {
206 if (err.name === 'AppCError') {
207 err.failWithStackTrace();
208 }
209 error.failWithError('com.appcelerator.install.download.server.response.error',err.message||String(err));
210 }
211 // if we get here, we didn't find a version and we need to install it
212 return lib.install(util.getInstallDir(), opts);
213 });
214 }
215 else {
216 if (opts.version) {
217 debug('main version found %s',opts.version);
218 // if we aren't asking to print out the version but to use a specific version
219 // we will set the version (remove the --version from args) and then run command
220 // with this version
221 if (opts.version!==true) {
222 // set it explicitly by passing the version (otherwise will resolve to available)
223 // if it doesn't explicitly find it, will return null and then install below
224 installBin = util.getInstallBinary(opts,opts.version);
225 debug('main explicitly set a version, installBin=%s',installBin);
226 // delete --version so we don't add to pass through args
227 var _args = [];
228 for (var c=0;c<process.argv.length;c++) {
229 var match = /^--version(=?)/.exec(process.argv[c]);
230 if (match) {
231 // if this is --version x.x.x we need to skip the next arg too
232 if (!match[1]) {
233 c++;
234 }
235 continue;
236 }
237 else {
238 _args.push(process.argv[c]);
239 }
240 }
241 process.argv = _args;
242 debug('main set new args to %o',_args);
243 if (!installBin) {
244 // doesn't exist so we need to first install and then run it, but do it
245 // here so that after doesn't get used below
246 return lib.install(util.getInstallDir(), opts);
247 }
248 }
249 }
250 }
251 }
252 // see if setup
253 if (args[0]==='setup') {
254 opts.setup = true;
255 installBin = null;
256 debug('main setup found');
257 }
258 // if we can't find a suitable install binary, we need to install one
259 if (!installBin || !fs.existsSync(installBin)) {
260 opts.setup = true;
261 debug("main - !installBin or couldn't find it %s",installBin);
262 lib.install(util.getInstallDir(), opts, after);
263 }
264 // otherwise, we should just run
265 else {
266 // first update check if required
267 util.updateCheck(opts, function(){
268
269 // check to see if this is a bundled subcommand
270 if (bundledSubcommands.test(subcommand)){
271 debug("main - bundled subcommand, calling subprocess");
272 return subprocess(subcommand,installBin);
273 }
274
275 // now run
276 lib.run(installBin);
277 });
278 }
279}
280
281// run our program
282main();