UNPKG

faf-tool

Version:
714 lines (598 loc) 25.2 kB
var path = require('path'), fs = require('fs'); module.exports = function(grunt) { var settings, async = require("async"), cwd = grunt.option("cwd") || ".", ftwd = grunt.option("ftwd"); require('logfile-grunt')(grunt, { filePath: './faf-tool.log' }); require('load-grunt-tasks')(grunt); !grunt.option("no-time") && require('time-grunt')(grunt); grunt.file.setBase(cwd); grunt.log.writeln("faf tool started") try { settings = grunt.file.readJSON('settings.json'); } catch (e) { writeHelp(); grunt.file.copy(ftwd + "/settings.json.example", cwd + "/settings.json.example"); grunt.fatal(e.message + "\r\nUse settings.json.example to create settings.json"); } var username = grunt.option("username") || settings["username"]; var password = grunt.option("password") || settings["password"]; var Modules = function(settings) { if (Array.isArray(settings.modules)) { var modules = {}; settings.modules.forEach(function(module) { modules[module] = {}; }); this.modules = modules; } else { this.modules = settings.modules; } }; Modules.prototype.getList = function() { return Object.keys(this.modules); }; Modules.prototype.forEach = function(fn) { this.getList().forEach(fn); }; Modules.prototype.contain = function(module) { return this.modules.hasOwnProperty(module); }; Modules.prototype.getCeOverlayVersion = function() { if (this.modules["jrs-ui"]["feature-name"]) { return this.modules["jrs-ui"]["feature-name"] + "-SNAPSHOT"; } if (settings["feature-name"]) { return settings["feature-name"] + "-SNAPSHOT"; } }; Modules.prototype.getProOverlayVersion = function() { if (this.modules["jrs-ui-pro"]["feature-name"]) { return this.modules["jrs-ui-pro"]["feature-name"] + "-SNAPSHOT"; } if (settings["feature-name"]) { return settings["feature-name"] + "-SNAPSHOT"; } }; Modules.prototype.getSourcePath = function(module) { return this.modules[module]["source-repo-path"] || settings["faf-source-repo-path"] || "trunk"; }; var modules = new Modules(settings); function findLocalDeploymentPath(modules){ var deployment = modules.slice(), jrsUiPro = deployment.indexOf('jrs-ui-pro'); if (jrsUiPro !== -1){ //push 'jrs-ui-pro' on top, it's next after faf-tool root folder to //search for '.workspaces' jrsUiPro = deployment.splice(jrsUiPro, 1); deployment = jrsUiPro.concat(deployment); } deployment = [''].concat(deployment) .map(function(module){ return path.join(cwd, module, '.workspace') }) .filter(function (module){ return fs.existsSync(module); }, '') .map(function(file){ return grunt.file.readJSON(file).server }) .filter(function(server){ return server; }); //use first settings from the stack, //by default it's root of your project deployment = deployment.length > 0 ? deployment[0] : ''; if (!fs.existsSync(deployment)){ grunt.log.write('Deployment target doesn\'t exist: '+ deployment); } return deployment; } function srcPathes(modules){ if (!modules.length) return []; return modules.map(function(module){ return [ path.join(module, '/src/**'), "!" + path.join(module, 'src/bower_components/**') ] }).reduce(function(memo,pair){ return memo.concat(pair); }); } function themesPathes(modules){ if (!modules.length) return []; return modules.map(function(module){ return [ path.join(module, '/themes/**') ] }).reduce(function(memo,pair){ return memo.concat(pair); }); } grunt.initConfig({ deployment: findLocalDeploymentPath(modules.getList()), clean: modules.getList().concat(["jasperserver", "jasperserver-pro"]), run: { mock: { cmd: "" } }, watch: { scripts: { files: srcPathes(modules.getList()), tasks: ['copy'], options: { nospawn: true } }, themes: { files: themesPathes(modules.getList().concat([ "jasperserver/jasperserver-war/src/main/webapp", "jasperserver-pro/jasperserver-war/src/main/webapp" ])), tasks: ['ftp_push'], options: { nospawn: true } } }, ftp_push: { themes: { options: { username: 'designer', password: 'designer', host: 'localhost', dest: '/themes', port: 2121 }, files: [{ expand: true, cwd: 'themes', src: [ 'default/samples.css' ] }] } } }); // Public tasks // This task only for buildomatic usage grunt.registerTask('create-feature', 'Create new feature branches and setup it.', [ "create-branches", "checkout-settings-files", "resolve-deps", "update-overlay-versions", "checkin-settings" ]); // This task for developers grunt.registerTask('setup', 'Checkout feature branches and setup FAF.', [ "checkout-full", "init" ]); grunt.registerTask('update-init', 'Update all feature branches and setup FAF.', [ "update-all", "init" ]); grunt.registerTask('downmerge', 'Downmerge project from trunk', function() { executeAsyncTaskForAllModules.call(this, svnUpModuleAndDownmerge, "Downmerge module: ", true); }); grunt.registerTask('removecl', 'Remove all modules from downmerge changelists', function() { executeAsyncTaskForAllModules.call(this, svnRemoveFromChangelist, "Remove all svn changelists from: ", true); }); grunt.registerTask('default', 'Default task.', function() { writeHelp(); }); grunt.event.on('watch', function(action, filepath) { var log = grunt.log.writeln, contains = function (container, chunk) { return container.indexOf(path.sep + chunk + path.sep) !== -1; }; if (contains(filepath, 'src') && !contains(filepath, 'webapp')) { //js and html templates assets here var deploy = grunt.config.get(['deployment']), copyConfig = { copy: { main: { expand: false, cwd: '' } } }; if (!deploy) { grunt.log.error('Can\'t find deployment path. Did you create .workspace ?'); } else { dest = path.join(deploy, "scripts/"); } if (!contains(filepath, 'jrs-ui-pro')) { dest = path.join(dest, 'bower_components/'); } else { var cwdSrc = 'jrs-ui-pro/src'; copyConfig.copy.main.cwd = path.join(cwd, cwdSrc); copyConfig.copy.main.expand = true; filepath = path.relative(cwdSrc, filepath); } copyConfig.copy.main.src = [filepath]; copyConfig.copy.main.dest = dest; grunt.config.merge(copyConfig); log('Coping file to : ', path.join(dest, filepath)); } else if (contains(filepath, 'themes')) { //themes assets here var cwdThemes = path.join(cwd, filepath.split('themes')[0], 'themes'); filepath = path.relative(cwdThemes, filepath); var ftpConfig = [{ expand: true, cwd: cwdThemes, src: [ filepath ] }]; grunt.config(['ftp_push', 'themes', 'files'], ftpConfig); } else if(contains(filepath,'i18n')) { //TODO: i18n bundles here } }); // Private tasks grunt.registerTask('create-branches', 'Creates svn branches for modules.', function() { executeAsyncTaskForAllModules.call(this, createBranch, "Create svn branch for: ", true, modules.getList()); }); grunt.registerTask('checkout-settings-files', 'Checkout bower.json and package.json for modules for updating it.', function() { executeAsyncTaskForAllModules.call(this, checkoutSettingsFiles, "Checkout required files for: ", true); }); grunt.registerTask('resolve-deps', 'Resolve bower dependencies.', function() { modules.forEach(function(module) { var bowerConfPath = module + "/bower.json", branchName = getBranchName(); grunt.verbose.subhead("Resolve bower dependencies for " + module + ": "); if (grunt.option("dry-run")) { return; } var bowerConfig = grunt.file.readJSON(bowerConfPath); bowerConfig.resolutions = bowerConfig.resolutions || {}; for (var depName in bowerConfig.dependencies) { if (!bowerConfig.dependencies.hasOwnProperty(depName)) continue; if (modules.contain(depName)) { bowerConfig.dependencies[depName] = bowerConfig.dependencies[depName].replace(/#(.+)$/, "#" + branchName); bowerConfig.resolutions[depName] = branchName; grunt.verbose.writeln(depName + "#" + branchName); } } grunt.file.write(bowerConfPath, JSON.stringify(bowerConfig, null, " ")); }); }); grunt.registerTask('update-overlay-versions', 'Update overlay versions in jrs-ui, jrs-ui-pro and in JRS poms.', function() { var fileContent, filePath, ceOverlayVersion, proOverlayVersion; grunt.verbose.subhead("Update overlay versions"); if (grunt.option("dry-run")) { return; } if (modules.contain("jrs-ui")) { grunt.verbose.writeln("Update jrs-ui overlay version"); fileContent = grunt.file.readJSON("jrs-ui/package.json"); //ceOverlayVersion = settings["feature-name"] + "-SNAPSHOT"; fileContent.overlayVersion = ceOverlayVersion = modules.getCeOverlayVersion(); grunt.file.write("jrs-ui/package.json", JSON.stringify(fileContent, null, " ")); if (settings["jasperserver-branch"] || settings["jasperserver-ci-path"]) { grunt.verbose.writeln("Update jrs-ui overlay version in jasperserver"); filePath = (settings["jasperserver-ci-path"] || "jasperserver") + "/jasperserver-war/pom.xml"; fileContent = grunt.file.read(filePath); // this is jasperserver/jasperserver-war/pom.xml file! fileContent = fileContent.replace(/(jrs-ui<\/artifactId>\s+<version>)[^<]+(<\/version>)/, "$1" + ceOverlayVersion + "$2"); grunt.file.write(filePath, fileContent); } } if (modules.contain("jrs-ui-pro")) { grunt.verbose.writeln("Update jrs-ui-pro overlay version"); fileContent = grunt.file.readJSON("jrs-ui-pro/package.json"); fileContent.overlayVersion = proOverlayVersion = modules.getProOverlayVersion(); grunt.file.write("jrs-ui-pro/package.json", JSON.stringify(fileContent, null, " ")); if (settings["jasperserver-pro-branch"] || settings["jasperserver-pro-ci-path"]) { grunt.verbose.writeln("Update jrs-ui-pro overlay version in jasperserver-pro"); filePath = (settings["jasperserver-pro-ci-path"] || "jasperserver-pro") + "/jasperserver-war/pom.xml"; fileContent = grunt.file.read(filePath); // this is jasperserver-pro/jasperserver-war/pom.xml file! fileContent = fileContent.replace(/(jrs-ui-pro<\/artifactId>\s+<version>)[^<]+(<\/version>)/, "$1" + proOverlayVersion + "$2"); grunt.file.write(filePath, fileContent); } } }); grunt.registerTask('checkin-settings', 'Checking in updated settings files to repos.', function() { executeAsyncTaskForAllModules.call(this, checkinSettings, "Checking in updated settings files for: ", true, modules.getList()); }); grunt.registerTask('init', 'Setup FAF. Install npm modules, init grunt.', [ "load-init-settings", "run-wait" ]); grunt.registerTask('checkout-full', 'Checkout full selected repos', function() { executeAsyncTaskForAllModules.call(this, checkoutFull, "Checkout module: ", true); }); grunt.registerTask('switch', 'Switch repositories to another branch', function() { executeAsyncTaskForAllModules.call(this, switchRepo, "Switch repository: ", true); }); grunt.registerTask('update-all', 'Update all selected repos', function() { executeAsyncTaskForAllModules.call(this, svnUpModule, "Update module: ", true); }); grunt.registerTask('cleanup', 'Run "svn cleanup" for each module', function() { executeAsyncTaskForAllModules.call(this, function(module, callback) { execSvn([ "cleanup", module ], callback) }, "Cleanup module: ", true); }); grunt.registerTask('run-wait', 'run-wait', function() { modules.forEach(function(task) { grunt.task.run("run:" + task); }); if (grunt.option("parallel") !== false) { //only wait for tasks to complete in parallel build modules.forEach(function(task) { grunt.task.run("wait:" + task); }); } }); grunt.registerTask('load-init-settings', 'Load settings and create config for initialization commands.', function(){ grunt.log.writeln("Load settings and create config for initialization commands."); var run_config = { options: { wait: grunt.option("parallel") === false, quiet: false, failOnError: false } }; modules.forEach(function(module) { run_config[module] = { exec: "npm install && npm prune && grunt init --config.storage.packages=./.cache/bower/packages", options: { cwd: "./" + module } }; }); grunt.log.ok("Settings loaded"); if (grunt.option("dry-run")) { grunt.log.writeln(JSON.stringify(run_config, null, 2)); } else { grunt.config.set("run", run_config); } }); function executeAsyncTaskForAllModules(taskFunc, logMessagePrefix, allowParallel, currentModules) { var tasks = [], done = this.async(); if (currentModules) { //Execute only for specified modules currentModules.forEach(function(module) { grunt.log.writeln(logMessagePrefix + module); tasks.push(async.apply(taskFunc, module)); }); } else { //Execute for all modules modules.forEach(function(module) { grunt.log.writeln(logMessagePrefix + module); tasks.push(async.apply(taskFunc, module)); }); if (settings["jasperserver-branch"]) { grunt.log.writeln(logMessagePrefix + "jasperserver"); tasks.push(async.apply(taskFunc, "jasperserver")); } if (settings["jasperserver-pro-branch"]) { grunt.log.writeln(logMessagePrefix + "jasperserver-pro"); tasks.push(async.apply(taskFunc, "jasperserver-pro")); } } if (grunt.option("dry-run")) { done(); } else if (grunt.option("parallel") === false || !allowParallel) { async.series(tasks, done); } else { async.parallel(tasks, done); } } function getTargetUrl(module) { if (getBranchName() === "trunk") { return getTrunkBranchPath(module); } if (module === "jasperserver") { return getRepoPath(module, "branches/" + settings["jasperserver-branch"]); } if (module === "jasperserver-pro") { return getRepoPath(module, "branches/" + settings["jasperserver-pro-branch"]); } return getRepoPath(module, "branches/" + getBranchName()); } function getTrunkBranchPath(module) { return getRepoPath(module, "trunk"); } function getSourceUrl(module) { return getRepoPath(module, modules.getSourcePath(module)); } function getRepoPath(module, path) { var defaultSchema = "https:/", domain = settings["svn-server"], repoPath = [domain, module, path]; if(domain.indexOf(':/') == -1){ repoPath = [defaultSchema].concat(repoPath); } return repoPath.join("/"); } function getBranchName() { if (settings["feature-name"] === "trunk") { return "trunk"; } else { return (settings["release-cycle"] ? settings["release-cycle"] + "-" : "") + settings["feature-name"]; } } function createBranch(module, callback) { var args = [ "copy", getSourceUrl(module), getTargetUrl(module), "-m", "Created a feature branch from Jenkins with name: " + getBranchName() ]; username && args.push("--username=" + username); password && args.push("--password=" + password); execSvn(args, callback); } function checkoutFull(module, callback) { execSvn([ "checkout", getTargetUrl(module), module ], callback); } function switchRepo(module, callback) { execSvn([ "switch", getTargetUrl(module), module ], callback); } function downmergeModule(module, callback) { execSvn([ "merge", getRepoPath(module, "trunk"), module, "--accept=" + (grunt.option["accept"] || "postpone") ], callback); } function svnUpModule(module, callback) { execSvn([ "up", module ], callback); } function svnAddToChangelist(module, changelist, callback) { grunt.log.writeln("Adding module [" + module + "] to a changelist [" + changelist + "]"); execSvn([ "changelist", "--recursive", changelist, module ], callback) } function svnRemoveFromChangelist(module, callback) { execSvn([ "changelist", "--remove", "--changelist", module, //name of changelist == name of the module "--depth", "infinity", module ], callback) } function svnUpModuleAndDownmerge(module, callback) { svnUpModule(module, function() { downmergeModule(module, function() { if (grunt.option("separate-changelist") === "false") { callback.apply(this, arguments); } else { svnAddToChangelist(module, module, callback); } }) }); } function checkoutSettingsFiles(module, callback) { if (module === "jasperserver-pro") { checkoutSettingsFilesJrsPro(module, callback); } else if (module === "jasperserver") { checkoutSettingsFilesJrs(module, callback); } else { checkoutSettingsFilesFAF(module, callback); } } function checkoutSettingsFilesFAF(module, callback) { execSvn([ "checkout", getTargetUrl(module), module, "--depth", "files" ], callback); } function checkoutSettingsFilesJrs(module, callback) { execSvn([ "checkout", getRepoPath("jasperserver", "branches/" + settings["jasperserver-branch"]), "jasperserver", "--depth", "immediates" ], function() { execSvn([ "up", "jasperserver/jasperserver-war", "--set-depth", "files" ], callback); }); } function checkoutSettingsFilesJrsPro(module, callback) { execSvn([ "checkout", getRepoPath("jasperserver-pro", "branches/" + settings["jasperserver-pro-branch"]), "jasperserver-pro", "--depth", "immediates" ], function() { execSvn([ "up", "jasperserver-pro/jasperserver-war", "--set-depth", "files" ], callback); }); } function checkinSettings(module, callback) { var args = [ "commit", module, "-m", ["jasperserver", "jasperserver-pro"].indexOf(module) !== -1 ? "Resolved bower dependencies and updated overlay version" : "Updated overlay version" ]; username && args.push("--username=" + username); password && args.push("--password=" + password); execSvn(args, callback); } function execSvn(args, callback) { grunt.util.spawn({ cmd: "svn", args: args }, function(error, result, code) { if (error) grunt.log.error(error); grunt.log.writeln("Finished: ", args[0], args[1]); grunt.verbose.writeln(result); callback(error, result); }); } function tab(n) { return new Array((n || 1) * 4).join(" "); } function writeHelp() { grunt.log.writeln("Full description: https://github.com/Jaspersoft/faf-tool"); grunt.log.writeln("Usage:"); grunt.log.writeln(tab() + "1. Rename settings.json.example in current folder to settings.json."); grunt.log.writeln(tab() + "2. Update settings.json with your requirements."); grunt.log.writeln(tab() + "3. run task \"faf-tool <task>\""); grunt.log.writeln(tab(2) + "\"create-feature\" - do new feature routine work:"); grunt.log.writeln(tab(3) + "creates faf branches"); grunt.log.writeln(tab(3) + "resolve bower dependencies and commit it"); grunt.log.writeln(tab(3) + "update overlay versions in JRS"); grunt.log.writeln(tab(2) + "\"setup\":"); grunt.log.writeln(tab(3) + "checkout FAF modules and JRS"); grunt.log.writeln(tab(3) + "install node modules, initialize node modules and grunt for each module, specified in settings.json"); grunt.log.writeln(tab(2) + "\"init\":"); grunt.log.writeln(tab(3) + "install node modules, initialize node modules and grunt for each module, specified in settings.json"); grunt.log.writeln(tab(2) + "\"update-init\":"); grunt.log.writeln(tab(3) + "update all modules from svn, install node modules,"); grunt.log.writeln(tab(3) + "initialize node modules and grunt for each module, specified in settings.json"); grunt.log.writeln(tab(2) + "\"checkout-full\":"); grunt.log.writeln(tab(3) + "checkout FAF modules and JRS"); grunt.log.writeln(tab(2) + "\"downmerge\":"); grunt.log.writeln(tab(3) + "runs svn merge from trunk command for each FAF module,"); grunt.log.writeln(tab(3) + "specified in settings.json and JRS if \"jasperserver-branch\" option specified"); grunt.log.writeln(tab(3) + "accepts one argument \"--accept=postpone\". Default \"postpone\"."); grunt.log.writeln(tab(2) + "\"removecl\":"); grunt.log.writeln(tab(3) + "removes all changelists which was created during downmerge task"); grunt.log.writeln(tab(2) + "\"cleanup\":"); grunt.log.writeln(tab(3) + "run svn cleanup command for each module"); grunt.log.writeln(); } };