var fs = require('graceful-fs'); var path = require('path'); var Q = require('q'); var semver = require('semver'); var mout = require('mout'); var rimraf = require('rimraf'); var mkdirp = require('mkdirp'); var chalk = require('chalk'); var cmd = require('../lib/util/cmd'); var packages = require('./packages.json'); var nopt = require('nopt'); var options = nopt({ 'force': Boolean }, { 'f': '--force' }); var env = { 'GIT_AUTHOR_DATE': 'Sun Apr 7 22:13:13 2013 +0000', 'GIT_AUTHOR_NAME': 'André Cruz', 'GIT_AUTHOR_EMAIL': 'amdfcruz@gmail.com', 'GIT_COMMITTER_DATE': 'Sun Apr 7 22:13:13 2013 +0000', 'GIT_COMMITTER_NAME': 'André Cruz', 'GIT_COMMITTER_EMAIL': 'amdfcruz@gmail.com' }; // Preserve the original environment mout.object.mixIn(env, process.env); function ensurePackage(dir) { var promise; // If force is specified, delete folder if (options.force) { promise = Q.nfcall(rimraf, dir) .then(function () { throw new Error(); }); // Otherwise check if .git is already created } else { promise = Q.nfcall(fs.stat, path.join(dir, '.git')); } // Only create if stat failed return promise.fail(function () { // Create dir return Q.nfcall(mkdirp, dir) // Init git repo .then(cmd.bind(null, 'git', ['init'], { cwd: dir })) // Create dummy file .then(function () { return Q.nfcall(fs.writeFile, path.join(dir, '.master'), 'based on master'); }) // Stage files .then(cmd.bind(null, 'git', ['add', '-A'], { cwd: dir })) // Commit // Note that we force a specific date and author so that the same // commit-sha's are always equal // These commit-sha's are used internally in tests! .then(function () { return cmd('git', ['commit', '-m"Initial commit."'], { cwd: dir, env: env }); }) .then(function () { return dir; }); }); } function checkRelease(dir, release) { if (semver.valid(release)) { return cmd('git', ['tag', '-l'], { cwd: dir }) .spread(function (stdout) { return stdout.split(/\s*\r*\n\s*/).some(function (tag) { return semver.clean(tag) === release; }); }); } return cmd('git', ['branch', '--list'], { cwd: dir }) .spread(function (stdout) { return stdout.split(/\s*\r*\n\s*/).some(function (branch) { branch = branch.trim().replace(/^\*?\s*/, ''); return branch === release; }); }); } function createRelease(dir, release, files) { var branch = semver.valid(release) ? 'branch-' + release : release; // Checkout master return cmd('git', ['checkout', 'master', '-f'], { cwd: dir }) // Attempt to delete branch, ignoring the error .then(function () { return cmd('git', ['branch', '-D', branch], { cwd: dir }) .fail(function () {}); }) // Checkout based on master .then(cmd.bind(null, 'git', ['checkout', '-b', branch, 'master'], { cwd: dir })) // Create files .then(function () { var promise; var promises = []; mout.object.forOwn(files, function (contents, name) { name = path.join(dir, name); // Convert contents to JSON if they are not a string if (typeof contents !== 'string') { contents = JSON.stringify(contents, null, ' '); } promise = Q.nfcall(mkdirp, path.dirname(name)) .then(function () { return Q.nfcall(fs.writeFile, name, contents); }); promises.push(promise); }); // Delete dummy .master file that is present on the master branch promise = Q.nfcall(fs.unlink, path.join(dir, '.master')); promises.push(promise); return Q.all(promises); }) // Stage files .then(cmd.bind(null, 'git', ['add', '-A'], { cwd: dir })) // Commit // Note that we force a specific date and author so that the same // commit-sha's are always equal // These commit-sha's are used internally in tests! .then(function () { return cmd('git', ['commit', '-m"Commit for ' + branch + '."'], { cwd: dir, env: env }); }) // Tag .then(function () { if (!semver.valid(release)) { return; } return cmd('git', ['tag', '-f', release], { cwd: dir }) // Delete branch (not necessary anymore) .then(cmd.bind(null, 'git', ['checkout', 'master', '-f'], { cwd: dir })) .then(cmd.bind(null, 'git', ['branch', '-D', branch], { cwd: dir })); }); } var promises = []; // Process packages.json mout.object.forOwn(packages, function (pkg, name) { var promise; var dir = path.join(__dirname, 'assets', name); // Ensure package is created promise = ensurePackage(dir); promise = promise.fail(function (err) { console.log('Failed to create ' + name); console.log(err.message); }); mout.object.forOwn(pkg, function (files, release) { // Check if the release already exists promise = promise.then(checkRelease.bind(null, dir, release)) .then(function (exists) { // Skip it if already created if (exists) { return console.log(chalk.cyan('> ') + 'Package ' + name + '#' + release + ' already created'); } // Create it based on the metadata return createRelease(dir, release, files) .then(function () { console.log(chalk.green('> ') + 'Package ' + name + '#' + release + ' successfully created'); }); }) .fail(function (err) { console.log(chalk.red('> ') + 'Failed to create ' + name + '#' + release); console.log(err.message.trim()); if (err.details) { console.log(err.details.trim()); } console.log(err.stack); }); }); promises.push(promise); }); Q.allSettled(promises, function (results) { results.forEach(function (result) { if (result.state !== 'fulfilled') { process.exit(1); } }); });