11/* global Promise */
22
3+ // Release versioning:
4+ //
5+ // If the user supplies a version q.r.s, it must be >= version x.y.z that is the version in the current branch. If an
6+ // x.y branch is being released and the user supplies a version. the version must be x.y.q >= the x.y.z-pre version of
7+ // the branch.
8+ //
9+ // 1. If releasing from master
10+ // A) and major version is 0.x.0-pre
11+ // i) release is 0.x.0
12+ // ii) next is 0.<x+1>.0-pre
13+ // B) and major version is x.y.0(-pre) (patch version must be 0)
14+ // i) release is x.y.0
15+ // ii) create new branch x.y
16+ // iii) next is x.<y+1>.0-pre
17+ // 2. If releasing from branch x.y, version x.y.z(-pre)
18+ // A) release is x.y.z
19+ // B) next is x.y.<z+1>-pre
20+
321var shell = require ( 'shelljs' ) ;
22+ var semver = require ( 'semver' ) ;
423var fs = require ( 'fs' ) ;
524var path = require ( 'path' ) ;
625var util = require ( 'util' ) ;
@@ -20,11 +39,11 @@ function print() {
2039}
2140
2241function printUsage ( ) {
23- print ( 'Usage: %s [options ] [branch] [version]\n' , process . argv [ 1 ] ) ;
42+ print ( 'Usage: %s [--help ] [branch [version] ]\n' , process . argv [ 1 ] ) ;
2443 print ( '\n' ) ;
25- print ( 'Branch defaults to "master".\n' ) ;
26- print ( 'Version defaults to what is listed in package.json in the branch. \n' ) ;
27- print ( 'Version should only be specified for pre-releases. \n' ) ;
44+ print ( '<branch> defaults to "master".\n' ) ;
45+ print ( '<version> is the version to release, and defaults to what is listed in the\n' ) ;
46+ print ( ' package.json in the branch. It should only be specified for pre-releases\n' ) ;
2847}
2948
3049function prompt ( ) {
@@ -69,12 +88,15 @@ var buildDir = path.join(rootDir, '_build');
6988var branch = args [ 0 ] || 'master' ;
7089var pushBranches = [ branch ] ;
7190var npmTag = 'latest' ;
91+
92+ // the version to be released
7293var version ;
73- var releaseTag ;
74- var makeBranch ;
94+ // the next pre-release version that will be set on the original branch after tagging
7595var preVersion ;
96+ // the name of the new release branch that should be created if this is not a patch release
97+ var newBranch ;
98+ // the pre-release version that will be set on the minor release branch
7699var branchVersion ;
77- var tagVersion ;
78100
79101if ( args [ 1 ] ) {
80102 version = args [ 1 ] ;
@@ -121,56 +143,54 @@ run('git config receive.denyCurrentBranch').then(
121143 var packageJson = loadPackageJson ( ) ;
122144
123145 // Determine the proper version numbers for release and for repo post-release
146+
124147 if ( ! version ) {
125- version = packageJson . version . replace ( '-pre' , '' ) ;
126- preVersion = version . split ( '.' ) . map ( Number ) ;
127-
128- // If the last digit is a 0, this is a new major/minor release
129- if ( preVersion [ 2 ] === 0 ) {
130- // We'll be creating a new minor release branch for this version for any future patch releases
131- // e.g., current is 2.1.0, branch will be 2.1.1-pre
132- branchVersion = util . format ( '%s.%s.%s-pre' , preVersion [ 0 ] , preVersion [ 1 ] , preVersion [ 2 ] + 1 ) ;
133-
134- // makeBranch is the new branch we'll be making for this major/minor release
135- makeBranch = util . format ( '%s.%s' , preVersion [ 0 ] , preVersion [ 1 ] ) ;
136-
137- // The next release is usually going to be a minor release; if the next version is to be a major release,
138- // the package version will need to be manually updated in Git before release
139- // e.g., current is 2.1.0, pre will be 2.2.0-pre
140- preVersion = util . format ( '%s.%s.0-pre' , preVersion [ 0 ] , preVersion [ 1 ] + 1 ) ;
141- }
142- // If the last digit isn't a 0, this is a new patch release
143- else {
144- // Patch releases do not get a branch, and the next release version will always be another patch version
145- // e.g., current is 2.1.0, pre will be 2.1.1-pre
146- preVersion = util . format ( '%s.%s.%s-pre' , preVersion [ 0 ] , preVersion [ 1 ] , preVersion [ 2 ] + 1 ) ;
148+ // Use the version from package.json in the currently checked out branch
149+ version = packageJson . version ;
150+
151+ if ( ! semver . prerelease ( version ) ) {
152+ throw new Error ( 'Releases may only be generated from pre-release versions' ) ;
147153 }
154+
155+ version = semver . major ( version ) + '.' + semver . minor ( version ) + '.' + semver . patch ( version ) ;
148156 }
149157 else {
150- preVersion = packageJson . version + '-pre' ;
158+ if ( semver . gte ( version , packageJson . version ) ) {
159+ throw new Error ( 'Provided version must be >= current version' ) ;
160+ }
151161 }
152162
153- releaseTag = tagVersion = version ;
154-
155- // At this point:
156- // `version` is the version of the package that is being released;
157- // `tagVersion` is the name that will be used for the Git tag for the release
158- // `preVersion` is the next pre-release version that will be set on the original branch after tagging
159- // `makeBranch` is the name of the new release branch that should be created if this is not a patch release
160- // `branchVersion` is the pre-release version that will be set on the minor release branch
161-
163+ // Check that the version hasn't already been tagged
162164 return run ( 'git tag' ) . then ( function ( tags ) {
163165 tags . split ( '\n' ) . forEach ( function ( tag ) {
164- if ( tag === tagVersion ) {
166+ if ( tag === version ) {
165167 throw new Error ( 'Version ' + tag + ' has already been tagged' ) ;
166168 }
167169 } ) ;
168170 } ) ;
171+ } ) . then ( function ( ) {
172+ // Pre-release or non-branching updates
173+ if ( semver . major ( version ) === 0 || semver . patch ( version ) !== 0 ) {
174+ preVersion = semver . inc ( version , 'patch' ) + '-pre' ;
175+ }
176+ // If the patch digit is a 0, this is a new major/minor release
177+ else {
178+ // The new branch we'll be making for this major/minor release
179+ newBranch = util . format ( '%s.%s' , semver . major ( version ) , semver . minor ( version ) ) ;
180+
181+ // The full version of the next release in the new branch
182+ branchVersion = semver . inc ( version , 'patch' ) + '-pre' ;
183+
184+ // The next version on master is usually going to be a minor release; if the next version is to be a major
185+ // release, the package version will need to be manually updated in Git before release e.g., current is
186+ // 2.1.0, pre will be 2.2.0-pre
187+ preVersion = semver . inc ( version , 'minor' ) + '-pre' ;
188+ }
169189} ) . then ( function ( ) {
170190 // Set the package version to release version and commit the new release
171191 updatePackageVersion ( version ) ;
172192 return run ( 'git commit -m "Updating metadata for ' + version + '" package.json' ) . then ( function ( ) {
173- return run ( 'git tag -a -m "Release ' + version + '" ' + tagVersion ) ;
193+ return run ( 'git tag -a -m "Release ' + version + '" ' + version ) ;
174194 } ) ;
175195} ) . then ( function ( ) {
176196 // Check out the previous package.json
@@ -183,22 +203,22 @@ run('git config receive.denyCurrentBranch').then(
183203 return run ( 'git commit -m "Updating source version to ' + preVersion + '" package.json' ) ;
184204} ) . then ( function ( ) {
185205 // If this is a major/minor release, we also create a new branch for it
186- if ( makeBranch ) {
187- print ( 'Creating new branch %s...\n' , makeBranch ) ;
206+ if ( newBranch ) {
207+ print ( 'Creating new branch %s...\n' , newBranch ) ;
188208 // Create the new branch starting at the tagged release version
189- return run ( 'git checkout -b ' + makeBranch + ' ' + tagVersion ) . then ( function ( ) {
209+ return run ( 'git checkout -b ' + newBranch + ' ' + version ) . then ( function ( ) {
190210 // Set the package version to the next patch pre-release version and commit the pre-release
191211 updatePackageVersion ( branchVersion ) ;
192212 return run ( 'git commit -m "Updating source version to ' + branchVersion + '" package.json' ) ;
193213 } ) . then ( function ( ) {
194214 // Store the branch as one that needs to be pushed when we are ready to deploy the release
195- pushBranches . push ( makeBranch ) ;
215+ pushBranches . push ( newBranch ) ;
196216 } ) ;
197217 }
198218} ) . then ( function ( ) {
199219 // Checkout and build the new release in preparation for publishing
200- print ( 'Checking out and building %s...\n' , releaseTag ) ;
201- return run ( 'git checkout ' + releaseTag ) . then ( function ( ) {
220+ print ( 'Checking out and building %s...\n' , version ) ;
221+ return run ( 'git checkout ' + version ) . then ( function ( ) {
202222 return run ( 'npm install' ) ;
203223 } ) . then ( function ( ) {
204224 return run ( 'node ./support/build.js dist' ) ;
@@ -208,13 +228,12 @@ run('git config receive.denyCurrentBranch').then(
208228 print ( '\nDone!\n\n' ) ;
209229
210230 var question = 'Please confirm packaging success, then enter "y" to publish to npm\n' +
211- npmTag + ', push tags ' + releaseTag + ', and upload. Enter any other key to bail.\n' +
231+ npmTag + ', push tags ' + version + ', and upload. Enter any other key to bail.\n' +
212232 '> ' ;
213233
214234 return prompt ( question ) . then ( function ( answer ) {
215235 if ( answer !== 'y' ) {
216- cleanup ( ) ;
217- process . exit ( 0 ) ;
236+ throw new Error ( 'Aborted' ) ;
218237 }
219238 } ) ;
220239} ) . then ( function ( ) {
@@ -228,12 +247,18 @@ run('git config receive.denyCurrentBranch').then(
228247 return run ( 'git push origin --tags' ) ;
229248 } ) ;
230249} ) . then ( function ( ) {
250+ rl . close ( ) ;
231251 cleanup ( ) ;
232252 print ( '\nAll done! Yay!\n' ) ;
233- process . exit ( 0 ) ;
234253} ) . catch ( function ( error ) {
235- // Something broke -- display an error
236- print ( error + '\n' ) ;
237- print ( 'Aborted.\n' ) ;
238- process . exit ( 1 ) ;
254+ rl . close ( ) ;
255+ if ( error . message === 'Aborted' ) {
256+ cleanup ( ) ;
257+ }
258+ else {
259+ // Something broke -- display an error
260+ print ( error + '\n' ) ;
261+ print ( 'Aborted.\n' ) ;
262+ process . exit ( 1 ) ;
263+ }
239264} ) ;
0 commit comments