first commit
This commit is contained in:
commit
120f3198cf
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/node_modules
|
||||||
|
/tmp
|
||||||
|
/docs/*.json
|
||||||
|
/docs/*.html
|
||||||
|
/build
|
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[submodule "deps/javascriptlint"]
|
||||||
|
path = deps/javascriptlint
|
||||||
|
url = git://github.com/davepacheco/javascriptlint.git
|
||||||
|
[submodule "deps/jsstyle"]
|
||||||
|
path = deps/jsstyle
|
||||||
|
url = git://github.com/davepacheco/jsstyle.git
|
1
CHANGES.md
Normal file
1
CHANGES.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# joyent CLI Changelog
|
41
Makefile
Normal file
41
Makefile
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2014, Joyent, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Makefile for joyent/joyent
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Vars, Tools, Files, Flags
|
||||||
|
#
|
||||||
|
JS_FILES := bin/joyent \
|
||||||
|
$(shell find lib -name '*.js' | grep -v '/tmp/')
|
||||||
|
JSL_CONF_NODE = tools/jsl.node.conf
|
||||||
|
JSL_FILES_NODE = $(JS_FILES)
|
||||||
|
JSSTYLE_FILES = $(JS_FILES)
|
||||||
|
JSSTYLE_FLAGS = -f tools/jsstyle.conf
|
||||||
|
CLEAN_FILES += ./node_modules
|
||||||
|
|
||||||
|
include ./tools/mk/Makefile.defs
|
||||||
|
|
||||||
|
#
|
||||||
|
# Targets
|
||||||
|
#
|
||||||
|
.PHONY: all
|
||||||
|
all:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
./test/runtests
|
||||||
|
|
||||||
|
.PHONY: dumpvar
|
||||||
|
dumpvar:
|
||||||
|
@if [[ -z "$(VAR)" ]]; then \
|
||||||
|
echo "error: set 'VAR' to dump a var"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@echo "$(VAR) is '$($(VAR))'"
|
||||||
|
|
||||||
|
|
||||||
|
include ./tools/mk/Makefile.deps
|
||||||
|
include ./tools/mk/Makefile.targ
|
22
README.md
Normal file
22
README.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
`joyent` is a CLI for the Joyent Public Cloud (https://my.joyentcloud.com).
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
1. Install [node.js](http://nodejs.org/).
|
||||||
|
2. `npm install -g joyent`
|
||||||
|
|
||||||
|
Verify that installed and is on your PATH:
|
||||||
|
|
||||||
|
$ joyent --version
|
||||||
|
joyent CLI 1.0.0
|
||||||
|
|
||||||
|
Before you can used the CLI you'll need a Joyent account, an SSH key uploaded
|
||||||
|
and `joyent` configured with those account details.
|
||||||
|
|
||||||
|
# Setup
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
# Getting Started
|
||||||
|
|
||||||
|
TODO
|
24
TODO.md
Normal file
24
TODO.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# first
|
||||||
|
|
||||||
|
- machines:
|
||||||
|
- short default output
|
||||||
|
- long '-l' output, -H, -o, -s
|
||||||
|
- get image defaults and fill those in
|
||||||
|
- couple commands: machines, machine, provision (create-machine?)
|
||||||
|
- re-write of cloudapi.js (eventually a separate module)
|
||||||
|
- uuid caching
|
||||||
|
- UUID prefix support
|
||||||
|
- profile command (adding profile, edit, etc.)
|
||||||
|
- multi-dc support... profile.dcs
|
||||||
|
|
||||||
|
|
||||||
|
# naming
|
||||||
|
|
||||||
|
node-joyentcloud.git that installs joyentcloud. Suggest 'jc' as alias if people want to.
|
||||||
|
|
||||||
|
node-smartdc installs joyentcloud and warns about deprecation on stderr.
|
||||||
|
|
||||||
|
# later (in no particular order)
|
||||||
|
|
||||||
|
- how to add/exclude DCs?
|
||||||
|
- cmdln.js support for bash tab completion
|
14
bin/joyent
Executable file
14
bin/joyent
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Joyent Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* joyent - Joyent public cloud (https://my.joyentcloud.com/) CLI
|
||||||
|
*/
|
||||||
|
|
||||||
|
var p = console.log;
|
||||||
|
var cmdln = require('cmdln');
|
||||||
|
var CLI = require('../lib/cli');
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
cmdln.main(CLI, process.arg, {showCode: true});
|
||||||
|
}
|
1
deps/javascriptlint
vendored
Submodule
1
deps/javascriptlint
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit e1bd0abfd424811af469d1ece3af131d95443924
|
1
deps/jsstyle
vendored
Submodule
1
deps/jsstyle
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 07d4f68251063be6496a42dd00a7f5bacd65c5e4
|
10
docs/index.md
Normal file
10
docs/index.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: sdcadm (Administer a SDC standup)
|
||||||
|
markdown2extras: wiki-tables, code-friendly, cuddled-lists, link-patterns
|
||||||
|
markdown2linkpatternsfile: link-patterns.txt
|
||||||
|
apisections:
|
||||||
|
---
|
||||||
|
|
||||||
|
# sdcadm
|
||||||
|
|
||||||
|
TODO
|
1
docs/link-patterns.txt
Normal file
1
docs/link-patterns.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
/([A-Z]+-\d+)/ https://devhub.joyent.com/jira/browse/\1
|
9
etc/defaults.json
Normal file
9
etc/defaults.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"defaultProfile": "env",
|
||||||
|
"dcs": {
|
||||||
|
"us-east-1": "https://us-east-1.api.joyentcloud.com",
|
||||||
|
"us-west-1": "https://us-west-1.api.joyentcloud.com",
|
||||||
|
"us-sw-1": "https://us-sw-1.api.joyentcloud.com",
|
||||||
|
"eu-ams-1": "https://eu-ams-1.api.joyentcloud.com"
|
||||||
|
}
|
||||||
|
}
|
241
lib/cli.js
Normal file
241
lib/cli.js
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Joyent Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* The 'joyent' CLI class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var p = console.log;
|
||||||
|
var e = console.error;
|
||||||
|
var util = require('util'),
|
||||||
|
format = util.format;
|
||||||
|
var child_process = require('child_process'),
|
||||||
|
spawn = child_process.spawn,
|
||||||
|
exec = child_process.exec;
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
var async = require('async');
|
||||||
|
var bunyan = require('bunyan');
|
||||||
|
var cmdln = require('cmdln'),
|
||||||
|
Cmdln = cmdln.Cmdln;
|
||||||
|
|
||||||
|
var common = require('./common');
|
||||||
|
var Joyent = require('./joyent');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---- globals
|
||||||
|
|
||||||
|
var pkg = require('../package.json');
|
||||||
|
var name = 'joyent';
|
||||||
|
var log = bunyan.createLogger({
|
||||||
|
name: name,
|
||||||
|
serializers: bunyan.stdSerializers,
|
||||||
|
stream: process.stderr,
|
||||||
|
level: 'warn'
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---- CLI class
|
||||||
|
|
||||||
|
function CLI() {
|
||||||
|
Cmdln.call(this, {
|
||||||
|
name: pkg.name,
|
||||||
|
desc: pkg.description,
|
||||||
|
options: [
|
||||||
|
{names: ['help', 'h'], type: 'bool', help: 'Print help and exit.'},
|
||||||
|
{name: 'version', type: 'bool', help: 'Print version and exit.'},
|
||||||
|
{names: ['verbose', 'v'], type: 'bool',
|
||||||
|
help: 'Verbose/debug output.'}
|
||||||
|
],
|
||||||
|
helpOpts: {
|
||||||
|
includeEnv: true,
|
||||||
|
minHelpCol: 23 /* line up with option help */
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
util.inherits(CLI, Cmdln);
|
||||||
|
|
||||||
|
CLI.prototype.init = function (opts, args, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (opts.version) {
|
||||||
|
p(this.name, pkg.version);
|
||||||
|
callback(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.opts = opts;
|
||||||
|
if (opts.verbose) {
|
||||||
|
process.env.DEBUG = 1; //TODO This is a lame requirement of cmdln.main().
|
||||||
|
log.level('trace');
|
||||||
|
log.src = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.__defineGetter__('joyent', function () {
|
||||||
|
if (self._joyent === undefined) {
|
||||||
|
self._joyent = new Joyent({log: log});
|
||||||
|
}
|
||||||
|
return self._joyent;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cmdln class handles `opts.help`.
|
||||||
|
Cmdln.prototype.init.apply(this, arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
CLI.prototype.do_profile = function (subcmd, opts, args, callback) {
|
||||||
|
if (opts.help) {
|
||||||
|
this.do_help('help', {}, [subcmd], callback);
|
||||||
|
return;
|
||||||
|
} else if (args.length > 1) {
|
||||||
|
return callback(new Error('too many args: ' + args));
|
||||||
|
}
|
||||||
|
|
||||||
|
var profs = common.deepObjCopy(this.joyent.profiles);
|
||||||
|
var currProfileName = this.joyent.profile.name;
|
||||||
|
for (var i = 0; i < profs.length; i++) {
|
||||||
|
profs[i].curr = (profs[i].name === currProfileName ? '*' : ' ');
|
||||||
|
profs[i].dcs = (profs[i].dcs ? Object.keys(profs[i].dcs) : ['all'])
|
||||||
|
.join(',');
|
||||||
|
}
|
||||||
|
if (opts.json) {
|
||||||
|
p(JSON.stringify(profs, null, 4));
|
||||||
|
} else {
|
||||||
|
common.tabulate(profs, {
|
||||||
|
columns: 'curr,name,dcs,account,keyId',
|
||||||
|
sort: 'name,account',
|
||||||
|
validFields: 'curr,name,dcs,account,keyId'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
CLI.prototype.do_profile.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['json', 'j'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'JSON output.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
CLI.prototype.do_profile.help = (
|
||||||
|
'Create, update or inpect joyent CLI profiles.\n'
|
||||||
|
+ '\n'
|
||||||
|
+ 'Usage:\n'
|
||||||
|
+ ' {{name}} profile\n'
|
||||||
|
+ '\n'
|
||||||
|
+ '{{options}}'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CLI.prototype.do_dcs = function (subcmd, opts, args, callback) {
|
||||||
|
if (opts.help) {
|
||||||
|
this.do_help('help', {}, [subcmd], callback);
|
||||||
|
return;
|
||||||
|
} else if (args.length > 1) {
|
||||||
|
return callback(new Error('too many args: ' + args));
|
||||||
|
}
|
||||||
|
|
||||||
|
var dcs = this.joyent.config.dcs;
|
||||||
|
var dcsArray = Object.keys(dcs).map(
|
||||||
|
function (n) { return {name: n, url: dcs[n]}; });
|
||||||
|
if (opts.json) {
|
||||||
|
p(JSON.stringify(dcsArray, null, 4));
|
||||||
|
} else {
|
||||||
|
common.tabulate(dcsArray, {
|
||||||
|
columns: 'name,url',
|
||||||
|
sort: 'name',
|
||||||
|
validFields: 'name,url'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
CLI.prototype.do_dcs.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['json', 'j'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'JSON output.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
CLI.prototype.do_dcs.help = (
|
||||||
|
'List, add or remove datacenters.\n'
|
||||||
|
+ '\n'
|
||||||
|
+ 'Usage:\n'
|
||||||
|
+ ' {{name}} dcs\n'
|
||||||
|
+ '\n'
|
||||||
|
+ '{{options}}'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CLI.prototype.do_machines = function (subcmd, opts, args, callback) {
|
||||||
|
var self = this;
|
||||||
|
if (opts.help) {
|
||||||
|
this.do_help('help', {}, [subcmd], callback);
|
||||||
|
return;
|
||||||
|
} else if (args.length > 1) {
|
||||||
|
return callback(new Error('too many args: ' + args));
|
||||||
|
}
|
||||||
|
|
||||||
|
//XXX joyent.listMachines should change to return a 'res' event emitter
|
||||||
|
// that emits 'dcError' and 'data'.
|
||||||
|
var listOpts = {
|
||||||
|
onDcError: function (dc, dcErr) {
|
||||||
|
console.warn('%s machines: dc %s error: %s', self.name, dc, dcErr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.joyent.listMachines(listOpts, function (err, machines) {
|
||||||
|
if (err)
|
||||||
|
return callback(err);
|
||||||
|
if (opts.json) {
|
||||||
|
p(JSON.stringify(machines, null, 4));
|
||||||
|
} else {
|
||||||
|
// TODO: get short output down to something like
|
||||||
|
// 'us-west-1 e91897cf testforyunong2 linux running 2013-11-08'
|
||||||
|
// 'us-west-1 e91897cf testforyunong2 ubuntu/13.3.0 running 2013-11-08'
|
||||||
|
common.tabulate(machines, {
|
||||||
|
//columns: 'dc,id,name,type,state,created,image,package,memory,disk',
|
||||||
|
columns: 'dc,id,name,state,created',
|
||||||
|
sort: 'created',
|
||||||
|
validFields: 'dc,id,name,type,state,image,package,memory,disk,created,updated,compute_node,primaryIp'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
CLI.prototype.do_machines.options = [
|
||||||
|
{
|
||||||
|
names: ['help', 'h'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'Show this help.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
names: ['json', 'j'],
|
||||||
|
type: 'bool',
|
||||||
|
help: 'JSON output.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
CLI.prototype.do_machines.help = (
|
||||||
|
'List machines.\n'
|
||||||
|
+ '\n'
|
||||||
|
+ 'Usage:\n'
|
||||||
|
+ ' {{name}} machines [<filters>...]\n'
|
||||||
|
+ '\n'
|
||||||
|
+ '{{options}}'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---- exports
|
||||||
|
|
||||||
|
module.exports = CLI;
|
201
lib/common.js
Executable file
201
lib/common.js
Executable file
@ -0,0 +1,201 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2014 Joyent Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var p = console.log;
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
var async = require('async');
|
||||||
|
var backoff = require('backoff');
|
||||||
|
var fs = require('fs');
|
||||||
|
var once = require('once');
|
||||||
|
var sprintf = require('extsprintf').sprintf;
|
||||||
|
var util = require('util'),
|
||||||
|
format = util.format;
|
||||||
|
var verror = require('verror');
|
||||||
|
|
||||||
|
var errors = require('./errors'),
|
||||||
|
InternalError = errors.InternalError;
|
||||||
|
|
||||||
|
|
||||||
|
function objCopy(obj, target) {
|
||||||
|
if (target === undefined) {
|
||||||
|
target = {};
|
||||||
|
}
|
||||||
|
Object.keys(obj).forEach(function (k) {
|
||||||
|
target[k] = obj[k];
|
||||||
|
});
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function deepObjCopy(obj) {
|
||||||
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function zeroPad(n, width) {
|
||||||
|
var s = String(n);
|
||||||
|
while (s.length < width) {
|
||||||
|
s = '0' + s;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a boolean or string representation into a boolean, or
|
||||||
|
* raise TypeError trying.
|
||||||
|
*
|
||||||
|
* @param value {Boolean|String} The input value to convert.
|
||||||
|
* @param default_ {Boolean} The default value is `value` is undefined.
|
||||||
|
* @param errName {String} The variable name to quote in the possibly
|
||||||
|
* raised TypeError.
|
||||||
|
*/
|
||||||
|
function boolFromString(value, default_, errName) {
|
||||||
|
if (value === undefined) {
|
||||||
|
return default_;
|
||||||
|
} else if (value === 'false' || value === '0') {
|
||||||
|
return false;
|
||||||
|
} else if (value === 'true' || value === '1') {
|
||||||
|
return true;
|
||||||
|
} else if (typeof (value) === 'boolean') {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
throw new TypeError(
|
||||||
|
format('invalid value for "%s": %j', errName, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a table of the given items.
|
||||||
|
*
|
||||||
|
* @params items {Array} of row objects.
|
||||||
|
* @params options {Object}
|
||||||
|
* - `columns` {String} of comma-separated field names for columns
|
||||||
|
* - `skipHeader` {Boolean} Default false.
|
||||||
|
* - `sort` {String} of comma-separate fields on which to alphabetically
|
||||||
|
* sort the rows. Optional.
|
||||||
|
* - `validFields` {String} valid fields for `columns` and `sort`
|
||||||
|
*/
|
||||||
|
function tabulate(items, options) {
|
||||||
|
assert.arrayOfObject(items, 'items');
|
||||||
|
assert.object(options, 'options');
|
||||||
|
assert.string(options.columns, 'options.columns');
|
||||||
|
assert.optionalBool(options.skipHeader, 'options.skipHeader');
|
||||||
|
assert.optionalString(options.sort, 'options.sort');
|
||||||
|
assert.optionalString(options.validFields, 'options.validFields');
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate.
|
||||||
|
var validFields = options.validFields && options.validFields.split(',');
|
||||||
|
var columns = options.columns.split(',');
|
||||||
|
var sort = options.sort ? options.sort.split(',') : [];
|
||||||
|
if (validFields) {
|
||||||
|
columns.forEach(function (c) {
|
||||||
|
if (validFields.indexOf(c) === -1) {
|
||||||
|
throw new TypeError(sprintf('invalid output field: "%s"', c));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
sort.forEach(function (s) {
|
||||||
|
if (s[0] === '-') s = s.slice(1);
|
||||||
|
if (validFields && validFields.indexOf(s) === -1) {
|
||||||
|
throw new TypeError(sprintf('invalid sort field: "%s"', s));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Function to lookup each column field in a row.
|
||||||
|
var colFuncs = columns.map(function (lookup) {
|
||||||
|
return new Function(
|
||||||
|
'try { return (this.' + lookup + '); } catch (e) {}');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Determine columns and widths.
|
||||||
|
var widths = {};
|
||||||
|
columns.forEach(function (c) { widths[c] = c.length; });
|
||||||
|
items.forEach(function (item) {
|
||||||
|
for (var j = 0; j < columns.length; j++) {
|
||||||
|
var col = columns[j];
|
||||||
|
var cell = colFuncs[j].call(item);
|
||||||
|
if (cell === null || cell === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
widths[col] = Math.max(
|
||||||
|
widths[col], (cell ? String(cell).length : 0));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var template = '';
|
||||||
|
for (var i = 0; i < columns.length; i++) {
|
||||||
|
if (i === columns.length - 1) {
|
||||||
|
// Last column, don't have trailing whitespace.
|
||||||
|
template += '%s';
|
||||||
|
} else {
|
||||||
|
template += '%-' + String(widths[columns[i]]) + 's ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cmp(a, b) {
|
||||||
|
for (var j = 0; j < sort.length; j++) {
|
||||||
|
var field = sort[j];
|
||||||
|
var invert = false;
|
||||||
|
if (field[0] === '-') {
|
||||||
|
invert = true;
|
||||||
|
field = field.slice(1);
|
||||||
|
}
|
||||||
|
assert.ok(field.length, 'zero-length sort field: ' + options.sort);
|
||||||
|
var a_cmp = Number(a[field]);
|
||||||
|
var b_cmp = Number(b[field]);
|
||||||
|
if (isNaN(a_cmp) || isNaN(b_cmp)) {
|
||||||
|
a_cmp = a[field] || '';
|
||||||
|
b_cmp = b[field] || '';
|
||||||
|
}
|
||||||
|
if (a_cmp < b_cmp) {
|
||||||
|
return (invert ? 1 : -1);
|
||||||
|
} else if (a_cmp > b_cmp) {
|
||||||
|
return (invert ? -1 : 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (sort.length) {
|
||||||
|
items.sort(cmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.skipHeader) {
|
||||||
|
var header = columns.map(function (c) { return c.toUpperCase(); });
|
||||||
|
header.unshift(template);
|
||||||
|
console.log(sprintf.apply(null, header));
|
||||||
|
}
|
||||||
|
items.forEach(function (item) {
|
||||||
|
var row = [];
|
||||||
|
for (var j = 0; j < colFuncs.length; j++) {
|
||||||
|
var cell = colFuncs[j].call(item);
|
||||||
|
if (cell === null || cell === undefined) {
|
||||||
|
row.push('-');
|
||||||
|
} else {
|
||||||
|
row.push(String(cell));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row.unshift(template);
|
||||||
|
console.log(sprintf.apply(null, row));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//---- exports
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
objCopy: objCopy,
|
||||||
|
deepObjCopy: deepObjCopy,
|
||||||
|
zeroPad: zeroPad,
|
||||||
|
boolFromString: boolFromString,
|
||||||
|
tabulate: tabulate
|
||||||
|
};
|
||||||
|
// vim: set softtabstop=4 shiftwidth=4:
|
50
lib/config.js
Executable file
50
lib/config.js
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2014 Joyent Inc. All rights reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var p = console.log;
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
var sprintf = require('extsprintf').sprintf;
|
||||||
|
|
||||||
|
var common = require('./common');
|
||||||
|
|
||||||
|
|
||||||
|
var CONFIG_PATH = path.resolve(process.env.HOME, '.joyentconfig.json');
|
||||||
|
var DEFAULTS_PATH = path.resolve(__dirname, '..', 'etc', 'defaults.json');
|
||||||
|
|
||||||
|
|
||||||
|
function loadConfigSync() {
|
||||||
|
var config = JSON.parse(fs.readFileSync(DEFAULTS_PATH, 'utf8'));
|
||||||
|
if (fs.existsSync(CONFIG_PATH)) {
|
||||||
|
var userConfig = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
||||||
|
common.objCopy(userConfig, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add 'env' profile.
|
||||||
|
if (!config.profiles) {
|
||||||
|
config.profiles = [];
|
||||||
|
config.profiles.push({
|
||||||
|
name: 'env',
|
||||||
|
account: process.env.SDC_USER || process.env.SDC_ACCOUNT,
|
||||||
|
keyId: process.env.SDC_KEY_ID,
|
||||||
|
//XXX true/false 0/1 handling
|
||||||
|
rejectUnauthorized: common.boolFromString(
|
||||||
|
process.env.SDC_TESTING || process.env.SDC_TLS_INSECURE)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---- exports
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
CONFIG_PATH: CONFIG_PATH,
|
||||||
|
loadConfigSync: loadConfigSync
|
||||||
|
};
|
||||||
|
// vim: set softtabstop=4 shiftwidth=4:
|
104
lib/errors.js
Normal file
104
lib/errors.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Error classes that the joyent CLI may produce.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var util = require('util'),
|
||||||
|
format = util.format;
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
var verror = require('verror'),
|
||||||
|
WError = verror.WError,
|
||||||
|
VError = verror.VError;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ---- error classes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base error. Instances will always have a string `message` and
|
||||||
|
* a string `code` (a CamelCase string).
|
||||||
|
*/
|
||||||
|
function JoyentError(options) {
|
||||||
|
assert.object(options, 'options');
|
||||||
|
assert.string(options.message, 'options.message');
|
||||||
|
assert.string(options.code, 'options.code');
|
||||||
|
assert.optionalObject(options.cause, 'options.cause');
|
||||||
|
assert.optionalNumber(options.statusCode, 'options.statusCode');
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
var args = [];
|
||||||
|
if (options.cause) args.push(options.cause);
|
||||||
|
args.push(options.message);
|
||||||
|
WError.apply(this, args);
|
||||||
|
|
||||||
|
var extra = Object.keys(options).filter(
|
||||||
|
function (k) { return ['cause', 'message'].indexOf(k) === -1; });
|
||||||
|
extra.forEach(function (k) {
|
||||||
|
self[k] = options[k];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
util.inherits(JoyentError, VError);
|
||||||
|
|
||||||
|
function InternalError(cause, message) {
|
||||||
|
if (message === undefined) {
|
||||||
|
message = cause;
|
||||||
|
cause = undefined;
|
||||||
|
}
|
||||||
|
assert.string(message);
|
||||||
|
JoyentError.call(this, {
|
||||||
|
cause: cause,
|
||||||
|
message: message,
|
||||||
|
code: 'InternalError',
|
||||||
|
exitStatus: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
util.inherits(InternalError, JoyentError);
|
||||||
|
|
||||||
|
function UsageError(cause, message) {
|
||||||
|
if (message === undefined) {
|
||||||
|
message = cause;
|
||||||
|
cause = undefined;
|
||||||
|
}
|
||||||
|
assert.string(message);
|
||||||
|
JoyentError.call(this, {
|
||||||
|
cause: cause,
|
||||||
|
message: message,
|
||||||
|
code: 'Usage',
|
||||||
|
exitStatus: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
util.inherits(UsageError, JoyentError);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiple errors in a group.
|
||||||
|
*/
|
||||||
|
function MultiError(errs) {
|
||||||
|
assert.arrayOfObject(errs, 'errs');
|
||||||
|
var lines = [format('multiple (%d) errors', errs.length)];
|
||||||
|
for (var i = 0; i < errs.length; i++) {
|
||||||
|
var err = errs[i];
|
||||||
|
lines.push(format(' error (%s): %s', err.code, err.message));
|
||||||
|
}
|
||||||
|
JoyentError.call(this, {
|
||||||
|
cause: errs[0],
|
||||||
|
message: lines.join('\n'),
|
||||||
|
code: 'MultiError',
|
||||||
|
exitStatus: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
MultiError.description = 'Multiple errors.';
|
||||||
|
util.inherits(MultiError, JoyentError);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ---- exports
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
JoyentError: JoyentError,
|
||||||
|
InternalError: InternalError,
|
||||||
|
UsageError: UsageError,
|
||||||
|
MultiError: MultiError
|
||||||
|
};
|
||||||
|
// vim: set softtabstop=4 shiftwidth=4:
|
201
lib/joyent.js
Normal file
201
lib/joyent.js
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Core Joyent driver class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var p = console.log;
|
||||||
|
var assert = require('assert-plus');
|
||||||
|
var async = require('async');
|
||||||
|
var format = require('util').format;
|
||||||
|
var fs = require('fs');
|
||||||
|
var path = require('path');
|
||||||
|
var smartdc = require('smartdc');
|
||||||
|
|
||||||
|
var common = require('./common');
|
||||||
|
var loadConfigSync = require('./config').loadConfigSync;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---- Joyent class
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Joyent.
|
||||||
|
*
|
||||||
|
* @param options {Object}
|
||||||
|
* - log {Bunyan Logger}
|
||||||
|
* - profile {String} Optional. Name of profile to use. Defaults to
|
||||||
|
* 'defaultProfile' in the config.
|
||||||
|
*/
|
||||||
|
function Joyent(options) {
|
||||||
|
assert.object(options, 'options');
|
||||||
|
assert.object(options.log, 'options.log');
|
||||||
|
assert.optionalString(options.profile, 'options.profile');
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.config = loadConfigSync();
|
||||||
|
this.profiles = this.config.profiles;
|
||||||
|
this.profile = this.getProfile(
|
||||||
|
options.profile || this.config.defaultProfile);
|
||||||
|
|
||||||
|
this.log = options.log;
|
||||||
|
this.log.trace({profile: this.profile}, 'profile data');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Joyent.prototype.setDefaultProfile = function setDefaultProfile(name, callback) {
|
||||||
|
if (!this.getProfile(name)) {
|
||||||
|
return callback(new Error('no such profile: ' + name));
|
||||||
|
}
|
||||||
|
this.defaultProfileName = this.config.defaultProfile = name;
|
||||||
|
common.saveConfigSync(this.config);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
Joyent.prototype.getProfile = function getProfile(name) {
|
||||||
|
for (var i = 0; i < this.profiles.length; i++) {
|
||||||
|
if (this.profiles[i].name === name) {
|
||||||
|
return this.profiles[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create or update a profile.
|
||||||
|
*
|
||||||
|
* @param profile {Object}
|
||||||
|
* @param options {Object}
|
||||||
|
* - setDefault {Boolean}
|
||||||
|
* @param callback {Function} `function (err)`
|
||||||
|
*/
|
||||||
|
Joyent.prototype.createOrUpdateProfile = function createOrUpdateProfile(
|
||||||
|
profile, options, callback) {
|
||||||
|
assert.object(profile, 'profile');
|
||||||
|
if (typeof(options) === 'function') {
|
||||||
|
callback = options;
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
assert.object(options, 'options')
|
||||||
|
assert.optionalBool(options.setDefault, 'options.setDefault')
|
||||||
|
assert.func(callback, 'callback')
|
||||||
|
|
||||||
|
var found = false;
|
||||||
|
for (var i = 0; i < this.profiles.length; i++) {
|
||||||
|
if (this.profiles[i].name === profile.name) {
|
||||||
|
this.profiles[i] = profile;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
this.profiles.push(profile);
|
||||||
|
}
|
||||||
|
if (options.setDefault) {
|
||||||
|
this.defaultProfileName = this.config.defaultProfile = profile.name;
|
||||||
|
}
|
||||||
|
common.saveConfigSync(this.config);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
Joyent.prototype.deleteProfile = function deleteProfile(name, callback) {
|
||||||
|
var found = false;
|
||||||
|
for (var i = 0; i < this.profiles.length; i++) {
|
||||||
|
if (this.profiles[i].name === name) {
|
||||||
|
found = true;
|
||||||
|
this.profiles.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
return callback(new Error('no such profile: ' + name));
|
||||||
|
}
|
||||||
|
if (this.defaultProfileName === name) {
|
||||||
|
this.defaultProfileName = this.config.defaultProfile = null;
|
||||||
|
}
|
||||||
|
common.saveConfigSync(this.config);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Joyent.prototype._clientFromDc = function _clientFromDc(dc) {
|
||||||
|
assert.string(dc, 'dc');
|
||||||
|
|
||||||
|
if (!this._clientFromDcCache) {
|
||||||
|
this._clientFromDcCache = {};
|
||||||
|
}
|
||||||
|
if (!this._clientFromDcCache[dc]) {
|
||||||
|
var prof = this.profile;
|
||||||
|
var sign;
|
||||||
|
if (prof.privKey) {
|
||||||
|
sign = smartdc.privateKeySigner({
|
||||||
|
user: prof.account,
|
||||||
|
keyId: prof.keyId,
|
||||||
|
key: prof.privKey
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
sign = smartdc.cliSigner({keyId: prof.keyId, user: prof.account});
|
||||||
|
}
|
||||||
|
var client = smartdc.createClient({
|
||||||
|
url: this.config.dcs[dc],
|
||||||
|
account: prof.account,
|
||||||
|
version: '*',
|
||||||
|
noCache: true, //XXX
|
||||||
|
rejectUnauthorized: Boolean(prof.rejectUnauthorized),
|
||||||
|
sign: sign,
|
||||||
|
// XXX cloudapi.js stupidly uses its own logger, but takes logLevel
|
||||||
|
logLevel: this.log && this.log.level(),
|
||||||
|
// Pass our logger to underlying restify client.
|
||||||
|
log: this.log
|
||||||
|
});
|
||||||
|
this._clientFromDcCache[dc] = client;
|
||||||
|
}
|
||||||
|
return this._clientFromDcCache[dc];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List machines for the current profile.
|
||||||
|
*
|
||||||
|
* @param {Object} options Optional
|
||||||
|
* - {Function} onDcError `function (dc, err)` called for each DC client
|
||||||
|
* error.
|
||||||
|
*/
|
||||||
|
Joyent.prototype.listMachines = function listMachines(options, callback) {
|
||||||
|
var self = this;
|
||||||
|
if (callback === undefined) {
|
||||||
|
callback = options;
|
||||||
|
options = {}
|
||||||
|
}
|
||||||
|
assert.object(options, 'options');
|
||||||
|
assert.optionalFunc(options.onDcError, 'options.onDcError');
|
||||||
|
assert.func(callback, 'callback');
|
||||||
|
|
||||||
|
var allMachines = [];
|
||||||
|
async.each(
|
||||||
|
self.profile.dcs || Object.keys(self.config.dcs),
|
||||||
|
function oneDc(dc, next) {
|
||||||
|
var client = self._clientFromDc(dc);
|
||||||
|
client.listMachines(function (err, machines) {
|
||||||
|
if (err) {
|
||||||
|
if (options.onDcError) {
|
||||||
|
options.onDcError(dc, err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < machines.length; i++) {
|
||||||
|
machines[i].dc = dc;
|
||||||
|
allMachines.push(machines[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function done(err) {
|
||||||
|
callback(err, allMachines);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---- exports
|
||||||
|
|
||||||
|
module.exports = Joyent;
|
24
package.json
Normal file
24
package.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "joyent",
|
||||||
|
"description": "Joyent Public Cloud CLI (https://my.joyentcloud.com/)",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Joyent (joyent.com)",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"async": "0.2.9",
|
||||||
|
"assert-plus": "0.1.4",
|
||||||
|
"backoff": "2.3.0",
|
||||||
|
"bunyan": "0.22.0",
|
||||||
|
"cmdln": "1.3.1",
|
||||||
|
"dashdash": "1.3.2",
|
||||||
|
"extsprintf": "1.0.2",
|
||||||
|
"mkdirp": "0.3.5",
|
||||||
|
"node-uuid": "1.4.1",
|
||||||
|
"once": "1.3.0",
|
||||||
|
"smartdc": "git+ssh://git@github.com:joyent/node-smartdc.git#master",
|
||||||
|
"verror": "1.3.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
}
|
139
tools/jsl.node.conf
Normal file
139
tools/jsl.node.conf
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#
|
||||||
|
# Configuration File for JavaScript Lint
|
||||||
|
#
|
||||||
|
# This configuration file can be used to lint a collection of scripts, or to enable
|
||||||
|
# or disable warnings for scripts that are linted via the command line.
|
||||||
|
#
|
||||||
|
|
||||||
|
### Warnings
|
||||||
|
# Enable or disable warnings based on requirements.
|
||||||
|
# Use "+WarningName" to display or "-WarningName" to suppress.
|
||||||
|
#
|
||||||
|
+ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent
|
||||||
|
+ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity
|
||||||
|
+ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement
|
||||||
|
-anon_no_return_value # anonymous function does not always return value
|
||||||
|
+assign_to_function_call # assignment to a function call
|
||||||
|
-block_without_braces # block statement without curly braces
|
||||||
|
+comma_separated_stmts # multiple statements separated by commas (use semicolons?)
|
||||||
|
+comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)
|
||||||
|
+default_not_at_end # the default case is not at the end of the switch statement
|
||||||
|
+dup_option_explicit # duplicate "option explicit" control comment
|
||||||
|
+duplicate_case_in_switch # duplicate case in switch statement
|
||||||
|
+duplicate_formal # duplicate formal argument {name}
|
||||||
|
+empty_statement # empty statement or extra semicolon
|
||||||
|
+identifier_hides_another # identifer {name} hides an identifier in a parent scope
|
||||||
|
-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement
|
||||||
|
+incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.
|
||||||
|
+invalid_fallthru # unexpected "fallthru" control comment
|
||||||
|
+invalid_pass # unexpected "pass" control comment
|
||||||
|
+jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax
|
||||||
|
+leading_decimal_point # leading decimal point may indicate a number or an object member
|
||||||
|
+legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax
|
||||||
|
+meaningless_block # meaningless block; curly braces have no impact
|
||||||
|
+mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence
|
||||||
|
+misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma
|
||||||
|
+missing_break # missing break statement
|
||||||
|
+missing_break_for_last_case # missing break statement for last case in switch
|
||||||
|
+missing_default_case # missing default case in switch statement
|
||||||
|
+missing_option_explicit # the "option explicit" control comment is missing
|
||||||
|
+missing_semicolon # missing semicolon
|
||||||
|
+missing_semicolon_for_lambda # missing semicolon for lambda assignment
|
||||||
|
+multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs
|
||||||
|
+nested_comment # nested comment
|
||||||
|
-no_return_value # function {name} does not always return a value
|
||||||
|
-octal_number # leading zeros make an octal number
|
||||||
|
+parseint_missing_radix # parseInt missing radix parameter
|
||||||
|
+partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag
|
||||||
|
+redeclared_var # redeclaration of {name}
|
||||||
|
+trailing_comma_in_array # extra comma is not recommended in array initializers
|
||||||
|
+trailing_decimal_point # trailing decimal point may indicate a number or an object member
|
||||||
|
+undeclared_identifier # undeclared identifier: {name}
|
||||||
|
+unreachable_code # unreachable code
|
||||||
|
-unreferenced_argument # argument declared but never referenced: {name}
|
||||||
|
-unreferenced_function # function is declared but never referenced: {name}
|
||||||
|
+unreferenced_variable # variable is declared but never referenced: {name}
|
||||||
|
+unsupported_version # JavaScript {version} is not supported
|
||||||
|
+use_of_label # use of label
|
||||||
|
+useless_assign # useless assignment
|
||||||
|
+useless_comparison # useless comparison; comparing identical expressions
|
||||||
|
-useless_quotes # the quotation marks are unnecessary
|
||||||
|
+useless_void # use of the void type may be unnecessary (void is always undefined)
|
||||||
|
+var_hides_arg # variable {name} hides argument
|
||||||
|
+want_assign_or_call # expected an assignment or function call
|
||||||
|
+with_statement # with statement hides undeclared variables; use temporary variable instead
|
||||||
|
|
||||||
|
|
||||||
|
### Output format
|
||||||
|
# Customize the format of the error message.
|
||||||
|
# __FILE__ indicates current file path
|
||||||
|
# __FILENAME__ indicates current file name
|
||||||
|
# __LINE__ indicates current line
|
||||||
|
# __COL__ indicates current column
|
||||||
|
# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__)
|
||||||
|
# __ERROR_NAME__ indicates error name (used in configuration file)
|
||||||
|
# __ERROR_PREFIX__ indicates error prefix
|
||||||
|
# __ERROR_MSG__ indicates error message
|
||||||
|
#
|
||||||
|
# For machine-friendly output, the output format can be prefixed with
|
||||||
|
# "encode:". If specified, all items will be encoded with C-slashes.
|
||||||
|
#
|
||||||
|
# Visual Studio syntax (default):
|
||||||
|
+output-format __FILE__(__LINE__): __ERROR__
|
||||||
|
# Alternative syntax:
|
||||||
|
#+output-format __FILE__:__LINE__: __ERROR__
|
||||||
|
|
||||||
|
|
||||||
|
### Context
|
||||||
|
# Show the in-line position of the error.
|
||||||
|
# Use "+context" to display or "-context" to suppress.
|
||||||
|
#
|
||||||
|
+context
|
||||||
|
|
||||||
|
|
||||||
|
### Control Comments
|
||||||
|
# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for
|
||||||
|
# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is
|
||||||
|
# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason,
|
||||||
|
# although legacy control comments are enabled by default for backward compatibility.
|
||||||
|
#
|
||||||
|
-legacy_control_comments
|
||||||
|
|
||||||
|
|
||||||
|
### Defining identifiers
|
||||||
|
# By default, "option explicit" is enabled on a per-file basis.
|
||||||
|
# To enable this for all files, use "+always_use_option_explicit"
|
||||||
|
-always_use_option_explicit
|
||||||
|
|
||||||
|
# Define certain identifiers of which the lint is not aware.
|
||||||
|
# (Use this in conjunction with the "undeclared identifier" warning.)
|
||||||
|
#
|
||||||
|
# Common uses for webpages might be:
|
||||||
|
+define __dirname
|
||||||
|
+define __filename
|
||||||
|
+define clearInterval
|
||||||
|
+define clearTimeout
|
||||||
|
+define console
|
||||||
|
+define exports
|
||||||
|
+define global
|
||||||
|
+define module
|
||||||
|
+define process
|
||||||
|
+define require
|
||||||
|
+define setInterval
|
||||||
|
+define setTimeout
|
||||||
|
+define Buffer
|
||||||
|
+define JSON
|
||||||
|
+define Math
|
||||||
|
|
||||||
|
### JavaScript Version
|
||||||
|
# To change the default JavaScript version:
|
||||||
|
#+default-type text/javascript;version=1.5
|
||||||
|
#+default-type text/javascript;e4x=1
|
||||||
|
|
||||||
|
### Files
|
||||||
|
# Specify which files to lint
|
||||||
|
# Use "+recurse" to enable recursion (disabled by default).
|
||||||
|
# To add a set of files, use "+process FileName", "+process Folder\Path\*.js",
|
||||||
|
# or "+process Folder\Path\*.htm".
|
||||||
|
#
|
||||||
|
|
5
tools/jsstyle.conf
Normal file
5
tools/jsstyle.conf
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
indent=4
|
||||||
|
doxygen
|
||||||
|
unparenthesized-return=0
|
||||||
|
blank-after-start-comment=0
|
||||||
|
leading-right-paren-ok=1
|
43
tools/mk/Makefile.defs
Normal file
43
tools/mk/Makefile.defs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# -*- mode: makefile -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Makefile.defs: common defines.
|
||||||
|
#
|
||||||
|
# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
|
||||||
|
# into other repos as-is without requiring any modifications. If you find
|
||||||
|
# yourself changing this file, you should instead update the original copy in
|
||||||
|
# eng.git and then update your repo to use the new version.
|
||||||
|
#
|
||||||
|
# This makefile defines some useful defines. Include it at the top of
|
||||||
|
# your Makefile.
|
||||||
|
#
|
||||||
|
# Definitions in this Makefile:
|
||||||
|
#
|
||||||
|
# TOP The absolute path to the project directory. The top dir.
|
||||||
|
# BRANCH The current git branch.
|
||||||
|
# TIMESTAMP The timestamp for the build. This can be set via
|
||||||
|
# the TIMESTAMP envvar (used by MG-based builds).
|
||||||
|
# STAMP A build stamp to use in built package names.
|
||||||
|
#
|
||||||
|
|
||||||
|
TOP := $(shell pwd)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Mountain Gorilla-spec'd versioning.
|
||||||
|
# See "Package Versioning" in MG's README.md:
|
||||||
|
# <https://mo.joyent.com/mountain-gorilla/blob/master/README.md#L139-200>
|
||||||
|
#
|
||||||
|
# Need GNU awk for multi-char arg to "-F".
|
||||||
|
_AWK := $(shell (which gawk >/dev/null && echo gawk) \
|
||||||
|
|| (which nawk >/dev/null && echo nawk) \
|
||||||
|
|| echo awk)
|
||||||
|
BRANCH := $(shell git symbolic-ref HEAD | $(_AWK) -F/ '{print $$3}')
|
||||||
|
ifeq ($(TIMESTAMP),)
|
||||||
|
TIMESTAMP := $(shell date -u "+%Y%m%dT%H%M%SZ")
|
||||||
|
endif
|
||||||
|
_GITDESCRIBE := g$(shell git describe --all --long --dirty | $(_AWK) -F'-g' '{print $$NF}')
|
||||||
|
STAMP := $(BRANCH)-$(TIMESTAMP)-$(_GITDESCRIBE)
|
||||||
|
|
||||||
|
# node-gyp will print build info useful for debugging with V=1
|
||||||
|
export V=1
|
44
tools/mk/Makefile.deps
Normal file
44
tools/mk/Makefile.deps
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# -*- mode: makefile -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Makefile.deps: Makefile for including common tools as dependencies
|
||||||
|
#
|
||||||
|
# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
|
||||||
|
# into other repos as-is without requiring any modifications. If you find
|
||||||
|
# yourself changing this file, you should instead update the original copy in
|
||||||
|
# eng.git and then update your repo to use the new version.
|
||||||
|
#
|
||||||
|
# This file is separate from Makefile.targ so that teams can choose
|
||||||
|
# independently whether to use the common targets in Makefile.targ and the
|
||||||
|
# common tools here.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# javascriptlint
|
||||||
|
#
|
||||||
|
JSL_EXEC ?= deps/javascriptlint/build/install/jsl
|
||||||
|
JSL ?= $(JSL_EXEC)
|
||||||
|
|
||||||
|
$(JSL_EXEC): | deps/javascriptlint/.git
|
||||||
|
cd deps/javascriptlint && make install
|
||||||
|
|
||||||
|
distclean::
|
||||||
|
if [[ -f deps/javascriptlint/Makefile ]]; then \
|
||||||
|
cd deps/javascriptlint && make clean; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# jsstyle
|
||||||
|
#
|
||||||
|
JSSTYLE_EXEC ?= deps/jsstyle/jsstyle
|
||||||
|
JSSTYLE ?= $(JSSTYLE_EXEC)
|
||||||
|
|
||||||
|
$(JSSTYLE_EXEC): | deps/jsstyle/.git
|
||||||
|
|
||||||
|
#
|
||||||
|
# restdown
|
||||||
|
#
|
||||||
|
RESTDOWN_EXEC ?= deps/restdown/bin/restdown
|
||||||
|
RESTDOWN ?= python $(RESTDOWN_EXEC)
|
||||||
|
$(RESTDOWN_EXEC): | deps/restdown/.git
|
309
tools/mk/Makefile.targ
Normal file
309
tools/mk/Makefile.targ
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
# -*- mode: makefile -*-
|
||||||
|
#
|
||||||
|
# Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Makefile.targ: common targets.
|
||||||
|
#
|
||||||
|
# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
|
||||||
|
# into other repos as-is without requiring any modifications. If you find
|
||||||
|
# yourself changing this file, you should instead update the original copy in
|
||||||
|
# eng.git and then update your repo to use the new version.
|
||||||
|
#
|
||||||
|
# This Makefile defines several useful targets and rules. You can use it by
|
||||||
|
# including it from a Makefile that specifies some of the variables below.
|
||||||
|
#
|
||||||
|
# Targets defined in this Makefile:
|
||||||
|
#
|
||||||
|
# check Checks JavaScript files for lint and style
|
||||||
|
# Checks bash scripts for syntax
|
||||||
|
# Checks SMF manifests for validity against the SMF DTD
|
||||||
|
#
|
||||||
|
# clean Removes built files
|
||||||
|
#
|
||||||
|
# docs Builds restdown documentation in docs/
|
||||||
|
#
|
||||||
|
# prepush Depends on "check" and "test"
|
||||||
|
#
|
||||||
|
# test Does nothing (you should override this)
|
||||||
|
#
|
||||||
|
# xref Generates cscope (source cross-reference index)
|
||||||
|
#
|
||||||
|
# For details on what these targets are supposed to do, see the Joyent
|
||||||
|
# Engineering Guide.
|
||||||
|
#
|
||||||
|
# To make use of these targets, you'll need to set some of these variables. Any
|
||||||
|
# variables left unset will simply not be used.
|
||||||
|
#
|
||||||
|
# BASH_FILES Bash scripts to check for syntax
|
||||||
|
# (paths relative to top-level Makefile)
|
||||||
|
#
|
||||||
|
# CLEAN_FILES Files to remove as part of the "clean" target. Note
|
||||||
|
# that files generated by targets in this Makefile are
|
||||||
|
# automatically included in CLEAN_FILES. These include
|
||||||
|
# restdown-generated HTML and JSON files.
|
||||||
|
#
|
||||||
|
# DOC_FILES Restdown (documentation source) files. These are
|
||||||
|
# assumed to be contained in "docs/", and must NOT
|
||||||
|
# contain the "docs/" prefix.
|
||||||
|
#
|
||||||
|
# JSL_CONF_NODE Specify JavaScriptLint configuration files
|
||||||
|
# JSL_CONF_WEB (paths relative to top-level Makefile)
|
||||||
|
#
|
||||||
|
# Node.js and Web configuration files are separate
|
||||||
|
# because you'll usually want different global variable
|
||||||
|
# configurations. If no file is specified, none is given
|
||||||
|
# to jsl, which causes it to use a default configuration,
|
||||||
|
# which probably isn't what you want.
|
||||||
|
#
|
||||||
|
# JSL_FILES_NODE JavaScript files to check with Node config file.
|
||||||
|
# JSL_FILES_WEB JavaScript files to check with Web config file.
|
||||||
|
#
|
||||||
|
# JSON_FILES JSON files to be validated
|
||||||
|
#
|
||||||
|
# JSSTYLE_FILES JavaScript files to be style-checked
|
||||||
|
#
|
||||||
|
# You can also override these variables:
|
||||||
|
#
|
||||||
|
# BASH Path to bash (default: "bash")
|
||||||
|
#
|
||||||
|
# CSCOPE_DIRS Directories to search for source files for the cscope
|
||||||
|
# index. (default: ".")
|
||||||
|
#
|
||||||
|
# JSL Path to JavaScriptLint (default: "jsl")
|
||||||
|
#
|
||||||
|
# JSL_FLAGS_NODE Additional flags to pass through to JSL
|
||||||
|
# JSL_FLAGS_WEB
|
||||||
|
# JSL_FLAGS
|
||||||
|
#
|
||||||
|
# JSON Path to json tool (default: "json")
|
||||||
|
#
|
||||||
|
# JSSTYLE Path to jsstyle (default: "jsstyle")
|
||||||
|
#
|
||||||
|
# JSSTYLE_FLAGS Additional flags to pass through to jsstyle
|
||||||
|
#
|
||||||
|
# RESTDOWN_EXT By default '.restdown' is required for DOC_FILES
|
||||||
|
# (see above). If you want to use, say, '.md' instead, then
|
||||||
|
# set 'RESTDOWN_EXT=.md' in your Makefile.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Defaults for the various tools we use.
|
||||||
|
#
|
||||||
|
BASH ?= bash
|
||||||
|
BASHSTYLE ?= tools/bashstyle
|
||||||
|
CP ?= cp
|
||||||
|
CSCOPE ?= cscope
|
||||||
|
CSCOPE_DIRS ?= .
|
||||||
|
JSL ?= jsl
|
||||||
|
JSON ?= json
|
||||||
|
JSSTYLE ?= jsstyle
|
||||||
|
MKDIR ?= mkdir -p
|
||||||
|
MV ?= mv
|
||||||
|
RESTDOWN_FLAGS ?=
|
||||||
|
RESTDOWN_EXT ?= .restdown
|
||||||
|
RMTREE ?= rm -rf
|
||||||
|
JSL_FLAGS ?= --nologo --nosummary
|
||||||
|
|
||||||
|
ifeq ($(shell uname -s),SunOS)
|
||||||
|
TAR ?= gtar
|
||||||
|
else
|
||||||
|
TAR ?= tar
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Defaults for other fixed values.
|
||||||
|
#
|
||||||
|
BUILD = build
|
||||||
|
DISTCLEAN_FILES += $(BUILD)
|
||||||
|
DOC_BUILD = $(BUILD)/docs/public
|
||||||
|
|
||||||
|
#
|
||||||
|
# Configure JSL_FLAGS_{NODE,WEB} based on JSL_CONF_{NODE,WEB}.
|
||||||
|
#
|
||||||
|
ifneq ($(origin JSL_CONF_NODE), undefined)
|
||||||
|
JSL_FLAGS_NODE += --conf=$(JSL_CONF_NODE)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(origin JSL_CONF_WEB), undefined)
|
||||||
|
JSL_FLAGS_WEB += --conf=$(JSL_CONF_WEB)
|
||||||
|
endif
|
||||||
|
|
||||||
|
#
|
||||||
|
# Targets. For descriptions on what these are supposed to do, see the
|
||||||
|
# Joyent Engineering Guide.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# Instruct make to keep around temporary files. We have rules below that
|
||||||
|
# automatically update git submodules as needed, but they employ a deps/*/.git
|
||||||
|
# temporary file. Without this directive, make tries to remove these .git
|
||||||
|
# directories after the build has completed.
|
||||||
|
#
|
||||||
|
.SECONDARY: $($(wildcard deps/*):%=%/.git)
|
||||||
|
|
||||||
|
#
|
||||||
|
# This rule enables other rules that use files from a git submodule to have
|
||||||
|
# those files depend on deps/module/.git and have "make" automatically check
|
||||||
|
# out the submodule as needed.
|
||||||
|
#
|
||||||
|
deps/%/.git:
|
||||||
|
git submodule update --init deps/$*
|
||||||
|
|
||||||
|
#
|
||||||
|
# These recipes make heavy use of dynamically-created phony targets. The parent
|
||||||
|
# Makefile defines a list of input files like BASH_FILES. We then say that each
|
||||||
|
# of these files depends on a fake target called filename.bashchk, and then we
|
||||||
|
# define a pattern rule for those targets that runs bash in check-syntax-only
|
||||||
|
# mode. This mechanism has the nice properties that if you specify zero files,
|
||||||
|
# the rule becomes a noop (unlike a single rule to check all bash files, which
|
||||||
|
# would invoke bash with zero files), and you can check individual files from
|
||||||
|
# the command line with "make filename.bashchk".
|
||||||
|
#
|
||||||
|
.PHONY: check-bash
|
||||||
|
check-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle)
|
||||||
|
|
||||||
|
%.bashchk: %
|
||||||
|
$(BASH) -n $^
|
||||||
|
|
||||||
|
%.bashstyle: %
|
||||||
|
$(BASHSTYLE) $^
|
||||||
|
|
||||||
|
.PHONY: check-json
|
||||||
|
check-json: $(JSON_FILES:%=%.jsonchk)
|
||||||
|
|
||||||
|
%.jsonchk: %
|
||||||
|
$(JSON) --validate -f $^
|
||||||
|
|
||||||
|
#
|
||||||
|
# The above approach can be slow when there are many files to check because it
|
||||||
|
# requires that "make" invoke the check tool once for each file, rather than
|
||||||
|
# passing in several files at once. For the JavaScript check targets, we define
|
||||||
|
# a variable for the target itself *only if* the list of input files is
|
||||||
|
# non-empty. This avoids invoking the tool if there are no files to check.
|
||||||
|
#
|
||||||
|
JSL_NODE_TARGET = $(if $(JSL_FILES_NODE), check-jsl-node)
|
||||||
|
.PHONY: check-jsl-node
|
||||||
|
check-jsl-node: $(JSL_EXEC)
|
||||||
|
$(JSL) $(JSL_FLAGS) $(JSL_FLAGS_NODE) $(JSL_FILES_NODE)
|
||||||
|
|
||||||
|
JSL_WEB_TARGET = $(if $(JSL_FILES_WEB), check-jsl-web)
|
||||||
|
.PHONY: check-jsl-web
|
||||||
|
check-jsl-web: $(JSL_EXEC)
|
||||||
|
$(JSL) $(JSL_FLAGS) $(JSL_FLAGS_WEB) $(JSL_FILES_WEB)
|
||||||
|
|
||||||
|
.PHONY: check-jsl
|
||||||
|
check-jsl: $(JSL_NODE_TARGET) $(JSL_WEB_TARGET)
|
||||||
|
|
||||||
|
JSSTYLE_TARGET = $(if $(JSSTYLE_FILES), check-jsstyle)
|
||||||
|
.PHONY: check-jsstyle
|
||||||
|
check-jsstyle: $(JSSTYLE_EXEC)
|
||||||
|
$(JSSTYLE) $(JSSTYLE_FLAGS) $(JSSTYLE_FILES)
|
||||||
|
|
||||||
|
.PHONY: check
|
||||||
|
check: check-jsl check-json $(JSSTYLE_TARGET) check-bash
|
||||||
|
@echo check ok
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean::
|
||||||
|
-$(RMTREE) $(CLEAN_FILES)
|
||||||
|
|
||||||
|
.PHONY: distclean
|
||||||
|
distclean:: clean
|
||||||
|
-$(RMTREE) $(DISTCLEAN_FILES)
|
||||||
|
|
||||||
|
CSCOPE_FILES = cscope.in.out cscope.out cscope.po.out
|
||||||
|
CLEAN_FILES += $(CSCOPE_FILES)
|
||||||
|
|
||||||
|
.PHONY: xref
|
||||||
|
xref: cscope.files
|
||||||
|
$(CSCOPE) -bqR
|
||||||
|
|
||||||
|
.PHONY: cscope.files
|
||||||
|
cscope.files:
|
||||||
|
find $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \
|
||||||
|
-o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@
|
||||||
|
|
||||||
|
#
|
||||||
|
# The "docs" target is complicated because we do several things here:
|
||||||
|
#
|
||||||
|
# (1) Use restdown to build HTML and JSON files from each of DOC_FILES.
|
||||||
|
#
|
||||||
|
# (2) Copy these files into $(DOC_BUILD) (build/docs/public), which
|
||||||
|
# functions as a complete copy of the documentation that could be
|
||||||
|
# mirrored or served over HTTP.
|
||||||
|
#
|
||||||
|
# (3) Then copy any directories and media from docs/media into
|
||||||
|
# $(DOC_BUILD)/media. This allows projects to include their own media,
|
||||||
|
# including files that will override same-named files provided by
|
||||||
|
# restdown.
|
||||||
|
#
|
||||||
|
# Step (3) is the surprisingly complex part: in order to do this, we need to
|
||||||
|
# identify the subdirectories in docs/media, recreate them in
|
||||||
|
# $(DOC_BUILD)/media, then do the same with the files.
|
||||||
|
#
|
||||||
|
DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$")
|
||||||
|
DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%)
|
||||||
|
DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%)
|
||||||
|
|
||||||
|
DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null)
|
||||||
|
DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%)
|
||||||
|
DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Like the other targets, "docs" just depends on the final files we want to
|
||||||
|
# create in $(DOC_BUILD), leveraging other targets and recipes to define how
|
||||||
|
# to get there.
|
||||||
|
#
|
||||||
|
.PHONY: docs
|
||||||
|
docs: \
|
||||||
|
$(DOC_FILES:%$(RESTDOWN_EXT)=$(DOC_BUILD)/%.html) \
|
||||||
|
$(DOC_FILES:%$(RESTDOWN_EXT)=$(DOC_BUILD)/%.json) \
|
||||||
|
$(DOC_MEDIA_FILES_BUILD)
|
||||||
|
|
||||||
|
#
|
||||||
|
# We keep the intermediate files so that the next build can see whether the
|
||||||
|
# files in DOC_BUILD are up to date.
|
||||||
|
#
|
||||||
|
.PRECIOUS: \
|
||||||
|
$(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.html) \
|
||||||
|
$(DOC_FILES:%$(RESTDOWN_EXT)=docs/%json)
|
||||||
|
|
||||||
|
#
|
||||||
|
# We do clean those intermediate files, as well as all of DOC_BUILD.
|
||||||
|
#
|
||||||
|
CLEAN_FILES += \
|
||||||
|
$(DOC_BUILD) \
|
||||||
|
$(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.html) \
|
||||||
|
$(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.json)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Before installing the files, we must make sure the directories exist. The |
|
||||||
|
# syntax tells make that the dependency need only exist, not be up to date.
|
||||||
|
# Otherwise, it might try to rebuild spuriously because the directory itself
|
||||||
|
# appears out of date.
|
||||||
|
#
|
||||||
|
$(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD)
|
||||||
|
|
||||||
|
$(DOC_BUILD)/%: docs/% | $(DOC_BUILD)
|
||||||
|
$(CP) $< $@
|
||||||
|
|
||||||
|
docs/%.json docs/%.html: docs/%$(RESTDOWN_EXT) | $(DOC_BUILD) $(RESTDOWN_EXEC)
|
||||||
|
$(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $<
|
||||||
|
|
||||||
|
$(DOC_BUILD):
|
||||||
|
$(MKDIR) $@
|
||||||
|
|
||||||
|
$(DOC_MEDIA_DIRS_BUILD):
|
||||||
|
$(MKDIR) $@
|
||||||
|
|
||||||
|
#
|
||||||
|
# The default "test" target does nothing. This should usually be overridden by
|
||||||
|
# the parent Makefile. It's included here so we can define "prepush" without
|
||||||
|
# requiring the repo to define "test".
|
||||||
|
#
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
|
||||||
|
.PHONY: prepush
|
||||||
|
prepush: check test
|
24
tools/rsync-to
Executable file
24
tools/rsync-to
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Rsync the master in this working copy to the install on the given HN.
|
||||||
|
#
|
||||||
|
|
||||||
|
#set -o xtrace
|
||||||
|
set -o errexit
|
||||||
|
|
||||||
|
TOP=$(cd $(dirname $0)/../; pwd)
|
||||||
|
NODE=$1
|
||||||
|
[[ -z "$NODE" ]] && echo 'rsync-to: error: no headnode given' && exit 1
|
||||||
|
BASEDIR=/opt/smartdc/sdcadm
|
||||||
|
|
||||||
|
extraOpts=
|
||||||
|
if [[ $(uname -s) != "SunOS" ]]; then
|
||||||
|
extraOpts="--exclude *.node --exclude build"
|
||||||
|
else
|
||||||
|
# Clean node_modules everytime.
|
||||||
|
ssh $NODE rm -rf $BASEDIR/node_modules
|
||||||
|
fi
|
||||||
|
|
||||||
|
for f in bin lib node_modules package.json; do
|
||||||
|
rsync -av ${TOP}/$f $NODE:$BASEDIR/$f $extraOpts
|
||||||
|
done
|
Reference in New Issue
Block a user