Beispiel #1
0
    def run(self, config_paths):
        try:
            os.makedirs(self.ext_dir)
        except OSError as e:
            if e.errno != errno.EEXIST:
                raise

        try:
            hg = which.which('hg')
        except which.WhichError as e:
            print(e)
            print('Try running |mach bootstrap| to ensure your environment is '
                'up to date.')
            return 1

        try:
            c = MercurialConfig(config_paths)
        except ConfigObjError as e:
            print('Error importing existing Mercurial config!\n')
            for error in e.errors:
                print(error.message)

            return 1
        except HgIncludeException as e:
            print(e.message)

            return 1

        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('')

        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`)')

        self.prompt_native_extension(c, 'mq', MQ_INFO)

        self.prompt_external_extension(c, 'bzexport', BZEXPORT_INFO)

        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)

        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)

        if 'mq' in c.extensions:
            self.prompt_external_extension(c, 'mqext', MQEXT_INFO,
                                           os.path.join(self.ext_dir, 'mqext'))

            if 'mqext' in c.extensions:
                self.update_mercurial_repo(
                    hg,
                    'https://bitbucket.org/sfink/mqext',
                    os.path.join(self.ext_dir, 'mqext'),
                    'default',
                    'Ensuring mqext extension is up to date...')

            if 'mqext' in c.extensions and not c.have_mqext_autocommit_mq():
                if self._prompt_yn('Would you like to configure mqext to '
                    'automatically commit changes as you modify patches'):
                    c.ensure_mqext_autocommit_mq()
                    print('Configured mqext to auto-commit.\n')

            self.prompt_external_extension(c, 'qimportbz', QIMPORTBZ_INFO)

            if not c.have_qnew_currentuser_default():
                print(QNEWCURRENTUSER_INFO)
                if self._prompt_yn('Would you like qnew to set patch author by '
                                   'default'):
                    c.ensure_qnew_currentuser_default()
                    print('Configured qnew to set patch author by default.')
                    print('')

        if 'reviewboard' in c.extensions or 'bzpost' in c.extensions:
            bzuser, bzpass = c.get_bugzilla_credentials()

            if not bzuser or not bzpass:
                print(MISSING_BUGZILLA_CREDENTIALS)

            if not bzuser:
                bzuser = self._prompt('What is your Bugzilla email address?',
                    allow_empty=True)

            if bzuser and not bzpass:
                bzpass = self._prompt('What is your Bugzilla password?',
                    allow_empty=True)

            if bzuser or bzpass:
                c.set_bugzilla_credentials(bzuser, bzpass)

        if self.update_vcs_tools:
            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...')

        # Look for and clean up old extensions.
        for ext in {'bzexport', 'qimportbz'}:
            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)

        c.add_mozilla_host_fingerprints()

        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

        print(FINISHED)
        return 0
Beispiel #2
0
    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
Beispiel #3
0
    def run(self, config_paths):
        try:
            os.makedirs(self.ext_dir)
        except OSError as e:
            if e.errno != errno.EEXIST:
                raise

        # We use subprocess in places, which expects a Win32 executable or
        # batch script. On some versions of MozillaBuild, we have "hg.exe",
        # "hg.bat," and "hg" (a Python script). "which" will happily return the
        # Python script, which will cause subprocess to choke. Explicitly favor
        # the Windows version over the plain script.
        try:
            hg = which.which('hg.exe')
        except which.WhichError:
            try:
                hg = which.which('hg')
            except which.WhichError as e:
                print(e)
                print(
                    'Try running |mach bootstrap| to ensure your environment is '
                    'up to date.')
                return 1

        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

        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`)')

        self.prompt_native_extension(c, 'mq', MQ_INFO)

        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)

        if 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 'mq' in c.extensions:
            self.prompt_external_extension(c, 'mqext', MQEXT_INFO)

            if 'mqext' in c.extensions and not c.have_mqext_autocommit_mq():
                if self._prompt_yn(
                        'Would you like to configure mqext to '
                        'automatically commit changes as you modify patches'):
                    c.ensure_mqext_autocommit_mq()
                    print('Configured mqext to auto-commit.\n')

            self.prompt_external_extension(c, 'qimportbz', QIMPORTBZ_INFO)

            if not c.have_qnew_currentuser_default():
                print(QNEWCURRENTUSER_INFO)
                if self._prompt_yn(
                        'Would you like qnew to set patch author by '
                        'default'):
                    c.ensure_qnew_currentuser_default()
                    print('Configured qnew to set patch author by default.')
                    print('')

        if 'reviewboard' in c.extensions or 'bzpost' in c.extensions:
            bzuser, bzpass, bzuserid, bzcookie = c.get_bugzilla_credentials()

            if (not bzuser or not bzpass) and (not bzuserid or not bzcookie):
                print(MISSING_BUGZILLA_CREDENTIALS)

            # Don't prompt for username if cookie is set.
            if not bzuser and not bzuserid:
                bzuser = self._prompt(
                    'What is your Bugzilla email address? (optional)',
                    allow_empty=True)

            if bzuser and not bzpass:
                bzpass = self._prompt(
                    'What is your Bugzilla password? (optional)',
                    allow_empty=True)

            if bzuser or bzpass:
                c.set_bugzilla_credentials(bzuser, bzpass)

        if self.update_vcs_tools:
            self.updater.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...')

        # 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)

        c.add_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

        # 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
