You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by st...@apache.org on 2014/09/25 03:18:29 UTC

[02/13] git commit: cb-7224: added suuport for platforms release process

cb-7224: added suuport for platforms release process


Project: http://git-wip-us.apache.org/repos/asf/cordova-coho/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-coho/commit/1b0b7a0b
Tree: http://git-wip-us.apache.org/repos/asf/cordova-coho/tree/1b0b7a0b
Diff: http://git-wip-us.apache.org/repos/asf/cordova-coho/diff/1b0b7a0b

Branch: refs/heads/master
Commit: 1b0b7a0b3952d266a800ed0ae719cc3dd95ee3d3
Parents: 08eca9f
Author: Steven Gill <st...@gmail.com>
Authored: Wed Aug 20 17:41:42 2014 -0700
Committer: Steven Gill <st...@gmail.com>
Committed: Wed Sep 24 17:17:03 2014 -0700

----------------------------------------------------------------------
 docs/platforms-release-process.md | 411 +++++++++++++++++++++++++++++++++
 src/main.js                       |   4 +-
 src/platform-release.js           | 302 ++++++++++++++++++++++++
 3 files changed, 715 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-coho/blob/1b0b7a0b/docs/platforms-release-process.md
----------------------------------------------------------------------
diff --git a/docs/platforms-release-process.md b/docs/platforms-release-process.md
new file mode 100644
index 0000000..05fbef9
--- /dev/null
+++ b/docs/platforms-release-process.md
@@ -0,0 +1,411 @@
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+-->
+
+# Release Process for ''Platforms Releases''
+
+Before cutting any releases, read the Apache's [Releases Policy](http://www.apache.org/dev/release)
+
+This page describes the technical steps for releasing a `Platforms Release` (see: [versioning-and-release-strategy.md](versioning-and-release-strategy.md)).
+
+TODO: We may want to be using [signed tags](http://git-scm.com/book/en/Git-Basics-Tagging), or at least annotated tags.
+
+## Getting Buy-in & Assigning a Release Manager
+
+ 1. Email the dev mailing-list and see if anyone has reason to postpone the release.
+   * If so, agree upon a branching date / time.
+ 1. Ask for a volunteer to be Release Manager for the release (or volunteer yourself)
+
+## Creating JIRA issues
+
+TODO: Create a new release bug script for independant platform releases.
+
+* Create the release bug for the Release Candidate:
+
+      `coho create-release-bug --version=3.0.0 --username=JiraUser --password=JiraPassword`
+    
+* Comments should be added to this bug after each top-level step below is taken
+
+* Set a variable for use later on:
+
+    `JIRA="CB-????"` # Set this to the release bug.
+
+## Branch & Tag for: cordova-mobile-spec and cordova-app-hello-world 
+
+TODO: mobile-spec should have it's own release process instead of being a part of platform release process
+
+TODO: app-hello-world should be tagged with tools
+
+This should be done *before* creating branches on other repos.
+
+This step involves:
+ * Updating version numbers
+ * Creating release branches
+ * Creating git tags
+
+Coho automates these steps:
+
+    coho prepare-release-branch --version 3.5.0 -r app-hello-world -r mobile-spec
+    coho repo-status -r app-hello-world -r mobile-spec -b master -b 3.5.x
+    # If changes look right:
+    coho repo-push -r app-hello-world -r mobile-spec -b master -b 3.5.x
+    coho tag-release --version 3.5.0 -r app-hello-world -r mobile-spec
+
+If the JS ever needs to be re-tagged, rerun the `tag-release` command, and then re-run the `prepare-release-branch` command for the platform repos.
+
+## Branch & Tag for Platform Repositories
+
+### Before creating the release branch:
+
+ 1. Run [Apache RAT](http://creadur.apache.org/rat/) to ensure copyright headers are present
+   * `coho audit-license-headers -r android | less`
+ 2. Update the copy of app-hello-world (if there were any changes to it)
+   * This usually lives within bin/templates somewhere
+   * TODO: More details needed here
+ 3. For iOS only:
+   * Update [CordovaLib/Classes/CDVAvailability.h](https://github.com/apache/cordova-ios/blob/master/CordovaLib/Classes/CDVAvailability.h)
+
+by adding a new macro for the new version, e.g.
+
+    #define __CORDOVA_2_1_0  20100
+
+
+and update `CORDOVA_VERSION_MIN_REQUIRED` with the latest version macro, e.g.
+
+    #ifndef CORDOVA_VERSION_MIN_REQUIRED
+        #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_2_1_0
+    #endif
+
+### Creating the release branch
+
+This step involves:
+ * Updating cordova.js snapshots
+ * Updating version numbers
+ * Creating release branches
+ * Creating git tags
+ * Updating version in package.json files
+
+Coho automates these steps (replace android with your platform):
+
+    coho prepare-release-branch --version 3.5.0 -r android
+    coho repo-status -r android -b master -b 3.5.x
+    # If changes look right:
+    coho repo-push -r android -b master -b 3.5.x
+    coho tag-release --version 3.5.0 -r android
+
+## Tagging RC1 of cordova-cli
+
+cordova-cli doesn't use a release branch. Follow the instructions at [tools-release-process.md](tools-release-process.md), but in addition:
+
+Update the tool to point to the new repo versions (within `cordova-cli/platforms.js`)
+
+Instead of the normal `npm publish` flow:
+
+    npm publish --tag rc
+
+** WATCH OUT! You may have to run `npm tag cordova@x.x.x latest` due to a bug in npm: https://github.com/npm/npm/issues/4837
+
+## Publish RC to dist/dev
+Ensure you have the svn repos checked out:
+
+    coho repo-clone -r dist -r dist/dev
+    
+Create archives from your tags:
+
+    coho foreach -r cadence "git checkout 3.4.x"
+    coho create-archive -r cadence --dest cordova-dist-dev/$JIRA/rc
+
+Sanity Check:
+
+    coho verify-archive cordova-dist-dev/$JIRA/rc/*.zip
+
+Upload:
+
+    (cd cordova-dist-dev && svn add $JIRA && svn commit -m "$JIRA Uploading release candidates for cadence release")
+
+Find your release here: https://dist.apache.org/repos/dist/dev/cordova/
+
+## Testing & Documentation
+
+Once all the repos are branched & tagged, we focus on testing & fixing all of the regressions we find.
+
+When a regression is found:
+
+ * Create a JIRA issue for it, and mark it as a blocker.
+
+To submit a fix:
+
+    git checkout master
+    git commit -am 'Your commit message'
+    git push origin master
+    git log     # note the first five or six digits of the commit hash
+    git checkout 2.7.x
+    git cherry-pick -x commit_hash
+    git push origin 2.7.x
+
+### What to Test
+
+ * Run [mobile-spec](http://git-wip-us.apache.org/repos/asf/cordova-mobile-spec.git)
+   * Don't forget to checkout mobile-spec at the appropriate tag instead of using master.
+   * Don't forget to set up your white-list
+   * Don't forget to run through the manual tests in addition to the automatic tests
+   * Test loading the app over HTTP (via "cordova serve" and setting the config.xml start page)
+ * Run each platform's ./bin/create script
+   * Ensure generated project builds & runs both through an IDE and through the cordova/* scripts
+ * Test Project Upgrades (old-style):
+   1. Create a project using the previous version of cordova
+     * `coho foreach "git checkout 2.9.0"`
+     * `coho foreach -r active-platform "./bin/create foo org.apache.foo foo"`
+   2. Upgrade the project via the bin/update_project script:
+     * `coho foreach "git checkout 3.0.x"`
+     * `coho foreach -r active-platform "cd foo && ../bin/update_project"`
+   3. Test the result:
+     * Project should run
+     * cordova/version should report the new version
+ * Test Project Upgrades (new-style):
+   1. Create a project using the previous version of cordova
+     * `coho foreach "git checkout 2.9.0"`
+     * `./cordova-mobile-spec/createmobilespec.sh`
+   2. Upgrade the project via the update command:
+     * `../cordova-cli/bin/cordova platform update PLATFORM`
+   3. Test the result:
+     * Project should run
+     * cordova/version should report the new version
+     * Mobile Spec should still run.
+
+#### Android Extras
+
+ * Unit tests in: [test](https://github.com/apache/incubator-cordova-android/tree/master/test) directory
+
+#### iOS Extras
+
+ * Unit tests in: [CordovaLibTests/CordovaTests.xcodeproj](https://git-wip-us.apache.org/repos/asf?p=cordova-ios.git;a=tree;f=CordovaLibTests;h=88ba8e3c286159151b378efb1b0c39ef26dac550;hb=HEAD)
+ * Test the Makefile via `make`
+ * Run `bin/diagnose_project` on a newly created project and ensure it reports no errors.
+
+### Documentation To Update
+
+For each repository:
+ 1. Update RELEASENOTES.md (if the file is missing, use the iOS one as a reference: [RELEASENOTES.md](https://github.com/apache/cordova-ios/blob/master/RELEASENOTES.md))
+
+Grab changes from the previous release until now.
+
+    # Changes:
+    git log --pretty=format:'* %s' --topo-order --no-merges origin/3.2.x..origin/3.3.x
+    # Commit count:
+    git log --pretty=format:'* %s' --topo-order --no-merges origin/3.2.x..origin/3.3.x | wc -l
+    # Author Count:
+    git log --pretty=format:'%an' --topo-order --no-merges origin/3.2.x..origin/3.3.x | sort | uniq | wc -l
+
+Edit the commit descriptions - don't add the commits verbatim, usually they are meaningless to the user. Only show the ones relevant for the user (fixes, new features)
+
+ 2. Update README.md (if necessary)
+ 3. Ensure the [Upgrade Guide](http://docs.phonegap.com/en/edge/guide_upgrading_index.md.html) for your platform is up-to-date
+ 4. Ensure the other guides listed in the sidebar are up-to-date for your platform
+
+## Final Tagging (non-RC)
+
+This is done for all repos once testing is complete, and documentation is up-to-date. If nothing has changed since the earlier tag, no need to run the steps below. If changes have occured, make sure to cherry-pick them into the release branch and rerun commands below.
+
+Note: If you get an error due to the tag already existing on the server, view the moving tags section at the bottom of this readme on how to delete a remote git tag.
+
+Use the same coho commands as for the RCs (it will update JS & VERSION):
+
+    coho prepare-release-branch --version 3.5.0 -r js -r app-hello-world -r mobile-spec
+    coho repo-status -r js -r app-hello-world -r mobile-spec -b master -b 3.5.x
+    # If changes look right:
+    coho repo-push -r js -r app-hello-world -r mobile-spec -b master -b 3.5.x
+    coho tag-release --version 3.5.0 -r js -r app-hello-world -r mobile-spec
+
+## Branching & Tagging cordova-docs
+
+ 1. Cherry pick relevant commits from master to 3.5.x branch
+ 2. Generate the docs for the release on the 3.5.x branch.
+ 3. Commit & tag on the 3.5.x branch.
+ 4. Cherry pick commit into master.
+
+
+See [Generating a Version Release](https://git-wip-us.apache.org/repos/asf?p=cordova-docs.git;a=blob;f=README.md#l127) for more details.
+
+## Publish final archives to dist/dev
+Create archives from your tags:
+
+    coho foreach -r cadence "git checkout 3.4.x"
+    coho create-archive -r cadence --dest cordova-dist-dev/$JIRA/final
+
+Sanity Check:
+
+    coho verify-archive cordova-dist-dev/$JIRA/final/*.zip
+
+Upload:
+
+    (cd cordova-dist-dev && svn add $JIRA/final && svn commit -m "$JIRA Uploading archives for cadence release vote")
+
+Find your release here: https://dist.apache.org/repos/dist/dev/cordova/
+
+## Prepare Blog Post
+ * Combine highlights from RELEASENOTES.md into a Release Announcement blog post
+   * Instructions on [sites page README](https://svn.apache.org/repos/asf/cordova/site/README.md)
+ * Get blog post proofread via [piratepad](http://piratepad.net/front-page/).
+
+## Start VOTE Thread
+Send an email to dev ML with:
+
+__Subject:__
+
+    [Vote] 3.5.0 Cadence Release
+
+__Body:__
+
+    Please review and vote on this 3.5.0 Cadence Release.
+
+    Release issue: https://issues.apache.org/jira/browse/CB-XXXX
+
+    Repos ready to be released have been published to dist/dev:
+    https://dist.apache.org/repos/dist/dev/cordova/CB-XXXX/final
+
+    The packages were published from their corresponding git tags:
+    PASTE OUTPUT OF: coho print-tags -r cadence
+
+    Upon a successful vote I will upload the archives to dist/, publish them to NPM, and post the corresponding blog post.
+
+    Voting guidelines: https://github.com/apache/cordova-coho/blob/master/docs/release-voting.md
+
+    Voting will go on for a minimum of 48 hours.
+
+    I vote +1:
+    * Ran coho audit-license-headers over the relevant repos
+    * Used `license-checker` to ensure all dependencies have Apache-compatible licenses
+    * Ensured continuous build was green when repos were tagged
+
+
+## Email the result of the vote
+Respond to the vote thread with:
+
+    The vote has now closed. The results are:
+
+    Positive Binding Votes: (# of PMC members that +1'ed)
+
+    .. names of all +1 PMC members ..
+
+    Negative Binding Votes: (# of PMC members that -1'ed)
+
+    .. names of all -1 PMC members ..
+
+    The vote has passed.
+
+_Note: list of PMC members: http://people.apache.org/committers-by-project.html#cordova-pmc_
+
+## If the Vote does *not* Pass
+* Revert adding of `-dev`
+* Address the concerns
+* Re-tag release using `git tag -f`
+* Add back `-dev`
+* Start a new vote
+
+## Otherwise: Publish to dist/
+
+    cd cordova-dist
+    svn up
+    svn rm tools/cordova-cli-*
+    svn rm tools/cordova-js*
+    svn rm platforms/*
+    cp ../cordova-dist-dev/$JIRA/final/cordova-js* tools/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-cli* tools/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-mobile-spec* tools/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-app-hello* tools/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-docs* docs/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-ios* platforms/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-android* platforms/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-blackberry* platforms/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-windows* platforms/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-wp8* platforms/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-firefoxos* platforms/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-ubuntu* platforms/
+    cp ../cordova-dist-dev/$JIRA/final/cordova-amazon-fireos* platforms/
+    svn add tools/*
+    svn add platforms/*
+    svn add docs/*
+    svn commit -m "$JIRA Published cadence release to dist"
+
+    cd ../cordova-dist-dev
+    svn up
+    svn rm $JIRA
+    svn commit -m "$JIRA Removing release candidates from dist/dev"
+    cd ..
+
+
+Find your release here: https://dist.apache.org/repos/dist/release/cordova/
+
+## Final Details
+
+### Update cordova.apache.org
+
+ * Refer to [this commit](http://svn.apache.org/viewvc?view=revision&revision=r1478146) (also includes updating the DOAP file)
+ * And the instructions at https://svn.apache.org/repos/asf/cordova/site/README.md
+
+### Update the Docs
+ 1. Upload the new docs to http://cordova.apache.org/docs
+   * Website README.md explains [How to update the docs](https://svn.apache.org/repos/asf/cordova/site/README.md)
+   * Commit should look like [this one](http://svn.apache.org/viewvc?view=revision&revision=r1478171)
+ 1. Ask Michael Brooks to update the docs.cordova.io redirect.
+   * Check out the branch `cordova-labs:redirect-docs-cordova-io`
+   * Repository README.md explains [How to update the HTTP redirect](https://github.com/apache/cordova-labs/tree/redirect-docs-cordova-io#usage)
+   * Nodejitsu is limited to one deployer, so Michael Brooks is currently the point of contact.
+
+### Push platforms and tools to npm
+Refer to [tools-release-process.md](tools-release-process.md)
+
+### Tell JIRA it's Released
+
+ * Visit https://issues.apache.org/jira/plugins/servlet/project-config/CB/versions
+ * Fill in the Release Date field and mark it as released.
+
+### Announce It!
+ 1. Announce the release to the world!
+   * Create a blog post for it (instructions on [sites page README](https://svn.apache.org/repos/asf/cordova/site/README.md))
+   * Tweet it on https://twitter.com/apachecordova
+   * Announce to [G+ Page](https://plus.google.com/u/0/113178331525415522084/posts)
+   * Get it posted to Apache's announce mailing list: send an mail to announce@apache.org, sent from your @apache.org email address. Send in plain text, and copy the model of other announcements in the archive(s). You'll then need to wait for the moderator to accept the message. The www.a.o home page is automatically updated with the announcement after the moderator approves.
+   * For major project milestones, email press@apache.org and they can provide custom help.
+
+# Additional Information
+ * [IOSReleaseChecklist](https://wiki.apache.org/cordova/IOSReleaseChecklist)
+ * [AndroidReleaseChecklist](https://wiki.apache.org/cordova/AndroidReleaseChecklist)
+
+## Moving Tags
+
+If you need to move a tag before the release, here is how to do that:
+
+    $ git tag -d 3.1.0
+    Deleted tag '3.1.0' (was 2a9bc20)
+    $ git push origin :refs/tags/3.1.0
+    To https://git-wip-us.apache.org/repos/asf/cordova-docs.git
+     - [deleted]         3.1.0
+    $ git tag 3.1.0 7cf9fea03d7d02a13aef97a09a459e8128bd3198
+    $ git push origin 3.1.0 --tags
+    Total 0 (delta 0), reused 0 (delta 0)
+    To https://git-wip-us.apache.org/repos/asf/cordova-docs.git
+     * [new tag]         3.1.0 -> 3.1.0
+
+Then send a note to the mailing list:
+
+    To verify you have the updated tag in your local clone, doing a "git rev-parse 3.1.0" in cordova-docs should reply with "7cf9fea03d7d02a13aef97a09a459e8128bd3198". If it is wrong, do "git fetch --tags".
+

http://git-wip-us.apache.org/repos/asf/cordova-coho/blob/1b0b7a0b/src/main.js
----------------------------------------------------------------------
diff --git a/src/main.js b/src/main.js
index 37aae0b..96d2a4a 100644
--- a/src/main.js
+++ b/src/main.js
@@ -67,11 +67,11 @@ module.exports = function() {
     var releaseCommands = [{
             name: 'prepare-release-branch',
             desc: 'Branches, updates JS, updates VERSION. Safe to run multiple times.',
-            entryPoint: lazyRequire('./cadence-release', 'prepareReleaseBranchCommand')
+            entryPoint: lazyRequire('./platform-release', 'prepareReleaseBranchCommand')
         }, {
             name: 'tag-release',
             desc: 'Tags repos for a release.',
-            entryPoint: lazyRequire('./cadence-release', 'tagReleaseBranchCommand')
+            entryPoint: lazyRequire('./platform-release', 'tagReleaseBranchCommand')
         }, {
             name: 'audit-license-headers',
             desc: 'Uses Apache RAT to look for missing license headers.',

http://git-wip-us.apache.org/repos/asf/cordova-coho/blob/1b0b7a0b/src/platform-release.js
----------------------------------------------------------------------
diff --git a/src/platform-release.js b/src/platform-release.js
new file mode 100644
index 0000000..20c0a19
--- /dev/null
+++ b/src/platform-release.js
@@ -0,0 +1,302 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+var fs = require('fs');
+var path = require('path');
+var optimist = require('optimist');
+var shelljs = require('shelljs');
+var apputil = require('./apputil');
+var executil = require('./executil');
+var flagutil = require('./flagutil');
+var gitutil = require('./gitutil');
+var repoutil = require('./repoutil');
+var repoupdate = require('./repo-update');
+var print = apputil.print;
+
+function createPlatformDevVersion(version) {
+    // e.g. "3.1.0" -> "3.2.0-dev".
+    // e.g. "3.1.2-0.8.0-rc2" -> "3.2.0-0.8.0-dev".
+    version = version.replace(/-rc.*$/, '');
+    var parts = version.split('.');
+    parts[1] = String(+parts[1] + 1);
+    var cliSafeParts = parts[2].split('-');
+    cliSafeParts[0] = '0';
+    parts[2] = cliSafeParts.join('-');
+    return parts.join('.') + '-dev';
+}
+
+function getVersionBranchName(version) {
+    if (/-dev$/.test(version)) {
+        return 'master';
+    }
+    return version.replace(/\d+(-?rc\d)?$/, 'x');
+}
+
+function cpAndLog(src, dest) {
+    print('Coping File:', src, '->', dest);
+    // Throws upon failure.
+    shelljs.cp('-f', src, dest);
+    if (shelljs.error()) {
+        apputil.fatal('Copy failed.');
+    }
+}
+
+function configureReleaseCommandFlags(opt) {
+    var opt = flagutil.registerRepoFlag(opt)
+    opt = opt
+        .options('version', {
+            desc: 'The version to use for the branch. Must match the pattern #.#.#[-rc#]',
+            demand: true
+         });
+    opt = flagutil.registerHelpFlag(opt);
+    argv = opt.argv;
+
+    if (argv.h) {
+        optimist.showHelp();
+        process.exit(1);
+    }
+    var version = flagutil.validateVersionString(argv.version);
+    return argv;
+}
+
+var hasBuiltJs = '';
+
+function *updateJsSnapshot(repo, version) {
+    function *ensureJsIsBuilt() {
+        var cordovaJsRepo = repoutil.getRepoById('js');
+        
+        if (hasBuiltJs != version) {
+            yield repoutil.forEachRepo([cordovaJsRepo], function*() {
+yield executil.execHelper(executil.ARGS('ls'));
+                yield gitutil.stashAndPop(cordovaJsRepo, function*() {
+                    //git fetch and update master for cordovajs
+                    yield repoupdate.updateRepos([cordovaJsRepo], ['master'], false);
+                    yield gitutil.gitCheckout('master'); 
+                    yield executil.execHelper(executil.ARGS('grunt compile:' +repo.id + ' --platformVersion='+version));
+                    hasBuiltJs = version;
+                });
+            });
+        }
+    }
+
+    if (repoutil.repoGroups.platform.indexOf(repo) == -1) {
+        return;
+    }
+
+    if (repo.cordovaJsPaths) {
+        yield ensureJsIsBuilt();
+        repo.cordovaJsPaths.forEach(function(jsPath) {
+            var src = path.join('..', 'cordova-js', 'pkg', repo.cordovaJsSrcName || ('cordova.' + repo.id + '.js'));
+            cpAndLog(src, jsPath);
+        });
+        if (yield gitutil.pendingChangesExist()) {
+            yield executil.execHelper(executil.ARGS('git commit -am', 'Update JS snapshot to version ' + version + ' (via coho)'));
+        }
+    } else if (repoutil.repoGroups.all.indexOf(repo) != -1) {
+        print('*** DO NOT KNOW HOW TO UPDATE cordova.js FOR THIS REPO ***');
+    }
+}
+
+function *updateRepoVersion(repo, version) {
+    // Update the VERSION files.
+    var versionFilePaths = repo.versionFilePaths || ['VERSION'];
+    if (fs.existsSync(versionFilePaths[0])) {
+        versionFilePaths.forEach(function(versionFilePath) {
+            fs.writeFileSync(versionFilePath, version + '\n');
+        });
+        shelljs.config.fatal = true;
+        if (repo.id == 'android') {
+            shelljs.sed('-i', /CORDOVA_VERSION.*=.*;/, 'CORDOVA_VERSION = "' + version + '";', path.join('framework', 'src', 'org', 'apache', 'cordova', 'CordovaWebView.java'));
+            shelljs.sed('-i', /VERSION.*=.*;/, 'VERSION = "' + version + '";', path.join('bin', 'templates', 'cordova', 'version'));
+        } else if (repo.id == 'firefoxos') {
+            shelljs.sed('-i', /VERSION.*=.*;/, 'VERSION = "' + version + '";', path.join('bin', 'templates', 'project','cordova', 'version'));
+        }
+        shelljs.config.fatal = false;
+        if (!(yield gitutil.pendingChangesExist())) {
+            print('VERSION file was already up-to-date.');
+        }
+    } else {
+        console.warn('No VERSION file exists in repo ' + repo.repoName);
+    }
+    
+    // Update the package.json VERSION.
+    var packageFilePaths = repo.packageFilePaths || ['package.json'];
+    if (fs.existsSync(packageFilePaths[0])) {
+        fs.readFile(packageFilePaths[0], {encoding: 'utf-8'},function (err, data) {
+            if (err) throw err;
+            var packageJSON = JSON.parse(data);
+            packageJSON.version = version;
+            fs.writeFileSync(packageFilePaths[0], JSON.stringify(packageJSON, null, "    "));
+        }); 
+        if (!(yield gitutil.pendingChangesExist())) {
+            print('package.json file was already up-to-date.');
+        }
+    } else {
+        console.warn('No package.json file exists in repo ' + repo.repoName);
+    }
+
+    if (yield gitutil.pendingChangesExist()) {
+        yield executil.execHelper(executil.ARGS('git commit -am', 'Set VERSION to ' + version + ' (via coho)'));
+    }
+}
+
+exports.prepareReleaseBranchCommand = function*() {
+    var argv = configureReleaseCommandFlags(optimist
+        .usage('Prepares release branches but does not create tags. This includes:\n' +
+               '    1. Creating the branch if it doesn\'t already exist\n' +
+               '    2. Generates and updates the cordova.js snapshot and VERSION file from master.\n' +
+               '\n' +
+               'Command is safe to run multiple times, and can be run for the purpose\n' +
+               'of checking out existing release branches.\n' +
+               '\n' +
+               'Command can also be used to update the JS snapshot after release \n' +
+               'branches have been created.\n' +
+               '\n' +
+               'Usage: $0 prepare-release-branch --version=3.6.0 -r platform')
+    );
+    var repos = flagutil.computeReposFromFlag(argv.r);
+    var version = flagutil.validateVersionString(argv.version);
+    var branchName = getVersionBranchName(version);
+
+    // First - perform precondition checks.
+    yield repoupdate.updateRepos(repos, [], true);
+    
+    yield repoutil.forEachRepo(repos, function*(repo) {
+        yield gitutil.stashAndPop(repo, function*() {
+            // git fetch + update master
+            yield repoupdate.updateRepos([repo], ['master'], false);
+
+            // Either create or pull down the branch.
+            if (yield gitutil.remoteBranchExists(repo, branchName)) {
+                print('Remote branch already exists for repo: ' + repo.repoName);
+                // Check out and rebase.
+                yield repoupdate.updateRepos([repo], [branchName], true);
+                yield gitutil.gitCheckout(branchName);
+            } else if (yield gitutil.localBranchExists(branchName)) {
+                yield executil.execHelper(executil.ARGS('git checkout ' + branchName));
+            } else {
+                yield gitutil.gitCheckout('master');
+                yield executil.execHelper(executil.ARGS('git checkout -b ' + branchName));
+            }
+            yield updateJsSnapshot(repo, version);
+            print(repo.repoName + ': ' + 'Setting VERSION to "' + version + '" on branch + "' + branchName + '".');
+            yield updateRepoVersion(repo, version);
+
+            yield gitutil.gitCheckout('master');
+            var devVersion = createPlatformDevVersion(version);
+            print(repo.repoName + ': ' + 'Setting VERSION to "' + devVersion + '" on branch + "master".');
+            yield updateRepoVersion(repo, devVersion);
+            yield updateJsSnapshot(repo, devVersion);
+            yield gitutil.gitCheckout(branchName);
+        });
+    });
+
+    executil.reportGitPushResult(repos, ['master', branchName]);
+}
+
+function *tagJs(repo, version, pretend) {
+
+    function *execOrPretend(cmd) {
+        if (pretend) {
+            print('PRETENDING TO RUN: ' + cmd.join(' '));
+        } else {
+            yield executil.execHelper(cmd);
+        }
+    }
+
+    //tag cordova.js platform-version
+    var cordovaJsRepo = repoutil.getRepoById('js');
+    yield repoutil.forEachRepo([cordovaJsRepo], function*() {
+        yield gitutil.stashAndPop(cordovaJsRepo, function*() {
+            // git fetch
+            yield repoupdate.updateRepos([cordovaJsRepo], ['master'], false);
+
+            if (yield gitutil.tagExists(repo.id + '-' + version)) {
+                yield execOrPretend(executil.ARGS('git tag ' + repo.id + '-' + version + ' --force'));
+            } else {
+                yield execOrPretend(executil.ARGS('git tag ' + repo.id + '-' + version));
+            }
+            yield execOrPretend(executil.ARGS('git push --tags ' + repo.remoteName)); 
+        });
+    });
+}
+
+
+exports.tagReleaseBranchCommand = function*(argv) {
+    var argv = configureReleaseCommandFlags(optimist
+        .usage('Tags a release branches.\n' +
+               '\n' +
+               'Usage: $0 tag-release --version=2.8.0-rc1 -r platform')
+        .options('pretend', {
+            desc: 'Don\'t actually run git commands, just print out what would be run.',
+            type: 'boolean'
+         })
+    );
+    var repos = flagutil.computeReposFromFlag(argv.r);
+    var version = flagutil.validateVersionString(argv.version);
+    var pretend = argv.pretend;
+    var branchName = getVersionBranchName(version);
+
+    // First - perform precondition checks.
+    yield repoupdate.updateRepos(repos, [], true);
+
+    function *execOrPretend(cmd) {
+        if (pretend) {
+            print('PRETENDING TO RUN: ' + cmd.join(' '));
+        } else {
+            yield executil.execHelper(cmd);
+        }
+    }
+    yield repoutil.forEachRepo(repos, function*(repo) {
+        yield gitutil.stashAndPop(repo, function*() {
+            // git fetch.
+            yield repoupdate.updateRepos([repo], [], false);
+
+            if (yield gitutil.remoteBranchExists(repo, branchName)) {
+                print('Remote branch already exists for repo: ' + repo.repoName);
+                yield gitutil.gitCheckout(branchName);
+            } else {
+                apputil.fatal('Release branch does not exist for repo ' + repo.repoName);
+            }
+
+            // git merge
+            yield repoupdate.updateRepos([repo], [branchName], true);
+
+            // Create/update the tag.
+            var tagName = yield gitutil.retrieveCurrentTagName();
+            console.log(tagName);
+            console.log(repo);
+            if (tagName != version) {
+                if (yield gitutil.tagExists(version)) {
+                    yield execOrPretend(executil.ARGS('git tag ' + version + ' --force'));
+                } else {
+                    yield execOrPretend(executil.ARGS('git tag ' + version));
+                }
+                yield execOrPretend(executil.ARGS('git push --tags ' + repo.remoteName + ' ' + branchName));
+            } else {
+                print('Repo ' + repo.repoName + ' is already tagged.');
+            }
+            yield tagJs(repo, version, pretend);
+                
+        });
+    });
+
+    print('');
+    print('All work complete.');
+}