refactor(audit-license-headers): clean up code (#263)

* refactor(audit-license-headers): single -e flag for RAT excludes

Previously the arguments passed to RAT would look like this:

    -d dir -e foo -e bar -e baz

With this change it will look like this:

    -d dir -e foo bar baz

The resulting exclude list is identical. But it might keep us from
hitting maximum number of arguments limitations.

* refactor(audit-license-headers): factor out .ratignore reading

* refactor(audit-license-headers): factor out RAT downloading

* refactor(audit-license-headers): use const & let
diff --git a/src/audit-license-headers.js b/src/audit-license-headers.js
index e2ac2a2..c2084dd 100644
--- a/src/audit-license-headers.js
+++ b/src/audit-license-headers.js
@@ -17,21 +17,20 @@
 under the License.
 */
 
-var fs = require('fs');
-var path = require('path');
-var chalk = require('chalk');
-var optimist = require('optimist');
-var executil = require('./executil');
-var flagutil = require('./flagutil');
-var repoutil = require('./repoutil');
+const fs = require('fs');
+const path = require('path');
 const { promisify } = require('util');
 const pipeline = promisify(require('stream').pipeline);
-const zlib = require('zlib');
+const { Gunzip } = require('zlib');
 const got = require('got');
 const tar = require('tar-fs');
+const chalk = require('chalk');
+const optimist = require('optimist');
+const executil = require('./executil');
+const flagutil = require('./flagutil');
+const repoutil = require('./repoutil');
 
-// constants
-var COMMON_RAT_EXCLUDES = [
+const COMMON_RAT_EXCLUDES = [
     // Binary Files
     '*.wav',
     '*.webloc',
@@ -53,90 +52,69 @@
     'thirdparty'
 ];
 
-var RAT_IGNORE_PATH = '.ratignore';
-var RATIGNORE_COMMENT_PREFIX = '#';
-
-var RAT_NAME = 'apache-rat-0.12';
-var RAT_URL = 'https://archive.apache.org/dist/creadur/apache-rat-0.12/apache-rat-0.12-bin.tar.gz';
-
-function startsWith (string, prefix) {
-    return string.indexOf(prefix) === 0;
-}
-
-function isComment (pattern) {
-    return startsWith(pattern.trim(), RATIGNORE_COMMENT_PREFIX);
-}
-
 module.exports = function * () {
-
-    var opt = flagutil.registerRepoFlag(optimist);
+    let opt = flagutil.registerRepoFlag(optimist);
     opt = flagutil.registerHelpFlag(opt);
 
     opt.usage('Uses Apache RAT to audit source files for license headers.\n' +
               '\n' +
               'Usage: $0 audit-license-headers --repo=name [-r repos]');
-    let argv = opt.argv;
+    const argv = opt.argv;
 
     if (argv.h) {
         optimist.showHelp();
         process.exit(1);
     }
 
-    var repos = flagutil.computeReposFromFlag(argv.r, { includeModules: true });
+    const repos = flagutil.computeReposFromFlag(argv.r, { includeModules: true });
     yield module.exports.scrubRepos(repos);
 };
 
 module.exports.scrubRepos = function * (repos, silent, ignoreError, win, fail) {
-    // Check that RAT command exists.
-    var ratPath;
-    yield repoutil.forEachRepo([repoutil.getRepoById('coho')], function * () {
-        ratPath = path.join(process.cwd(), RAT_NAME, RAT_NAME + '.jar');
-    });
-
-    if (!fs.existsSync(ratPath)) {
-        console.log('RAT tool not found, downloading to: ' + ratPath);
-        yield repoutil.forEachRepo([repoutil.getRepoById('coho')], function * () {
-            yield pipeline(
-                got.stream(RAT_URL),
-                zlib.createGunzip(),
-                tar.extract('.')
-            ).catch(error => {
-                error.message = 'Failed to get RAT binary:\n' + error.message;
-                throw error;
-            });
-        });
-    }
+    const ratPath = yield getRatJar();
 
     console.log(chalk.red('Note: ignore filters reside in a repo\'s .ratignore and in COMMON_RAT_EXCLUDES in audit-license-headers.js.'));
 
-    // NOTE:
-    //      the CWD in a callback is the directory for its respective repo
     yield repoutil.forEachRepo(repos, function * (repo) {
-        var excludePatterns = COMMON_RAT_EXCLUDES;
-
-        // read in exclude patterns from repo's .ratignore, one pattern per line
-        if (fs.existsSync(RAT_IGNORE_PATH)) {
-
-            var ratignoreFile = fs.readFileSync(RAT_IGNORE_PATH);
-            var ratignoreLines = ratignoreFile.toString().trim().split('\n');
-
-            // add only non-empty and non-comment lines
-            ratignoreLines.forEach(function (line) {
-                if (line.length > 0 && !isComment(line)) {
-                    excludePatterns.push(line);
-                }
-            });
-        }
-
-        // add flags for excludes
-        var excludeFlags = [];
-        excludePatterns.forEach(function (pattern) {
-            excludeFlags.push('-e', pattern);
-        });
+        // NOTE: the CWD in a callback is the directory for its respective repo
+        const ratignorePatterns = readRatignorePatterns(process.cwd());
+        const excludePatterns = COMMON_RAT_EXCLUDES.concat(ratignorePatterns);
 
         // run Rat
-        yield executil.execHelper(executil.ARGS('java -jar', ratPath, '-d', '.').concat(excludeFlags), silent, ignoreError, function (stdout) {
+        const args = executil.ARGS('java -jar', ratPath, '-d', '.', '-e').concat(excludePatterns);
+        yield executil.execHelper(args, silent, ignoreError, function (stdout) {
             if (win) win(repo, stdout);
         });
     });
 };
+
+// Returns path to Apache RAT JAR; downloads it first if necessary
+async function getRatJar () {
+    const RAT_ID = 'apache-rat-0.12';
+    const RAT_URL = `https://archive.apache.org/dist/creadur/${RAT_ID}/${RAT_ID}-bin.tar.gz`;
+
+    const cohoRoot = repoutil.getRepoDir(repoutil.getRepoById('coho'));
+    const ratJarPath = path.join(cohoRoot, RAT_ID, RAT_ID + '.jar');
+
+    if (!fs.existsSync(ratJarPath)) {
+        console.log('RAT tool not found, downloading to: ' + ratJarPath);
+        await pipeline(
+            got.stream(RAT_URL), new Gunzip(), tar.extract(cohoRoot)
+        ).catch(err => {
+            throw new Error('Failed to get RAT JAR:\n' + err.message);
+        });
+    }
+    return ratJarPath;
+}
+
+// Read in exclude patterns from .ratignore at dir
+function readRatignorePatterns (dir) {
+    const ratignorePath = path.join(dir, '.ratignore');
+    if (!fs.existsSync(ratignorePath)) return [];
+
+    // return only non-empty and non-comment lines
+    return fs.readFileSync(ratignorePath, 'utf-8')
+        .split('\n')
+        .map(line => line.trim())
+        .filter(line => line.length > 0 && !line.startsWith('#'));
+}