Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merging my repo with the main titanium cli repo.

  • Loading branch information...
commit 5956a56260dc468519eff89534bdacbc9fd9c6c9 1 parent 57c4724
@cb1kenobi cb1kenobi authored
Showing with 1,904 additions and 10,010 deletions.
  1. +3 −1 .gitignore
  2. +5 −0 .npmignore
  3. +24 −15 README.md
  4. +3 −21 bin/titanium
  5. +532 −0 lib/cli.js
  6. +16 −0 lib/commands/config.js
  7. +360 −0 lib/commands/help.js
  8. +26 −0 lib/commands/login.js
  9. +16 −0 lib/commands/logout.js
  10. +439 −0 lib/commands/sdk.js
  11. +31 −0 lib/commands/status.js
  12. +18 −0 lib/commands/version.js
  13. +37 −0 lib/config.js
  14. +126 −0 lib/logger.js
  15. +102 −96 lib/titanium.js
  16. +0 −24 lib/titanium/analytics.js
  17. +0 −18 lib/titanium/builtins/builtins.js
  18. +0 −122 lib/titanium/builtins/help.js
  19. +0 −25 lib/titanium/builtins/login.js
  20. +0 −24 lib/titanium/builtins/logout.js
  21. +0 −298 lib/titanium/builtins/sdk.js
  22. +0 −55 lib/titanium/config.js
  23. +0 −115 lib/titanium/environ.js
  24. +0 −180 lib/titanium/externals/externals.js
  25. +0 −104 lib/titanium/log.js
  26. +0 −68 lib/titanium/path.js
  27. +0 −20 lib/titanium/string.js
  28. +110 −0 locales/en.js
  29. +0 −1  node_modules/.bin/asciimo
  30. +0 −1  node_modules/.bin/semver
  31. +0 −1  node_modules/.bin/unzip.js
  32. +0 −22 node_modules/colors/MIT-LICENSE.txt
  33. +0 −77 node_modules/colors/ReadMe.md
  34. +0 −269 node_modules/colors/colors.js
  35. +0 −74 node_modules/colors/example.html
  36. +0 −65 node_modules/colors/example.js
  37. +0 −14 node_modules/colors/package.json
  38. +0 −65 node_modules/colors/test.js
  39. +0 −4 node_modules/optimist/.travis.yml
  40. +0 −21 node_modules/optimist/LICENSE
  41. +0 −487 node_modules/optimist/README.markdown
  42. +0 −10 node_modules/optimist/example/bool.js
  43. +0 −7 node_modules/optimist/example/boolean_double.js
  44. +0 −7 node_modules/optimist/example/boolean_single.js
  45. +0 −8 node_modules/optimist/example/default_hash.js
  46. +0 −7 node_modules/optimist/example/default_singles.js
  47. +0 −8 node_modules/optimist/example/divide.js
  48. +0 −20 node_modules/optimist/example/line_count.js
  49. +0 −29 node_modules/optimist/example/line_count_options.js
  50. +0 −29 node_modules/optimist/example/line_count_wrap.js
  51. +0 −4 node_modules/optimist/example/nonopt.js
  52. +0 −2  node_modules/optimist/example/reflect.js
  53. +0 −3  node_modules/optimist/example/short.js
  54. +0 −11 node_modules/optimist/example/string.js
  55. +0 −19 node_modules/optimist/example/usage-options.js
  56. +0 −10 node_modules/optimist/example/xup.js
  57. +0 −475 node_modules/optimist/index.js
  58. +0 −1  node_modules/optimist/node_modules/wordwrap/.npmignore
  59. +0 −70 node_modules/optimist/node_modules/wordwrap/README.markdown
  60. +0 −10 node_modules/optimist/node_modules/wordwrap/example/center.js
  61. +0 −3  node_modules/optimist/node_modules/wordwrap/example/meat.js
  62. +0 −76 node_modules/optimist/node_modules/wordwrap/index.js
  63. +0 −37 node_modules/optimist/node_modules/wordwrap/package.json
  64. +0 −30 node_modules/optimist/node_modules/wordwrap/test/break.js
  65. +0 −63 node_modules/optimist/node_modules/wordwrap/test/idleness.txt
  66. +0 −31 node_modules/optimist/node_modules/wordwrap/test/wrap.js
  67. +0 −43 node_modules/optimist/package.json
  68. +0 −71 node_modules/optimist/test/_.js
  69. +0 −2  node_modules/optimist/test/_/argv.js
  70. +0 −3  node_modules/optimist/test/_/bin.js
  71. +0 −420 node_modules/optimist/test/parse.js
  72. +0 −292 node_modules/optimist/test/usage.js
  73. +0 −2  node_modules/pkginfo/.npmignore
  74. +0 −85 node_modules/pkginfo/README.md
  75. +0 −194 node_modules/pkginfo/docs/docco.css
  76. +0 −101 node_modules/pkginfo/docs/pkginfo.html
  77. +0 −19 node_modules/pkginfo/examples/all-properties.js
  78. +0 −20 node_modules/pkginfo/examples/array-argument.js
  79. +0 −19 node_modules/pkginfo/examples/multiple-properties.js
  80. +0 −22 node_modules/pkginfo/examples/object-argument.js
  81. +0 −10 node_modules/pkginfo/examples/package.json
  82. +0 −19 node_modules/pkginfo/examples/single-property.js
  83. +0 −132 node_modules/pkginfo/lib/pkginfo.js
  84. +0 −17 node_modules/pkginfo/package.json
  85. +0 −69 node_modules/pkginfo/test/pkginfo-test.js
  86. +0 −55 node_modules/request/LICENSE
  87. +0 −287 node_modules/request/README.md
  88. +0 −103 node_modules/request/forever.js
  89. +0 −913 node_modules/request/main.js
  90. +0 −152 node_modules/request/mimetypes.js
  91. +0 −34 node_modules/request/oauth.js
  92. +0 −15 node_modules/request/package.json
  93. BIN  node_modules/request/tests/googledoodle.png
  94. +0 −38 node_modules/request/tests/run.js
  95. +0 −82 node_modules/request/tests/server.js
  96. +0 −77 node_modules/request/tests/squid.conf
  97. +0 −20 node_modules/request/tests/ssl/ca/ca.cnf
  98. 0  node_modules/request/tests/ssl/ca/ca.crl
  99. +0 −17 node_modules/request/tests/ssl/ca/ca.crt
  100. +0 −13 node_modules/request/tests/ssl/ca/ca.csr
  101. +0 −18 node_modules/request/tests/ssl/ca/ca.key
  102. +0 −1  node_modules/request/tests/ssl/ca/ca.srl
  103. +0 −19 node_modules/request/tests/ssl/ca/server.cnf
  104. +0 −16 node_modules/request/tests/ssl/ca/server.crt
  105. +0 −11 node_modules/request/tests/ssl/ca/server.csr
  106. +0 −28 node_modules/request/tests/ssl/ca/server.js
  107. +0 −9 node_modules/request/tests/ssl/ca/server.key
  108. +0 −16 node_modules/request/tests/ssl/npm-ca.crt
  109. +0 −15 node_modules/request/tests/ssl/test.crt
  110. +0 −15 node_modules/request/tests/ssl/test.key
  111. +0 −95 node_modules/request/tests/test-body.js
  112. +0 −29 node_modules/request/tests/test-cookie.js
  113. +0 −90 node_modules/request/tests/test-cookiejar.js
  114. +0 −68 node_modules/request/tests/test-defaults.js
  115. +0 −37 node_modules/request/tests/test-errors.js
  116. +0 −52 node_modules/request/tests/test-headers.js
  117. +0 −94 node_modules/request/tests/test-httpModule.js
  118. +0 −97 node_modules/request/tests/test-https-strict.js
  119. +0 −86 node_modules/request/tests/test-https.js
  120. +0 −117 node_modules/request/tests/test-oauth.js
  121. +0 −92 node_modules/request/tests/test-params.js
  122. +0 −202 node_modules/request/tests/test-pipes.js
  123. +0 −39 node_modules/request/tests/test-proxy.js
  124. +0 −28 node_modules/request/tests/test-qs.js
  125. +0 −154 node_modules/request/tests/test-redirect.js
  126. +0 −87 node_modules/request/tests/test-timeout.js
  127. +0 −14 node_modules/request/tests/test-toJSON.js
  128. +0 −61 node_modules/request/tests/test-tunnel.js
  129. +0 −229 node_modules/request/tunnel.js
  130. +0 −19 node_modules/request/uuid.js
  131. +0 −65 node_modules/request/vendor/cookie/index.js
  132. +0 −72 node_modules/request/vendor/cookie/jar.js
  133. +0 −23 node_modules/semver/LICENSE
  134. +0 −119 node_modules/semver/README.md
  135. +0 −71 node_modules/semver/bin/semver
  136. +0 −11 node_modules/semver/package.json
  137. +0 −305 node_modules/semver/semver.js
  138. +0 −397 node_modules/semver/test.js
  139. +0 −4 node_modules/temp/.npmignore
  140. +0 −20 node_modules/temp/LICENSE
  141. +0 −176 node_modules/temp/README.md
  142. +0 −18 node_modules/temp/examples/grepcount.js
  143. +0 −22 node_modules/temp/examples/pdfcreator.js
  144. +0 −144 node_modules/temp/lib/temp.js
  145. +0 −28 node_modules/temp/package.json
  146. +0 −52 node_modules/temp/test/temp-test.js
  147. +56 −42 package.json
