Bug 1277406 - Delete now-unused Mercurial setup wizard; r=glandium draft
authorGregory Szorc <gps@mozilla.com>
Thu, 09 Jun 2016 13:20:53 -0700
changeset 377538 6c1e2363793fe2cd3a506ce5d962788657871203
parent 377537 56697d504ff41ad02d77ddd1241cebafe610751a
child 523376 34a3d6ee298da19e313a9a3e87e50135383fb71c
push id20820
push userbmo:gps@mozilla.com
push dateFri, 10 Jun 2016 16:15:26 +0000
reviewersglandium
bugs1277406
milestone50.0a1
Bug 1277406 - Delete now-unused Mercurial setup wizard; r=glandium The wizard has been ported to the version-control-tools repository and in-tree consumers have been switched to consume it from there. This code is now dead. Kill it. References to the now-defunct code have been removed/updated. MozReview-Commit-ID: 5fpCXdNIp8L
build/mach_bootstrap.py
python/mozboot/mozboot/base.py
tools/mercurial/hgsetup/__init__.py
tools/mercurial/hgsetup/config.py
tools/mercurial/hgsetup/update.py
tools/mercurial/hgsetup/wizard.py
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -85,17 +85,16 @@ SEARCH_PATHS = [
     'testing/mozbase/manifestparser',
     'testing/puppeteer/firefox',
     'testing/taskcluster',
     'testing/tools/autotry',
     'testing/web-platform',
     'testing/web-platform/harness',
     'testing/web-platform/tests/tools/wptserve',
     'testing/xpcshell',
-    'tools/mercurial',
     'xpcom/idl-parser',
 ]
 
 # Individual files providing mach commands.
 MACH_MODULES = [
     'addon-sdk/mach_commands.py',
     'build/valgrind/mach_commands.py',
     'dom/bindings/mach_commands.py',
--- a/python/mozboot/mozboot/base.py
+++ b/python/mozboot/mozboot/base.py
@@ -69,17 +69,17 @@ We recommend the following tools for ins
     pyenv   -- https://github.com/yyuu/pyenv)
     pythonz -- https://github.com/saghul/pythonz
     official installers -- http://www.python.org/
 '''
 
 
 # Upgrade Mercurial older than this.
 # This should match OLDEST_NON_LEGACY_VERSION from
-# tools/mercurial/hgsetup/wizard.py.
+# the hg setup wizard in version-control-tools.
 MODERN_MERCURIAL_VERSION = LooseVersion('3.7.3')
 
 # Upgrade Python older than this.
 MODERN_PYTHON_VERSION = LooseVersion('2.7.3')
 
 
 class BaseBootstrapper(object):
     """Base class for system bootstrappers."""
deleted file mode 100644
deleted file mode 100644
--- a/tools/mercurial/hgsetup/config.py
+++ /dev/null
@@ -1,235 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this,
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from __future__ import unicode_literals
-
-from configobj import ConfigObj
-import codecs
-import re
-import os
-
-
-HOST_FINGERPRINTS = {
-    'bitbucket.org': '3f:d3:c5:17:23:3c:cd:f5:2d:17:76:06:93:7e:ee:97:42:21:14:aa',
-    'bugzilla.mozilla.org': '7c:7a:c4:6c:91:3b:6b:89:cf:f2:8c:13:b8:02:c4:25:bd:1e:25:17',
-    'hg.mozilla.org': 'af:27:b9:34:47:4e:e5:98:01:f6:83:2b:51:c9:aa:d8:df:fb:1a:27',
-}
-
-
-def config_file(files):
-    """Select the most appropriate config file from a list."""
-    if not files:
-        return None
-
-    if len(files) > 1:
-        picky = [(os.path.getsize(f), f) for f in files if os.path.isfile(f)]
-        if picky:
-            return max(picky)[1]
-
-    return files[0]
-
-
-class ParseException(Exception):
-    def __init__(self, line, msg):
-        self.line = line
-        super(Exception, self).__init__(msg)
-
-
-class MercurialConfig(object):
-    """Interface for manipulating a Mercurial config file."""
-
-    def __init__(self, path=None):
-        """Create a new instance, optionally from an existing hgrc file."""
-
-        self.config_path = path
-
-        # Mercurial configuration files allow an %include directive to include
-        # other files, this is not supported by ConfigObj, so throw a useful
-        # error saying this.
-        if os.path.exists(path):
-            with codecs.open(path, 'r', encoding='utf-8') as f:
-                for i, line in enumerate(f):
-                    if line.startswith('%include'):
-                        raise ParseException(i + 1,
-                            '%include directive is not supported by MercurialConfig')
-                    if line.startswith(';'):
-                        raise ParseException(i + 1,
-                            'semicolon (;) comments are not supported; '
-                            'use # instead')
-
-        # write_empty_values is necessary to prevent built-in extensions (which
-        # have no value) from being dropped on write.
-        # list_values aren't needed by Mercurial and disabling them prevents
-        # quotes from being added.
-        self._c = ConfigObj(infile=path, encoding='utf-8',
-            write_empty_values=True, list_values=False)
-
-    @property
-    def config(self):
-        return self._c
-
-    @property
-    def extensions(self):
-        """Returns the set of currently enabled extensions (by name)."""
-        return set(self._c.get('extensions', {}).keys())
-
-    def write(self, fh):
-        return self._c.write(fh)
-
-    def have_valid_username(self):
-        if 'ui' not in self._c:
-            return False
-
-        if 'username' not in self._c['ui']:
-            return False
-
-        # TODO perform actual validation here.
-
-        return True
-
-    def add_mozilla_host_fingerprints(self):
-        """Add host fingerprints so SSL connections don't warn."""
-        if 'hostfingerprints' not in self._c:
-            self._c['hostfingerprints'] = {}
-
-        for k, v in HOST_FINGERPRINTS.items():
-            self._c['hostfingerprints'][k] = v
-
-    def update_mozilla_host_fingerprints(self):
-        """Update host fingerprints if they are present."""
-        if 'hostfingerprints' not in self._c:
-            return
-
-        for k, v in HOST_FINGERPRINTS.items():
-            if k in self._c['hostfingerprints']:
-                self._c['hostfingerprints'][k] = v
-
-    def set_username(self, name, email):
-        """Set the username to use for commits.
-
-        The username consists of a name (typically <firstname> <lastname>) and
-        a well-formed e-mail address.
-        """
-        if 'ui' not in self._c:
-            self._c['ui'] = {}
-
-        username = '%s <%s>' % (name, email)
-
-        self._c['ui']['username'] = username.strip()
-
-    def activate_extension(self, name, path=None):
-        """Activate an extension.
-
-        An extension is defined by its name (in the config) and a filesystem
-        path). For built-in extensions, an empty path is specified.
-        """
-        if not path:
-            path = ''
-
-        if 'extensions' not in self._c:
-            self._c['extensions'] = {}
-
-        self._c['extensions'][name] = path
-
-    def have_recommended_diff_settings(self):
-        if 'diff' not in self._c:
-            return False
-
-        old = dict(self._c['diff'])
-        try:
-            self.ensure_recommended_diff_settings()
-        finally:
-            self._c['diff'].update(old)
-
-        return self._c['diff'] == old
-
-    def ensure_recommended_diff_settings(self):
-        if 'diff' not in self._c:
-            self._c['diff'] = {}
-
-        d = self._c['diff']
-        d['git'] = 1
-        d['showfunc'] = 1
-        d['unified'] = 8
-
-    def get_bugzilla_credentials(self):
-        if 'bugzilla' not in self._c:
-            return None, None, None, None, None
-
-        b = self._c['bugzilla']
-        return (
-            b.get('username', None),
-            b.get('password', None),
-            b.get('userid', None),
-            b.get('cookie', None),
-            b.get('apikey', None),
-        )
-
-    def set_bugzilla_credentials(self, username, api_key):
-        b = self._c.setdefault('bugzilla', {})
-        if username:
-            b['username'] = username
-        if api_key:
-            b['apikey'] = api_key
-
-    def clear_legacy_bugzilla_credentials(self):
-        if 'bugzilla' not in self._c:
-            return
-
-        b = self._c['bugzilla']
-        for k in ('password', 'userid', 'cookie'):
-            if k in b:
-                del b[k]
-
-    def have_clonebundles(self):
-        return 'clonebundles' in self._c.get('experimental', {})
-
-    def activate_clonebundles(self):
-        exp = self._c.setdefault('experimental', {})
-        exp['clonebundles'] = 'true'
-
-        # bundleclone is redundant with clonebundles. Remove it if it
-        # is installed.
-        ext = self._c.get('extensions', {})
-        try:
-            del ext['bundleclone']
-        except KeyError:
-            pass
-
-    def have_wip(self):
-        return 'wip' in self._c.get('alias', {})
-
-    def install_wip_alias(self):
-        """hg wip shows a concise view of work in progress."""
-        alias = self._c.setdefault('alias', {})
-        alias['wip'] = 'log --graph --rev=wip --template=wip'
-
-        revsetalias = self._c.setdefault('revsetalias', {})
-        revsetalias['wip'] = ('('
-                'parents(not public()) '
-                'or not public() '
-                'or . '
-                'or (head() and branch(default))'
-            ') and (not obsolete() or unstable()^) '
-            'and not closed()')
-
-        templates = self._c.setdefault('templates', {})
-        templates['wip'] = ("'"
-            # prefix with branch name
-            '{label("log.branch", branches)} '
-            # rev:node
-            '{label("changeset.{phase}", rev)}'
-            '{label("changeset.{phase}", ":")}'
-            '{label("changeset.{phase}", short(node))} '
-            # just the username part of the author, for brevity
-            '{label("grep.user", author|user)}'
-            # tags and bookmarks
-            '{label("log.tag", if(tags," {tags}"))}'
-            '{label("log.tag", if(fxheads," {fxheads}"))} '
-            '{label("log.bookmark", if(bookmarks," {bookmarks}"))}'
-            '\\n'
-            # first line of commit message
-            '{label(ifcontains(rev, revset("."), "desc.here"),desc|firstline)}'
-            "'"
-        )
deleted file mode 100644
--- a/tools/mercurial/hgsetup/update.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this,
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from __future__ import unicode_literals
-
-import os
-
-from mozversioncontrol import get_hg_path
-from mozversioncontrol.repoupdate import update_mercurial_repo
-
-from .config import (
-    HOST_FINGERPRINTS,
-)
-
-FINISHED = '''
-Your Mercurial recommended extensions are now up to date!
-'''.lstrip()
-
-
-class MercurialUpdater(object):
-
-    def __init__(self, state_dir):
-        self.state_dir = os.path.normpath(state_dir)
-        self.vcs_tools_dir = os.path.join(self.state_dir, 'version-control-tools')
-        self.hgwatchman_dir = os.path.join(self.state_dir, 'hgwatchman')
-
-    def update_all(self):
-        hg = get_hg_path()
-
-        repo_existed = os.path.isdir(self.vcs_tools_dir)
-        self.update_mercurial_repo(
-            hg,
-            'https://hg.mozilla.org/hgcustom/version-control-tools',
-            self.vcs_tools_dir,
-            'default',
-            'Ensuring version-control-tools is up to date...')
-        self.update_mercurial_repo(
-            hg,
-            'https://bitbucket.org/facebook/hgwatchman',
-            self.hgwatchman_dir,
-            '5ca0f920df7ec8a93d322f06d554f778184cdcd1',
-            'Ensuring hgwatchman is up to date...')
-        if repo_existed:
-            print(FINISHED)
-        return 0
-
-    def update_mercurial_repo(self, hg, url, dest, branch, msg):
-        # 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',
-        ]
-        global_args = []
-        for ext in disable_exts:
-            global_args.extend(['--config', 'extensions.%s=!' % ext])
-
-        # We always pass the host fingerprints that we "know" to be canonical
-        # because the existing config may have outdated fingerprints and this
-        # may cause Mercurial to abort.
-        return self._update_repo(hg, url, dest, branch, msg,
-                                 update_mercurial_repo,
-                                 hostfingerprints=HOST_FINGERPRINTS,
-                                 global_args=global_args)
-
-    def _update_repo(self, binary, url, dest, branch, msg, fn, *args, **kwargs):
-        print('=' * 80)
-        print(msg)
-        try:
-            fn(binary, url, dest, branch, *args, **kwargs)
-        finally:
-            print('=' * 80)
-            print('')
deleted file mode 100644
--- a/tools/mercurial/hgsetup/wizard.py
+++ /dev/null
@@ -1,611 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this,
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from __future__ import unicode_literals
-
-import difflib
-import errno
-import os
-import shutil
-import ssl
-import stat
-import sys
-import subprocess
-
-from distutils.version import LooseVersion
-
-from configobj import ConfigObjError
-from StringIO import StringIO
-
-from mozversioncontrol import get_hg_path, get_hg_version
-
-from .update import MercurialUpdater
-from .config import (
-    config_file,
-    MercurialConfig,
-    ParseException,
-)
-
-
-INITIAL_MESSAGE = '''
-I'm going to help you ensure your Mercurial is configured for optimal
-development on Mozilla projects.
-
-If your environment is missing some recommended settings, I'm going to prompt
-you whether you want me to make changes: I won't change anything you might not
-want me changing without your permission!
-
-If your config is up-to-date, I'm just going to ensure all 3rd party extensions
-are up to date and you won't have to do anything.
-
-To begin, press the enter/return key.
-'''.strip()
-
-# This should match MODERN_MERCURIAL_VERSION in
-# python/mozboot/mozboot/base.py.
-OLDEST_NON_LEGACY_VERSION = LooseVersion('3.7.3')
-LEGACY_MERCURIAL = '''
-You are running an out of date Mercurial client (%s).
-
-For a faster and better Mercurial experience, we HIGHLY recommend you
-upgrade.
-
-Legacy versions of Mercurial have known security vulnerabilities. Failure
-to upgrade may leave you exposed. You are highly encouraged to upgrade
-in case you aren't running a patched version.
-'''.strip()
-
-MISSING_USERNAME = '''
-You don't have a username defined in your Mercurial config file. In order to
-send patches to Mozilla, you'll need to attach a name and email address. If you
-aren't comfortable giving us your full name, pseudonames are acceptable.
-
-(Relevant config option: ui.username)
-'''.strip()
-
-BAD_DIFF_SETTINGS = '''
-Mozilla developers produce patches in a standard format, but your Mercurial is
-not configured to produce patches in that format.
-
-(Relevant config options: diff.git, diff.showfunc, diff.unified)
-'''.strip()
-
-BZEXPORT_INFO = '''
-If you plan on uploading patches to Mozilla, there is an extension called
-bzexport that makes it easy to upload patches from the command line via the
-|hg bzexport| command. More info is available at
-https://hg.mozilla.org/hgcustom/version-control-tools/file/default/hgext/bzexport/README
-
-(Relevant config option: extensions.bzexport)
-
-Would you like to activate bzexport
-'''.strip()
-
-FINISHED = '''
-Your Mercurial should now be properly configured and recommended extensions
-should be up to date!
-'''.strip()
-
-REVIEWBOARD_MINIMUM_VERSION = LooseVersion('3.5')
-
-REVIEWBOARD_INCOMPATIBLE = '''
-Your Mercurial is too old to use the reviewboard extension, which is necessary
-to conduct code review.
-
-Please upgrade to Mercurial %s or newer to use this extension.
-'''.strip()
-
-MISSING_BUGZILLA_CREDENTIALS = '''
-You do not have your Bugzilla API Key defined in your Mercurial config.
-
-Various extensions make use of a Bugzilla API Key to interface with
-Bugzilla to enrich your development experience.
-
-The Bugzilla API Key is optional. If you do not provide one, associated
-functionality will not be enabled, we will attempt to find a Bugzilla cookie
-from a Firefox profile, or you will be prompted for your Bugzilla credentials
-when they are needed.
-
-You should only need to configure a Bugzilla API Key once.
-'''.lstrip()
-
-BUGZILLA_API_KEY_INSTRUCTIONS = '''
-Bugzilla API Keys can only be obtained through the Bugzilla web interface.
-
-Please perform the following steps:
-
-  1) Open https://bugzilla.mozilla.org/userprefs.cgi?tab=apikey
-  2) Generate a new API Key
-  3) Copy the generated key and paste it here
-'''.lstrip()
-
-LEGACY_BUGZILLA_CREDENTIALS_DETECTED = '''
-Your existing Mercurial config uses a legacy method for defining Bugzilla
-credentials. Bugzilla API Keys are the most secure and preferred method
-for defining Bugzilla credentials. Bugzilla API Keys are also required
-if you have enabled 2 Factor Authentication in Bugzilla.
-
-All consumers formerly looking at these options should support API Keys.
-'''.lstrip()
-
-BZPOST_MINIMUM_VERSION = LooseVersion('3.5')
-
-BZPOST_INFO = '''
-The bzpost extension automatically records the URLs of pushed commits to
-referenced Bugzilla bugs after push.
-
-(Relevant config option: extensions.bzpost)
-
-Would you like to activate bzpost
-'''.strip()
-
-FIREFOXTREE_MINIMUM_VERSION = LooseVersion('3.5')
-
-FIREFOXTREE_INFO = '''
-The firefoxtree extension makes interacting with the multiple Firefox
-repositories easier:
-
-* Aliases for common trees are pre-defined. e.g. `hg pull central`
-* Pulling from known Firefox trees will create "remote refs" appearing as
-  tags. e.g. pulling from fx-team will produce a "fx-team" tag.
-* The `hg fxheads` command will list the heads of all pulled Firefox repos
-  for easy reference.
-* `hg push` will limit itself to pushing a single head when pushing to
-  Firefox repos.
-* A pre-push hook will prevent you from pushing multiple heads to known
-  Firefox repos. This acts quicker than a server-side hook.
-
-The firefoxtree extension is *strongly* recommended if you:
-
-a) aggregate multiple Firefox repositories into a single local repo
-b) perform head/bookmark-based development (as opposed to mq)
-
-(Relevant config option: extensions.firefoxtree)
-
-Would you like to activate firefoxtree
-'''.strip()
-
-PUSHTOTRY_MINIMUM_VERSION = LooseVersion('3.5')
-
-PUSHTOTRY_INFO = '''
-The push-to-try extension generates a temporary commit with a given
-try syntax and pushes it to the try server. The extension is intended
-to be used in concert with other tools generating try syntax so that
-they can push to try without depending on mq or other workarounds.
-
-(Relevant config option: extensions.push-to-try)
-
-Would you like to activate push-to-try
-'''.strip()
-
-CLONEBUNDLES_INFO = '''
-Mercurial 3.6 and hg.mozilla.org support transparently cloning from a CDN,
-making clones faster and more reliable.
-
-(Relevant config option: experimental.clonebundles)
-
-Would you like to activate this feature and have faster clones
-'''.strip()
-
-BUNDLECLONE_MINIMUM_VERSION = LooseVersion('3.1')
-
-BUNDLECLONE_INFO = '''
-The bundleclone extension makes cloning faster and saves server resources.
-
-We highly recommend you activate this extension.
-
-(Relevant config option: extensions.bundleclone)
-
-Would you like to activate bundleclone
-'''.strip()
-
-WIP_INFO = '''
-It is common to want a quick view of changesets that are in progress.
-
-The ``hg wip`` command provides should a view.
-
-Example Usage:
-
-  $ hg wip
-  o   4084:fcfa34d0387b dminor  @
-  |  mozreview: use repository name when displaying treeherder results (bug 1230548) r=mcote
-  | @   4083:786baf6d476a gps
-  | |  mozreview: create child review requests from batch API
-  | o   4082:3f100fa4a94f gps
-  | |  mozreview: copy more read-only processing code; r?smacleod
-  | o   4081:939417680cbe gps
-  |/   mozreview: add web API to submit an entire series of commits (bug 1229468); r?smacleod
-
-(Not shown are the colors that help denote the state each changeset
-is in.)
-
-(Relevant config options: alias.wip, revsetalias.wip, templates.wip)
-
-Would you like to install the `hg wip` alias?
-'''.strip()
-
-HGWATCHMAN_MINIMUM_VERSION = LooseVersion('3.5.2')
-FSMONITOR_MINIMUM_VERSION = LooseVersion('3.8')
-
-FSMONITOR_INFO = '''
-Filesystem monitor integrates the watchman filesystem watching
-tool with Mercurial. Commands like `hg status`, `hg diff`, and
-`hg commit` that need to examine filesystem state can query watchman
-and obtain filesystem state nearly instantaneously. The result is much
-faster command execution.
-
-When enabled, the filesystem monitor tool will launch a background
-watchman file watching daemon for accessed Mercurial repositories.
-
-Would you like to enable filesystem monitor tool
-'''.strip()
-
-FILE_PERMISSIONS_WARNING = '''
-Your hgrc file is currently readable by others.
-
-Sensitive information such as your Bugzilla credentials could be
-stolen if others have access to this file/machine.
-'''.strip()
-
-MULTIPLE_VCT = '''
-*** WARNING ***
-
-Multiple version-control-tools repositories are referenced in your
-Mercurial config. Extensions and other code within the
-version-control-tools repository could run with inconsistent results.
-
-Please manually edit the following file to reference a single
-version-control-tools repository:
-
-    %s
-'''.lstrip()
-
-
-class MercurialSetupWizard(object):
-    """Command-line wizard to help users configure Mercurial."""
-
-    def __init__(self, state_dir):
-        # We use normpath since Mercurial expects the hgrc to use native path
-        # separators, but state_dir uses unix style paths even on Windows.
-        self.state_dir = os.path.normpath(state_dir)
-        self.ext_dir = os.path.join(self.state_dir, 'mercurial', 'extensions')
-        self.vcs_tools_dir = os.path.join(self.state_dir, 'version-control-tools')
-        self.updater = MercurialUpdater(state_dir)
-
-    def run(self, config_paths):
-        try:
-            os.makedirs(self.ext_dir)
-        except OSError as e:
-            if e.errno != errno.EEXIST:
-                raise
-
-        hg = get_hg_path()
-        config_path = config_file(config_paths)
-
-        try:
-            c = MercurialConfig(config_path)
-        except ConfigObjError as e:
-            print('Error importing existing Mercurial config: %s\n' % config_path)
-            for error in e.errors:
-                print(error.message)
-
-            return 1
-        except ParseException as e:
-            print('Error importing existing Mercurial config: %s\n' % config_path)
-            print('Line %d: %s' % (e.line, e.message))
-
-            return 1
-
-        self.updater.update_all()
-
-        print(INITIAL_MESSAGE)
-        raw_input()
-
-        hg_version = get_hg_version(hg)
-        if hg_version < OLDEST_NON_LEGACY_VERSION:
-            print(LEGACY_MERCURIAL % hg_version)
-            print('')
-
-            if os.name == 'nt':
-                print('Please upgrade to the latest MozillaBuild to upgrade '
-                    'your Mercurial install.')
-                print('')
-            else:
-                print('Please run |mach bootstrap| to upgrade your Mercurial '
-                    'install.')
-                print('')
-
-            if not self._prompt_yn('Would you like to continue using an old '
-                'Mercurial version'):
-                return 1
-
-        if not c.have_valid_username():
-            print(MISSING_USERNAME)
-            print('')
-
-            name = self._prompt('What is your name?')
-            email = self._prompt('What is your email address?')
-            c.set_username(name, email)
-            print('Updated your username.')
-            print('')
-
-        if not c.have_recommended_diff_settings():
-            print(BAD_DIFF_SETTINGS)
-            print('')
-            if self._prompt_yn('Would you like me to fix this for you'):
-                c.ensure_recommended_diff_settings()
-                print('Fixed patch settings.')
-                print('')
-
-        # Progress is built into core and enabled by default in Mercurial 3.5.
-        if hg_version < LooseVersion('3.5'):
-            self.prompt_native_extension(c, 'progress',
-                'Would you like to see progress bars during Mercurial operations')
-
-        self.prompt_native_extension(c, 'color',
-            'Would you like Mercurial to colorize output to your terminal')
-
-        self.prompt_native_extension(c, 'rebase',
-            'Would you like to enable the rebase extension to allow you to move'
-            ' changesets around (which can help maintain a linear history)')
-
-        self.prompt_native_extension(c, 'histedit',
-            'Would you like to enable the histedit extension to allow history '
-            'rewriting via the "histedit" command (similar to '
-            '`git rebase -i`)')
-
-        # hgwatchman is provided by MozillaBuild and we don't yet support
-        # Linux/BSD.
-        # Note that the hgwatchman project has been renamed to fsmonitor and has
-        # been moved into Mercurial core, as of version 3.8. So, if your Mercurial
-        # version is modern enough (>=3.8), you could set fsmonitor in hgrc file
-        # directly.
-        if ('hgwatchman' not in c.extensions
-            and sys.platform.startswith('darwin')
-            and hg_version >= HGWATCHMAN_MINIMUM_VERSION
-            and self._prompt_yn(FSMONITOR_INFO)):
-            if (hg_version >= FSMONITOR_MINIMUM_VERSION):
-                c.activate_extension('fsmonitor')
-            else:
-                # Unlike other extensions, we need to run an installer
-                # to compile a Python C extension.
-                try:
-                    subprocess.check_output(
-                        ['make', 'local'],
-                        cwd=self.updater.hgwatchman_dir,
-                        stderr=subprocess.STDOUT)
-
-                    ext_path = os.path.join(self.updater.hgwatchman_dir,
-                                            'hgwatchman')
-                    if self.can_use_extension(c, 'hgwatchman', ext_path):
-                        c.activate_extension('hgwatchman', ext_path)
-                except subprocess.CalledProcessError as e:
-                    print('Error compiling hgwatchman; will not install hgwatchman')
-                    print(e.output)
-
-        if 'reviewboard' not in c.extensions:
-            if hg_version < REVIEWBOARD_MINIMUM_VERSION:
-                print(REVIEWBOARD_INCOMPATIBLE % REVIEWBOARD_MINIMUM_VERSION)
-            else:
-                p = os.path.join(self.vcs_tools_dir, 'hgext', 'reviewboard',
-                    'client.py')
-                self.prompt_external_extension(c, 'reviewboard',
-                    'Would you like to enable the reviewboard extension so '
-                    'you can easily initiate code reviews against Mozilla '
-                    'projects',
-                    path=p)
-
-        self.prompt_external_extension(c, 'bzexport', BZEXPORT_INFO)
-
-        if hg_version >= BZPOST_MINIMUM_VERSION:
-            self.prompt_external_extension(c, 'bzpost', BZPOST_INFO)
-
-        if hg_version >= FIREFOXTREE_MINIMUM_VERSION:
-            self.prompt_external_extension(c, 'firefoxtree', FIREFOXTREE_INFO)
-
-        # Functionality from bundleclone is experimental in Mercurial 3.6.
-        # There was a bug in 3.6, so look for 3.6.1.
-        if hg_version >= LooseVersion('3.6.1'):
-            if not c.have_clonebundles() and self._prompt_yn(CLONEBUNDLES_INFO):
-                c.activate_clonebundles()
-                print('Enabled the clonebundles feature.\n')
-        elif hg_version >= BUNDLECLONE_MINIMUM_VERSION:
-            self.prompt_external_extension(c, 'bundleclone', BUNDLECLONE_INFO)
-
-        if hg_version >= PUSHTOTRY_MINIMUM_VERSION:
-            self.prompt_external_extension(c, 'push-to-try', PUSHTOTRY_INFO)
-
-        if not c.have_wip():
-            if self._prompt_yn(WIP_INFO):
-                c.install_wip_alias()
-
-        if 'reviewboard' in c.extensions or 'bzpost' in c.extensions:
-            bzuser, bzpass, bzuserid, bzcookie, bzapikey = c.get_bugzilla_credentials()
-
-            if not bzuser or not bzapikey:
-                print(MISSING_BUGZILLA_CREDENTIALS)
-
-            if not bzuser:
-                bzuser = self._prompt('What is your Bugzilla email address? (optional)',
-                    allow_empty=True)
-
-            if bzuser and not bzapikey:
-                print(BUGZILLA_API_KEY_INSTRUCTIONS)
-                bzapikey = self._prompt('Please enter a Bugzilla API Key: (optional)',
-                    allow_empty=True)
-
-            if bzuser or bzapikey:
-                c.set_bugzilla_credentials(bzuser, bzapikey)
-
-            if bzpass or bzuserid or bzcookie:
-                print(LEGACY_BUGZILLA_CREDENTIALS_DETECTED)
-
-                # Clear legacy credentials automatically if an API Key is
-                # found as it supercedes all other credentials.
-                if bzapikey:
-                    print('The legacy credentials have been removed.\n')
-                    c.clear_legacy_bugzilla_credentials()
-                elif self._prompt_yn('Remove legacy credentials'):
-                    c.clear_legacy_bugzilla_credentials()
-
-        # Look for and clean up old extensions.
-        for ext in {'bzexport', 'qimportbz', 'mqext'}:
-            path = os.path.join(self.ext_dir, ext)
-            if os.path.exists(path):
-                if self._prompt_yn('Would you like to remove the old and no '
-                    'longer referenced repository at %s' % path):
-                    print('Cleaning up old repository: %s' % path)
-                    shutil.rmtree(path)
-
-        # Python + Mercurial didn't have terrific TLS handling until Python
-        # 2.7.9 and Mercurial 3.4. For this reason, it was recommended to pin
-        # certificates in Mercurial config files. In modern versions of
-        # Mercurial, the system CA store is used and old, legacy TLS protocols
-        # are disabled. The default connection/security setting should
-        # be sufficient and pinning certificates is no longer needed.
-        have_modern_ssl = hasattr(ssl, 'SSLContext')
-        if hg_version < LooseVersion('3.4') or not have_modern_ssl:
-            c.add_mozilla_host_fingerprints()
-
-        # We always update fingerprints if they are present. We /could/ offer to
-        # remove fingerprints if running modern Python and Mercurial. But that
-        # just adds more UI complexity and isn't worth it.
-        c.update_mozilla_host_fingerprints()
-
-        # References to multiple version-control-tools checkouts can confuse
-        # version-control-tools, since various Mercurial extensions resolve
-        # dependencies via __file__ and repos could reference another copy.
-        seen_vct = set()
-        for k, v in c.config.get('extensions', {}).items():
-            if 'version-control-tools' not in v:
-                continue
-
-            i = v.index('version-control-tools')
-            vct = v[0:i + len('version-control-tools')]
-            seen_vct.add(os.path.realpath(os.path.expanduser(vct)))
-
-        if len(seen_vct) > 1:
-            print(MULTIPLE_VCT % c.config_path)
-
-        # At this point the config should be finalized.
-
-        b = StringIO()
-        c.write(b)
-        new_lines = [line.rstrip() for line in b.getvalue().splitlines()]
-        old_lines = []
-
-        config_path = c.config_path
-        if os.path.exists(config_path):
-            with open(config_path, 'rt') as fh:
-                old_lines = [line.rstrip() for line in fh.readlines()]
-
-        diff = list(difflib.unified_diff(old_lines, new_lines,
-            'hgrc.old', 'hgrc.new'))
-
-        if len(diff):
-            print('Your Mercurial config file needs updating. I can do this '
-                'for you if you like!')
-            if self._prompt_yn('Would you like to see a diff of the changes '
-                'first'):
-                for line in diff:
-                    print(line)
-                print('')
-
-            if self._prompt_yn('Would you like me to update your hgrc file'):
-                with open(config_path, 'wt') as fh:
-                    c.write(fh)
-                print('Wrote changes to %s.' % config_path)
-            else:
-                print('hgrc changes not written to file. I would have '
-                    'written the following:\n')
-                c.write(sys.stdout)
-                return 1
-
-        if sys.platform != 'win32':
-            # Config file may contain sensitive content, such as passwords.
-            # Prompt to remove global permissions.
-            mode = os.stat(config_path).st_mode
-            if mode & (stat.S_IRWXG | stat.S_IRWXO):
-                print(FILE_PERMISSIONS_WARNING)
-                if self._prompt_yn('Remove permissions for others to '
-                                   'read your hgrc file'):
-                    # We don't care about sticky and set UID bits because
-                    # this is a regular file.
-                    mode = mode & stat.S_IRWXU
-                    print('Changing permissions of %s' % config_path)
-                    os.chmod(config_path, mode)
-
-        print(FINISHED)
-        return 0
-
-    def prompt_native_extension(self, c, name, prompt_text):
-        # Ask the user if the specified extension bundled with Mercurial should be enabled.
-        if name in c.extensions:
-            return
-        if self._prompt_yn(prompt_text):
-            c.activate_extension(name)
-            print('Activated %s extension.\n' % name)
-
-    def can_use_extension(self, c, name, path=None):
-        # Load extension to hg and search stdout for printed exceptions
-        if not path:
-            path = os.path.join(self.vcs_tools_dir, 'hgext', name)
-        result = subprocess.check_output(['hg',
-             '--config', 'extensions.testmodule=%s' % path,
-             '--config', 'ui.traceback=true'],
-            stderr=subprocess.STDOUT)
-        return b"Traceback" not in result
-
-    def prompt_external_extension(self, c, name, prompt_text, path=None):
-        # Ask the user if the specified extension should be enabled. Defaults
-        # to treating the extension as one in version-control-tools/hgext/
-        # in a directory with the same name as the extension and thus also
-        # flagging the version-control-tools repo as needing an update.
-        if name not in c.extensions:
-            if not self.can_use_extension(c, name, path):
-                return
-            print(name)
-            print('=' * len(name))
-            print('')
-            if not self._prompt_yn(prompt_text):
-                print('')
-                return
-        if not path:
-            # We replace the user's home directory with ~ so the
-            # config file doesn't depend on the path to the home
-            # directory
-            path = os.path.join(self.vcs_tools_dir.replace(os.path.expanduser('~'), '~'), 'hgext', name)
-        c.activate_extension(name, path)
-        print('Activated %s extension.\n' % name)
-
-    def _prompt(self, msg, allow_empty=False):
-        print(msg)
-
-        while True:
-            response = raw_input().decode('utf-8')
-
-            if response:
-                return response
-
-            if allow_empty:
-                return None
-
-            print('You must type something!')
-
-    def _prompt_yn(self, msg):
-        print('%s? [Y/n]' % msg)
-
-        while True:
-            choice = raw_input().lower().strip()
-
-            if not choice:
-                return True
-
-            if choice in ('y', 'yes'):
-                return True
-
-            if choice in ('n', 'no'):
-                return False
-
-            print('Must reply with one of {yes, no, y, n}.')