UNPKG

servant-cli

Version:

Command line tool for building Commands with Servant (http://www.getservant.com)

306 lines (214 loc) 9.84 kB
'use strict'; /** * Servant Command Line Interface */ var aws = require('aws-sdk'); var exec = require('child_process').exec; var fs = require('fs'); var os = require('os'); var packageJson = require('./../package.json'); var path = require('path'); var async = require('async'); var zip = new require('node-zip')(); var wrench = require('wrench'); var jsonfile = require('jsonfile'); var del = require('del'); var Lambda = function() { this.version = packageJson.version; return this; }; /** * Lambda: Init */ Lambda.prototype.init = function(program) { console.log('****** Setting up a new Servant Extension...'); // Create .env File fs.writeFileSync(process.cwd() + '/.env', fs.readFileSync(__dirname + '/.env.template')); // Create package.json fs.writeFileSync(process.cwd() + '/package.json', fs.readFileSync(__dirname + '/package.json')); // Create Extension Root File: index.js fs.writeFileSync(process.cwd() + '/index.js', fs.readFileSync(__dirname + '/index.js')); // Create Extension Core File: extension.js fs.writeFileSync(process.cwd() + '/extension.js', fs.readFileSync(__dirname + '/extension.js')); // Create Command Router File: extension_router.json jsonfile.writeFileSync(process.cwd() + '/extension_router.json', { Commands: {} }); // Create Commands Folder var dir = './commands'; if (!fs.existsSync(dir)) fs.mkdirSync(dir); console.log('****** Success: Created boilerplate files for a new Servant Extension'); console.log('****** What\'s Next: If your AWS account is set up, open the .env file and add your AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_ROLE_ARN, AWS_FUNCTION_NAME. If your AWS Account is not set up, follow the instructions in the documentation here: https://github.com/servant-app/servant-cli'); }; /** * Command * - Creates a new Servant Command */ Lambda.prototype.command = function(program) { // Validate // Check Command Router JSON File if (!fs.existsSync('./extension_router.json')) return console.log('****** Servant Error: You don\'t have a extension_router.json file. Make one before creating a command.'); // Check Root Extension Folder if (!fs.existsSync('./commands')) return console.log('****** Servant Error: You don\'t have a Commands folder. Are you in the root directory of your Extension?'); if (fs.existsSync('./commands/' + program.name)) return console.log('****** Servant Error: You already have a command with this name in your Commands folder.'); // Check Name is included if (!program.name || program.name === 'none') return console.log('****** Servant Error: You must include a Name. Like: -n test_command'); // Check Command ID is included if (!program.command_id || program.command_id === 'none') return console.log('****** Servant Error: You must include a Command ID. Like: -i com_jkaI19lL'); // Sanitize Command Name program.name = program.name.toLowerCase().replace(/[^a-zA-Z0-9]/g, '').replace(/ /g, "_"); // Create Command Subfolder fs.mkdirSync('./commands/' + program.name); // Create Command Root File fs.writeFileSync(process.cwd() + '/commands/' + program.name + '/command.js', fs.readFileSync(__dirname + '/command_simple.js')); // Create Command Event File jsonfile.writeFileSync(process.cwd() + '/commands/' + program.name + '/event.json', { command_id: program.command_id, data: {} }); // Add Command To Command Router var extension_router_file = process.cwd() + '/extension_router.json'; var router_object = jsonfile.readFileSync(extension_router_file); router_object.Commands[program.command_id] = program.name; jsonfile.writeFileSync(extension_router_file, router_object); // Conclude console.log('****** Success: Created boilerplate files for a new Servant Command called: ' + program.name); }; /** * Lambda: Run Locally */ Lambda.prototype.run = function(program) { // Check if command name is given if (!program.name || program.name === 'none') return console.log('****** Servant Error: Please specify a command to run with the -c flag. Like this: servant run -c example_command'); // Check Command Exists if (!fs.existsSync(process.cwd() + '/commands/' + program.name)) return console.log('****** Servant Error: Command not found. Are you sure you typed its name correctly?'); // Check if in extension's root directory if (!fs.existsSync(process.cwd() + '/index.js') || !fs.existsSync(process.cwd() + '/commands/' + program.name + '/event.json')) return console.log('****** Servant Error: You must be in the root directory of your extension to test running a command.'); var extension = require(process.cwd() + '/index.js'); var event = require(process.cwd() + '/commands/' + program.name + '/event.json'); // Run Handler this._runHandler(extension.handler, event); }; Lambda.prototype._runHandler = function(handler, event) { var context = { done: function(error, result) { if (error) { console.log('****** Command Returned An Error: '); console.log(error); } if (result) { console.log('****** Command Finished Successfully: '); console.log(result); } return process.exit(0); } }; handler(event, context); }; Lambda.prototype._params = function(program, buffer) { var params = { FunctionName: program.functionName, FunctionZip: buffer, Handler: program.handler, Mode: program.mode, Role: program.role, Runtime: program.runtime, Description: program.description, MemorySize: program.memorySize, Timeout: program.timeout }; return params; }; Lambda.prototype._zipfileTmpPath = function(program) { var ms_since_epoch = +new Date; var filename = program.functionName + '-' + ms_since_epoch + '.zip'; var zipfile = path.join(os.tmpDir(), filename); return zipfile; }; Lambda.prototype._rsync = function(program, codeDirectory, callback) { exec('rsync -r --exclude=.git --exclude=*.log --exclude=node_modules . ' + codeDirectory, function(err, stdout, stderr) { if (err) { throw err; } return callback(null, true); }); }; Lambda.prototype._npmInstall = function(program, codeDirectory, callback) { exec('npm install --production --prefix ' + codeDirectory, function(err, stdout, stderr) { if (err) { throw err; } return callback(null, true); }); }; Lambda.prototype._zip = function(program, codeDirectory, callback) { var zipfile = this._zipfileTmpPath(program); var options = { type: 'nodebuffer', compression: 'DEFLATE' } var files = wrench.readdirSyncRecursive(codeDirectory); files.forEach(function(file) { var filePath = [codeDirectory, file].join('/'); var isFile = fs.lstatSync(filePath).isFile(); if (isFile) { var content = fs.readFileSync(filePath); zip.file(file, content); } }); var data = zip.generate(options); return callback(null, data); }; Lambda.prototype._codeDirectory = function(program) { var epoch_time = +new Date; return os.tmpDir() + '/' + program.functionName + '-' + epoch_time; }; /** * Deploy * - Zip dependencies and upload to AWS Lambda */ Lambda.prototype.deploy = function(program) { console.log('****** Deploying your Servant Extension to AWS Lambda. This might take a few minutes...'); // Defaults var _this = this; var regions = program.region.split(','); var codeDirectory = _this._codeDirectory(program); var fs = require('fs'); var dir = './stmp'; // Create Temp Folder if (fs.existsSync(dir)) del.sync(['./stmp']); fs.mkdirSync(dir); // Move all files to tmp folder (except .git, .log, event.json and node_modules) _this._rsync(program, codeDirectory, function(err, result) { _this._npmInstall(program, codeDirectory, function(err, result) { _this._zip(program, codeDirectory, function(err, buffer) { console.log('****** Zipping up your Extension\'s files...'); //var buffer = fs.readFileSync(zipfile); var params = _this._params(program, buffer); async.map(regions, function(region, cb) { console.log('****** Uploading your Extension to AWS Lambda with these parameters: '); console.log(params); aws.config.update({ accessKeyId: program.accessKey, secretAccessKey: program.secretKey, region: region }); var lambda = new aws.Lambda({ apiVersion: '2014-11-11' }); lambda.uploadFunction(params, function(err, data) { cb(err, data); }); }, function(err, results) { if (err) return console.log(err); // Remove Temp Directory del(['./stmp'], function(err, paths) { if (err) return console.log(err); return console.log('****** Success: Your Servant Extension has been successfully deployed to AWS Lambda. If you haven\'t already, register your Extension @ http://www.getservant.com as an AWS Lambda App with this AWS Lambda ARN: ' + results[0].FunctionARN); }); }); }); }); }); }; module.exports = new Lambda();