summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAngelos Evripiotis <angelos.evripiotis@gmail.com>2018-11-20 12:12:11 +0000
committerAngelos Evripiotis <angelos.evripiotis@gmail.com>2018-11-20 12:12:11 +0000
commit8071c00c6fe886ac42b729f5979b2e466a84ce66 (patch)
tree4ae64f1775a4f82de378bb4d87e85ac025a4cea4
parent3f663d82415c17f21a2c6216a317aea4aee1608a (diff)
parent7ae3a3d2433f0909a6c4d43655b8fff95fb8c318 (diff)
downloadbuildstream-8071c00c6fe886ac42b729f5979b2e466a84ce66.tar.gz
Merge branch 'aevri/prompt-config' into 'master'
Add 'prompt.*' config options to buildstream.conf See merge request BuildStream/buildstream!887
-rw-r--r--NEWS6
-rw-r--r--buildstream/_context.py72
-rw-r--r--buildstream/_frontend/app.py7
-rw-r--r--buildstream/_frontend/cli.py4
-rw-r--r--buildstream/_yaml.py1
-rw-r--r--buildstream/data/userconfig.yaml32
6 files changed, 108 insertions, 14 deletions
diff --git a/NEWS b/NEWS
index 56cacc623..544435dc7 100644
--- a/NEWS
+++ b/NEWS
@@ -45,6 +45,12 @@ buildstream 1.3.1
instead of just a specially-formatted build-root with a `root` and `scratch`
subdirectory.
+ o The buildstream.conf file learned new 'prompt.auto-init',
+ 'prompt.really-workspace-close-remove-dir', and
+ 'prompt.really-workspace-reset-hard' options. These allow users to suppress
+ certain confirmation prompts, e.g. double-checking that the user meant to
+ run the command as typed.
+
o Due to the element `build tree` being cached in the respective artifact their
size in some cases has significantly increased. In *most* cases the build trees
are not utilised when building targets, as such by default bst 'pull' & 'build'
diff --git a/buildstream/_context.py b/buildstream/_context.py
index b39c82b1d..7a47a30d3 100644
--- a/buildstream/_context.py
+++ b/buildstream/_context.py
@@ -110,6 +110,18 @@ class Context():
# Whether or not to attempt to pull build trees globally
self.pull_buildtrees = None
+ # Boolean, whether to offer to create a project for the user, if we are
+ # invoked outside of a directory where we can resolve the project.
+ self.prompt_auto_init = None
+
+ # Boolean, whether we double-check with the user that they meant to
+ # remove a workspace directory.
+ self.prompt_workspace_close_remove_dir = None
+
+ # Boolean, whether we double-check with the user that they meant to do
+ # a hard reset of a workspace, potentially losing changes.
+ self.prompt_workspace_reset_hard = None
+
# Whether elements must be rebuilt when their dependencies have changed
self._strict_build_plan = None
@@ -165,7 +177,7 @@ class Context():
_yaml.node_validate(defaults, [
'sourcedir', 'builddir', 'artifactdir', 'logdir',
'scheduler', 'artifacts', 'logging', 'projects',
- 'cache'
+ 'cache', 'prompt'
])
for directory in ['sourcedir', 'builddir', 'artifactdir', 'logdir']:
@@ -214,12 +226,34 @@ class Context():
'on-error', 'fetchers', 'builders',
'pushers', 'network-retries'
])
- self.sched_error_action = _yaml.node_get(scheduler, str, 'on-error')
+ self.sched_error_action = _node_get_option_str(
+ scheduler, 'on-error', ['continue', 'quit', 'terminate'])
self.sched_fetchers = _yaml.node_get(scheduler, int, 'fetchers')
self.sched_builders = _yaml.node_get(scheduler, int, 'builders')
self.sched_pushers = _yaml.node_get(scheduler, int, 'pushers')
self.sched_network_retries = _yaml.node_get(scheduler, int, 'network-retries')
+ # Load prompt preferences
+ #
+ # We convert string options to booleans here, so we can be both user
+ # and coder-friendly. The string options are worded to match the
+ # responses the user would give at the cli, for least surprise. The
+ # booleans are converted here because it's easiest to eyeball that the
+ # strings are right.
+ #
+ prompt = _yaml.node_get(
+ defaults, Mapping, 'prompt')
+ _yaml.node_validate(prompt, [
+ 'auto-init', 'really-workspace-close-remove-dir',
+ 'really-workspace-reset-hard',
+ ])
+ self.prompt_auto_init = _node_get_option_str(
+ prompt, 'auto-init', ['ask', 'no']) == 'ask'
+ self.prompt_workspace_close_remove_dir = _node_get_option_str(
+ prompt, 'really-workspace-close-remove-dir', ['ask', 'yes']) == 'ask'
+ self.prompt_workspace_reset_hard = _node_get_option_str(
+ prompt, 'really-workspace-reset-hard', ['ask', 'yes']) == 'ask'
+
# Load per-projects overrides
self._project_overrides = _yaml.node_get(defaults, Mapping, 'projects', default_value={})
@@ -230,13 +264,6 @@ class Context():
profile_end(Topics.LOAD_CONTEXT, 'load')
- valid_actions = ['continue', 'quit']
- if self.sched_error_action not in valid_actions:
- provenance = _yaml.node_get_provenance(scheduler, 'on-error')
- raise LoadError(LoadErrorReason.INVALID_DATA,
- "{}: on-error should be one of: {}".format(
- provenance, ", ".join(valid_actions)))
-
@property
def artifactcache(self):
if not self._artifactcache:
@@ -589,3 +616,30 @@ class Context():
os.environ['XDG_CONFIG_HOME'] = os.path.expanduser('~/.config')
if not os.environ.get('XDG_DATA_HOME'):
os.environ['XDG_DATA_HOME'] = os.path.expanduser('~/.local/share')
+
+
+# _node_get_option_str()
+#
+# Like _yaml.node_get(), but also checks value is one of the allowed option
+# strings. Fetches a value from a dictionary node, and makes sure it's one of
+# the pre-defined options.
+#
+# Args:
+# node (dict): The dictionary node
+# key (str): The key to get a value for in node
+# allowed_options (iterable): Only accept these values
+#
+# Returns:
+# The value, if found in 'node'.
+#
+# Raises:
+# LoadError, when the value is not of the expected type, or is not found.
+#
+def _node_get_option_str(node, key, allowed_options):
+ result = _yaml.node_get(node, str, key)
+ if result not in allowed_options:
+ provenance = _yaml.node_get_provenance(node, key)
+ raise LoadError(LoadErrorReason.INVALID_DATA,
+ "{}: {} should be one of: {}".format(
+ provenance, key, ", ".join(allowed_options)))
+ return result
diff --git a/buildstream/_frontend/app.py b/buildstream/_frontend/app.py
index b42b94bc1..4094eec17 100644
--- a/buildstream/_frontend/app.py
+++ b/buildstream/_frontend/app.py
@@ -222,9 +222,10 @@ class App():
# Let's automatically start a `bst init` session in this case
if e.reason == LoadErrorReason.MISSING_PROJECT_CONF and self.interactive:
click.echo("A project was not detected in the directory: {}".format(directory), err=True)
- click.echo("", err=True)
- if click.confirm("Would you like to create a new project here ?"):
- self.init_project(None)
+ if self.context.prompt_auto_init:
+ click.echo("", err=True)
+ if click.confirm("Would you like to create a new project here?"):
+ self.init_project(None)
self._error_exit(e, "Error loading project")
diff --git a/buildstream/_frontend/cli.py b/buildstream/_frontend/cli.py
index 725dc8c1d..92abd96e8 100644
--- a/buildstream/_frontend/cli.py
+++ b/buildstream/_frontend/cli.py
@@ -772,7 +772,7 @@ def workspace_close(app, remove_dir, all_, elements):
if nonexisting:
raise AppError("Workspace does not exist", detail="\n".join(nonexisting))
- if app.interactive and remove_dir:
+ if app.interactive and remove_dir and app.context.prompt_workspace_close_remove_dir:
if not click.confirm('This will remove all your changes, are you sure?'):
click.echo('Aborting', err=True)
sys.exit(-1)
@@ -806,7 +806,7 @@ def workspace_reset(app, soft, track_, all_, elements):
if all_ and not app.stream.workspace_exists():
raise AppError("No open workspaces to reset")
- if app.interactive and not soft:
+ if app.interactive and not soft and app.context.prompt_workspace_reset_hard:
if not click.confirm('This will remove all your changes, are you sure?'):
click.echo('Aborting', err=True)
sys.exit(-1)
diff --git a/buildstream/_yaml.py b/buildstream/_yaml.py
index f44572ca5..c8f11bc96 100644
--- a/buildstream/_yaml.py
+++ b/buildstream/_yaml.py
@@ -351,6 +351,7 @@ _sentinel = object()
# expected_type (type): The expected type for the value being searched
# key (str): The key to get a value for in node
# indices (list of ints): Optionally decend into lists of lists
+# default_value: Optionally return this value if the key is not found
#
# Returns:
# The value if found in node, otherwise default_value is returned
diff --git a/buildstream/data/userconfig.yaml b/buildstream/data/userconfig.yaml
index f540a97f6..dfc18545e 100644
--- a/buildstream/data/userconfig.yaml
+++ b/buildstream/data/userconfig.yaml
@@ -100,3 +100,35 @@ logging:
[%{elapsed}][%{key}][%{element}] %{action} %{message}
+#
+# Prompt overrides
+#
+# Here you can suppress 'are you sure?' and other kinds of prompts by supplying
+# override values. Note that e.g. 'yes' and 'no' have the same meaning here as
+# they do in the actual cli prompt.
+#
+prompt:
+
+ # Whether to create a project with 'bst init' if we are invoked outside of a
+ # directory where we can resolve the project.
+ #
+ # ask - Prompt the user to choose.
+ # no - Never create the project.
+ #
+ auto-init: ask
+
+ # Whether to really proceed with 'bst workspace close --remove-dir' removing
+ # a workspace directory, potentially losing changes.
+ #
+ # ask - Ask the user if they are sure.
+ # yes - Always remove, without asking.
+ #
+ really-workspace-close-remove-dir: ask
+
+ # Whether to really proceed with 'bst workspace reset' doing a hard reset of
+ # a workspace, potentially losing changes.
+ #
+ # ask - Ask the user if they are sure.
+ # yes - Always hard reset, without asking.
+ #
+ really-workspace-reset-hard: ask