Build: New release script.

This commit is contained in:
Scott González 2012-07-19 20:49:07 -04:00
parent 6888cec62a
commit f8bdd6e192
2 changed files with 381 additions and 336 deletions

View File

@ -1,336 +0,0 @@
#!/bin/sh
base_dir="`pwd`/jquery-ui-release"
repo_dir="$base_dir/jquery-ui"
release_dir="$repo_dir/build/release"
github_repo="git@github.com:jquery/jquery-ui.git"
remote_cmd="ssh jqadmin@ui-dev.jquery.com /srv/dev.jqueryui.com/prepare-release"
#
# Setup environment
#
echo
echo "--------------------------"
echo "| SETTING UP ENVIRONMENT |"
echo "--------------------------"
echo
mkdir $base_dir
cd $base_dir
echo "Cloning repo from $github_repo..."
git clone $github_repo
cd $repo_dir
echo
echo "Environment setup complete."
echo
#
# Figure out which versions we're dealing with
#
echo
echo "------------------------"
echo "| CALCULATING VERSIONS |"
echo "------------------------"
echo
# NOTE: this will be different for minor and major releases
version=`$remote_cmd/get-latest-version`
major_minor=${version%.*}
point=${version##*.}
version_new="${major_minor}.$(($point + 1))"
version_next=`cat version.txt`
echo "We are going from $version to $version_new."
echo "version.txt will be set to $version_next when complete."
echo "Press enter to continue, or ctrl+c to cancel."
read
#
# Generate shell for changelog
#
echo
echo "------------------------"
echo "| GENERATING CHANGELOG |"
echo "------------------------"
echo
echo "Creating shell for changelog..."
changelog_url="http:\/\/docs.jquery.com\/action\/edit\/UI\/Changelog\/$version_new"
`sed "s/CHANGELOG_URL/$changelog_url/" <$release_dir/changelog-shell >$base_dir/changelog`
# find all commits
echo "Adding commits to changelog..."
format_ticket='[http://dev.jqueryui.com/ticket/XXXX #XXXX]'
format_commit='[http://github.com/jquery/jquery-ui/commit/%H %h]'
format_full="* %s ($format_ticket, $format_commit)"
git whatchanged $version... --pretty=format:"$format_full" \
-- ui themes demos build \
| sed '/^:/ d' \
| sed '/^$/ d' \
| sed 's/\(Fixe[sd] #\)\([0-9][0-9]*\)\(.*\)\(XXXX #XXXX\)/Fixed #\2\3\2 #\2/' \
| LC_ALL='C' sort -f \
>> $base_dir/changelog
# find all fixed tickets
echo "Adding Trac tickets to changelog..."
$remote_cmd/generate-changelog >> $base_dir/changelog
echo
echo "Changelog complete."
echo
#
# Generate list of contributors
#
echo
echo "--------------------------"
echo "| GATHERING CONTRIBUTORS |"
echo "--------------------------"
echo
# find all committers and authors
echo "Adding commiters and authors..."
format_contributors='%aN%n%cN'
git whatchanged $version... --pretty=format:"$format_contributors" \
| sed '/^:/ d' \
| sed '/^$/ d' \
> $base_dir/thankyou
# find all reporters and commenters from Trac
echo "Adding reporters and commenters from Trac..."
$remote_cmd/generate-contributors >> $base_dir/thankyou
# sort names
echo "Sorting contributors..."
LC_ALL='C' sort -f $base_dir/thankyou | uniq > $base_dir/_thankyou
mv $base_dir/_thankyou $base_dir/thankyou
# find all people that were thanked
echo "Adding people thanked in commits..."
git whatchanged $version... \
| grep -i thank \
>> $base_dir/thankyou
echo
echo "Find contributors from duplicates of fixed tickets and add them to:"
echo "$base_dir/thankyou"
echo "Press enter when done."
read
echo
echo "Contributors list complete."
echo
#
# Update version
#
echo
echo "--------------------"
echo "| UPDATING VERSION |"
echo "--------------------"
echo
echo "Updating version.txt to $version_new..."
echo $version_new > version.txt
git commit -a -m "Tagging the $version_new release."
version_new_time=`git log -1 --pretty=format:"%ad"`
echo "Committed version.txt at $version_new_time..."
echo "Tagging $version_new..."
git tag $version_new
echo "Updating version.txt to $version_next..."
echo $version_next > version.txt
git commit -a -m "Updating the master version to $version_next"
echo "Committed version.txt..."
echo
echo "Version update complete."
echo
#
# Push to GitHub
#
echo
echo "---------------------"
echo "| PUSHING TO GITHUB |"
echo "---------------------"
echo
echo "Please review the output and generated files as a sanity check."
echo "Press enter to continue or ctrl+c to abort."
read
git push
git push --tags
echo
echo "Push to GitHub complete."
echo
#
# Update Trac
#
echo
echo "-----------------"
echo "| UPDATING TRAC |"
echo "-----------------"
echo
# TODO: automate this
# NOTE: this will be different for minor and major releases
milestone=`$remote_cmd/get-latest-milestone`
# Create new milestrone and version
echo "$version_new was tagged at $version_new_time."
echo "Create and close the $version_new Milestone with the above date and time."
echo "Create the $version_new Version with the above date and time."
echo "Press enter when done."
read
# Update milestone for all fixed tickets
echo "Change all $milestone fixed tickets to $version_new."
echo "Press enter when done."
read
echo
echo "Trac updates complete."
echo
#
# Build jQuery UI
#
echo
echo "----------------------"
echo "| BUILDING JQUERY UI |"
echo "----------------------"
echo
# check out the tagged version
echo "Checking out $version_new..."
git checkout $version_new
cd build
# Update the link to the docs (never contains the patch version)
echo "Updating URL for API docs..."
sed "s/UI\/API\/\${release\.version}/UI\/API\/$major_minor/" build.xml >build.xml.tmp
mv build.xml.tmp build.xml
# Run the build
echo "Running build..."
ant
echo
echo "Build complete."
echo
#
# Upload zip to Google Code
#
echo
echo "----------------------"
echo "| UPDATE GOOGLE CODE |"
echo "----------------------"
echo
echo "Upload zip to Google Code."
echo " http://code.google.com/p/jquery-ui/downloads/entry"
echo " Summary: jQuery UI $version_new (Source, demos, docs, themes, tests) STABLE"
echo " Labels: Featured, Type-Source, OpSys-All"
echo "Modify the previous release to no longer say STABLE at the end."
echo "Remove the featured label from the previous release."
echo "Press enter when done."
read
echo
echo "Google Code update complete."
echo
#
# Update SVN
#
echo
echo "----------------"
echo "| UPDATING SVN |"
echo "----------------"
echo
cd $base_dir
mkdir svn
cd svn
echo "Checking out SVN tags..."
svn co --depth immediates https://jquery-ui.googlecode.com/svn/tags
cd tags
echo "Unzipping build into tags/$version_new..."
unzip $repo_dir/build/dist/jquery-ui-$version_new.zip
mv jquery-ui-$version_new $version_new
echo "Adding files to SVN..."
svn add $version_new
echo "Setting svn:mime-type..."
find $version_new -name \*.js -exec svn propset svn:mime-type text/javascript {} \;
find $version_new -name \*.css -exec svn propset svn:mime-type text/css {} \;
find $version_new -name \*.html -exec svn propset svn:mime-type text/html {} \;
find $version_new -name \*.png -exec svn propset svn:mime-type image/png {} \;
find $version_new -name \*.gif -exec svn propset svn:mime-type image/gif {} \;
# TODO: commit
echo
echo "svn commit with the following message:"
echo "Created $version_new tag from http://jquery-ui.googlecode.com/files/jquery-ui-$version_new.zip"
echo "Press enter when done."
read
echo
echo "SVN update complete."
echo
#
# Generate themes
#
# ruby -e 'puts File.read("thankyou").split("\n").join(", ")' > thankyou2

381
build/release/release.js Normal file
View File

@ -0,0 +1,381 @@
#!/usr/bin/env node
var baseDir, repoDir, prevVersion, newVersion, nextVersion, tagTime,
fs = require( "fs" ),
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( "tagging release" ),
tagRelease,
confirm,
pushRelease,
section( "updating branch version" ),
updateBranchVersion,
confirm,
pushBranch,
section( "generating changelog" ),
generateChangelog,
section( "gathering contributors" ),
gatherContributors,
section( "updating trac" ),
updateTrac,
confirm,
section( "building release" ),
buildRelease
]);
function cloneRepo() {
if ( test( "-d", baseDir ) ) {
abort( "The directory '" + baseDir + "' already exists." );
}
echo( "Cloning " + repo + "..." );
git( "clone " + repo + " " + repoDir, "Error cloning repo." );
cd( repoDir );
echo( "Checking out " + branch + " branch..." );
git( "checkout " + branch, "Error checking out branch." );
echo( "Installing dependencies..." );
if ( exec( "npm install" ).code !== 0 ) {
abort( "Error installing dependencies." );
}
}
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 + "." );
echo( "Last actual author is " + lastActualAuthor + "." );
abort( "Please update AUTHORS.txt." );
}
echo( "Last listed author (" + lastListedAuthor + ") is correct." );
}
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 + "." );
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 );
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 + " to " + newVersion + "." );
echo( "After the release, the version will be " + nextVersion + "." );
}
function tagRelease() {
var pkg;
echo( "Creating release branch..." );
git( "checkout -b release", "Error creating release branch." );
echo( "Updating package.json..." );
pkg = readPackage();
pkg.version = newVersion;
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( "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( "Tagging release..." );
git( "tag " + newVersion, "Error tagging " + newVersion + "." );
tagTime = git( "log -1 --format='%ad'", "Error getting tag timestamp." ).trim();
echo();
echo( "Please review the output and generated files as a sanity check." );
}
function pushRelease() {
echo( "Pushing release to GitHub..." );
git( "push --tags", "Error pushing tags to GitHub." );
}
function updateBranchVersion() {
var pkg;
echo( "Checking out " + branch + " branch..." );
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." );
echo();
echo( "Please review the output and generated files as a sanity check." );
}
function pushBranch() {
echo( "Pushing " + branch + " to GitHub..." );
git( "push", "Error pushing to GitHub." );
}
function generateChangelog() {
var commits,
changelogPath = baseDir + "/changelog",
changelog = cat( "build/release/changelog-shell" ) + "\n",
fullFormat = "* %s (TICKETREF, [http://github.com/jquery/jquery-ui/commit/%H %h])";
echo ( "Adding commits..." );
commits = gitLog( fullFormat );
echo( "Adding links to tickets..." );
changelog += commits
// Add ticket references
.map(function( commit ) {
var tickets = [];
// TODO: Don't use .replace() since we're not actually replacing
commit.replace( /Fixe[sd] #(\d+)/g, function( match, ticket ) {
tickets.push( ticket );
});
return tickets.length ?
commit.replace( "TICKETREF", tickets.map(function( ticket ) {
return "[http://bugs.jqueryui.com/ticket/" + ticket + " #" + ticket + "]";
}).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 + "." );
}
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 )
.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 + "." );
}
function updateTrac() {
echo( newVersion + " was tagged at " + tagTime + "." );
echo( "Close the " + newVersion + " Milestone with the above date and time." );
echo( "Create the " + newVersion + " Version with the above date and time." );
echo( "Create a Milestone for the next minor release." );
}
function buildRelease() {
echo( "Checking out " + newVersion + "..." );
git( "checkout " + newVersion, "Error checking out " + newVersion + "." );
echo( "Building release..." );
if ( exec( "grunt release" ).code !== 0 ) {
abort( "Error building 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" );
}
function bootstrap( fn ) {
require( "child_process" ).exec( "npm root -g", function( error, stdout ) {
if ( error ) {
console.log( error );
return process.exit( 1 );
}
var rootDir = stdout.trim();
require( rootDir + "/shelljs/global" );
baseDir = pwd() + "/__release";
repoDir = baseDir + "/repo";
fn();
});
}
function section( name ) {
var line = new Array( name.length + 5 ).join( "-" );
return function() {
echo();
// https://github.com/arturadib/shelljs/issues/20
console.log( line );
echo( "| " + name.toUpperCase() + " |" );
console.log( line );
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." );
prompt( fn );
}
function abort( msg ) {
echo( msg );
echo( "Aborting." );
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 );
}
}