Bug 1277406 - Offer to clone Firefox source repo during bootstrap; r?glandium draft
authorGregory Szorc <gps@mozilla.com>
Thu, 09 Jun 2016 16:19:51 -0700
changeset 377203 963e1dc1c89643adf97b48afaa488242d262b195
parent 377202 d721f3a4062a6c9e3ccbf1714d5e7312225015ec
child 523339 333152c5c67269efb74eb9fb3a5eab92b089f1e2
push id20781
push userbmo:gps@mozilla.com
push dateThu, 09 Jun 2016 23:21:17 +0000
reviewersglandium
bugs1277406
milestone50.0a1
Bug 1277406 - Offer to clone Firefox source repo during bootstrap; r?glandium I've always been bothered that the one-line bootstrap configures your system then leaves you on the hook to clone source code and configure the build system. I'd like for there to be an easy transition and a wizard that guides you through from end to end. This commit addresses part of the disconnect by offering to clone the Mercurial source repository at the end of bootstrap. We only offer to clone if we aren't running from a source checkout (likely the one-line bootstrap invocation) and if we are in interactive mode. We clone from https://hg.mozilla.org/firefox - a unified Firefox repo. We attempt to perform a streaming clone, because this will likely be the fastest and will result in the local repository having optimal delta chains and performance. This is ~500 MB larger than a gzip bundle and ~150 MB larger than a mozilla-central gzip bundle (the latter is likely what is being downloaded by most users today). I think the extra 150 MB for all changesets is beneficial to users: we want to encourage people to use the unified repo going forward because the separate repos are not needed. Again, I'd like to eventually offer Git support here. Again, Mercurial is canonical and you have to start somewhere. MozReview-Commit-ID: 6TSZwxB3702
python/mozboot/mozboot/bootstrap.py
--- a/python/mozboot/mozboot/bootstrap.py
+++ b/python/mozboot/mozboot/bootstrap.py
@@ -77,20 +77,23 @@ Would you like to create this directory?
 
   1. Yes
   2. No
 
 Your choice:
 '''
 
 FINISHED = '''
-Your system should be ready to build %s! If you have not already,
-obtain a copy of the source code by running:
+Your system should be ready to build %s!
+'''
 
-    hg clone https://hg.mozilla.org/mozilla-central
+SOURCE_ADVERTISE = '''
+Source code can be obtained by running
+
+    hg clone https://hg.mozilla.org/firefox
 
 Or, if you prefer Git, you should install git-cinnabar, and follow the
 instruction here to clone from the Mercurial repository:
 
     https://github.com/glandium/git-cinnabar/wiki/Mozilla:-A-git-workflow-for-Gecko-development
 
 Or, if you really prefer vanilla flavor Git:
 
@@ -104,16 +107,25 @@ experience with it.
 Would you like to run a configuration wizard to ensure Mercurial is
 optimally configured?
 
   1. Yes
   2. No
 
 Please enter your reply: '''.lstrip()
 
+CLONE_MERCURIAL = '''
+If you would like to clone the canonical Mercurial repository, please
+enter the destination path below.
+
+(If you prefer to use Git, leave this blank.)
+
+Destination directory for Mercurial clone (leave empty to not clone): '''.lstrip()
+
+
 DEBIAN_DISTROS = (
     'Debian',
     'debian',
     'Ubuntu',
     # Most Linux Mint editions are based on Ubuntu. One is based on Debian.
     # The difference is reported in dist_id from platform.linux_distribution.
     # But it doesn't matter since we share a bootstrapper between Debian and
     # Ubuntu.
@@ -238,16 +250,33 @@ class Bootstrapper(object):
                 if choice == 1:
                     configure_hg = True
             else:
                 configure_hg = self.hg_configure
 
             if configure_hg:
                 configure_mercurial(self.instance.which('hg'), state_dir)
 
+        # Offer to clone if we're not inside a clone.
+        checkout_type = current_vcs()
+        have_clone = False
+
+        if checkout_type:
+            have_clone = True
+        elif hg_installed and not self.instance.no_interactive:
+            dest = raw_input(CLONE_MERCURIAL)
+            dest = dest.strip()
+            if dest:
+                dest = os.path.expanduser(dest)
+                clone_firefox(self.instance.which('hg'), dest)
+                have_clone = True
+
+        if not have_clone:
+            print(SOURCE_ADVERTISE)
+
         print(self.finished % name)
 
         # Like 'suggest_browser_mozconfig' or 'suggest_mobile_android_mozconfig'.
         getattr(self.instance, 'suggest_%s_mozconfig' % application)()
 
 
 def update_vct(hg, root_state_dir):
     """Ensure version-control-tools in the state directory is up to date."""
@@ -303,8 +332,60 @@ def update_mercurial_repo(hg, url, dest,
     print('=' * 80)
     print('Ensuring %s is up to date at %s' % (url, dest))
 
     try:
         subprocess.check_call(args, cwd=cwd)
         subprocess.check_call([hg, 'update', '-r', revision], cwd=dest)
     finally:
         print('=' * 80)
+
+
+def clone_firefox(hg, dest):
+    """Clone the Firefox repository to a specified destination."""
+    print('Cloning Firefox Mercurial repository to %s' % dest)
+
+    try:
+        subprocess.check_call([hg, 'config', 'ui.clonebundleprefers'])
+        have_bundle_prefers = True
+    except subprocess.CalledProcessError:
+        have_bundle_prefers = False
+
+    args = [hg]
+
+    # Prefer an uncompressed clone because the delta chains on the server
+    # are optimal and will be preserved on the client, unlike with a gzip
+    # clone.
+    if not have_bundle_prefers:
+        args.extend(['--config', 'ui.clonebundleprefers=VERSION=packed1'])
+
+    args.extend([
+        'clone',
+        '--noupdate',
+        'https://hg.mozilla.org/firefox',
+        dest,
+    ])
+
+    subprocess.check_call(args)
+    print('updating to "central" (main head of Firefox development)')
+    subprocess.check_call([hg, 'update', '-r', 'central'], cwd=dest)
+    print('')
+    print('Firefox source code available at %s' % dest)
+
+def current_vcs():
+    """Determine the type of the version control tool for the current directory.
+
+    Returns one of None, ``git``, or ``hg``.
+    """
+    path = os.getcwd()
+    while path:
+        hg = os.path.join(path, '.hg')
+        git = os.path.join(path, '.git')
+        if os.path.exists(hg):
+            return 'hg'
+        elif os.path.exists(git):
+            return 'git'
+
+        path, child = os.path.split(path)
+        if child == '':
+            return None
+
+    return None