View
4 .gitignore
@@ -1,2 +1,4 @@
+._*
+.DS_Store
npm-debug.log
-
+node_modules
View
5 .npmignore
@@ -0,0 +1,5 @@
+.DS_Store
+.git*
+node_modules
+*.sublime-project
+*.sublime-workspace
View
39 README.md
@@ -1,31 +1,40 @@
## Overview
-[Titanium](https://github.com/appcelerator/titanium) is a [Command Line Tool (CLI)](http://en.wikipedia.org/wiki/Command-line_interface) for managing and deploying Titanium mobile applications and modules. It's open-source and easy to use. [We've](https://github.com/appcelertor) designed `titanium` to be suitable for command line beginners, but still be powerful and extensible enough for production usage.
+[Titanium](https://github.com/appcelerator/titanium) is a [Command Line Tool (CLI)](http://en.wikipedia.org/wiki/Command-line_interface)
+for managing and deploying Titanium Mobile applications and modules. It's open-source and easy to use. [We've](https://github.com/appcelertor)
+designed Titanium to be suitable for command line beginners, but still be powerful and extensible enough for production usage.
-`titanium` requires `npm`, the [node package manager](http://npmjs.org).
+## Local Installation
-## One-line npm install
-
- [sudo] curl http://npmjs.org/install.sh | sh
+ [sudo] npm install titanium -g
+## Bleeding Edge
-## One-line titanium install
+ // do this once
+ git clone https://github.com/appcelerator/titanium.git
+
+ // do this frequently
+ cd /path/to/titanium
+ git pull origin master
+ [sudo] npm install -g .
- [sudo] npm install titanium -g
+## Commands
-<a name="Libraries"></a>
-## Libraries
-`titanium` is built on a few well developed, well maintained Node.js libraries.
+* config - Configure your CLI settings
+* help - Displays help or help for a specific command
+* login - Logs into the Appcelerator Network
+* logout - Logs out of the Appcelerator Network
+* sdk - Download and install Titanium SDKs
+* status - Check authentication, SDK, and project status
+* version - Titanium CLI version
-- [npm](http://npmjs.org) - Node Package Manager
-- [colors](https://github.com/marak/colors.js) - Terminal Colors module
-- [optimist](https://github.com/substack/node-optimist) - CLI Options Parsing
+## Usage
+ titanium <command> [options]
-<a name="License"></a>
## License
This project is open source and provided under the Apache Public License (version 2). Please make sure you see the `LICENSE` file
included in this distribution for more details on the license. Also, please take notice of the privacy notice at the end of the file.
-#### (C) Copyright 2012, [Appcelerator](http://www.appcelerator.com) Inc. All Rights Reserved.
+#### (C) Copyright 2012, [Appcelerator](http://www.appcelerator.com/) Inc. All Rights Reserved.
View
24 bin/titanium
@@ -1,22 +1,4 @@
-#!/usr/bin/env node
-
-var titanium = require('../lib/titanium');
-
-titanium.start(function (err)
-{
-
- process.stdout.on('drain', function () {
-// process.exit(err ? 1 : 0);
- })
-
- function onexit (code, status) {
- if (err) {
- process.removeListener('exit', onexit);
- process.exit(code);
- }
- }
-
- process.on('exit', onexit);
-
-});
+#!/usr/bin/env node --no-deprecation
+// --debug-brk
+require('../lib/titanium');
View
532 lib/cli.js
@@ -0,0 +1,532 @@
+/*
+ * cli.js: Titanium CLI processor
+ *
+ * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
+ * See the LICENSE file for more information.
+ *
+ * Portions derived from optimist under the MIT license.
+ * Copyright 2010 James Halliday (mail@substack.net)
+ * https://github.com/substack/node-optimist
+ *
+ * Portions derived from complete under the MIT license.
+ * Copyright (c) 2010 hij1nx <http://www.twitter.com/hij1nx>
+ * https://github.com/hij1nx/complete
+ */
+
+var cli = exports,
+ fs = require('fs'),
+ path = require('path'),
+ logger = require('./logger'),
+ config = require('./config'),
+ appc = require('node-appc'),
+ aliases = {},
+ flags = {},
+ cmds = cli.cmds = {
+ __global__:{
+ options: {},
+ flags: {}
+ }
+ };
+
+cli.startTime = Date.now();
+
+// TODO: cache the commands/options/flags
+
+// flag/option function creator
+function create(type) {
+ var fn = function (name, params, context, sdk, platform) {
+ if (typeof name == 'object') {
+ platform = sdk;
+ sdk = context;
+ context = params;
+ Object.keys(name).forEach(function (k) {
+ fn(k, name[k], context, sdk, platform);
+ });
+ } else {
+ params = params || {};
+
+ if (context !== null) {
+ context || (context = cmds['__global__']);
+ context[type] || (context[type] = {});
+ context[type][name] = params;
+ }
+
+ if (params.alias) {
+ Array.isArray(params.alias) || (params.alias = [ params.alias ]);
+ Array.isArray(aliases[params.alias]) || (aliases[params.alias] = []);
+ params.alias.forEach(function (alias) {
+ ~aliases[alias].indexOf(name) || aliases[alias].push(name);
+ });
+ }
+
+ if (params.abbr) {
+ Array.isArray(aliases[params.abbr]) || (aliases[params.abbr] = []);
+ ~aliases[params.abbr].indexOf(name) || aliases[params.abbr].push(name);
+ }
+
+ // need to special case flags so the parse won't be stupid
+ if (type === 'flags') {
+ flags[name] = 1;
+ }
+ }
+ return cli;
+ };
+ return fn;
+}
+
+cli.flag = cli.flags = create('flags');
+
+cli.option = cli.options = create('options');
+
+cli.command = cli.commands = function (name, params, sdk, platform) {
+ if (typeof name == 'object') {
+ platform = sdk;
+ sdk = params;
+ Object.keys(name).forEach(function (k) {
+ cli.command(k, name[k], sdk, platform);
+ });
+ } else {
+ cmds[name] || (cmds[name] = {});
+
+ if (sdk) {
+ platform = platform || '__global__';
+ cmds[name][sdk] || (cmds[name][sdk] = {});
+ cmds[name][sdk][platform] = params;
+ } else {
+ cmds[name].__global__ = params;
+ }
+
+ params.options && Object.keys(params.options).forEach(function (name) {
+ cli.option(name, params.options[name], cmds[name], sdk, platform);
+ });
+
+ params.flags && Object.keys(params.flags).forEach(function (name) {
+ cli.flag(name, params.flags[name], cmds[name], sdk, platform);
+ });
+ }
+
+ return cli;
+};
+
+// helper function that allows arbitrary code to run in the cli chain
+cli.then = function (fn) {
+ fn && fn.call && fn.call(cli, logger);
+ return cli;
+};
+
+// finds and returns a command if it exists, otherwise crashes and shows an error (and possibly suggestions)
+function assertAndFetchCommand(cmd) {
+ if (cmds.hasOwnProperty(cmd)) {
+ return cmds[cmd];
+ }
+
+ logger.banner();
+
+ tierror(__('"%s" is an unrecognized command.', cmd) + '\n');
+
+ var suggestions = Object.keys(cmds).filter(function (c) {
+ if (c != '__global__' && (c.indexOf(cmd) == 0 || appc.string.levenshtein(cmd, c) <= 3)) {
+ return c;
+ }
+ });
+
+ if (suggestions.length) {
+ console.log(__('Did you mean this?'));
+ suggestions.forEach(function (s) {
+ console.log(' ' + s.cyan);
+ });
+ console.log();
+ }
+
+ console.log(__("Run '%s' for available commands.", (cli.argv.$ + ' help').cyan) + '\n');
+
+ process.exit(1);
+}
+
+function initCompletion(program) {
+ var bashrc,
+ files = ['.bash_profile', '.bash_login', '.profile'],
+ l = files.length,
+ i = 0,
+ dir = path.join(process.env.HOME, '.node-completion'),
+ file = path.join(dir, program),
+ source = [
+ '',
+ '# {{{',
+ '# Node Completion - Auto-generated, do not touch.',
+ 'shopt -s progcomp',
+ 'for f in $(command ls ~/.node-completion); do',
+ ' f="$HOME/.node-completion/$f"',
+ ' test -f "$f" && . "$f"',
+ 'done',
+ '# }}}',
+ ''
+ ].join('\n'),
+ completion = [
+ '__{{NAME}}_comp() {',
+ ' COMPREPLY=()',
+ ' COMPREPLY=($({{NAME}} --compgen "${COMP_WORDS[@]}"))',
+ ' return 0',
+ '}',
+ '',
+ 'complete -F __{{NAME}}_comp {{NAME}} 2>/dev/null',
+ ''
+ ].join('\n').replace(/{{NAME}}/g, program);
+
+ if (process.platform !== 'darwin') {
+ bashrc = path.join(process.env.HOME, '.bashrc');
+ } else {
+ for (; i < l; i++) {
+ bashrc = path.join(process.env.HOME, files[i]);
+ if (appc.fs.exists(bashrc)) {
+ break;
+ } else {
+ bashrc = '';
+ }
+ }
+
+ // If none exist, create a .bash_profile.
+ bashrc || (bashrc = path.join(process.env.HOME, '.bash_profile'));
+ }
+
+ fs.readFile(bashrc, 'utf8', function (err, data) {
+ data = data || '';
+ if ((err && err.code === 'ENOENT') || !~data.indexOf('# Node Completion')) {
+ data += source;
+ fs.writeFile(bashrc, data);
+ }
+ });
+
+ fs.mkdir(dir, 0755, function () {
+ fs.stat(file, function (err) {
+ if (err && err.code === 'ENOENT') {
+ fs.writeFile(file, completion);
+ }
+ });
+ });
+}
+
+cli.parse = function (args) {
+ var argv = cli.argv = {
+ _: [],
+ $0: process.argv.slice(0, 2).join(' '),
+ $command: null
+ },
+ idx,
+ file,
+ cd;
+
+ args = args || process.argv.slice();
+ args[0].slice(-4) === 'node' && args.shift();
+ cd = path.dirname(file = args[0]);
+
+ try {
+ while (fs.lstatSync(file).isSymbolicLink()) {
+ file = fs.readlinkSync(file);
+ cd = path.resolve(cd, path.dirname(file));
+ file = path.resolve(cd, path.basename(file));
+ }
+ } catch (e) {
+ file = args[0];
+ }
+
+ argv.$ = path.basename(file);
+ args.shift();
+ idx = args.indexOf('--compgen');
+
+ // make sure the bash completion stuff is installed
+ initCompletion(argv.$);
+
+ if (~idx) {
+ // doing tab completion, squelch all output
+ process.on('uncaughtException', function () {});
+
+ if (config.cli.completion) {
+ args = args.slice(idx + 2);
+ } else {
+ process.exit(0);
+ }
+ }
+
+ function setArg(key, val) {
+ var keys = key.split('.'),
+ num = Number(val),
+ value = typeof val !== 'string' || isNaN(num) ? val : num,
+ obj = argv;
+
+ key = keys.pop();
+
+ keys.forEach(function (k) {
+ obj.hasOwnProperty(k) || (obj[k] = {});
+ obj = obj[k];
+ });
+
+ if (obj[key] === undefined || typeof obj[key] === 'boolean') {
+ obj[key] = value;
+ } else if (Array.isArray(obj[key])) {
+ ~obj[key].indexOf(value) || obj[key].push(value);
+ } else {
+ obj[key] = value;
+ }
+
+ (aliases[key] || []).forEach(function (x) {
+ Array.isArray(x) || (x = [x]);
+ x.forEach(function (y) {
+ argv[y] = argv[key];
+ });
+ });
+ }
+
+ // set the default values
+ var _global = cmds.__global__;
+ _global.flags && Object.keys(_global.flags).forEach(function (name) {
+ setArg(name, _global.flags[name].default || false);
+ });
+ _global.options && Object.keys(_global.options).forEach(function (name) {
+ _global.options[name].hasOwnProperty('default') && setArg(name, _global.options[name].default);
+ });
+
+ for (var i = 0; i < args.length; i++) {
+ var arg = args[i];
+
+ if (arg === '--') {
+ argv._.push.apply(argv._, args.slice(i + 1));
+ break;
+ } else if (arg.match(/^--.+=/)) {
+ // --option=value
+ var m = arg.match(/^--([^=]+)=(.*)/);
+ setArg(m[1], m[2]);
+ } else if (arg.match(/^--no-.+/)) {
+ // --no-flag
+ var key = arg.match(/^--no-(.+)/)[1];
+ setArg(key, false);
+ } else if (arg.match(/^--.+/)) {
+ // --flag or --option
+ var key = arg.match(/^--(.+)/)[1],
+ next = args[i + 1];
+ if (next !== undefined && !next.match(/^-/) && !flags[key] && (aliases[key] ? !flags[aliases[key]] : true)) {
+ // --option value
+ setArg(key, next);
+ i++;
+ } else if (next === undefined && !flags[key] && (aliases[key] ? !flags[aliases[key]] : true)) {
+ // do nothing
+ } else if (/true|false/.test(next)) {
+ // --flag true
+ setArg(key, next === 'true');
+ i++;
+ } else {
+ // --flag
+ setArg(key, true);
+ }
+ } else if (arg.match(/^-[^-]+/)) {
+ var letters = arg.slice(1, -1).split(''),
+ broken = false;
+
+ for (var j = 0; j < letters.length; j++) {
+ if (letters[j+1] && letters[j+1].match(/\W/)) {
+ setArg(letters[j], arg.slice(j+2));
+ broken = true;
+ break;
+ } else {
+ setArg(letters[j], true);
+ }
+ }
+
+ if (!broken) {
+ var key = arg.slice(-1)[0],
+ next = args[i + 1];
+ if (next && !next.match(/^-/) && !flags[key] && (aliases[key] ? !flags[aliases[key]] : true)) {
+ setArg(key, next);
+ i++;
+ } else if (next === undefined && !flags[key] && (aliases[key] ? !flags[aliases[key]] : true)) {
+ // do nothing
+ } else if (next && /true|false/.test(next)) {
+ setArg(key, next === 'true');
+ i++;
+ } else {
+ setArg(key, true);
+ }
+ }
+ } else {
+ var n = Number(arg);
+ argv._.push(isNaN(n) ? arg : n);
+
+ if (argv._.length === 1) {
+ if (!argv.$command) {
+ // the first argument! it's gotta be a command!
+ argv.$command = argv._.shift();
+
+ function loadCommand(ctx, sdk, platform) {
+ try {
+ var cmd = require(ctx.modulePath),
+ conf = (cmd.config && cmd.config(logger, config, cli)) || {};
+
+ if (conf.requireAuth) {
+ conf.options.user = {
+ desc: __('user to log in as, if not already logged in')
+ };
+ conf.options.password = {
+ desc: __('the password to log in with'),
+ promptHidden: true
+ };
+ }
+
+ for (var c in conf) {
+ conf.hasOwnProperty(c) && (ctx[c] = conf[c]);
+ }
+
+ conf.options && Object.keys(conf.options).forEach(function (name) {
+ var opt = conf.options[name];
+ cli.option(name, opt, ctx, sdk, platform);
+ opt.hasOwnProperty('default') && setArg(name, opt.default);
+ });
+
+ conf.flags && Object.keys(conf.flags).forEach(function (name) {
+ var fl = conf.flags[name];
+ cli.flag(name, fl, ctx, sdk, platform);
+ fl.hasOwnProperty('default') && setArg(name, fl.default || false);
+ });
+ } catch (ex) {}
+ }
+
+ // add this command's options and flags to the pool.
+ // note that there could be multiple version of the same command, so we need
+ // to walk everything. last option/flag wins.
+ var c = assertAndFetchCommand(argv.$command);
+ Object.keys(c).forEach(function (sdk) {
+ if (sdk === '__global__') {
+ loadCommand(c[sdk], sdk);
+ } else {
+ Object.keys(c[sdk]).forEach(function (platform) {
+ loadCommand(c[sdk][platform], sdk, platform);
+ });
+ }
+ });
+ } else if (!argv.$subcommand) {
+ // check if we're running a subcommand
+ var match = false,
+ subcommand = argv._[0];
+
+ function loadSubcommand(ctx, sdk, platform) {
+ if (ctx.subcommands && ctx.subcommands.hasOwnProperty(subcommand)) {
+ var o = ctx.subcommands[subcommand].options,
+ f = ctx.subcommands[subcommand].flags;
+
+ o && Object.keys(o).forEach(function (name) {
+ var opt = o[name];
+ cli.option(name, opt, ctx, sdk, platform);
+ opt.hasOwnProperty('default') && setArg(name, opt.default);
+ });
+
+ f && Object.keys(f).forEach(function (name) {
+ var fl = f[name];
+ cli.flag(name, fl, ctx, sdk, platform);
+ fl.hasOwnProperty('default') && setArg(name, fl.default || false);
+ });
+
+ match = true;
+ }
+ }
+
+ var c = cmds[argv.$command];
+ Object.keys(c).forEach(function (sdk) {
+ if (sdk === '__global__') {
+ loadSubcommand(c[sdk], sdk);
+ } else {
+ Object.keys(c[sdk]).forEach(function (platform) {
+ loadSubcommand(c[sdk][platform], sdk, platform);
+ });
+ }
+ });
+
+ match && (argv.$subcommand = argv._[0]);
+ }
+ }
+ }
+ }
+
+ // if we didn't find a command, then show the help
+ argv.$command = argv.$command || 'help';
+
+ if (~idx && config.cli.completion) {
+ // TODO: complete words!
+ console.log(args.join('|'));
+
+ throw 'stop';
+ }
+
+ return cli;
+};
+
+cli.validate = function () {
+ var argv = cli.argv,
+ sdk = argv.sdk || 'latest',
+ platform = argv.platform,
+ module;
+
+ function apply(ctx) {
+ function call(name, items) {
+ if (items[name].callback) {
+ var abbr = items[name].abbr;
+ argv.hasOwnProperty(name) && items[name].callback(argv[name], logger, cli);
+ argv.hasOwnProperty(abbr) && items[name].callback(argv[abbr], logger, cli);
+ }
+ }
+ ctx.options && Object.keys(ctx.options).forEach(function (name) {
+ call(name, ctx.options);
+ });
+ ctx.flags && Object.keys(ctx.flags).forEach(function (name) {
+ call(name, ctx.flags);
+ });
+ }
+
+ apply(cmds.__global__);
+ cmds[argv.$command] && apply(cmds[argv.$command]);
+
+ if (sdk === 'latest') {
+ sdk = Object.keys(cmds[argv.$command]).filter(function (c) {
+ return c !== '__global__';
+ }).sort().pop();
+ }
+
+ if (sdk && !(sdk in cmds[argv.$command])) {
+ tierror(__('Command "%s" not support by SDK version %s', argv.$command, sdk) + '\n');
+ process.exit(1);
+ }
+
+ if (cmds[argv.$command][sdk]) {
+ platform = platform || '__global__';
+ argv.$module = (cmds[argv.$command][sdk][platform] ? cmds[argv.$command][sdk][platform] : cmds[argv.$command][sdk]['__global__']).modulePath;
+ } else {
+ argv.$module = (cmds[argv.$command]['__global__'] || {}).modulePath;
+ }
+
+ if (!argv.$module || !appc.fs.exists(argv.$module)) {
+ tierror(__('Unable to find command "%s"', argv.$command) + '\n');
+ process.exit(1);
+ }
+
+ // TODO: validate options
+
+ try {
+ var mod = require(argv.$module);
+ mod.validate && mod.validate(logger, config, cli);
+ } catch (ex) {
+ argv.exception = ex;
+ argv._.unshift(argv.$command);
+ argv.$module = (cmds['help']['__global__'] || {}).modulePath;
+ }
+
+ // TODO: if validation fails or param is missing, prompt or exit
+
+ return cli;
+};
+
+cli.run = function () {
+ try {
+ require(cli.argv.$module).run(logger, config, cli);
+ } catch (ex) {
+ tierror(__('Unable to run command "%s"', cli.argv.$command) + '\n');
+ tiexception(ex);
+ }
+};
View
16 lib/commands/config.js
@@ -0,0 +1,16 @@
+/*
+ * config.js: Titanium CLI config command
+ *
+ * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
+ * See the LICENSE file for more information.
+ */
+
+exports.config = function (logger, config, cli) {
+ return {
+ desc: __('get and set config options')
+ };
+};
+
+exports.run = function (logger, config, cli) {
+ dump(cli.argv);
+};
View
360 lib/commands/help.js
@@ -0,0 +1,360 @@
+/*
+ * help.js: Titanium CLI help command
+ *
+ * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
+ * See the LICENSE file for more information.
+ */
+
+var string = require('node-appc').string,
+ path = require('path');
+
+require('pkginfo')(module, 'version', 'about');
+
+exports.config = function (logger, config, cli) {
+ return {
+ desc: __('displays this help screen')
+ };
+};
+
+exports.run = function (logger, config, cli) {
+ var argv = cli.argv,
+ command = argv._.shift(),
+ subcommand;
+
+ while (command == 'help') {
+ command = argv._.shift();
+ }
+ subcommand = argv._.shift();
+
+ if (argv.exception) {
+ logger.exception(argv.exception);
+ }
+
+ function printList(heading, items) {
+ if (items.length) {
+ var maxlen = items.reduce(function (a, b) {
+ return Math.max(a, (Array.isArray(b[0]) ? b[0][0] : b[0]).length);
+ }, 0);
+ console.log(heading);
+ items.forEach(function (item) {
+ if (Array.isArray(item)) {
+ item.forEach(function (i) {
+ console.log(' %s %s', string.rpad(i[0], maxlen).cyan, i[1] || "");
+ });
+ } else {
+ console.log(' %s %s', string.rpad(item[0], maxlen).cyan, item[1] || "");
+ }
+ });
+ console.log();
+ }
+ };
+
+ function printOptionsFlags(ctx, title, skipSubcommands) {
+ var subcommands = {},
+ args = {},
+ flags = {},
+ options = {};
+
+ Object.keys(ctx).sort().forEach(function (sdk) {
+ var s = ctx[sdk];
+ if (sdk == '__global__') {
+ s.subcommands && Object.keys(s.subcommands)
+ .filter(function (name) {
+ return !s.subcommands[name].hidden;
+ })
+ .sort()
+ .forEach(function (a) {
+ subcommands[a] || (subcommands[a] = []);
+ subcommands[a].push(s.subcommands[a]);
+ });
+
+ s.args && s.args
+ .forEach(function (a) {
+ if (a.name) {
+ args[a.name] || (args[a.name] = []);
+ args[a.name].push(a);
+ }
+ });
+
+ s.flags && Object.keys(s.flags)
+ .filter(function (name) {
+ return !s.flags[name].hidden;
+ })
+ .sort()
+ .forEach(function (a) {
+ flags[a] || (flags[a] = []);
+ flags[a].push(s.flags[a]);
+ });
+
+ s.options && Object.keys(s.options)
+ .filter(function (name) {
+ return !s.options[name].hidden;
+ })
+ .sort()
+ .forEach(function (a) {
+ options[a] || (options[a] = []);
+ options[a].push(s.options[a]);
+ });
+ } else {
+ Object.keys(s).forEach(function (p) {
+ s[p].args && s[p].args
+ .forEach(function (a) {
+ if (a.name) {
+ a.sdk = sdk;
+ p != '__global__' && (a.platform = p);
+ args[a.name] || (args[a.name] = []);
+ args[a.name].push(a);
+ }
+ });
+
+ s[p].flags && Object.keys(s[p].flags)
+ .filter(function (name) {
+ return !s[p].flags[name].hidden;
+ })
+ .sort()
+ .forEach(function (a) {
+ s[p].flags[a].sdk = sdk;
+ p != '__global__' && (s[p].flags[a].platform = p);
+ flags[a] || (flags[a] = []);
+ flags[a].push(s[p].flags[a]);
+ });
+
+ s[p].options && Object.keys(s[p].options)
+ .filter(function (name) {
+ return !s[p].options[name].hidden;
+ })
+ .sort()
+ .forEach(function (a) {
+ s[p].options[a].sdk = sdk;
+ p != '__global__' && (s[p].options[a].platform = p);
+ options[a] || (options[a] = []);
+ options[a].push(s[p].options[a]);
+ });
+ });
+ }
+ });
+
+ if (subcommands && !skipSubcommands) {
+ printList(__('Subcommands:'),
+ Object.keys(subcommands)
+ .sort()
+ .map(function (name) {
+ var s = [];
+ subcommands[name].forEach(function (subcommand, j) {
+ var n = name;
+ s.push([
+ j > 0 ? n.replace(/./g, ' ') : n,
+ subcommand.desc
+ ]);
+ });
+ return s;
+ })
+ );
+ }
+
+ if (args) {
+ printList((title ? title + ' ' : '') + __('Arguments:'),
+ Object.keys(args)
+ .map(function (arg) {
+ var a = [];
+ args[arg].forEach(function (i, j) {
+ var n = '<' + i.name + '>';
+ a.push([
+ j > 0 ? n.replace(/./g, ' ') : n,
+ i.desc + ' ' + (i.sdk ? ' [--sdk=' + i.sdk + (i.platform ? ' ' + '--platform=' + i.platform : '') + ']' : '')
+ ]);
+ });
+ return a;
+ })
+ );
+ }
+
+ if (flags) {
+ printList((title ? title + ' ' : '') + __('Flags:'),
+ Object.keys(flags)
+ .map(function (name) {
+ var f = [];
+ flags[name].forEach(function (flag, j) {
+ var n = (flag.abbr ? '-' + flag.abbr + ', ' : '') + '--' + name + (flag.negate ? ', --no-' + name : '') + (flag.alias ? '|--' + flag.alias + (flag.negate ? ', --no-' + flag.alt : '') : '');
+ f.push([
+ j > 0 ? n.replace(/./g, ' ') : n,
+ flag.desc + ' ' + (flag.default ? ' ' + __('[default: %s]', flag.default) : '') + (flag.sdk ? ' [--sdk=' + flag.sdk + (flag.platform ? ' --platform=' + flag.platform : '') + ']' : '')
+ ]);
+ });
+ return f;
+ })
+ );
+ }
+
+ if (options) {
+ printList((title ? title + ' ' : '') + __('Options:'),
+ Object.keys(options)
+ .map(function (name) {
+ var o = [];
+ options[name].forEach(function (opt, j) {
+ var n = (opt.abbr ? '-' + opt.abbr + ', ' : '') + '--' + name + (opt.alias ? ' | --' + opt.alias : '') + ' ' + (opt.hint ? '<' + opt.hint + '>' : __('<value>')),
+ extra = '';
+
+ if (opt.values) {
+ extra = ' [' + opt.values.map(function (v) {
+ return v + (v == opt.default ? ' (' + __('default') + ')' : '');
+ }).join(', ') + ']';
+ } else if (opt.default) {
+ extra = ' [' + __('default') + ': ' + opt.default + ']';
+ }
+
+ o.push([
+ j > 0 ? n.replace(/./g, ' ') : n,
+ opt.desc + extra
+ ]);
+ });
+ return o;
+ })
+ );
+ }
+ }
+
+ if (command && command != '__global__' && cli.cmds[command]) {
+
+ // command specific usage
+ var cmd = cli.cmds[command],
+ subcmd,
+ required = [],
+ optional = [];
+
+ function load(ctx, sub) {
+ try {
+ var cmd = require(ctx.modulePath),
+ conf = (cmd.config && cmd.config(logger, config, cli)) || {};
+
+ if (sub && conf.subcommands) {
+ subcmd = conf.subcommands[sub];
+ required.unshift(subcmd ? sub : '<subcommand>');
+ } else {
+ for (var c in conf) {
+ conf.hasOwnProperty(c) && (ctx[c] = conf[c]);
+ }
+
+ ctx.flags && Object.keys(ctx.flags).forEach(function (name) {
+ var meta = ctx.flags[name];
+ if (meta.required) {
+ required.push('--' + name + (meta.alias ? '|--' + meta.alias : ''));
+ } else {
+ optional.push('[--' + name + (meta.alias ? '|--' + meta.alias : '') + ']');
+ }
+ });
+
+ ctx.options && Object.keys(ctx.options).forEach(function (name) {
+ var meta = ctx.options[name];
+ if (meta.required) {
+ required.push('--' + name + (meta.alias ? '|--' + meta.alias : '') + ' ' + (meta.hint ? '<' + meta.hint + '>' : __('<value>')));
+ } else {
+ optional.push('[--' + name + (meta.alias ? '|--' + meta.alias : '') + ' ' + (meta.hint ? '<' + meta.hint + '>' : __('<value>')) + ']');
+ }
+ });
+ }
+ } catch (ex) {}
+ }
+
+ function usage(ctx, sub) {
+ Object.keys(ctx).forEach(function (sdk) {
+ if (sdk === '__global__') {
+ load(ctx[sdk], sub)
+ } else {
+ // a real sdk
+ Object.keys(ctx[sdk]).forEach(function (platform) {
+ load(ctx[sdk][platform], sub);
+ });
+ }
+ });
+ }
+
+ subcommand && usage(cmd, subcommand);
+ usage(cmd);
+
+ required = required.concat(optional);
+
+ if (subcmd) {
+ subcmd.args && subcmd.args.forEach(function (arg) {
+ required.push(arg.required ? '<' + arg.name + '>' : '[<' + arg.name + '>]')
+ });
+ } else {
+ cmd.args && cmd.args.forEach(function (arg) {
+ required.push(arg.required ? '<' + arg.name + '>' : '[<' + arg.name + '>]')
+ });
+ }
+
+ console.log(__('Usage') + ': ' + (cli.argv.$ + ' ' + command + ' ' + required.join(' ')).cyan + '\n');
+
+ var title = '',
+ orderedSDKs = Object.keys(cmd).sort();
+
+ // command/subcommand description
+ if (subcmd) {
+ subcmd.desc && console.log(subcmd.desc.substring(0, 1).toUpperCase() + subcmd.desc.substring(1) + (/\.$/.test(subcmd.desc) ? '' : '.') + '\n');
+ } else {
+ // since there can be more than one command implementation, we start with the most recent
+ var desc = '';
+
+ for (var i = 0; !desc && i < orderedSDKs.length; i++) {
+ if (orderedSDKs[i] == '__global__') {
+ desc = cmd[orderedSDKs[i]].desc;
+ } else {
+ var platforms = Object.keys(cmd[orderedSDKs[i]]).sort();
+ for (var j = 0; !desc && j < platforms.length; j++) {
+ desc = cmd[orderedSDKs[i]][platforms[j]].desc;
+ }
+ }
+ }
+
+ if (desc) {
+ console.log(desc.substring(0, 1).toUpperCase() + desc.substring(1) + (/\.$/.test(desc) ? '' : '.') + '\n');
+ }
+ }
+
+ // command title for option/flag heading
+ for (var i = 0; !title && i < orderedSDKs.length; i++) {
+ if (orderedSDKs[i] == '__global__') {
+ title = cmd[orderedSDKs[i]].title;
+ } else {
+ var platforms = Object.keys(cmd[orderedSDKs[i]]).sort();
+ for (var j = 0; !title && j < platforms.length; j++) {
+ title = cmd[orderedSDKs[i]][platforms[j]].title;
+ }
+ }
+ }
+
+ // if we have a subcommand, display the subcommand details
+ subcmd && printOptionsFlags({ __global__: subcmd }, subcmd.title || string.capitalize(subcommand));
+
+ // display the current command's options/flags/args
+ printOptionsFlags(cmd, title || cmd.title || string.capitalize(command), !!subcmd);
+
+ // display the global options/flags... we have to hack a special __global__ object so options/flags aren't duplicated
+ printOptionsFlags({ __global__: cli.cmds.__global__ }, __('Global'), !!subcmd);
+
+ } else {
+
+ // check if we even know what the command is
+ if (command && command != '__global__') {
+ console.log(('[ERROR] ' + __('Unrecognized command "%s"', command)).red + '\n');
+ }
+
+ // general usage
+ console.log(__('Usage') + ': ' + (cli.argv.$ + ' <command> [options]').cyan + '\n');
+
+ printList(__('Commands:'),
+ Object.keys(cli.cmds)
+ .filter(function (name) {
+ return name != '__global__' && !cli.cmds[name].hidden;
+ })
+ .sort()
+ .map(function (name) {
+ return [[name, cli.cmds[name].desc]];
+ })
+ );
+
+ printOptionsFlags(cli.cmds, __('Global'));
+
+ }
+};
View
26 lib/commands/login.js
@@ -0,0 +1,26 @@
+/*
+ * login.js: Titanium CLI login command
+ *
+ * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
+ * See the LICENSE file for more information.
+ */
+
+exports.config = function (logger, config, cli) {
+ return {
+ desc: __('logs into the Appcelerator network'),
+ options: {
+ user: {
+ desc: __('user to log in as, if not already logged in'),
+ required: true
+ },
+ password: {
+ desc: __('the password to log in with'),
+ required: true
+ }
+ }
+ };
+};
+
+exports.run = function (logger, config, cli) {
+ dump(cli.argv);
+};
View
16 lib/commands/logout.js
@@ -0,0 +1,16 @@
+/*
+ * logout.js: Titanium CLI logout command
+ *
+ * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
+ * See the LICENSE file for more information.
+ */
+
+exports.config = function (logger, config, cli) {
+ return {
+ desc: __('logs out of the Appcelerator network')
+ };
+};
+
+exports.run = function (logger, config, cli) {
+ dump(cli.argv);
+};
View
439 lib/commands/sdk.js
@@ -0,0 +1,439 @@
+/*
+ * sdk.js: Titanium CLI SDK command
+ *
+ * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
+ * See the LICENSE file for more information.
+ */
+
+var async = require('async'),
+ fs = require('fs'),
+ request = require('request'),
+ temp = require('temp'),
+ wrench = require('wrench'),
+ appc = require('node-appc'),
+ urls = {
+ branches: 'http://builds.appcelerator.com.s3.amazonaws.com/mobile/branches.json',
+ branch: 'http://builds.appcelerator.com.s3.amazonaws.com/mobile/$BRANCH/index.json',
+ build: 'http://builds.appcelerator.com.s3.amazonaws.com/mobile/$BRANCH/$FILENAME',
+ releases: 'http://api.appcelerator.net/p/v1/release-list'
+ };
+
+exports.config = function (logger, config, cli) {
+ return {
+ title: __('SDK'),
+ desc: __('manages installed Titanium SDKs'),
+ options: {
+ 'log-level': {
+ callback: function (value) {
+ logger.levels[value] && logger.setLevel(value);
+ },
+ desc: __('minimum logging level'),
+ default: 'warn',
+ values: Object.keys(logger.levels)
+ }
+ },
+ subcommands: {
+ install: {
+ desc: __('download the latest Titanium SDK or a specific version'),
+ args: [
+ {
+ abbr: 'v',
+ default: 'latest',
+ desc: __('the version to install or "latest"'),
+ name: 'version',
+ required: true
+ }
+ ],
+ flags: {
+ force: {
+ abbr: 'f',
+ desc: __('force re-install')
+ }
+ },
+ options: {
+ branch: {
+ abbr: 'b',
+ desc: __('the branch to install from or "latest" (stable)'),
+ hint: __('branch name')
+ }
+ }
+ },
+ list: {
+ desc: __('print a list of installed SDK versions'),
+ flags: {
+ branches: {
+ abbr: 'b',
+ desc: __('retreive and print all branches')
+ },
+ releases: {
+ abbr: 'r',
+ desc: __('retreive and print all releases')
+ }
+ }
+ },
+ update: {
+ desc: __('check to find the latest version of the Titanium SDK'),
+ flags: {
+ install: {
+ abbr: 'i',
+ desc: __('install latest version'),
+ }
+ },
+ options: {
+ branch: {
+ abbr: 'b',
+ desc: __('the branch to update from'),
+ hint: __('branch name')
+ }
+ }
+ }
+ }
+ };
+};
+
+exports.validate = function (logger, config, cli) {
+ if (!cli.argv._.length) {
+ throw __('Missing subcommand');
+ }
+};
+
+exports.run = function (logger, config, cli) {
+ switch (cli.argv._.shift()) {
+ case 'install':
+ install(logger, cli.argv, cli.env);
+ break;
+
+ case 'list':
+ list(logger, cli.argv, cli.env);
+ break;
+
+ case 'update':
+ update(logger, cli.argv, cli.env);
+ break;
+ }
+};
+
+function fetch(url, desc, logger, callback, errback) {
+ request(url, function (error, response, body) {
+ if (error) {
+ logger.error(__('Failed to retrieve %s: %s', desc, error.toString()) + '\n');
+ errback ? errback() : process.exit(1);
+ }
+
+ if (response.statusCode != 200) {
+ logger.error(__('Failed to retrieve %s: expected 200, got %s', desc, response.statusCode) + '\n');
+ errback ? errback() : process.exit(1);
+ }
+
+ var data;
+ try {
+ data = JSON.parse(body);
+ } catch (ex) {
+ logger.error(__('Unable to parse %s results', desc) + '\n');
+ errback ? errback() : process.exit(1);
+ }
+
+ callback(data);
+ });
+}
+
+function getBranches(logger, callback, errback) {
+ fetch(urls.branches, 'list of branches', logger, callback, errback);
+}
+
+function getReleases(logger, os, callback, errback) {
+ fetch(urls.releases, 'list of releases', logger, function (data) {
+ var releases = {};
+ data && data.releases && data.releases.forEach(function (r) {
+ r.os == os && r.name == 'mobilesdk' && (releases[r.version] = r.url);
+ });
+ callback && callback(releases);
+ }, errback);
+}
+
+function downloadSDK(logger, url, version, env) {
+ logger.log(__('Downloading %s', url.cyan));
+
+ var tempName = temp.path({suffix: '.zip'}),
+ tempStream = fs.createWriteStream(tempName),
+ req = request(url),
+ pipeResp = req.pipe(tempStream);
+
+ req.on('error', function (err) {
+ fs.unlinkSync(tempName);
+ logger.error(__('Failed to download SDK: %s', err.toString()));
+ });
+
+ req.on('response', function (req) {
+ var bar = new appc.progress(' :paddedPercent [:bar] :etas', {
+ complete: '='.cyan,
+ incomplete: '.'.grey,
+ width: 65,
+ total: parseInt(req.headers['content-length'])
+ });
+
+ req.on('data', function (buffer) {
+ bar.tick(buffer.length);
+ });
+
+ tempStream.on('close', function (e) {
+ logger.log('\n');
+ extractSDK(logger, tempName, version, env);
+ });
+ });
+}
+
+function extractSDK(logger, filename, version, env) {
+ wrench.mkdirSyncRecursive(env.installPath);
+
+ logger.log(__('Extracting SDK...'));
+
+ var AdmZip = require('adm-zip'),
+ zip = new AdmZip(filename),
+ zipEntries = zip.getEntries(),
+ bar = new appc.progress(' :paddedPercent [:bar] :etas', {
+ complete: '='.cyan,
+ incomplete: '.'.grey,
+ width: 65,
+ total: zipEntries.length
+ }),
+ errors = 0,
+ tasks = [],
+ exec = require("child_process").exec;
+
+ function finished() {
+ fs.unlinkSync(filename);
+ if (errors) {
+ logger.log('\n\n' + __('Titanium SDK %s installed, but with errors.', version.cyan) + '\n');
+ } else {
+ logger.log('\n\n' + __('Titanium SDK %s successfully installed!', version.cyan) + '\n');
+ }
+ process.exit(1);
+ }
+
+ zipEntries.forEach(
+ /osx|linux/.test(env.os)
+ ? function (entry) {
+ tasks.push(function (callback) {
+ var cmd = 'unzip -o -qq "' + filename + '" "' + entry.entryName + '" -d "' + env.installPath + '"';
+ exec(cmd, function (err, stdout, stderr) {
+ err && (errors++);
+ bar.tick();
+ callback();
+ });
+ });
+ }
+ : function (entry) {
+ zip.extractEntryTo(entry, env.installPath, true, true);
+ bar.tick();
+ finished();
+ }
+ );
+
+ tasks.length && async.series(tasks, finished);
+}
+
+function getBranch(logger, branches, argv, env, callback) {
+ var branch = argv.branch,
+ version = argv._[0] || 'latest';
+
+ if (branch == 'latest') {
+ branches.sort().reverse();
+ for (var i = 0; i < branches.length; i++) {
+ if (branches[i] != 'master') {
+ branch = branches[i];
+ break;
+ }
+ }
+ } else if (!~branches.indexOf(branch)) {
+ logger.error(__('Branch "%s" does not exist', argv.branch) + '\n');
+
+ var suggestions = branches.filter(function (n) {
+ if (n.indexOf(argv.branch) == 0 || appc.string.levenshtein(String(argv.branch), n) <= 2) {
+ return n;
+ }
+ });
+
+ if (suggestions.length) {
+ logger.log(__('Did you mean this?'));
+ suggestions.forEach(function (s) {
+ logger.log(' ' + s.cyan);
+ });
+ logger.log();
+ }
+
+ logger.log(__("Run '%s' for a list of all branches.", (argv.$ + ' sdk list --branches').cyan) + '\n');
+ process.exit(1);
+ }
+
+ fetch(urls.branch.replace(/\$BRANCH/, branch), 'list of builds', logger, function (data) {
+ var builds = {},
+ re = new RegExp('-' + env.os + '\.zip$'),
+ reFilename = new RegExp('^(mobilesdk-)(.*)(-' + env.os + '.zip)$');
+ for (var i = 0; i < data.length; i++) {
+ re.test(data[i].filename) && data[i].build_type == 'mobile' && (builds[data[i].filename.replace(reFilename, '$2')] = data[i].filename);
+ }
+ version == 'latest' && (version = Object.keys(builds).sort().reverse().shift());
+ callback(branch, version, builds[version]);
+ });
+}
+
+function install(logger, argv, env) {
+ if (argv.branch) {
+ getBranches(logger, function (data) {
+ if (!data || !data.branches.length) {
+ logger.error(__('No branches found') + '\n');
+ process.exit(1);
+ }
+
+ getBranch(logger, data.branches, argv, env, function (branch, version, filename) {
+ if (!argv.force && env.sdks[version]) {
+ logger.error(__('SDK "%s" is already installed!', version) + '\n');
+ logger.log(__("Run '%s' to re-install.", (argv.$ + ' sdk install ' + version + ' --force --branch ' + branch).cyan) + '\n');
+ process.exit(1);
+ }
+ downloadSDK(logger, urls.build.replace(/\$BRANCH/, branch).replace(/\$FILENAME/, filename), version, env);
+ });
+ });
+ } else {
+ getReleases(logger, env.os, function (releases) {
+ var names = Object.keys(releases) || [],
+ version = argv._[0] || 'latest',
+ isLatest = version == 'latest';
+
+ if (!names.length) {
+ logger.error(__('No releases found') + '\n');
+ process.exit(1);
+ }
+
+ isLatest && (version = names.sort().reverse()[0]);
+
+ if (!~names.indexOf(version)) {
+ logger.error(__('Invalid version "%s"', version) + '\n');
+
+ var suggestions = names.filter(function (n) {
+ if (n.indexOf(version) == 0 || appc.string.levenshtein(version, n) <= 1) {
+ return n;
+ }
+ });
+
+ if (suggestions.length) {
+ logger.log(__('Did you mean this?'));
+ suggestions.forEach(function (s) {
+ logger.log(' ' + s.cyan);
+ });
+ logger.log();
+ }
+
+ logger.log(__("Run '%s' for available releases.", (argv.$ + ' sdk list --releases').cyan) + '\n');
+
+ process.exit(1);
+ }
+
+ if (!argv.force && env.sdks[version]) {
+ if (isLatest) {
+ logger.log(__("You're up-to-date. Version %s is currently the newest version available.", version.cyan) + '\n');
+ } else {
+ logger.error(__('SDK "%s" is already installed!', version) + '\n');
+ }
+ logger.log(__("Run '%s' to re-install.", (argv.$ + ' sdk install ' + version + ' --force').cyan) + '\n');
+ process.exit(1);
+ }
+
+ downloadSDK(logger, releases[version], version, env);
+ });
+ }
+}
+
+function list(logger, argv, env) {
+ var tasks = [];
+
+ argv.releases && tasks.push(function (callback) {
+ getReleases(logger, env.os, function (data) {
+ callback(null, { type:'releases', data:data });
+ });
+ });
+
+ argv.branches && tasks.push(function (callback) {
+ getBranches(logger, function (data) {
+ callback(null, { type:'branches', data:data });
+ });
+ });
+
+ async.parallel(tasks, function (err, results) {
+ var vers = Object.keys(env.sdks);
+ if (!vers.length) {
+ logger.log(__('No SDKs are installed'));
+ return;
+ }
+
+ var maxlen = vers.reduce(function (a, b) {
+ return Math.max(a, (Array.isArray(b) ? b[0] : b).length);
+ }, 0);
+
+ logger.log(__('Installed SDKs:'));
+ vers.forEach(function (v) {
+ logger.log(' ' + appc.string.rpad(v.cyan, maxlen + 2) + env.sdks[v].path);
+ });
+ logger.log();
+
+ results.forEach(function (r) {
+ switch (r.type) {
+ case 'releases':
+ logger.log(__('Releases:'));
+ var i = 0,
+ data = r.data;
+ Object.keys(data).sort().reverse().forEach(function (r) {
+ logger.log(' ' + r.cyan + (env.sdks.hasOwnProperty(r) ? ' ' + __('[installed]') : '') + (i++ == 0 ? ' ' + __('[latest]') : ''));
+ });
+ i || logger.log(' ' + __('No releases found'));
+ logger.log();
+ break;
+
+ case 'branches':
+ logger.log(__('Branches:'));
+ var data = r.data;
+ if (data && data.branches.length) {
+ data.branches.sort().reverse().forEach(function (b) {
+ logger.log(' ' + b.cyan + (b == data.defaultBranch ? ' ' + __('[default]') : ''));
+ });
+ } else {
+ logger.log(' ' + __('No branches found'));
+ }
+ logger.log();
+ }
+ });
+ });
+}
+
+function update(logger, argv, env) {
+ if (argv.branch) {
+ getBranches(logger, function (data) {
+ if (!data || !data.branches.length) {
+ logger.error(__('No branches found') + '\n');
+ process.exit(1);
+ }
+
+ getBranch(logger, data.branches, argv, env, function (branch, version, filename) {
+ if (!argv.force && env.sdks[version]) {
+ logger.log(__("You're up-to-date. Version %s is currently the newest version available.", version.cyan) + '\n');
+ logger.log(__("Run '%s' to re-install.", (argv.$ + ' sdk install ' + version + ' --force --branch ' + branch).cyan) + '\n');
+ process.exit(1);
+ }
+ downloadSDK(logger, urls.build.replace(/\$BRANCH/, branch).replace(/\$FILENAME/, filename), version, env);
+ });
+ });
+ } else {
+ getReleases(logger, env.os, function (releases) {
+ var latest = Object.keys(releases).shift();
+ if (env.sdks[latest]) {
+ logger.log(__("You're up-to-date. Version %s is currently the newest version available.", latest.cyan) + '\n');
+ } else if (argv.install) {
+ downloadSDK(logger, releases[latest], latest, env);
+ } else {
+ logger.log(__('New version available! %s', latest.cyan) + '\n');
+ logger.log(__("Run '%s' to download and install", (argv.$ + ' sdk update --install').cyan) + '\n');
+ }
+ });
+ }
+}
View
31 lib/commands/status.js
@@ -0,0 +1,31 @@
+/*
+ * status.js: Titanium CLI status command
+ *
+ * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
+ * See the LICENSE file for more information.
+ */
+
+exports.config = function (logger, config, cli) {
+ return {
+ desc: __('displays session and project information'),
+ options: {
+ output: {
+ alias: 'o',
+ default: 'report',
+ desc: 'output format',
+ values: ['report', 'json', 'xml']
+ }
+ },
+ args: [
+ {
+ desc: __('the directory where the project is located'),
+ name: 'project-dir',
+ required: true
+ }
+ ]
+ };
+};
+
+exports.run = function (logger, config, cli) {
+ dump(cli.argv);
+};
View
18 lib/commands/version.js
@@ -0,0 +1,18 @@
+/*
+ * version.js: Titanium CLI version command
+ *
+ * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
+ * See the LICENSE file for more information.
+ */
+
+require('pkginfo')(module, 'version', 'about');
+
+exports.config = function (logger, config, cli) {
+ return {
+ desc: __('print the version and exits')
+ };
+};
+
+exports.run = function (logger, config, cli) {
+ // don't have to do anything... by default the banner will be displayed
+};
View
37 lib/config.js
@@ -0,0 +1,37 @@
+/*
+ * completion.js: Titanium CLI config processor
+ *
+ * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
+ * See the LICENSE file for more information.
+ */
+
+var fs = require('fs'),
+ path = require('path'),
+ appc = require('node-appc'),
+ config = module.exports = {
+ // default config
+ user: {},
+
+ app: {
+ sdk: 'latest'
+ },
+
+ cli: {
+ colors: true,
+ completion: true,
+ logLevel: 'warn',
+ prompt: true
+ },
+
+ load: function () {
+ var cfg = path.join(appc.fs.home(), '.titanium', 'config.json');
+ if (appc.fs.exists(cfg)) {
+ try {
+ appc.util.mix(config, JSON.parse(fs.readFileSync(cfg, 'utf-8')));
+ } catch (ex) {
+ tierror('Unable to parse config file:', ex);
+ }
+ }
+ return config;
+ }
+ };
View
126 lib/logger.js
@@ -0,0 +1,126 @@
+/*
+ * logger.js: Titanium CLI logger
+ *
+ * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
+ * See the LICENSE file for more information.
+ *
+ * Portions derived from winston under the MIT license.
+ * Copyright (c) 2010 Charlie Robbins
+ * https://github.com/flatiron/winston
+ */
+
+var path = require('path'),
+ winston = require('winston'),
+ common = require(path.join(path.dirname(require.resolve('winston')), 'winston', 'common.js')),
+ config = require('./config'),
+ consoul = new winston.transports.Console({
+ level: config.cli.logLevel || 'warn',
+ colorize: !!config.cli.colors
+ }),
+ logger = exports = module.exports = new winston.Logger({
+ transports: [ consoul ],
+ silent: !!config.cli.quiet
+ }),
+ origLoggerLog = logger.log,
+ bannerEnabled = true;
+
+logger.silence = function (val) {
+ consoul.silent = val;
+};
+
+logger.setLevel = function (n) {
+ consoul.level = n;
+};
+
+logger.log = function () {
+ var args = Array.prototype.slice.call(arguments),
+ padLevels = logger.padLevels;
+ args.length || args.unshift(' '); // blank lines need
+ args[0] in logger.levels || args.unshift('_');
+ logger.padLevels = false; // disable padding for general output
+ origLoggerLog.apply(logger, args);
+ logger.padLevels = padLevels;
+ return logger;
+};
+
+require('pkginfo')(module, 'version', 'about');
+
+logger.banner = function () {
+ bannerEnabled && console.log(exports.about.name.cyan.bold + ', version ' + exports.version + '\n' + exports.about.copyright + '\n');
+};
+
+logger.bannerEnabled = function (b) {
+ bannerEnabled = !!b;
+};
+
+// override the Console log() function to strip off the ':' after the level
+consoul.log = function (level, msg, meta, callback) {
+ if (this.silent) {
+ return callback(null, true);
+ }
+
+ var self = this,
+ output;
+
+ if (level != '_') {
+ msg = '\b\b' + msg;
+ }
+
+ output = common.log({
+ colorize: this.colorize,
+ json: this.json,
+ level: level,
+ message: level == 'error' ? msg.red : msg,
+ meta: meta,
+ stringify: this.stringify,
+ timestamp: this.timestamp,
+ prettyPrint: this.prettyPrint,
+ raw: this.raw
+ });
+
+ if (/^\: /.test(output) && level == '_') {
+ output = output.substring(2);
+ }
+
+ if (level === 'error' || level === 'debug') {
+ console.error(output);
+ } else {
+ console.log(output);
+ }
+
+ self.emit('logged');
+ callback(null, true);
+};
+
+// override the colorize() function so we can change the level formatting
+winston.config.colorize = function (level) {
+ return level == '_' ? '' : ('[' + level.toUpperCase() + '] ')[winston.config.allColors[level]];
+};
+
+logger.exception = function (ex) {
+ if (ex.stack) {
+ ex.stack.split('\n').forEach(logger.error);
+ } else {
+ logger.error(ex.toString());
+ }
+ logger.log();
+};
+
+// init the logger with sensible cli defaults
+logger.cli();
+
+// override levels, must be done after calling cli()
+logger.setLevels({
+ trace: 0,
+ debug: 1,
+ info: 2,
+ warn: 3,
+ error: 4,
+ _: 5 // generic log() call
+});
+
+// override colors, must be done after calling cli()
+winston.addColors({
+ trace: 'grey',
+ debug: 'magenta'
+});
View
198 lib/titanium.js
@@ -1,110 +1,116 @@
/*
- * titanium.js: Top-level include for the titanium CLI
+ * titanium.js: Top-level include for the Titanium CLI
*
* Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
* See the LICENSE file for more information.
*/
-var path = require("path"),
- colors = require("colors"),
- optimist = require("optimist"),
- fs = require("fs"),
- environ = require('./titanium/environ.js'),
- config = require('./titanium/config.js'),
- builtins = require('./titanium/builtins/builtins.js').builtins
- externals = require('./titanium/externals/externals.js').externals,
- log = require('./titanium/log.js'),
- analytics = require('./titanium/analytics.js'),
- pkginfo = require('pkginfo')(module, 'name', 'version');
-var titanium = module.exports;
+var config = require('./config').load(),
+ env = require('node-appc').environ,
+ colors = require('colors'),
+ path = require('path');
-function banner()
-{
- // yes, that's correct: ascii art FTW
- var prompt = [
- "________ __ ",
- "___ __/___(_)__ /_______ ________ ___(_)____ _________ ___",
- " / / __ / _ __/_ __ `/__ __ \\__ / _ / / /__ __ `__ \\",
- " / / _ / / /_ / /_/ / _ / / /_ / / /_/ / _ / / / / /",
- "/_/ /_/ \\__/ \\__,_/ /_/ /_/ /_/ \\__,_/ /_/ /_/ /_/"
- ];
- console.log(prompt.join("\n").cyan);
- console.log();
- console.log("Welcome to ".white + "Titanium".bold.cyan + " - the open source mobile framework by".white + " Appcelerator".red.bold+" and contributors".white);
- console.log();
-}
+config.cli.colors || (colors.mode = 'none');
-titanium.start = function(setup)
-{
- setup();
-
- var cmd = optimist.argv._[0];
- var args = optimist.argv._.splice(1);
- var showBanner = optimist.argv.banner===false ? false : true;
+/*
+global.dump = function (it) {
+ console.error(require('util').inspect(it, false, null, true));
+};
+*/
- if (optimist.argv._.length < 1)
- {
- cmd = 'help';
- }
- if (optimist.argv.version && cmd=='help' || (cmd=='help' && args[0]=='version'))
- {
- args = ['version'];
- showBanner = false;
- }
-
- // turn off the colors
- if (optimist.argv.colors === false || config.config.colors === false)
- {
- colors.mode = "none";
- }
-
- // turn off the analytics
- if (optimist.argv.analytics === false || config.config.analytics === false)
- {
- analytics.on = false;
- }
-
- // check for quiet settings
- if (optimist.argv.quiet)
- {
- log.quiet = true;
- showBanner = false;
- }
-
- if (showBanner)
- {
- banner();
- }
+global.tierror = function () {
+ var args = Array.prototype.slice.call(arguments);
+ args[0] = ('[ERROR] ' + args[0]).red;
+ console.error.apply(null, args);
+};
- // see if we have a log file
- if (optimist.argv.logpath || config.config.logpath)
- {
- log.logpath = optimist.argv.logpath || config.config.logpath;
- log.configure(cmd!='help');
- }
-
- if (!environ.ok)
- {
- //TODO: it would be nice to allow the user to install titanium directly from here
- log.error("It appears that no Titanium Mobile SDK could be found on this system. Please make sure to install before continuing.");
- }
-
- if (builtins[cmd])
- {
- require('./titanium/builtins/'+cmd+'.js').execute(module,environ,config.config,args,optimist.argv);
- }
- else if (externals[cmd])
- {
- require('./titanium/externals/externals.js').execute(module,environ,config.config,args,optimist.argv,cmd);
- }
- else
- {
- log.error("Couldn't find command: " + cmd.grey.bold + ". You can run " + "titanium help commands".cyan.bold + " to get a list of all commands",2);
+global.tiexception = function (ex) {
+ if (ex.stack) {
+ ex.stack.split('\n').forEach(console.error);
+ } else {
+ console.error(ex.toString());
}
+};
+require('i18n').configure({
+ directory: path.join(module.filename, '..', '..', 'locales'),
+ register: global
+});
- // TEST
- // config.config['b']=0xabc;
- // config.saveConfig();
-};
+// find all built-in commands
+env.scanCommands(env.commands, path.join(path.dirname(module.filename), 'commands'));
+// initialize the cli processor
+require('./cli')
+ .flag('help', {
+ abbr: 'h',
+ callback: function (value, logger, cli) {
+ if (value) {
+ cli.argv._.unshift(cli.argv.$command)
+ cli.argv.$command = 'help';
+ }
+ },
+ desc: __('displays help'),
+ optional: true
+ })
+ .flag('version', {
+ abbr: 'v',
+ callback: function (value, logger, cli) {
+ value && (cli.argv.$command = 'version');
+ },
+ desc: __('displays the current version'),
+ optional: true
+ })
+ .flag('colors', {
+ callback: function (value) {
+ colors.mode = value == false ? 'none' : 'console';
+ },
+ default: true,
+ desc: __('use colors in the terminal'),
+ negate: true,
+ optional: true
+ })
+ .flag('quiet', {
+ abbr: 'q',
+ callback: function (value, logger, cli) {
+ logger.silence(config.cli.quiet = value);
+ },
+ default: false,
+ desc: __('suppress all output'),
+ optional: true
+ })
+ .then(function (logger) {
+ var cli = this;
+ cli.env = env;
+
+ function initCommands(cmds, sdk, platform) {
+ Object.keys(cmds).forEach(function (name) {
+ cli.command(name, {
+ modulePath: cmds[name]
+ }, sdk, platform);
+ });
+ }
+
+ initCommands(env.commands);
+ initCommands(env.project.commands);
+ Object.keys(env.sdks).forEach(function (sdk) {
+ initCommands(env.sdks[sdk].commands, sdk);
+ Object.keys(env.sdks[sdk].platforms).forEach(function (platform) {
+ initCommands(env.sdks[sdk].platforms[platform].commands, sdk, platform);
+ });
+ });
+ })
+ .parse()
+ .then(function (logger) {
+ logger.banner(); // TODO: check if this command wants a banner
+ /*
+ projectDir = argv['project-dir'] || '';
+ if (projectDir) {
+ projectDir = util.resolvePath(projectDir);
+ util.exists(projectDir) || (projectDir = process.cwd());
+ util.exists(projectDir) && scanCommands(env.project.commands, path.join(projectDir, 'modules', 'cli', 'commands'));
+ }
+ */
+ })
+ .validate()
+ .run();
View
24 lib/titanium/analytics.js
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
- * See the LICENSE file for more information.
- */
-
-var config = require('./config.js').config;
-
-function addEvent (name,payload)
-{
- // allow the analytics to be turned off
- if (config.analytics === false || module.exports.on === false)
- {
- return;
- }
-
- // TODO: implement logging
-
- // ideally we would store to a local SQLite (or JSON file) and
- // send when connected and store when not connected for later sending
- // this would be similar to what we do on the phone
-}
-
-module.exports.addEvent = addEvent;
-module.exports.on = true;
View
18 lib/titanium/builtins/builtins.js
@@ -1,18 +0,0 @@
-/*
- * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
- * See the LICENSE file for more information.
- */
-
-
-/**
- * these are the built-in commands that are handled and packaged with each version of the
- * CLI instead of delegated to the SDK itself (which are called externals).
- */
-
-module.exports.builtins =
-{
- 'help': "provide usage information for this tool",
- 'sdk': "manage SDK versions (download, list)",
- 'login': "login to the Appcelerator cloud",
- 'logout': "logout of the Appcelerator cloud"
-};
View
122 lib/titanium/builtins/help.js
@@ -1,122 +0,0 @@
-/*
- * Copyright (c) 2012, Appcelerator, Inc. All Rights Reserved.
- * See the LICENSE file for more information.
- */
-
-/**
- * command: help
- * purpose: display help for this CLI
- *
- */
-var builtins = require('./builtins.js').builtins,
- externals = require('../externals/externals.js').externals,
- log = require('../log.js'),
- string = require('../string.js');
-
-
-function help(titanium, environ, config, args, params)
-{
- // print any additional arguments
- console.log([
- "Sub-commands:".cyan.bold.underline,
- "",
- " " + string.rpad("version",15) + "print the version of the CLI".grey,
- " " + string.rpad("commands",15) + "print all available commands".grey,
- ].join("\n"));
- console.log();
-}
-
-function execute (titanium, environ, config, args, params)
-{
- if (args.length > 0)
- {
- var cmd = args[0];
-
- if (builtins[cmd])
- {
- console.log("Command:".cyan.bold.underline);
- console.log();
- console.log(' ' + string.rpad(cmd,15) + builtins[cmd].grey);
- console.log();
- require('./'+cmd+'.js').help(titanium, environ, config, args, params, cmd);
- }
- else if (externals[cmd])
- {
- console.log("Command:".cyan.bold.underline);
- console.log();
- console.log(' ' + string.rpad(cmd,15) + externals[cmd].grey);
- console.log();
- require('../externals/externals.js').help(titanium, environ, config, args, params, cmd);
- }
- else if (cmd == 'version')
- {
- console.log(titanium.exports.version);
- }
- else if (cmd == 'commands')
- {
- // make a listing of all available commands
- var commands = [];
- for (var k in builtins) { commands.push(' ' + string.rpad(k,15) + builtins[k].grey) }
- for (var k in externals) { commands.push(' ' + string.rpad(k,15) + externals[k].grey) }
- var prompt = [
- "Available Commands:".cyan.underline.bold,
- "",
- commands.join("\n"),
- ""
- ];
- console.log(prompt.join("\n"));
- }
- else
- {
- // don't have that command
- log.error("I don't support the command "+cmd.red.bold);
- }
- }
- else
- {
- var prompt = [
- "Usage:".cyan.underline.bold,
- "",
- " titanium [command] <arguments>",
- "",
- "",
- "Common Commands:".cyan.underline.bold,
- "",
- "To install latest SDK".cyan,
- " titanium sdk install",
- "",
- "To create a project".cyan,
- " titanium create",
- "",
- "To build a project".cyan,
- " titanium build",
- "",
- "To run a project".cyan,
- " titanium run",
- "",
- "To install a project".cyan,
- " titanium install",
- // "",
- // "Other Commands:".cyan.underline.bold,
- // " titanium login " + "login of the Appcelerator cloud".grey,
- // " titanium logout " + "logout of the Appcelerator cloud".grey,
- // " titanium clean " + "fully clean project removing all generated files".grey,
- // " titanium help " + "get a detailed listing of all available commands".grey,
- // "",
- "To get a list of all available commands: ".cyan + "titanium help commands",
- "",
- "Common Examples:".cyan.underline.bold,
- "",