Bug 1277406 - Offer to configure Mercurial during bootstrap; r=glandium draft
authorGregory Szorc <gps@mozilla.com>
Wed, 01 Jun 2016 18:15:17 -0700
changeset 377536 c727017bbdc703399fa67e1d831280441026614b
parent 377535 085b67183ec4fda8a23ead3328130c962c95617d
child 377537 56697d504ff41ad02d77ddd1241cebafe610751a
push id20819
push userbmo:gps@mozilla.com
push dateFri, 10 Jun 2016 16:14:00 +0000
reviewersglandium
bugs1277406
milestone50.0a1
Bug 1277406 - Offer to configure Mercurial during bootstrap; r=glandium If a state directory is available and we're running in interactive mode (or have been told otherwise), we now configure Mercurial during bootstrap. This consists of cloning version-control-tools to the state directory (mimicking code in `mach mercurial-setup` today) and running the config wizard from version-control-tools. Code for cloning/updating repositories has been stolen from tools/mercurial/hgsetup. As the inline TODO notes, I'd like to eventually support configuring Git during bootstrap. Since Mercurial is the canonical VCS for Firefox and since we already have a Mercurial setup wizard (and don't have a Git one yet), I don't think we should block on implementing Git support. MozReview-Commit-ID: 1FZyWIlHZNy
python/mozboot/mozboot/base.py
python/mozboot/mozboot/bootstrap.py
--- a/python/mozboot/mozboot/base.py
+++ b/python/mozboot/mozboot/base.py
@@ -321,37 +321,39 @@ class BaseBootstrapper(object):
         return True, our >= MODERN_MERCURIAL_VERSION, our
 
     def ensure_mercurial_modern(self):
         installed, modern, version = self.is_mercurial_modern()
 
         if modern:
             print('Your version of Mercurial (%s) is sufficiently modern.' %
                   version)
-            return
+            return installed, modern
 
         self._ensure_package_manager_updated()
 
         if installed:
             print('Your version of Mercurial (%s) is not modern enough.' %
                   version)
             print('(Older versions of Mercurial have known security vulnerabilities. '
                   'Unless you are running a patched Mercurial version, you may be '
                   'vulnerable.')
         else:
             print('You do not have Mercurial installed')
 
         if self.upgrade_mercurial(version) is False:
-            return
+            return installed, modern
 
         installed, modern, after = self.is_mercurial_modern()
 
         if installed and not modern:
             print(MERCURIAL_UPGRADE_FAILED % (MODERN_MERCURIAL_VERSION, after))
 
+        return installed, modern
+
     def upgrade_mercurial(self, current):
         """Upgrade Mercurial.
 
         Child classes should reimplement this.
 
         Return False to not perform a version check after the upgrade is
         performed.
         """
--- a/python/mozboot/mozboot/bootstrap.py
+++ b/python/mozboot/mozboot/bootstrap.py
@@ -3,16 +3,17 @@
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # If we add unicode_literals, Python 2.6.1 (required for OS X 10.6) breaks.
 from __future__ import print_function
 
 import platform
 import sys
 import os.path
+import subprocess
 
 # Don't forgot to add new mozboot modules to the bootstrap download
 # list in bin/bootstrap.py!
 from mozboot.centosfedora import CentOSFedoraBootstrapper
 from mozboot.debian import DebianBootstrapper
 from mozboot.freebsd import FreeBSDBootstrapper
 from mozboot.gentoo import GentooBootstrapper
 from mozboot.osx import OSXBootstrapper
@@ -91,16 +92,28 @@ instruction here to clone from the Mercu
 
     https://github.com/glandium/git-cinnabar/wiki/Mozilla:-A-git-workflow-for-Gecko-development
 
 Or, if you really prefer vanilla flavor Git:
 
     git clone https://git.mozilla.org/integration/gecko-dev.git
 '''
 
+CONFIGURE_MERCURIAL = '''
+Mozilla recommends a number of changes to Mercurial to enhance your
+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()
+
 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.
@@ -120,20 +133,22 @@ def get_state_dir():
     state_user_dir = os.path.expanduser('~/.mozbuild')
     state_env_dir = os.environ.get('MOZBUILD_STATE_PATH')
     return state_env_dir or state_user_dir
 
 
 class Bootstrapper(object):
     """Main class that performs system bootstrap."""
 
-    def __init__(self, finished=FINISHED, choice=None, no_interactive=False):
+    def __init__(self, finished=FINISHED, choice=None, no_interactive=False,
+                 hg_configure=False):
         self.instance = None
         self.finished = finished
         self.choice = choice
+        self.hg_configure = hg_configure
         cls = None
         args = {'no_interactive': no_interactive}
 
         if sys.platform.startswith('linux'):
             distro, version, dist_id = platform.linux_distribution()
 
             if distro in ('CentOS', 'CentOS Linux', 'Fedora'):
                 cls = CentOSFedoraBootstrapper
@@ -190,17 +205,17 @@ class Bootstrapper(object):
         else:
             name, application = APPLICATIONS[self.choice]
 
         self.instance.install_system_packages()
 
         # Like 'install_browser_packages' or 'install_mobile_android_packages'.
         getattr(self.instance, 'install_%s_packages' % application)()
 
-        self.instance.ensure_mercurial_modern()
+        hg_installed, hg_modern = self.instance.ensure_mercurial_modern()
         self.instance.ensure_python_modern()
 
         # The state directory code is largely duplicated from mach_bootstrap.py.
         # We can't easily import mach_bootstrap.py because the bootstrapper may
         # run in self-contained mode and only the files in this directory will
         # be available. We /could/ refactor parts of mach_bootstrap.py to be
         # part of this directory to avoid the code duplication.
         state_dir = get_state_dir()
@@ -211,12 +226,90 @@ class Bootstrapper(object):
                     prompt=STATE_DIR_INFO.format(statedir=state_dir),
                     low=1,
                     high=2)
 
                 if choice == 1:
                     print('Creating global state directory: %s' % state_dir)
                     os.makedirs(state_dir, mode=0o770)
 
