]> source.dussan.org Git - jquery.git/commitdiff
Release script: jquery-release integration
authorTimmy Willison <timmywillisn@gmail.com>
Fri, 20 Dec 2013 22:13:48 +0000 (17:13 -0500)
committerTimmy Willison <timmywillisn@gmail.com>
Fri, 20 Dec 2013 22:29:35 +0000 (17:29 -0500)
Conflicts:
build/release.js

build/release.js
package.json

index be9f425cf4f791041bbfe460f37f75e380e3b319..8624fbbd39f1857e01336d50fe95265b0107c86c 100644 (file)
-#!/usr/bin/env node
-/*
- * jQuery Core Release Management
- */
-
-// Debugging variables
-var    debug = false,
-       skipRemote = false;
-
-var fs = require("fs"),
-       child = require("child_process"),
-       path = require("path"),
-       archiver = require("archiver");
-
-var releaseVersion,
-       nextVersion,
-       isBeta,
-       pkg,
-       branch,
-
-       scpURL = "jqadmin@code.origin.jquery.com:/var/www/html/code.jquery.com/",
-       cdnURL = "http://code.origin.jquery.com/",
-       repoURL = "git@github.com:jquery/jquery.git",
-
-       // Windows needs the .cmd version but will find the non-.cmd
-       // On Windows, ensure the HOME environment variable is set
-       gruntCmd = process.platform === "win32" ? "grunt.cmd" : "grunt",
-
-       devFile = "dist/jquery.js",
-       minFile = "dist/jquery.min.js",
-       mapFile = "dist/jquery.min.map",
-
-       releaseFiles = {
-               "jquery-VER.js": devFile,
-               "jquery-VER.min.js": minFile,
-               "jquery-VER.min.map": mapFile,
-               "jquery.js": devFile,
-               "jquery.min.js": minFile,
-               "jquery.min.map": mapFile,
-               "jquery-latest.js": devFile,
-               "jquery-latest.min.js": minFile,
-               "jquery-latest.min.map": mapFile
-       },
-
-       jQueryFilesCDN = [],
-
-       googleFilesCDN = [
-               "jquery.js", "jquery.min.js", "jquery.min.map"
-       ],
-
-       msFilesCDN = [
-               "jquery-VER.js", "jquery-VER.min.js", "jquery-VER.min.map"
-       ];
-
-
-steps(
-       initialize,
-       checkGitStatus,
-       setReleaseVersion,
-       gruntBuild,
-       makeReleaseCopies,
-       copyTojQueryCDN,
-       buildGoogleCDN,
-       buildMicrosoftCDN,
-       createTag,
-       setNextVersion,
-       pushToGithub,
-       // publishToNpm,
-       exit
-);
-
-function initialize( next ) {
-
-       if ( process.argv[2] === "-d" ) {
-               process.argv.shift();
-               debug = true;
-               console.warn("=== DEBUG MODE ===" );
+module.exports = function( Release ) {
+
+       var
+               fs = require( "fs" ),
+               shell = require( "shelljs" ),
+
+               // Windows needs the .cmd version but will find the non-.cmd
+               // On Windows, ensure the HOME environment variable is set
+               gruntCmd = process.platform === "win32" ? "grunt.cmd" : "grunt",
+
+               devFile = "dist/jquery.js",
+               minFile = "dist/jquery.min.js",
+               mapFile = "dist/jquery.min.map",
+
+               cdnFolder = "dist/cdn",
+
+               releaseFiles = {
+                       "jquery-VER.js": devFile,
+                       "jquery-VER.min.js": minFile,
+                       "jquery-VER.min.map": mapFile,
+                       "jquery.js": devFile,
+                       "jquery.min.js": minFile,
+                       "jquery.min.map": mapFile,
+                       "jquery-latest.js": devFile,
+                       "jquery-latest.min.js": minFile,
+                       "jquery-latest.min.map": mapFile
+               },
+
+               googleFilesCDN = [
+                       "jquery.js", "jquery.min.js", "jquery.min.map"
+               ],
+
+               msFilesCDN = [
+                       "jquery-VER.js", "jquery-VER.min.js", "jquery-VER.min.map"
+               ],
+
+               _complete = Release.complete;
+
+       /**
+        * Generates copies for the CDNs
+        */
+       function makeReleaseCopies() {
+               shell.mkdir( "-p", cdnFolder );
+
+               Object.keys( releaseFiles ).forEach(function( key ) {
+                       var text,
+                               builtFile = releaseFiles[ key ],
+                               unpathedFile = key.replace( /VER/g, Release.newVersion ),
+                               releaseFile = cdnFolder + "/" + unpathedFile;
+
+                       // Beta releases don't update the jquery-latest etc. copies
+                       if ( !Release.preRelease || key.indexOf( "VER" ) >= 0 ) {
+
+                               if ( /\.map$/.test( releaseFile ) ) {
+                                       // Map files need to reference the new uncompressed name;
+                                       // assume that all files reside in the same directory.
+                                       // "file":"jquery.min.js","sources":["jquery.js"]
+                                       text = fs.readFileSync( builtFile, "utf8" )
+                                               .replace( /"file":"([^"]+)","sources":\["([^"]+)"\]/,
+                                                       "\"file\":\"" + unpathedFile.replace( /\.min\.map/, ".min.js" ) +
+                                                       "\",\"sources\":[\"" + unpathedFile.replace( /\.min\.map/, ".js" ) + "\"]" );
+                                       fs.writeFileSync( releaseFile, text );
+                               } else if ( /\.min\.js$/.test( releaseFile ) ) {
+                                       // Remove the source map comment; it causes way too many problems.
+                                       // Keep the map file in case DevTools allow manual association.
+                                       text = fs.readFileSync( builtFile, "utf8" )
+                                               .replace( /\/\/# sourceMappingURL=\S+/, "" );
+                                       fs.writeFileSync( releaseFile, text );
+                               } else if ( builtFile !== releaseFile ) {
+                                       shell.cp( "-f", builtFile, releaseFile );
+                               }
+                       }
+               });
        }
 
-       // First arg should be the version number being released
-       var newver, oldver,
-               exists = fs.existsSync || path.existsSync,
-               rsemver = /^(\d+)\.(\d+)\.(\d+)(?:-([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?$/,
-               version = ( process.argv[3] || "" ).toLowerCase().match( rsemver ) || {},
-               major = version[1],
-               minor = version[2],
-               patch = version[3],
-               xbeta = version[4];
-
-       branch = process.argv[2];
-       releaseVersion = process.argv[3];
-       isBeta = !!xbeta;
-
-       if ( !branch || !major || !minor || !patch ) {
-               die( "Usage: " + process.argv[1] + " branch releaseVersion" );
-       }
-       if ( xbeta === "pre" ) {
-               die( "Cannot release a 'pre' version!" );
-       }
-       if ( !exists( "package.json" ) ) {
-               die( "No package.json in this directory" );
+       function buildGoogleCDN( next ) {
+               makeArchive( "googlecdn", googleFilesCDN, next );
        }
-       pkg = readJSON( "package.json" );
 
-       console.log( "Current version is " + pkg.version + "; generating release " + releaseVersion );
-       version = pkg.version.match( rsemver );
-       oldver = ( +version[1] ) * 10000 + ( +version[2] * 100 ) + ( +version[3] );
-       newver = ( +major ) * 10000 + ( +minor * 100 ) + ( +patch );
-       if ( newver < oldver ) {
-               die( "Next version is older than current version!" );
+       function buildMicrosoftCDN( next ) {
+               makeArchive( "mscdn", msFilesCDN, next );
        }
 
-       nextVersion = major + "." + minor + "." + ( isBeta ? patch : +patch + 1 ) + "-pre";
-       next();
-}
-
-function checkGitStatus( next ) {
-       git( [ "status" ], function( error, stdout ) {
-               var onBranch = ((stdout||"").match( /On branch (\S+)/ ) || [])[1];
-               if ( onBranch !== branch ) {
-                       dieIfReal( "Branches don't match: Wanted " + branch + ", got " + onBranch );
-               }
-               if ( /Changes to be committed/i.test( stdout ) ) {
-                       dieIfReal( "Please commit changed files before attempting to push a release." );
+       function makeArchive( cdn, files, next ) {
+               if ( Release.preRelease ) {
+                       console.log( "Skipping archive creation for " + cdn + "; this is a beta release." );
+                       return;
                }
-               if ( /Changes not staged for commit/i.test( stdout ) ) {
-                       dieIfReal( "Please stash files before attempting to push a release." );
-               }
-               next();
-       });
-}
-
-function setReleaseVersion( next ) {
-       updateVersion( releaseVersion );
-       git( [ "commit", "-a", "-m", "Updating version to " + releaseVersion + "." ], next, debug );
-}
-
-function gruntBuild( next ) {
-       // First clean the dist directory of anything we're not about to rebuild
-       git( [ "clean", "-dfx", "dist/" ], function() {
-               exec( gruntCmd, [], function( error, stdout, stderr ) {
-                       if ( error ) {
-                               die( error + stderr );
-                       }
-                       console.log( stdout );
-                       next();
-               }, false );
-       }, debug );
-}
-
-function makeReleaseCopies( next ) {
-       Object.keys( releaseFiles ).forEach(function( key ) {
-               var text,
-                       builtFile = releaseFiles[ key ],
-                       unpathedFile = key.replace( /VER/g, releaseVersion ),
-                       releaseFile = "dist/" + unpathedFile;
 
-               // Beta releases don't update the jquery-latest etc. copies
-               if ( !isBeta || key.indexOf( "VER" ) >= 0 ) {
+               console.log( "Creating production archive for " + cdn );
 
-                       if ( /\.map$/.test( releaseFile ) ) {
-                               // Map files need to reference the new uncompressed name;
-                               // assume that all files reside in the same directory.
-                               // "file":"jquery.min.js","sources":["jquery.js"]
-                               text = fs.readFileSync( builtFile, "utf8" )
-                                       .replace( /"file":"([^"]+)","sources":\["([^"]+)"\]/,
-                                               "\"file\":\"" + unpathedFile.replace( /\.min\.map/, ".min.js" ) +
-                                               "\",\"sources\":[\"" + unpathedFile.replace( /\.min\.map/, ".js" ) + "\"]" );
-                               fs.writeFileSync( releaseFile, text );
-                       } else if ( /\.min\.js$/.test( releaseFile ) ) {
-                               // Remove the source map comment; it causes way too many problems.
-                               // Keep the map file in case DevTools allow manual association.
-                               text = fs.readFileSync( builtFile, "utf8" )
-                                       .replace( /\/\/# sourceMappingURL=\S+/, "" );
-                               fs.writeFileSync( releaseFile, text );
-                       } else if ( builtFile !== releaseFile ) {
-                               copy( builtFile, releaseFile );
-                       }
+               var archiver = require( "archiver" ),
+                       md5file = cdnFolder + "/" + cdn + "-md5.txt",
+                       output = fs.createWriteStream( cdnFolder + "/" + cdn + "-jquery-" + Release.newVersion + ".zip" );
 
-                       jQueryFilesCDN.push( releaseFile );
-               }
-       });
-       next();
-}
-
-function copyTojQueryCDN( next ) {
-       var cmds = [];
-
-       jQueryFilesCDN.forEach(function( name ) {
-               cmds.push(function( nxt ){
-                       exec( "scp", [ name, scpURL ], nxt, debug || skipRemote );
+               output.on( "error", function( err ) {
+                       throw err;
                });
-               cmds.push(function( nxt ){
-                       exec( "curl", [ cdnURL + name + "?reload" ], nxt, debug || skipRemote );
-               });
-       });
-       cmds.push( next );
 
-       steps.apply( this, cmds );
-}
+               output.on( "close", next );
+               archiver.pipe( output );
 
-function buildGoogleCDN( next ) {
-       makeArchive( "googlecdn", googleFilesCDN, next );
-}
-
-function buildMicrosoftCDN( next ) {
-       makeArchive( "mscdn", msFilesCDN, next );
-}
-
-function createTag( next ) {
-       steps(
-               checkoutCommit,
-               commitDistFiles,
-               tagRelease,
-               checkoutBranch,
-               next
-       );
-}
-
-function setNextVersion( next ) {
-       updateVersion( nextVersion );
-       git( [ "commit", "-a", "-m", "Updating the source version to " + nextVersion + "✓™" ], next, debug );
-}
-
-function pushToGithub( next ) {
-       git( [ "push", "--tags", repoURL, branch ], next, debug || skipRemote );
-}
-
-/* Utilities
----------------------------------------------------------------------- */
-function steps() {
-       var cur = 0,
-               st = arguments;
-       (function next(){
-               process.nextTick(function(){
-                       st[ cur++ ]( next );
+               files = files.map(function( item ) {
+                       return cdnFolder + "/" + item.replace( /VER/g, Release.newVersion );
                });
-       })();
-}
 
-function readJSON( filename ) {
-       return JSON.parse( fs.readFileSync( filename ) );
-}
+               shell.exec( "md5sum", files, function( code, stdout ) {
+                       fs.writeFileSync( md5file, stdout );
+                       files.push( md5file );
 
-function replaceVersionJSON( file, version ) {
-       var text = fs.readFileSync( file, "utf8" );
-       text = text.replace( /("version":\s*")[^"]+/, "$1" + version );
-       fs.writeFileSync( file, text );
-}
+                       files.forEach(function( file ) {
+                               archiver.append( fs.createReadStream( file ), { name: file } );
+                       });
 
-function updateVersion( ver ) {
-       console.log( "Updating version to " + ver );
-       pkg.version = ver;
-       if ( !debug ) {
-               [ "package.json", "bower.json" ].forEach(function( filename ) {
-                       replaceVersionJSON( filename, ver );
+                       archiver.finalize();
                });
        }
-}
 
-function copy( oldFile, newFile, skip ) {
-       console.log( "Copying " + oldFile + " to " + newFile );
-       if ( !skip ) {
-               fs.writeFileSync( newFile, fs.readFileSync( oldFile, "utf8" ) );
-       }
-}
-
-function exec( cmd, args, fn, skip ) {
-       if ( skip ) {
-               console.log( "# " + cmd + " " + args.join(" ") );
-               fn( "", "", "" );
-       } else {
-               console.log( cmd + " " + args.join(" ") );
-               child.execFile( cmd, args, { env: process.env },
-                       function( err, stdout, stderr ) {
-                               if ( err ) {
-                                       die( stderr || stdout || err );
-                               }
-                               fn.apply( this, arguments );
+       Release.define({
+               issueTracker: "trac",
+               contributorReportId: 508,
+               /**
+                * Generates any release artifacts that should be included in the release.
+                * The callback must be invoked with an array of files that should be
+                * committed before creating the tag.
+                * @param {Function} callback
+                */
+               generateArtifacts: function( callback ) {
+                       if ( Release.exec( gruntCmd ).code !== 0 ) {
+                               Release.abort("Grunt command failed");
                        }
-               );
-       }
-}
-
-function git( args, fn, skip ) {
-       exec( "git", args, fn, skip );
-}
-
-
-/* Tag creation
----------------------------------------------------------------------- */
-function checkoutCommit( next ) {
-       git( [ "checkout", "HEAD^0" ], next, debug );
-}
-
-function commitDistFiles( next ) {
-       // Remove scripts property from package.json
-       // Building and bower are irrelevant as those files will be committed
-       // Makes for a clean npm install
-       var pkgClone = readJSON( "package.json" );
-       delete pkgClone.scripts;
-       fs.writeFileSync( "package.json", JSON.stringify( pkgClone, null, "\t" ) );
-       // Add files to be committed
-       git( [ "add", "package.json" ], function() {
-               git( [ "commit", "-m", "Remove scripts property from package.json" ], function() {
-                       // Add jquery files for distribution in a final commit
-                       git( [ "add", "-f", devFile, minFile, mapFile ], function() {
-                               git( [ "commit", "-m", releaseVersion ], next, debug );
-                       }, debug );
-               }, debug );
-       }, debug );
-}
-
-function tagRelease( next ) {
-       git( [ "tag", releaseVersion ], next, debug );
-}
-
-function checkoutBranch( next ) {
-       // Reset files to previous state before leaving the commit
-       git( [ "reset", "--hard", "HEAD" ], function() {
-               git( [ "checkout", branch ], next, debug );
-       }, debug );
-}
-
-/* Archive creation
----------------------------------------------------------------------- */
-function makeArchive( cdn, files, fn ) {
-       if ( isBeta ) {
-               console.log( "Skipping archive creation for " + cdn + "; " + releaseVersion + " is beta" );
-               process.nextTick( fn );
-               return;
-       }
-
-       console.log( "Creating production archive for " + cdn );
-
-       var archive = archiver( "zip" ),
-               md5file = "dist/" + cdn + "-md5.txt",
-               output = fs.createWriteStream( "dist/" + cdn + "-jquery-" + releaseVersion + ".zip" );
-
-       archive.on( "error", function( err ) {
-               throw err;
-       });
-
-       output.on( "close", fn );
-       archive.pipe( output );
-
-       files = files.map(function( item ) {
-               return "dist/" + item.replace( /VER/g, releaseVersion );
-       });
-
-       exec( "md5sum", files, function( err, stdout ) {
-               fs.writeFileSync( md5file, stdout );
-               files.push( md5file );
-
-               files.forEach(function( file ) {
-                       archive.append( fs.createReadStream( file ), { name: file } );
-               });
-
-               archive.finalize();
-       }, false );
-}
-
-/* NPM
----------------------------------------------------------------------- */
-/*
-function publishToNpm( next ) {
-       // Only publish the master branch to NPM
-       // You must be the jquery npm user for this not to fail
-       // To check, run `npm whoami`
-       // Log in to the jquery user with `npm adduser`
-       if ( branch !== "master" ) {
-               next();
-               return;
-       }
-       git( [ "checkout", releaseVersion ], function() {
-               // Only publish committed files
-               git( [ "clean", "-dfx" ], function() {
-                       var args = [ "publish" ];
-                       // Tag the version as beta if necessary
-                       if ( isBeta ) {
-                               args.push( "--tag", "beta" );
+                       makeReleaseCopies();
+                       callback([ "dist/jquery.js", "dist/jquery.min.js", "dist/jquery.min.map" ]);
+               },
+               /**
+                * Release completion
+                */
+               complete: function() {
+                       // Build CDN archives
+                       Release._walk( buildGoogleCDN, buildMicrosoftCDN, _complete );
+               },
+               /**
+                * Our trac milestones are different than the new version
+                * @example
+                *
+                * // For Release.newVersion equal to 2.1.0 or 1.11.0
+                * Release._tracMilestone();
+                * // => 1.11/2.1
+                *
+                * // For Release.newVersion equal to 2.1.1 or 1.11.1
+                * Release._tracMilestone();
+                * // => 1.11.1/2.1.1
+                */
+               _tracMilestone: function() {
+                       var otherVersion,
+                               m = Release.newVersion.split( "." ),
+                               major = m[0] | 0,
+                               minor = m[1] | 0,
+                               patch = m[2] | 0 ? "." + m[2] : "",
+                               version = major + "." + minor + patch;
+                       if ( major === 1) {
+                               otherVersion = "2." + ( minor - 10 ) + patch;
+                               return version + "/" + otherVersion;
                        }
-                       exec( "npm", args, function() {
-                               git( [ "checkout", branch ], next, debug );
-                       }, debug || skipRemote );
-               }, debug );
-       }, debug);
-}*/
-
-/* Death
----------------------------------------------------------------------- */
-function die( msg ) {
-       console.error( "ERROR: " + msg );
-       process.exit( 1 );
-}
-
-function dieIfReal( msg ) {
-       if ( debug ) {
-               console.log ( "DIE: " + msg );
-       } else {
-               die( msg );
-       }
-}
-
-function exit() {
-       process.exit( 0 );
-}
+                       otherVersion = "1." + ( minor + 10 ) + patch;
+                       return otherVersion + "/" + version;
+               }
+       });
+};
index 95c759cc676e5ca10a1c798b57318f83121ed23b..7dc24fc6ac11cec522356bf7506c1235ea7aa79a 100644 (file)
@@ -35,6 +35,7 @@
     "testswarm": "1.1.0",
     "load-grunt-tasks": "0.2.0",
     "requirejs": "2.1.9",
+    "shelljs": "0.2.6",
 
     "grunt": "0.4.2",
     "grunt-cli": "0.1.11",