From f8bdd6e1927c0a08a7397ab57247c3d99cf6f1ed Mon Sep 17 00:00:00 2001 From: =?utf8?q?Scott=20Gonz=C3=A1lez?= Date: Thu, 19 Jul 2012 20:49:07 -0400 Subject: [PATCH] Build: New release script. --- build/release/prepare-release | 336 ------------------------------ build/release/release.js | 381 ++++++++++++++++++++++++++++++++++ 2 files changed, 381 insertions(+), 336 deletions(-) delete mode 100755 build/release/prepare-release create mode 100644 build/release/release.js diff --git a/build/release/prepare-release b/build/release/prepare-release deleted file mode 100755 index 91665e1eb..000000000 --- a/build/release/prepare-release +++ /dev/null @@ -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 diff --git a/build/release/release.js b/build/release/release.js new file mode 100644 index 000000000..e86ebef71 --- /dev/null +++ b/build/release/release.js @@ -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 ); + } +} -- 2.39.5