From 1ec1737d24a1a37e39ca4c24b0fdb7efd877e628 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Sat, 14 Dec 2019 01:49:55 +0100 Subject: [PATCH] test/style: refactor remaining grunt tasks to use promises instead of callbacks --- tasks/metrics.js | 37 +++++----- tasks/publish.js | 170 ++++++++++++++++++++++------------------------ tasks/util/git.js | 155 ++++++++++++++++-------------------------- tasks/version.js | 108 ++++++++++++++--------------- 4 files changed, 211 insertions(+), 259 deletions(-) diff --git a/tasks/metrics.js b/tasks/metrics.js index bcb76318f..acc3cece4 100644 --- a/tasks/metrics.js +++ b/tasks/metrics.js @@ -1,26 +1,29 @@ -const _ = require('underscore'), - async = require('neo-async'), - metrics = require('../bench'); +const metrics = require('../bench'); +const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task'); module.exports = function(grunt) { - grunt.registerTask('metrics', function() { - const done = this.async(), - execName = grunt.option('name'), - events = {}; + const registerAsyncTask = createRegisterAsyncTaskFn(grunt); - async.each( - _.keys(metrics), - function(name, complete) { - if (/^_/.test(name) || (execName && name !== execName)) { - return complete(); - } + registerAsyncTask('metrics', function() { + const onlyExecuteName = grunt.option('name'); + const events = {}; + const promises = Object.keys(metrics).map(async name => { + if (/^_/.test(name)) { + return; + } + if (onlyExecuteName != null && name !== onlyExecuteName) { + return; + } + + return new Promise(resolve => { metrics[name](grunt, function(data) { events[name] = data; - complete(); + resolve(); }); - }, - done - ); + }); + }); + + return Promise.all(promises); }); }; diff --git a/tasks/publish.js b/tasks/publish.js index 207e281fe..54c99f5ca 100644 --- a/tasks/publish.js +++ b/tasks/publish.js @@ -1,54 +1,38 @@ -const _ = require('underscore'), - async = require('neo-async'), - AWS = require('aws-sdk'), - git = require('./util/git'), - semver = require('semver'); +const AWS = require('aws-sdk'); +const git = require('./util/git'); +const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task'); +const semver = require('semver'); module.exports = function(grunt) { - grunt.registerTask('publish:latest', function() { - const done = this.async(); - - git.debug(function(remotes, branches) { - grunt.log.writeln('remotes: ' + remotes); - grunt.log.writeln('branches: ' + branches); - - git.commitInfo(function(err, info) { - grunt.log.writeln('tag: ' + info.tagName); - - const files = []; - - // Publish the master as "latest" and with the commit-id - if (info.isMaster) { - files.push('-latest'); - files.push('-' + info.head); - } - - // Publish tags by their tag-name - if (info.tagName && semver.valid(info.tagName)) { - files.push('-' + info.tagName); - } - - if (files.length > 0) { - initSDK(); - grunt.log.writeln('publishing files: ' + JSON.stringify(files)); - publish(fileMap(files), done); - } else { - // Silently ignore for branches - done(); - } - }); - }); - }); - grunt.registerTask('publish:version', function() { - const done = this.async(); - initSDK(); + const registerAsyncTask = createRegisterAsyncTaskFn(grunt); - git.commitInfo(function(err, info) { - if (!info.tagName) { - throw new Error('The current commit must be tagged'); - } - publish(fileMap(['-' + info.tagName]), done); - }); + registerAsyncTask('publish:latest', async () => { + grunt.log.writeln('remotes: ' + (await git.remotes())); + grunt.log.writeln('branches: ' + (await git.branches())); + + const commitInfo = await git.commitInfo(); + grunt.log.writeln('tag: ' + commitInfo.tagName); + + const suffixes = []; + + // Publish the master as "latest" and with the commit-id + if (commitInfo.isMaster) { + suffixes.push('-latest'); + suffixes.push('-' + commitInfo.headSha); + } + + // Publish tags by their tag-name + if (commitInfo.tagName && semver.valid(commitInfo.tagName)) { + suffixes.push('-' + commitInfo.tagName); + } + + if (suffixes.length > 0) { + initSDK(); + grunt.log.writeln( + 'publishing file-suffixes: ' + JSON.stringify(suffixes) + ); + await publish(suffixes); + } }); function initSDK() { @@ -62,45 +46,57 @@ module.exports = function(grunt) { AWS.config.update({ accessKeyId: key, secretAccessKey: secret }); } - function publish(files, callback) { - const s3 = new AWS.S3(), - bucket = process.env.S3_BUCKET_NAME; - - async.each( - _.keys(files), - function(file, callback) { - const params = { - Bucket: bucket, - Key: file, - Body: grunt.file.read(files[file]) - }; - s3.putObject(params, function(err) { - if (err) { - throw err; - } else { - grunt.log.writeln('Published ' + file + ' to build server.'); - callback(); - } - }); - }, - callback - ); + + async function publish(suffixes) { + const publishPromises = suffixes.map(suffix => publishSuffix(suffix)); + return Promise.all(publishPromises); } - function fileMap(suffixes) { - const map = {}; - _.each( - [ - 'handlebars.js', - 'handlebars.min.js', - 'handlebars.runtime.js', - 'handlebars.runtime.min.js' - ], - function(file) { - _.each(suffixes, function(suffix) { - map[file.replace(/\.js$/, suffix + '.js')] = 'dist/' + file; - }); - } - ); - return map; + + async function publishSuffix(suffix) { + const filenames = [ + 'handlebars.js', + 'handlebars.min.js', + 'handlebars.runtime.js', + 'handlebars.runtime.min.js' + ]; + const publishPromises = filenames.map(filename => { + const nameInBucket = getNameInBucket(filename, suffix); + const localFile = getLocalFile(filename); + uploadToBucket(localFile, nameInBucket); + grunt.log.writeln( + `Published ${localFile} to build server (${nameInBucket})` + ); + }); + return Promise.all(publishPromises); + } + + async function uploadToBucket(localFile, nameInBucket) { + const bucket = process.env.S3_BUCKET_NAME; + const uploadParams = { + Bucket: bucket, + Key: nameInBucket, + Body: grunt.file.read(localFile) + }; + return s3PutObject(uploadParams); } }; + +function s3PutObject(uploadParams) { + const s3 = new AWS.S3(); + return new Promise((resolve, reject) => { + s3.putObject(uploadParams, err => { + if (err != null) { + return reject(err); + } + resolve(); + }); + }); +} + +function getNameInBucket(filename, suffix) { + return filename.replace(/\.js$/, suffix + '.js'); +} + +function getLocalFile(filename) { + return 'dist/' + filename; +} diff --git a/tasks/util/git.js b/tasks/util/git.js index 4dc5cbde0..a86fcbb6e 100644 --- a/tasks/util/git.js +++ b/tasks/util/git.js @@ -1,115 +1,74 @@ const childProcess = require('child_process'); module.exports = { - debug: function(callback) { - childProcess.exec('git remote -v', {}, function(err, remotes) { - if (err) { - throw new Error('git.remote: ' + err.message); - } - - childProcess.exec('git branch -a', {}, function(err, branches) { - if (err) { - throw new Error('git.branch: ' + err.message); - } - - callback(remotes, branches); - }); - }); + async remotes() { + return git('remotes', '-v'); }, - clean: function(callback) { - childProcess.exec('git diff-index --name-only HEAD --', {}, function( - err, - stdout - ) { - callback(undefined, !err && !stdout); - }); + async branches() { + return git('branch', '-a'); }, - - commitInfo: function(callback) { - module.exports.head(function(err, headSha) { - module.exports.master(function(err, masterSha) { - module.exports.tagName(function(err, tagName) { - callback(undefined, { - head: headSha, - master: masterSha, - tagName: tagName, - isMaster: headSha === masterSha - }); - }); - }); - }); + async clean() { + const stdout = git('diff-index', '--name-only', 'HEAD', '--'); + return stdout === ''; }, - - head: function(callback) { - childProcess.exec('git rev-parse --short HEAD', {}, function(err, stdout) { - if (err) { - throw new Error('git.head: ' + err.message); - } - - callback(undefined, stdout.trim()); - }); + async commitInfo() { + const headSha = await this.headSha(); + const masterSha = await this.masterSha(); + return { + headSha, + masterSha, + tagName: await this.tagName(), + isMaster: headSha === masterSha + }; }, - master: function(callback) { - childProcess.exec('git rev-parse --short origin/master', {}, function( - err, - stdout - ) { - // This will error if master was not checked out but in this case we know we are not master - // so we can ignore. - if (err && !/Needed a single revision/.test(err.message)) { - throw new Error('git.master: ' + err.message); - } - - callback(undefined, stdout.trim()); - }); + async headSha() { + const stdout = await git(' rev-parse', '--short', 'HEAD'); + return stdout.trim(); }, - - add: function(path, callback) { - childProcess.exec('git add -f ' + path, {}, function(err) { - if (err) { - throw new Error('git.add: ' + err.message); + async masterSha() { + try { + const stdout = await git('rev-parse', '--short', 'origin/master'); + return stdout.trim(); + } catch (error) { + if (/Needed a single revision/.test(error.message)) { + // Master was not checked out but in this case, so we know we are not master. We can ignore this + return ''; } - - callback(); - }); + throw error; + } }, - commit: function(name, callback) { - childProcess.exec('git commit --message=' + name, {}, function(err) { - if (err) { - throw new Error('git.commit: ' + err.message); - } - callback(); - }); + async add(path) { + return git('add', '-f', path); }, - tag: function(name, callback) { - childProcess.exec('git tag -a --message=' + name + ' ' + name, {}, function( - err - ) { - if (err) { - throw new Error('git.tag: ' + err.message); - } - - callback(); - }); + async commit(message) { + return git('commit', '--message', message); }, - tagName: function(callback) { - childProcess.exec('git describe --tags', {}, function(err, stdout) { - if (err) { - throw new Error('git.tagName: ' + err.message); - } - - let tags = stdout.trim().split(/\n/); - tags = tags.filter(function(info) { - info = info.split('-'); - return info.length == 1; - }); + async tag(name) { + return git('tag', '-a', `--message=${name}`, name); + }, + async tagName() { + const stdout = await git('tag', '-l', '--points-at', 'HEAD'); - const versionTags = tags.filter(function(info) { - return /^v/.test(info[0]); - }); + const tags = stdout.trim().split(/\n|\r\n/); + const versionTags = tags.filter(tag => /^v/.test(tag)); - callback(undefined, versionTags[0] || tags[0]); - }); + if (versionTags[0] != null) { + return versionTags; + } + return tags[0]; } }; + +async function git(...args) { + return new Promise((resolve, reject) => + childProcess.execFile('git', args, (err, stdout) => { + if (err != null) { + return reject( + new Error(`"git ${args.join(' ')}" caused error: ${err.message}`) + ); + } + resolve(stdout); + }) + ); +} diff --git a/tasks/version.js b/tasks/version.js index f9d5c5675..ba89d1fd7 100644 --- a/tasks/version.js +++ b/tasks/version.js @@ -1,68 +1,62 @@ -const async = require('neo-async'), - git = require('./util/git'), - semver = require('semver'); +const git = require('./util/git'); +const semver = require('semver'); +const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task'); module.exports = function(grunt) { - grunt.registerTask( - 'version', - 'Updates the current release version', - function() { - const done = this.async(), - pkg = grunt.config('pkg'), - version = grunt.option('ver'); + const registerAsyncTask = createRegisterAsyncTaskFn(grunt); - if (!semver.valid(version)) { - throw new Error( - 'Must provide a version number (Ex: --ver=1.0.0):\n\t' + - version + - '\n\n' - ); - } - - pkg.version = version; - grunt.config('pkg', pkg); - - grunt.log.writeln('Updating to version ' + version); - - async.each( - [ - [ - 'lib/handlebars/base.js', - /const VERSION = ['"](.*)['"];/, - "const VERSION = '" + version + "';" - ], - [ - 'components/bower.json', - /"version":.*/, - '"version": "' + version + '",' - ], - [ - 'components/package.json', - /"version":.*/, - '"version": "' + version + '",' - ], - [ - 'components/handlebars.js.nuspec', - /.*<\/version>/, - '' + version + '' - ] - ], - function(args, callback) { - replace.apply(undefined, args); - grunt.log.writeln(' - ' + args[0]); - git.add(args[0], callback); - }, - function() { - grunt.task.run(['default']); - done(); - } + registerAsyncTask('version', async () => { + const pkg = grunt.config('pkg'); + const version = grunt.option('ver'); + if (!semver.valid(version)) { + throw new Error( + 'Must provide a version number (Ex: --ver=1.0.0):\n\t' + + version + + '\n\n' ); } - ); + pkg.version = version; + grunt.config('pkg', pkg); + + const replaceSpec = [ + { + path: 'lib/handlebars/base.js', + regex: /const VERSION = ['"](.*)['"];/, + replacement: `const VERSION = '${version}';` + }, + { + path: 'components/bower.json', + regex: /"version":.*/, + replacement: `"version": "${version}",` + }, + { + path: 'components/package.json', + regex: /"version":.*/, + replacement: `"version": "${version}",` + }, + { + path: 'components/handlebars.js.nuspec', + regex: /.*<\/version>/, + replacement: `${version}` + } + ]; + + await Promise.all( + replaceSpec.map(replaceSpec => + replaceAndAdd( + replaceSpec.path, + replaceSpec.regex, + replaceSpec.replacement + ) + ) + ); + grunt.task.run(['default']); + }); - function replace(path, regex, value) { + async function replaceAndAdd(path, regex, value) { let content = grunt.file.read(path); content = content.replace(regex, value); grunt.file.write(path, content); + await git.add(path); } };