From f0ab95aa0826e3c3ba47acc1cddee23cd5f18ee4 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 9 Jun 2016 13:20:53 -0700 Subject: [PATCH] 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 --HG-- extra : rebase_source : 6c1e2363793fe2cd3a506ce5d962788657871203 extra : histedit_source : c40d2203aaa54bbd48e4e2b46178e277dcdc2e3f --- build/mach_bootstrap.py | 1 - python/mozboot/mozboot/base.py | 2 +- tools/mercurial/hgsetup/__init__.py | 0 tools/mercurial/hgsetup/config.py | 235 ----------- tools/mercurial/hgsetup/update.py | 81 ---- tools/mercurial/hgsetup/wizard.py | 611 ---------------------------- 6 files changed, 1 insertion(+), 929 deletions(-) delete mode 100644 tools/mercurial/hgsetup/__init__.py delete mode 100644 tools/mercurial/hgsetup/config.py delete mode 100644 tools/mercurial/hgsetup/update.py delete mode 100644 tools/mercurial/hgsetup/wizard.py diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py index 772debf7a3f6..8d0cff0db647 100644 --- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -90,7 +90,6 @@ SEARCH_PATHS = [ 'testing/web-platform/harness', 'testing/web-platform/tests/tools/wptserve', 'testing/xpcshell', - 'tools/mercurial', 'xpcom/idl-parser', ] diff --git a/python/mozboot/mozboot/base.py b/python/mozboot/mozboot/base.py index fbb1a146834c..3a94df8c1e0e 100644 --- a/python/mozboot/mozboot/base.py +++ b/python/mozboot/mozboot/base.py @@ -74,7 +74,7 @@ We recommend the following tools for installing Python: # 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. diff --git a/tools/mercurial/hgsetup/__init__.py b/tools/mercurial/hgsetup/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/tools/mercurial/hgsetup/config.py b/tools/mercurial/hgsetup/config.py deleted file mode 100644 index 646fa0985f63..000000000000 --- 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 ) 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)}' - "'" - ) diff --git a/tools/mercurial/hgsetup/update.py b/tools/mercurial/hgsetup/update.py deleted file mode 100644 index 8b306b7bf440..000000000000 --- 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('') diff --git a/tools/mercurial/hgsetup/wizard.py b/tools/mercurial/hgsetup/wizard.py deleted file mode 100644 index f0912a95d496..000000000000 --- 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}.')