jquery-ui/build/release/release.js

437 lines
11 KiB
JavaScript
Raw Normal View History

2012-07-20 00:49:07 +00:00
#!/usr/bin/env node
2012-09-21 00:21:32 +00:00
var baseDir, repoDir, majorMinorVersion, patchVersion, prevVersion, newVersion,
nextVersion, tagTime,
2012-07-20 00:49:07 +00:00
fs = require( "fs" ),
path = require( "path" ),
// support: node <0.8
existsSync = fs.existsSync || path.existsSync,
2012-07-20 00:49:07 +00:00
rnewline = /\r?\n/,
repo = "git@github.com:jquery/jquery-ui.git",
branch = "master";
walk([
bootstrap,
section( "setting up repo" ),
cloneRepo,
checkState,
section( "calculating versions" ),
getVersions,
confirm,
section( "building release" ),
buildRelease,
section( "pushing tag" ),
confirmReview,
2012-07-20 00:49:07 +00:00
pushRelease,
section( "updating branch version" ),
updateBranchVersion,
section( "pushing " + branch ),
confirmReview,
2012-07-20 00:49:07 +00:00
pushBranch,
section( "generating changelog" ),
generateChangelog,
section( "gathering contributors" ),
gatherContributors,
section( "updating trac" ),
updateTrac,
confirm
// TODO: upload release zip to GitHub
2012-07-20 00:49:07 +00:00
]);
function cloneRepo() {
echo( "Cloning " + repo.cyan + "..." );
2012-07-20 00:49:07 +00:00
git( "clone " + repo + " " + repoDir, "Error cloning repo." );
cd( repoDir );
echo( "Checking out " + branch.cyan + " branch..." );
2012-07-20 00:49:07 +00:00
git( "checkout " + branch, "Error checking out branch." );
echo();
2012-07-20 00:49:07 +00:00
echo( "Installing dependencies..." );
if ( exec( "npm install" ).code !== 0 ) {
abort( "Error installing dependencies." );
}
echo();
2012-07-20 00:49:07 +00:00
}
function checkState() {
echo( "Checking AUTHORS.txt..." );
var result, lastActualAuthor,
lastListedAuthor = cat( "AUTHORS.txt" ).trim().split( rnewline ).pop();
result = exec( "grunt authors", { silent: true });
if ( result.code !== 0 ) {
abort( "Error getting list of authors." );
}
lastActualAuthor = result.output.split( rnewline ).splice( -4, 1 )[ 0 ];
if ( lastListedAuthor !== lastActualAuthor ) {
echo( "Last listed author is " + lastListedAuthor.red + "." );
echo( "Last actual author is " + lastActualAuthor.green + "." );
2012-07-20 00:49:07 +00:00
abort( "Please update AUTHORS.txt." );
}
echo( "Last listed author (" + lastListedAuthor.cyan + ") is correct." );
2012-07-20 00:49:07 +00:00
}
function getVersions() {
// prevVersion, newVersion, nextVersion are defined in the parent scope
var parts, major, minor, patch,
currentVersion = readPackage().version;
echo( "Validating current version..." );
if ( currentVersion.substr( -3, 3 ) !== "pre" ) {
echo( "The current version is " + currentVersion.red + "." );
2012-07-20 00:49:07 +00:00
abort( "The version must be a pre version." );
}
newVersion = currentVersion.substr( 0, currentVersion.length - 3 );
parts = newVersion.split( "." );
major = parseInt( parts[ 0 ], 10 );
minor = parseInt( parts[ 1 ], 10 );
patch = parseInt( parts[ 2 ], 10 );
2012-09-21 00:21:32 +00:00
// TODO: handle 2.0.0
if ( minor === 0 ) {
abort( "This script is not smart enough to handle the 2.0.0 release." );
}
2012-09-21 00:21:32 +00:00
majorMinorVersion = [ major, minor ].join( "." );
patchVersion = patch;
2012-07-20 00:49:07 +00:00
prevVersion = patch === 0 ?
[ major, minor - 1, 0 ].join( "." ) :
[ major, minor, patch - 1 ].join( "." );
// TODO: Remove version hack after 1.9.0 release
if ( prevVersion === "1.8.0" ) {
prevVersion = "1.8";
}
nextVersion = [ major, minor, patch + 1 ].join( "." ) + "pre";
echo( "We are going from " + prevVersion.cyan + " to " + newVersion.cyan + "." );
echo( "After the release, the version will be " + nextVersion.cyan + "." );
2012-07-20 00:49:07 +00:00
}
function buildRelease() {
2012-07-20 00:49:07 +00:00
var pkg;
echo( "Creating " + "release".cyan + " branch..." );
2012-07-20 00:49:07 +00:00
git( "checkout -b release", "Error creating release branch." );
echo();
2012-07-20 00:49:07 +00:00
echo( "Updating package.json..." );
pkg = readPackage();
pkg.version = newVersion;
pkg.author.url = pkg.author.url.replace( "master", newVersion );
2012-07-20 00:49:07 +00:00
pkg.licenses.forEach(function( license ) {
license.url = license.url.replace( "master", newVersion );
});
writePackage( pkg );
echo( "Generating manifest files..." );
if ( exec( "grunt manifest" ).code !== 0 ) {
abort( "Error generating manifest files." );
}
echo();
echo( "Building release..." );
// TODO: Build themes
if ( exec( "grunt release" ).code !== 0 ) {
abort( "Error building release." );
}
echo();
2012-07-20 00:49:07 +00:00
echo( "Committing release artifacts..." );
git( "add *.jquery.json", "Error adding manifest files to git." );
git( "commit -am 'Tagging the " + newVersion + " release.'",
"Error committing release changes." );
echo();
2012-07-20 00:49:07 +00:00
echo( "Tagging release..." );
git( "tag " + newVersion, "Error tagging " + newVersion + "." );
tagTime = git( "log -1 --format='%ad'", "Error getting tag timestamp." ).trim();
}
function pushRelease() {
echo( "Pushing release to GitHub..." );
git( "push --tags", "Error pushing tags to GitHub." );
}
function updateBranchVersion() {
var pkg;
echo( "Checking out " + branch.cyan + " branch..." );
2012-07-20 00:49:07 +00:00
git( "checkout " + branch, "Error checking out " + branch + " branch." );
echo( "Updating package.json..." );
pkg = readPackage();
pkg.version = nextVersion;
writePackage( pkg );
echo( "Committing version update..." );
git( "commit -am 'Updating the " + branch + " version to " + nextVersion + ".'",
"Error committing package.json." );
}
function pushBranch() {
echo( "Pushing " + branch.cyan + " to GitHub..." );
2012-07-20 00:49:07 +00:00
git( "push", "Error pushing to GitHub." );
}
function generateChangelog() {
var commits,
changelogPath = baseDir + "/changelog",
changelog = cat( "build/release/changelog-shell" ) + "\n",
2012-09-21 00:21:32 +00:00
fullFormat = "* %s (TICKETREF, [%h](http://github.com/jquery/jquery-ui/commit/%H))";
changelog = changelog
.replace( "{title}", "jQuery UI " + newVersion + " Changelog" )
.replace( "{summary}", patchVersion === 0 ?
"This is the final release of jQuery UI " + majorMinorVersion + "." :
"This is the " + ordinal( patchVersion ) + " maintenance release for " +
"[jQuery UI " + majorMinorVersion + "](/changelog/" + majorMinorVersion + ")." );
2012-07-20 00:49:07 +00:00
echo ( "Adding commits..." );
commits = gitLog( fullFormat );
echo( "Adding links to tickets..." );
changelog += commits
// Add ticket references
.map(function( commit ) {
var tickets = [];
commit.replace( /Fixe[sd] #(\d+)/g, function( match, ticket ) {
tickets.push( ticket );
});
return tickets.length ?
commit.replace( "TICKETREF", tickets.map(function( ticket ) {
2012-09-21 00:21:32 +00:00
return "[#" + ticket + "](http://bugs.jqueryui.com/ticket/" + ticket + ")";
2012-07-20 00:49:07 +00:00
}).join( ", " ) ) :
// Leave TICKETREF token in place so it's easy to find commits without tickets
commit;
})
// Sort commits so that they're grouped by component
.sort()
.join( "\n" ) + "\n";
echo( "Adding Trac tickets..." );
changelog += trac( "/query?milestone=" + newVersion + "&resolution=fixed" +
"&col=id&col=component&col=summary&order=component" ) + "\n";
fs.writeFileSync( changelogPath, changelog );
echo( "Stored changelog in " + changelogPath.cyan + "." );
2012-07-20 00:49:07 +00:00
}
function gatherContributors() {
var contributors,
contributorsPath = baseDir + "/contributors";
echo( "Adding committers and authors..." );
contributors = gitLog( "%aN%n%cN" );
echo( "Adding reporters and commenters from Trac..." );
contributors = contributors.concat(
trac( "/report/22?V=" + newVersion + "&max=-1" )
.split( rnewline )
// Remove header and trailing newline
2012-07-20 00:49:07 +00:00
.slice( 1, -1 ) );
echo( "Sorting contributors..." );
contributors = unique( contributors ).sort(function( a, b ) {
return a.toLowerCase() < b.toLowerCase() ? -1 : 1;
});
echo ( "Adding people thanked in commits..." );
contributors = contributors.concat(
gitLog( "%b%n%s" ).filter(function( line ) {
return /thank/i.test( line );
}));
fs.writeFileSync( contributorsPath, contributors.join( "\n" ) );
echo( "Stored contributors in " + contributorsPath.cyan + "." );
2012-07-20 00:49:07 +00:00
}
function updateTrac() {
echo( newVersion.cyan + " was tagged at " + tagTime.cyan + "." );
echo( "Close the " + newVersion.cyan + " Milestone with the above date and time." );
echo( "Create the " + newVersion.cyan + " Version with the above date and time." );
2012-07-20 00:49:07 +00:00
echo( "Create a Milestone for the next minor release." );
}
// ===== HELPER FUNCTIONS ======================================================
function git( command, errorMessage ) {
var result = exec( "git " + command );
if ( result.code !== 0 ) {
abort( errorMessage );
}
return result.output;
}
function gitLog( format ) {
var result = exec( "git log " + prevVersion + ".." + newVersion + " " +
"--format='" + format + "'",
{ silent: true });
if ( result.code !== 0 ) {
abort( "Error getting git log." );
}
result = result.output.split( rnewline );
if ( result[ result.length - 1 ] === "" ) {
result.pop();
}
return result;
}
function trac( path ) {
var result = exec( "curl -s 'http://bugs.jqueryui.com" + path + "&format=tab'",
{ silent: true });
if ( result.code !== 0 ) {
abort( "Error getting Trac data." );
}
return result.output;
}
function unique( arr ) {
var obj = {};
arr.forEach(function( item ) {
obj[ item ] = 1;
});
return Object.keys( obj );
}
function readPackage() {
return JSON.parse( fs.readFileSync( repoDir + "/package.json" ) );
}
function writePackage( pkg ) {
fs.writeFileSync( repoDir + "/package.json",
JSON.stringify( pkg, null, "\t" ) + "\n" );
}
2012-09-21 00:21:32 +00:00
function ordinal( number ) {
return number === 1 ? "first" :
number === 2 ? "second" :
number === 3 ? "third" :
number === 4 ? "fourth" :
number === 5 ? "fifth" :
number === 6 ? "sixth" :
number === 7 ? "seventh" :
number === 8 ? "eighth" :
number === 9 ? "ninth" :
number === 10 ? "tenth" :
number === 11 ? "eleventh" :
number === 12 ? "twelfth" :
number === 13 ? "thirteenth" :
number === 14 ? "fourteenth" :
number === 15 ? "fifteenth" :
number === 16 ? "sixteenth" :
number === 17 ? "seventeenth" :
number === 18 ? "eighteenth" :
number === 19 ? "nineteenth" :
number === 20 ? "twentieth" :
"twenty " + ordinal( number - 20 );
}
2012-07-20 00:49:07 +00:00
function bootstrap( fn ) {
console.log( "Determining directories..." );
baseDir = process.cwd() + "/__release";
repoDir = baseDir + "/repo";
if ( existsSync( baseDir ) ) {
console.log( "The directory '" + baseDir + "' already exists." );
console.log( "Aborting." );
process.exit( 1 );
}
console.log( "Creating directory..." );
fs.mkdirSync( baseDir );
console.log( "Installing dependencies..." );
require( "child_process" ).exec( "npm install shelljs colors", {
cwd: baseDir
}, function( error ) {
2012-07-20 00:49:07 +00:00
if ( error ) {
console.log( error );
return process.exit( 1 );
}
require( baseDir + "/node_modules/shelljs/global" );
require( baseDir + "/node_modules/colors" );
2012-07-20 00:49:07 +00:00
fn();
});
}
function section( name ) {
return function() {
echo();
echo( "##" );
echo( "## " + name.toUpperCase().magenta );
echo( "##" );
2012-07-20 00:49:07 +00:00
echo();
};
}
function prompt( fn ) {
process.stdin.once( "data", function( chunk ) {
process.stdin.pause();
fn( chunk.toString().trim() );
});
process.stdin.resume();
}
function confirm( fn ) {
echo( "Press enter to continue, or ctrl+c to cancel.".yellow );
2012-07-20 00:49:07 +00:00
prompt( fn );
}
function confirmReview( fn ) {
echo( "Please review the output and generated files as a sanity check.".yellow );
confirm( fn );
}
2012-07-20 00:49:07 +00:00
function abort( msg ) {
echo( msg.red );
echo( "Aborting.".red );
2012-07-20 00:49:07 +00:00
exit( 1 );
}
function walk( methods ) {
var method = methods.shift();
function next( error ) {
if ( methods.length ) {
walk( methods );
}
}
if ( !method.length ) {
method();
next();
} else {
method( next );
}
}