Beispiel #4
0
    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.
        if (
            "hgwatchman" not in c.extensions
            and sys.platform.startswith("darwin")
            and hg_version >= HGWATCHMAN_MINIMUM_VERSION
            and self._prompt_yn(HGWATCHMAN_INFO)
        ):
            # 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)

        self.prompt_native_extension(c, "mq", MQ_INFO)

        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 "mq" in c.extensions:
            self.prompt_external_extension(c, "mqext", MQEXT_INFO)

            if "mqext" in c.extensions and not c.have_mqext_autocommit_mq():
                if self._prompt_yn(
                    "Would you like to configure mqext to " "automatically commit changes as you modify patches"
                ):
                    c.ensure_mqext_autocommit_mq()
                    print("Configured mqext to auto-commit.\n")

            self.prompt_external_extension(c, "qimportbz", QIMPORTBZ_INFO)

            if not c.have_qnew_currentuser_default():
                print(QNEWCURRENTUSER_INFO)
                if self._prompt_yn("Would you like qnew to set patch author by " "default"):
                    c.ensure_qnew_currentuser_default()
                    print("Configured qnew to set patch author by default.")
                    print("")

        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
Beispiel #5
0
    def run(self, config_paths):
        try:
            os.makedirs(self.ext_dir)
        except OSError as e:
            if e.errno != errno.EEXIST:
                raise

        try:
            hg = which.which('hg')
        except which.WhichError as e:
            print(e)
            print('Try running |mach bootstrap| to ensure your environment is '
                  'up to date.')
            return 1

        try:
            c = MercurialConfig(config_paths)
        except ConfigObjError as e:
            print('Error importing existing Mercurial config!\n')
            for error in e.errors:
                print(error.message)

            return 1
        except HgIncludeException as e:
            print(e.message)

            return 1

        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('')

        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`)')

        self.prompt_native_extension(c, 'mq', MQ_INFO)

        self.prompt_external_extension(c, 'bzexport', BZEXPORT_INFO)

        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)

        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)

        if 'mq' in c.extensions:
            self.prompt_external_extension(c, 'mqext', MQEXT_INFO,
                                           os.path.join(self.ext_dir, 'mqext'))

            if 'mqext' in c.extensions:
                self.updater.update_mercurial_repo(
                    hg, 'https://bitbucket.org/sfink/mqext',
                    os.path.join(self.ext_dir, 'mqext'), 'default',
                    'Ensuring mqext extension is up to date...')

            if 'mqext' in c.extensions and not c.have_mqext_autocommit_mq():
                if self._prompt_yn(
                        'Would you like to configure mqext to '
                        'automatically commit changes as you modify patches'):
                    c.ensure_mqext_autocommit_mq()
                    print('Configured mqext to auto-commit.\n')

            self.prompt_external_extension(c, 'qimportbz', QIMPORTBZ_INFO)

            if not c.have_qnew_currentuser_default():
                print(QNEWCURRENTUSER_INFO)
                if self._prompt_yn(
                        'Would you like qnew to set patch author by '
                        'default'):
                    c.ensure_qnew_currentuser_default()
                    print('Configured qnew to set patch author by default.')
                    print('')

        if 'reviewboard' in c.extensions or 'bzpost' in c.extensions:
            bzuser, bzpass = c.get_bugzilla_credentials()

            if not bzuser or not bzpass:
                print(MISSING_BUGZILLA_CREDENTIALS)

            if not bzuser:
                bzuser = self._prompt('What is your Bugzilla email address?',
                                      allow_empty=True)

            if bzuser and not bzpass:
                bzpass = self._prompt('What is your Bugzilla password?',
                                      allow_empty=True)

            if bzuser or bzpass:
                c.set_bugzilla_credentials(bzuser, bzpass)

        if self.update_vcs_tools:
            self.updater.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...')

        # Look for and clean up old extensions.
        for ext in {'bzexport', 'qimportbz'}:
            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)

        c.add_mozilla_host_fingerprints()

        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

        print(FINISHED)
        return 0
Beispiel #6
0
    def run(self, config_paths):
        try:
            os.makedirs(self.ext_dir)
        except OSError as e:
            if e.errno != errno.EEXIST:
                raise

        # We use subprocess in places, which expects a Win32 executable or
        # batch script. On some versions of MozillaBuild, we have "hg.exe",
        # "hg.bat," and "hg" (a Python script). "which" will happily return the
        # Python script, which will cause subprocess to choke. Explicitly favor
        # the Windows version over the plain script.
        try:
            hg = which.which("hg.exe")
        except which.WhichError:
            try:
                hg = which.which("hg")
            except which.WhichError as e:
                print(e)
                print("Try running |mach bootstrap| to ensure your environment is " "up to date.")
                return 1

        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

        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`)",
        )

        self.prompt_native_extension(c, "mq", MQ_INFO)

        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)

        if 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 "mq" in c.extensions:
            self.prompt_external_extension(c, "mqext", MQEXT_INFO)

            if "mqext" in c.extensions and not c.have_mqext_autocommit_mq():
                if self._prompt_yn(
                    "Would you like to configure mqext to " "automatically commit changes as you modify patches"
                ):
                    c.ensure_mqext_autocommit_mq()
                    print("Configured mqext to auto-commit.\n")

            self.prompt_external_extension(c, "qimportbz", QIMPORTBZ_INFO)

            if not c.have_qnew_currentuser_default():
                print(QNEWCURRENTUSER_INFO)
                if self._prompt_yn("Would you like qnew to set patch author by " "default"):
                    c.ensure_qnew_currentuser_default()
                    print("Configured qnew to set patch author by default.")
                    print("")

        if "reviewboard" in c.extensions or "bzpost" in c.extensions:
            bzuser, bzpass, bzuserid, bzcookie = c.get_bugzilla_credentials()

            if (not bzuser or not bzpass) and (not bzuserid or not bzcookie):
                print(MISSING_BUGZILLA_CREDENTIALS)

            # Don't prompt for username if cookie is set.
            if not bzuser and not bzuserid:
                bzuser = self._prompt("What is your Bugzilla email address? (optional)", allow_empty=True)

            if bzuser and not bzpass:
                bzpass = self._prompt("What is your Bugzilla password? (optional)", allow_empty=True)

            if bzuser or bzpass:
                c.set_bugzilla_credentials(bzuser, bzpass)

        if self.update_vcs_tools:
            self.updater.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...",
            )

        # 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)

        c.add_mozilla_host_fingerprints()

        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

        # 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
Beispiel #7
0
    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`)')

        self.prompt_native_extension(c, 'mq', MQ_INFO)

        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)

        if 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 'mq' in c.extensions:
            self.prompt_external_extension(c, 'mqext', MQEXT_INFO)

            if 'mqext' in c.extensions and not c.have_mqext_autocommit_mq():
                if self._prompt_yn('Would you like to configure mqext to '
                    'automatically commit changes as you modify patches'):
                    c.ensure_mqext_autocommit_mq()
                    print('Configured mqext to auto-commit.\n')

            self.prompt_external_extension(c, 'qimportbz', QIMPORTBZ_INFO)

            if not c.have_qnew_currentuser_default():
                print(QNEWCURRENTUSER_INFO)
                if self._prompt_yn('Would you like qnew to set patch author by '
                                   'default'):
                    c.ensure_qnew_currentuser_default()
                    print('Configured qnew to set patch author by default.')
                    print('')

        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)

        c.add_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