var fs = require('../lib/util/fs'); var path = require('path'); var Q = require('q'); var semver = require('semver'); var mout = require('mout'); var rimraf = require('../lib/util/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); } }); });