+        state_dir_available = os.path.exists(state_dir)
+
+        # Possibly configure Mercurial if the user wants to.
+        # TODO offer to configure Git.
+        if hg_installed and state_dir_available:
+            configure_hg = False
+            if not self.instance.no_interactive:
+                choice = self.instance.prompt_int(prompt=CONFIGURE_MERCURIAL,
+                                                  low=1, high=2)
+                if choice == 1:
+                    configure_hg = True
+            else:
+                configure_hg = self.hg_configure
+
+            if configure_hg:
+                configure_mercurial(self.instance.which('hg'), state_dir)
+
         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."""
+    vct_dir = os.path.join(root_state_dir, 'version-control-tools')
+
+    # Ensure the latest revision of version-control-tools is present.
+    update_mercurial_repo(hg, 'https://hg.mozilla.org/hgcustom/version-control-tools',
+                          vct_dir, '@')
+
+    return vct_dir
+
+
+def configure_mercurial(hg, root_state_dir):
+    """Run the Mercurial configuration wizard."""
+    vct_dir = update_vct(hg, root_state_dir)
+
+    # Run the config wizard from v-c-t.
+    args = [
+        hg,
+        '--config', 'extensions.configwizard=%s/hgext/configwizard' % vct_dir,
+        'configwizard',
+    ]
+    subprocess.call(args)
+
+
+def update_mercurial_repo(hg, url, dest, revision):
+    """Perform a clone/pull + update of a Mercurial repository."""
+    args = [hg]
+
+    # Disable common extensions whose older versions may cause `hg`
+    # invocations to abort.
+    disable_exts = [
+        'bzexport',
+        'bzpost',
+        'firefoxtree',
+        'hgwatchman',
+        'mozext',
+        'mqext',
+        'qimportbz',
+        'push-to-try',
+        'reviewboard',
+    ]
+    for ext in disable_exts:
+        args.extend(['--config', 'extensions.%s=!' % ext])
+
+    if os.path.exists(dest):
+        args.extend(['pull', url])
+        cwd = dest
+    else:
+        args.extend(['clone', '--noupdate', url, dest])
+        cwd = '/'
+
+    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)