mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
bug 1483941 - prompt to enable build system telemetry in bootstrap. r=nalexander
This change adds a prompt to enable build system telemetry as part of bootstrap. The prompt will only be shown if the build.telemetry config value is not present, so users will not be prompted again if they have already opted-in. However, if a user answers 'no' we don't save that value to the config file because the default is to not send telemetry, so unless they manually add `telemetry = false` to their config file they will be prompted again the next time they run bootstrap. The config value is always written to `~/.mozbuild/machrc` where we store other Firefox build-related state. A standalone function is used to write the config file so that we can do so even when running from bootstrap.py outside of the context of a mach command. As part of this change a `prompt_yesno` method is added to `BaseBootstrapper`. Differential Revision: https://phabricator.services.mozilla.com/D9781 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
405251a9f4
commit
8a92d6f72c
@ -55,6 +55,7 @@ with Files('mach/docs/**'):
|
||||
|
||||
PYTHON_UNITTEST_MANIFESTS += [
|
||||
'mach/mach/test/python.ini',
|
||||
'mozboot/mozboot/test/python.ini',
|
||||
'mozbuild/dumbmake/test/python.ini',
|
||||
'mozlint/test/python.ini',
|
||||
'mozrelease/test/python.ini',
|
||||
|
@ -408,6 +408,20 @@ class BaseBootstrapper(object):
|
||||
else:
|
||||
raise Exception("Error! Reached max attempts of entering option.")
|
||||
|
||||
def prompt_yesno(self, prompt):
|
||||
''' Prompts the user with prompt and requires a yes/no answer.'''
|
||||
valid = False
|
||||
while not valid:
|
||||
choice = raw_input(prompt + ' [Y/n]: ').strip().lower()[:1]
|
||||
if choice == '':
|
||||
choice = 'y'
|
||||
if choice not in ('y', 'n'):
|
||||
print('ERROR! Please enter y or n!')
|
||||
else:
|
||||
valid = True
|
||||
|
||||
return choice == 'y'
|
||||
|
||||
def _ensure_package_manager_updated(self):
|
||||
if self.package_manager_updated:
|
||||
return
|
||||
|
@ -9,6 +9,16 @@ import platform
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
try:
|
||||
from ConfigParser import (
|
||||
Error as ConfigParserError,
|
||||
RawConfigParser,
|
||||
)
|
||||
except ImportError:
|
||||
from configparser import (
|
||||
Error as ConfigParserError,
|
||||
RawConfigParser,
|
||||
)
|
||||
|
||||
# Don't forgot to add new mozboot modules to the bootstrap download
|
||||
# list in bin/bootstrap.py!
|
||||
@ -185,17 +195,54 @@ lines:
|
||||
Then restart your shell.
|
||||
'''
|
||||
|
||||
TELEMETRY_OPT_IN_PROMPT = '''
|
||||
Would you like to enable build system telemetry?
|
||||
|
||||
Mozilla collects data about local builds in order to make builds faster and
|
||||
improve developer tooling. To learn more about the data we intend to collect
|
||||
read here:
|
||||
https://firefox-source-docs.mozilla.org/build/buildsystem/telemetry.html.
|
||||
|
||||
If you have questions, please ask in #build in irc.mozilla.org. If you would
|
||||
like to opt out of data collection, select (N) at the prompt.
|
||||
|
||||
Your choice'''
|
||||
|
||||
|
||||
def update_or_create_build_telemetry_config(path):
|
||||
"""Write a mach config file enabling build telemetry to `path`. If the file does not exist,
|
||||
create it. If it exists, add the new setting to the existing data.
|
||||
|
||||
This is standalone from mach's `ConfigSettings` so we can use it during bootstrap
|
||||
without a source checkout.
|
||||
"""
|
||||
config = RawConfigParser()
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
config.read([path])
|
||||
except ConfigParserError as e:
|
||||
print('Your mach configuration file at `{path}` is not parseable:\n{error}'.format(
|
||||
path=path, error=e))
|
||||
return False
|
||||
if not config.has_section('build'):
|
||||
config.add_section('build')
|
||||
config.set('build', 'telemetry', 'true')
|
||||
with open(path, 'wb') as f:
|
||||
config.write(f)
|
||||
return True
|
||||
|
||||
|
||||
class Bootstrapper(object):
|
||||
"""Main class that performs system bootstrap."""
|
||||
|
||||
def __init__(self, finished=FINISHED, choice=None, no_interactive=False,
|
||||
hg_configure=False, no_system_changes=False):
|
||||
hg_configure=False, no_system_changes=False, mach_context=None):
|
||||
self.instance = None
|
||||
self.finished = finished
|
||||
self.choice = choice
|
||||
self.hg_configure = hg_configure
|
||||
self.no_system_changes = no_system_changes
|
||||
self.mach_context = mach_context
|
||||
cls = None
|
||||
args = {'no_interactive': no_interactive,
|
||||
'no_system_changes': no_system_changes}
|
||||
@ -336,6 +383,20 @@ class Bootstrapper(object):
|
||||
self.instance.ensure_stylo_packages(state_dir, checkout_root)
|
||||
self.instance.ensure_node_packages(state_dir, checkout_root)
|
||||
|
||||
def check_telemetry_opt_in(self, state_dir):
|
||||
# We can't prompt the user.
|
||||
if self.instance.no_interactive:
|
||||
return
|
||||
# Don't prompt if the user already has a setting for this value.
|
||||
if self.mach_context is not None and 'telemetry' in self.mach_context.settings.build:
|
||||
return
|
||||
choice = self.instance.prompt_yesno(prompt=TELEMETRY_OPT_IN_PROMPT)
|
||||
if choice:
|
||||
cfg_file = os.path.join(state_dir, 'machrc')
|
||||
if update_or_create_build_telemetry_config(cfg_file):
|
||||
print('\nThanks for enabling build telemetry! You can change this setting at ' +
|
||||
'any time by editing the config file `{}`\n'.format(cfg_file))
|
||||
|
||||
def bootstrap(self):
|
||||
if self.choice is None:
|
||||
# Like ['1. Firefox for Desktop', '2. Firefox for Android Artifact Mode', ...].
|
||||
@ -360,6 +421,8 @@ class Bootstrapper(object):
|
||||
(checkout_type, checkout_root) = r
|
||||
have_clone = bool(checkout_type)
|
||||
|
||||
if state_dir_available:
|
||||
self.check_telemetry_opt_in(state_dir)
|
||||
self.maybe_install_private_packages_or_exit(state_dir,
|
||||
state_dir_available,
|
||||
have_clone,
|
||||
@ -435,6 +498,8 @@ class Bootstrapper(object):
|
||||
if not have_clone:
|
||||
print(SOURCE_ADVERTISE)
|
||||
|
||||
if state_dir_available:
|
||||
self.check_telemetry_opt_in(state_dir)
|
||||
self.maybe_install_private_packages_or_exit(state_dir,
|
||||
state_dir_available,
|
||||
have_clone,
|
||||
|
@ -16,6 +16,8 @@ from mach.decorators import (
|
||||
@CommandProvider
|
||||
class Bootstrap(object):
|
||||
"""Bootstrap system and mach for optimal development experience."""
|
||||
def __init__(self, context):
|
||||
self._context = context
|
||||
|
||||
@Command('bootstrap', category='devenv',
|
||||
description='Install required system packages for building.')
|
||||
@ -32,9 +34,12 @@ class Bootstrap(object):
|
||||
def bootstrap(self, application_choice=None, no_interactive=False, no_system_changes=False):
|
||||
from mozboot.bootstrap import Bootstrapper
|
||||
|
||||
bootstrapper = Bootstrapper(choice=application_choice,
|
||||
no_interactive=no_interactive,
|
||||
no_system_changes=no_system_changes)
|
||||
bootstrapper = Bootstrapper(
|
||||
choice=application_choice,
|
||||
no_interactive=no_interactive,
|
||||
no_system_changes=no_system_changes,
|
||||
mach_context=self._context,
|
||||
)
|
||||
bootstrapper.bootstrap()
|
||||
|
||||
|
||||
|
4
python/mozboot/mozboot/test/python.ini
Normal file
4
python/mozboot/mozboot/test/python.ini
Normal file
@ -0,0 +1,4 @@
|
||||
[DEFAULT]
|
||||
skip-if = python == 3
|
||||
|
||||
[test_write_config.py]
|
95
python/mozboot/mozboot/test/test_write_config.py
Normal file
95
python/mozboot/mozboot/test/test_write_config.py
Normal file
@ -0,0 +1,95 @@
|
||||
# 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 absolute_import
|
||||
|
||||
import mozunit
|
||||
import pytest
|
||||
|
||||
from mach.config import ConfigSettings
|
||||
from mach.decorators import SettingsProvider
|
||||
from mozboot.bootstrap import update_or_create_build_telemetry_config
|
||||
|
||||
|
||||
# Duplicated from python/mozbuild/mozbuild/mach_commands.py because we can't
|
||||
# actually import that module here.
|
||||
@SettingsProvider
|
||||
class TelemetrySettings():
|
||||
config_settings = [
|
||||
('build.telemetry', 'boolean', """
|
||||
Enable submission of build system telemetry.
|
||||
""".strip(), False),
|
||||
]
|
||||
|
||||
|
||||
@SettingsProvider
|
||||
class OtherSettings():
|
||||
config_settings = [
|
||||
('foo.bar', 'int', '', 1),
|
||||
('build.abc', 'string', '', ''),
|
||||
]
|
||||
|
||||
|
||||
def read(path):
|
||||
s = ConfigSettings()
|
||||
s.register_provider(TelemetrySettings)
|
||||
s.register_provider(OtherSettings)
|
||||
s.load_file(path)
|
||||
return s
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_path(tmpdir):
|
||||
return unicode(tmpdir.join('machrc'))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def write_config(config_path):
|
||||
def _config(contents):
|
||||
with open(config_path, 'wb') as f:
|
||||
f.write(contents)
|
||||
return _config
|
||||
|
||||
|
||||
def test_nonexistent(config_path):
|
||||
update_or_create_build_telemetry_config(config_path)
|
||||
s = read(config_path)
|
||||
assert(s.build.telemetry)
|
||||
|
||||
|
||||
def test_file_exists_no_build_section(config_path, write_config):
|
||||
write_config('''[foo]
|
||||
bar = 2
|
||||
''')
|
||||
update_or_create_build_telemetry_config(config_path)
|
||||
s = read(config_path)
|
||||
assert(s.build.telemetry)
|
||||
assert(s.foo.bar == 2)
|
||||
|
||||
|
||||
def test_existing_build_section(config_path, write_config):
|
||||
write_config('''[foo]
|
||||
bar = 2
|
||||
|
||||
[build]
|
||||
abc = xyz
|
||||
''')
|
||||
update_or_create_build_telemetry_config(config_path)
|
||||
s = read(config_path)
|
||||
assert(s.build.telemetry)
|
||||
assert(s.build.abc == 'xyz')
|
||||
assert(s.foo.bar == 2)
|
||||
|
||||
|
||||
def test_malformed_file(config_path, write_config):
|
||||
"""Ensure that a malformed config file doesn't cause breakage."""
|
||||
write_config('''[foo
|
||||
bar = 1
|
||||
''')
|
||||
assert(not update_or_create_build_telemetry_config(config_path))
|
||||
# Can't read config, it will not have been written!
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
mozunit.main()
|
Loading…
Reference in New Issue
Block a user