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 | */
|
10 | var chalk = require('chalk'),
|
11 | semver = require('semver'),
|
12 | pkg = require('../package');
|
13 |
|
14 | // check Node.js version
|
15 | try {
|
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 |
|
28 | var 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
|
40 | process.__argv = [].concat(process.argv);
|
41 |
|
42 | // set appc npm version
|
43 | process.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
|
48 | var npmCacheDir = util.getNpmCacheDirectory();
|
49 | if (fs.existsSync(npmCacheDir)) {
|
50 | dirError = util.checkDirectory(npmCacheDir,'npm');
|
51 | }
|
52 | else {
|
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
|
59 | if (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
|
65 | process.env.npm_config_cache = npmCacheDir;
|
66 | process.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
|
70 | var installDir = util.getInstallDir();
|
71 | if (fs.existsSync(installDir)) {
|
72 | dirError = util.checkDirectory(installDir,'install');
|
73 | }
|
74 | else {
|
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
|
81 | if (dirError) {
|
82 | util.fail(dirError);
|
83 | }
|
84 |
|
85 | // cleanup stale install tag
|
86 | var installTag = util.getInstallTag();
|
87 | if (fs.existsSync(installTag)) {
|
88 | fs.unlinkSync(installTag);
|
89 | }
|
90 |
|
91 | // set an environment to the location of our install directory and bin directory
|
92 | process.env.APPC_INSTALL_DIR = installDir;
|
93 | process.env.APPC_INSTALL_BIN_DIR = installBin;
|
94 |
|
95 | debug('install bin is %o',installBin);
|
96 |
|
97 | process.env.APPC_CONFIG_PROXY = util.getProxyServer();
|
98 |
|
99 | function 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
|
132 | var 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 | */
|
142 | function 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 |
|
196 | function 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
|
282 | main();
|