From f50ae655609d2afd41f729a20deaf8026cf3257c Mon Sep 17 00:00:00 2001 From: Cameron Diver Date: Mon, 24 Apr 2017 13:48:43 +0100 Subject: [PATCH] Add cloud-builder builder output parity to build and deploy Change-type: minor Signed-off-by: Cameron Diver --- build/actions/build.js | 4 +++- build/actions/deploy.js | 40 ++++++++++++++++--------------- build/utils/docker.js | 17 +++++++------ build/utils/logging.js | 50 +++++++++++++++++++++++++++++++++++++++ lib/actions/build.coffee | 3 ++- lib/actions/deploy.coffee | 34 ++++++++++++++++---------- lib/utils/docker.coffee | 16 ++++++------- lib/utils/logging.coffee | 40 +++++++++++++++++++++++++++++++ package.json | 5 ++-- 9 files changed, 156 insertions(+), 53 deletions(-) create mode 100644 build/utils/logging.js create mode 100644 lib/utils/logging.coffee diff --git a/build/actions/build.js b/build/actions/build.js index f96b9af8..870b6d91 100644 --- a/build/actions/build.js +++ b/build/actions/build.js @@ -43,6 +43,8 @@ module.exports = { } ]), action: function(params, options, done) { - return dockerUtils.runBuild(params, options, getBundleInfo).asCallback(done); + var logging; + logging = require('../utils/logging'); + return dockerUtils.runBuild(params, options, getBundleInfo, logging.getLogStreams()).asCallback(done); } }; diff --git a/build/actions/deploy.js b/build/actions/deploy.js index ffe5a550..d4251d73 100644 --- a/build/actions/deploy.js +++ b/build/actions/deploy.js @@ -37,12 +37,14 @@ parseInput = Promise.method(function(params, options) { return [appName, options.build, source, image]; }); -pushProgress = function(imageSize, request, timeout) { - var progressReporter; +pushProgress = function(imageSize, request, logStreams, timeout) { + var ansiEscapes, logging, progressReporter; if (timeout == null) { timeout = 250; } - process.stdout.write('Initialising...'); + logging = require('../utils/logging'); + ansiEscapes = require('ansi-escapes'); + logging.logInfo(logStreams, 'Initialising...'); return progressReporter = setInterval(function() { var percent, sent; sent = request.req.connection._bytesDispatched; @@ -51,12 +53,10 @@ pushProgress = function(imageSize, request, timeout) { clearInterval(progressReporter); percent = 100; } + process.stdout.write(ansiEscapes.cursorUp(1)); process.stdout.clearLine(); process.stdout.cursorTo(0); - process.stdout.write("Uploaded " + (percent.toFixed(1)) + "%"); - if (percent === 100) { - return console.log(); - } + return logging.logInfo(logStreams, "Uploaded " + (percent.toFixed(1)) + "%"); }, timeout); }; @@ -68,7 +68,7 @@ getBundleInfo = function(options) { }); }; -performUpload = function(image, token, username, url, size, appName) { +performUpload = function(image, token, username, url, size, appName, logStreams) { var post, request; request = require('request'); url = url || process.env.RESINRC_RESIN_URL; @@ -79,18 +79,18 @@ performUpload = function(image, token, username, url, size, appName) { }, body: image }); - return uploadToPromise(post, size); + return uploadToPromise(post, size, logStreams); }; -uploadToPromise = function(request, size) { +uploadToPromise = function(request, size, logStreams) { + var logging; + logging = require('../utils/logging'); return new Promise(function(resolve, reject) { var handleMessage; handleMessage = function(data) { var obj; data = data.toString(); - if (process.env.DEBUG) { - console.log("Received data: " + data); - } + logging.logDebug(logStreams, "Received data: " + data); obj = JSON.parse(data); if (obj.type != null) { switch (obj.type) { @@ -99,7 +99,7 @@ uploadToPromise = function(request, size) { case 'success': return resolve(obj.image); case 'status': - return console.log("Remote: " + obj.message); + return logging.logInfo(logStreams, "Remote: " + obj.message); default: return reject(new Error("Received unexpected reply from remote: " + data)); } @@ -108,7 +108,7 @@ uploadToPromise = function(request, size) { } }; request.on('error', reject).on('data', handleMessage); - return pushProgress(size, request); + return pushProgress(size, request, logStreams); }); }; @@ -131,11 +131,13 @@ module.exports = { } ]), action: function(params, options, done) { - var _, docker, resin, tmp, tmpNameAsync; + var _, docker, logStreams, logging, resin, tmp, tmpNameAsync; _ = require('lodash'); tmp = require('tmp'); tmpNameAsync = Promise.promisify(tmp.tmpName); resin = require('resin-sdk-preconfigured'); + logging = require('../utils/logging'); + logStreams = logging.getLogStreams(); tmp.setGracefulCleanup(); docker = dockerUtils.getDocker(options); return parseInput(params, options).then(function(arg) { @@ -150,18 +152,18 @@ module.exports = { }); return Promise["try"](function() { if (build) { - return dockerUtils.runBuild(params, options, getBundleInfo); + return dockerUtils.runBuild(params, options, getBundleInfo, logStreams); } else { return imageName; } }).then(function(imageName) { - return Promise.join(dockerUtils.bufferImage(docker, imageName, tmpPath), resin.auth.getToken(), resin.auth.whoami(), resin.settings.get('resinUrl'), dockerUtils.getImageSize(docker, imageName), params.appName, performUpload); + return Promise.join(dockerUtils.bufferImage(docker, imageName, tmpPath), resin.auth.getToken(), resin.auth.whoami(), resin.settings.get('resinUrl'), dockerUtils.getImageSize(docker, imageName), params.appName, logStreams, performUpload); })["finally"](function() { return require('fs').unlink(tmpPath); }); }); }).then(function(imageName) { - return console.log("Successfully deployed image: " + (formatImageName(imageName))); + return logging.logSuccess(logStreams, "Successfully deployed image: " + (formatImageName(imageName))); }).asCallback(done); } }; diff --git a/build/utils/docker.js b/build/utils/docker.js index 4cacef06..44213bbf 100644 --- a/build/utils/docker.js +++ b/build/utils/docker.js @@ -98,11 +98,12 @@ exports.tarDirectory = tarDirectory = function(dir) { }); }; -exports.runBuild = function(params, options, getBundleInfo) { - var Promise, dockerBuild, resolver; +exports.runBuild = function(params, options, getBundleInfo, logStreams) { + var Promise, dockerBuild, logging, resolver; Promise = require('bluebird'); dockerBuild = require('resin-docker-build'); resolver = require('resin-bundle-resolve'); + logging = require('../utils/logging'); if (params.source == null) { params.source = '.'; } @@ -121,25 +122,23 @@ exports.runBuild = function(params, options, getBundleInfo) { getBundleInfo(options).then(function(info) { var arch, bundle, deviceType; if (info == null) { - console.log('Warning: No architecture/device type or application information provided.\n Dockerfile/project pre-processing will not be performed.'); + logging.logWarn(logStreams, 'Warning: No architecture/device type or application information provided.\n Dockerfile/project pre-processing will not be performed.'); return tarStream.pipe(stream); } else { arch = info[0], deviceType = info[1]; bundle = new resolver.Bundle(tarStream, deviceType, arch); return resolver.resolveBundle(bundle, resolver.getDefaultResolvers()).then(function(resolved) { - console.log("Building " + resolved.projectType + " project"); + logging.logInfo(logStreams, "Building " + resolved.projectType + " project"); return resolved.tarStream.pipe(stream); }); } })["catch"](reject); - return stream.pipe(process.stdout); + return stream.pipe(logStreams.build); } }; connectOpts = generateConnectOpts(options); - if (process.env.DEBUG != null) { - console.log('Connecting with the following options:'); - console.log(JSON.stringify(connectOpts, null, ' ')); - } + logging.logDebug(logStreams, 'Connecting with the following options:'); + logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' ')); builder = new dockerBuild.Builder(connectOpts); opts = {}; if (options.tag != null) { diff --git a/build/utils/logging.js b/build/utils/logging.js new file mode 100644 index 00000000..a39b49d2 --- /dev/null +++ b/build/utils/logging.js @@ -0,0 +1,50 @@ +// Generated by CoffeeScript 1.12.5 +var eol; + +eol = require('os').EOL; + +exports.getLogStreams = function() { + var StreamLogger, _, colors, logger, streams; + StreamLogger = require('resin-stream-logger').StreamLogger; + colors = require('colors'); + _ = require('lodash'); + logger = new StreamLogger(); + logger.addPrefix('build', colors.blue('[Build]')); + logger.addPrefix('info', colors.cyan('[Info]')); + logger.addPrefix('debug', colors.magenta('[Debug]')); + logger.addPrefix('success', colors.green('[Success]')); + logger.addPrefix('warn', colors.yellow('[Warn]')); + streams = { + build: logger.createLogStream('build'), + info: logger.createLogStream('info'), + debug: logger.createLogStream('debug'), + success: logger.createLogStream('success'), + warn: logger.createLogStream('warn') + }; + _.mapKeys(streams, function(stream, key) { + if (key !== 'debug') { + return stream.pipe(process.stdout); + } else { + if (process.env.DEBUG != null) { + return stream.pipe(process.stdout); + } + } + }); + return streams; +}; + +exports.logInfo = function(logStreams, msg) { + return logStreams.info.write(msg + eol); +}; + +exports.logDebug = function(logStreams, msg) { + return logStreams.debug.write(msg + eol); +}; + +exports.logSuccess = function(logStreams, msg) { + return logStreams.success.write(msg + eol); +}; + +exports.logWarn = function(logStreams, msg) { + return logStreams.warn.write(msg + eol); +}; diff --git a/lib/actions/build.coffee b/lib/actions/build.coffee index 925a2146..edcf8143 100644 --- a/lib/actions/build.coffee +++ b/lib/actions/build.coffee @@ -58,6 +58,7 @@ module.exports = }, ] action: (params, options, done) -> - dockerUtils.runBuild(params, options, getBundleInfo) + logging = require('../utils/logging') + dockerUtils.runBuild(params, options, getBundleInfo, logging.getLogStreams()) .asCallback(done) diff --git a/lib/actions/deploy.coffee b/lib/actions/deploy.coffee index 4d8e259c..0b5745e9 100644 --- a/lib/actions/deploy.coffee +++ b/lib/actions/deploy.coffee @@ -26,18 +26,21 @@ parseInput = Promise.method (params, options) -> return [appName, options.build, source, image] -pushProgress = (imageSize, request, timeout = 250) -> - process.stdout.write('Initialising...') +pushProgress = (imageSize, request, logStreams, timeout = 250) -> + logging = require('../utils/logging') + ansiEscapes = require('ansi-escapes') + + logging.logInfo(logStreams, 'Initialising...') progressReporter = setInterval -> sent = request.req.connection._bytesDispatched percent = (sent / imageSize) * 100 if percent >= 100 clearInterval(progressReporter) percent = 100 + process.stdout.write(ansiEscapes.cursorUp(1)) process.stdout.clearLine() process.stdout.cursorTo(0) - process.stdout.write("Uploaded #{percent.toFixed(1)}%") - console.log() if percent == 100 + logging.logInfo(logStreams, "Uploaded #{percent.toFixed(1)}%") , timeout getBundleInfo = (options) -> @@ -47,7 +50,7 @@ getBundleInfo = (options) -> .then (app) -> [app.arch, app.device_type] -performUpload = (image, token, username, url, size, appName) -> +performUpload = (image, token, username, url, size, appName, logStreams) -> request = require('request') url = url || process.env.RESINRC_RESIN_URL post = request.post @@ -56,22 +59,22 @@ performUpload = (image, token, username, url, size, appName) -> bearer: token body: image - uploadToPromise(post, size) + uploadToPromise(post, size, logStreams) -uploadToPromise = (request, size) -> +uploadToPromise = (request, size, logStreams) -> + logging = require('../utils/logging') new Promise (resolve, reject) -> handleMessage = (data) -> data = data.toString() - if process.env.DEBUG - console.log("Received data: #{data}") + logging.logDebug(logStreams, "Received data: #{data}") obj = JSON.parse(data) if obj.type? switch obj.type when 'error' then reject(new Error("Remote error: #{obj.error}")) when 'success' then resolve(obj.image) - when 'status' then console.log("Remote: #{obj.message}") + when 'status' then logging.logInfo(logStreams, "Remote: #{obj.message}") else reject(new Error("Received unexpected reply from remote: #{data}")) else reject(new Error("Received unexpected reply from remote: #{data}")) @@ -81,7 +84,7 @@ uploadToPromise = (request, size) -> .on('data', handleMessage) # Set up upload reporting - pushProgress(size, request) + pushProgress(size, request, logStreams) module.exports = @@ -120,6 +123,10 @@ module.exports = tmpNameAsync = Promise.promisify(tmp.tmpName) resin = require('resin-sdk-preconfigured') + logging = require('../utils/logging') + + logStreams = logging.getLogStreams() + # Ensure the tmp files gets deleted tmp.setGracefulCleanup() @@ -136,7 +143,7 @@ module.exports = Promise.try -> if build - dockerUtils.runBuild(params, options, getBundleInfo) + dockerUtils.runBuild(params, options, getBundleInfo, logStreams) else imageName .then (imageName) -> @@ -147,10 +154,11 @@ module.exports = resin.settings.get('resinUrl') dockerUtils.getImageSize(docker, imageName) params.appName + logStreams performUpload ) .finally -> require('fs').unlink(tmpPath) .then (imageName) -> - console.log("Successfully deployed image: #{formatImageName(imageName)}") + logging.logSuccess(logStreams, "Successfully deployed image: #{formatImageName(imageName)}") .asCallback(done) diff --git a/lib/utils/docker.coffee b/lib/utils/docker.coffee index ca499634..4bb7147c 100644 --- a/lib/utils/docker.coffee +++ b/lib/utils/docker.coffee @@ -112,10 +112,12 @@ exports.tarDirectory = tarDirectory = (dir) -> # Pass in the command line parameters and options and also # a function which will return the information about the bundle -exports.runBuild = (params, options, getBundleInfo) -> +exports.runBuild = (params, options, getBundleInfo, logStreams) -> + Promise = require('bluebird') dockerBuild = require('resin-docker-build') resolver = require('resin-bundle-resolve') + logging = require('../utils/logging') # The default build context is the current directory params.source ?= '.' @@ -134,7 +136,7 @@ exports.runBuild = (params, options, getBundleInfo) -> getBundleInfo(options) .then (info) -> if !info? - console.log ''' + logging.logWarn logStreams, ''' Warning: No architecture/device type or application information provided. Dockerfile/project pre-processing will not be performed. ''' @@ -145,21 +147,19 @@ exports.runBuild = (params, options, getBundleInfo) -> bundle = new resolver.Bundle(tarStream, deviceType, arch) resolver.resolveBundle(bundle, resolver.getDefaultResolvers()) .then (resolved) -> - console.log("Building #{resolved.projectType} project") + logging.logInfo(logStreams, "Building #{resolved.projectType} project") # Send the resolved tar stream to the docker daemon resolved.tarStream.pipe(stream) .catch(reject) - # And print the output - stream.pipe(process.stdout) + stream.pipe(logStreams.build) # Create a builder connectOpts = generateConnectOpts(options) # Allow degugging output, hidden behind an env var - if process.env.DEBUG? - console.log('Connecting with the following options:') - console.log(JSON.stringify(connectOpts, null, ' ')) + logging.logDebug(logStreams, 'Connecting with the following options:') + logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' ')) builder = new dockerBuild.Builder(connectOpts) opts = {} diff --git a/lib/utils/logging.coffee b/lib/utils/logging.coffee new file mode 100644 index 00000000..5166c2c2 --- /dev/null +++ b/lib/utils/logging.coffee @@ -0,0 +1,40 @@ +eol = require('os').EOL + +exports.getLogStreams = -> + { StreamLogger } = require('resin-stream-logger') + colors = require('colors') + _ = require('lodash') + + logger = new StreamLogger() + logger.addPrefix('build', colors.blue('[Build]')) + logger.addPrefix('info', colors.cyan('[Info]')) + logger.addPrefix('debug', colors.magenta('[Debug]')) + logger.addPrefix('success', colors.green('[Success]')) + logger.addPrefix('warn', colors.yellow('[Warn]')) + + streams = + build: logger.createLogStream('build'), + info: logger.createLogStream('info'), + debug: logger.createLogStream('debug'), + success: logger.createLogStream('success'), + warn: logger.createLogStream('warn') + + _.mapKeys streams, (stream, key) -> + if key isnt 'debug' + stream.pipe(process.stdout) + else + stream.pipe(process.stdout) if process.env.DEBUG? + + streams + +exports.logInfo = (logStreams, msg) -> + logStreams.info.write(msg + eol) + +exports.logDebug = (logStreams, msg) -> + logStreams.debug.write(msg + eol) + +exports.logSuccess = (logStreams, msg) -> + logStreams.success.write(msg + eol) + +exports.logWarn = (logStreams, msg) -> + logStreams.warn.write(msg + eol) diff --git a/package.json b/package.json index cf19449d..38f8ef33 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "gulp-shell": "^0.5.2" }, "dependencies": { + "ansi-escapes": "^2.0.0", "any-promise": "^1.3.0", "babel-preset-es2015": "^6.16.0", "babel-register": "^6.16.3", @@ -58,11 +59,10 @@ "president": "^2.0.1", "prettyjson": "^1.1.3", "raven": "^1.2.0", - "resin-cli-auth": "^1.1.3", "reconfix": "^0.0.3", "request": "^2.81.0", "resin-bundle-resolve": "^0.0.2", - "resin-cli-auth": "^1.0.0", + "resin-cli-auth": "^1.1.3", "resin-cli-errors": "^1.2.0", "resin-cli-form": "^1.4.1", "resin-cli-visuals": "^1.3.0", @@ -73,6 +73,7 @@ "resin-image-fs": "^2.1.2", "resin-image-manager": "^4.1.1", "resin-sdk-preconfigured": "^6.0.0", + "resin-stream-logger": "^0.0.4", "resin-sync": "^7.0.0", "rimraf": "^2.4.3", "rindle": "^1.0.0",