diff --git a/lib/autoproj/build_option.rb b/lib/autoproj/build_option.rb index 3644e408..ca54e0da 100644 --- a/lib/autoproj/build_option.rb +++ b/lib/autoproj/build_option.rb @@ -45,18 +45,35 @@ def doc end end + # Return either the current value if it is not nil, or use + # a default value + # + # @return [value, Boolean] Current value, and flag whether this is a + # default value + def ensure_value(current_value) + if !current_value.nil? + return current_value.to_s, false + elsif options[:default] + return options[:default].to_str, true + else + return '', true + end + end + + # Ask the user for the setting of this option + # by providing the current value as input and falling + # back to default values if needed + # + # @param [String] current_value the option's current value + # @param [String] doc a string to override the default option banner def ask(current_value, doc = nil) - default_value = - if !current_value.nil? then current_value.to_s - elsif options[:default] then options[:default].to_str - else '' - end + value,_ = ensure_value(current_value) - STDOUT.print " #{doc || self.doc} [#{default_value}] " + STDOUT.print " #{doc || self.doc} [#{value}] " STDOUT.flush answer = STDIN.readline.chomp if answer == '' - answer = default_value + answer = value end validate(answer) diff --git a/lib/autoproj/cli/base.rb b/lib/autoproj/cli/base.rb index d6f22e06..1dd4feaa 100644 --- a/lib/autoproj/cli/base.rb +++ b/lib/autoproj/cli/base.rb @@ -166,6 +166,7 @@ def validate_user_selection(user_selection, resolved_selection) end def validate_options(args, options) + ws.config.interactive = options[:interactive] self.class.validate_options(args, options) end diff --git a/lib/autoproj/cli/main.rb b/lib/autoproj/cli/main.rb index a2f93d78..816f55c3 100644 --- a/lib/autoproj/cli/main.rb +++ b/lib/autoproj/cli/main.rb @@ -30,6 +30,8 @@ class Main < Thor desc: 'enables or disables colored display (enabled by default if the terminal supports it)' class_option :progress, type: :boolean, default: TTY::Color.color?, desc: 'enables or disables progress display (enabled by default if the terminal supports it)' + class_option 'interactive', type: :boolean, default: nil, + desc: 'tell autoproj to run (non)interactively' stop_on_unknown_option! :exec check_unknown_options! except: :exec diff --git a/lib/autoproj/configuration.rb b/lib/autoproj/configuration.rb index 7ad77f80..e3777fab 100644 --- a/lib/autoproj/configuration.rb +++ b/lib/autoproj/configuration.rb @@ -22,6 +22,9 @@ class Configuration attr_reader :displayed_options # The path to the underlying configuration file attr_reader :path + # Whether the configuration should be performed interactively + # or not + attr_accessor :interactive def initialize(path = nil) @config = Hash.new @@ -43,16 +46,24 @@ def reset_modified @modified = false end - # Deletes the current value for an option + # Deletes the current configuration for all options or the one specified # - # The user will be asked for a new value next time the option is needed + # The user will be asked for a new value next time one of the reset + # options is needed # - # @param [String] the option name + # @param [String] name the option name (or by default nil to reset all + # options) # @return the deleted value - def reset(name) - @modified ||= config.has_key?(name) - config.delete(name) - overrides.delete(name) + def reset(name = nil) + if name + @modified ||= config.has_key?(name) + config.delete(name) + overrides.delete(name) + else + @config.clear + @overrides.clear + @modified = true + end end # Sets a configuration option @@ -177,10 +188,18 @@ def configure(option_name) if current_value = config[option_name] current_value = current_value.first end - value = opt.ask(current_value) + is_default = false + if interactive? + value = opt.ask(current_value, nil) + else + value, is_default = opt.ensure_value(current_value) + Autoproj.info " using: #{value} (noninteractive mode)" + end @modified = true - config[option_name] = [value, true] - displayed_options[option_name] = value + if !is_default + config[option_name] = [value, true] + displayed_options[option_name] = value + end value else raise ConfigError.new, "undeclared option '#{option_name}'" @@ -440,6 +459,21 @@ def randomize_layout=(value) set('randomize_layout', value, true) end + # Returns true if the configuration should be performed interactively + # + # @return [Boolean] + # @see interactive= + def interactive? + if !interactive.nil? + return interactive + elsif ENV['AUTOPROJ_NONINTERACTIVE'] == '1' + return false + elsif has_value_for?("interactive") + return get('interactive') + end + true + end + DEFAULT_UTILITY_SETUP = Hash[ 'doc' => true, 'test' => false] @@ -545,7 +579,11 @@ def prefer_indep_over_os_packages? def to_hash result = Hash.new @config.each do |key, (value, _)| - result[key] = value + if declared_options.include?(key) + result[key] = declared_options[key].ensure_value(value) + else + result[key] = value + end end overrides.each do |key, value| result[key] = value diff --git a/lib/autoproj/test.rb b/lib/autoproj/test.rb index 796e695e..f0659b43 100644 --- a/lib/autoproj/test.rb +++ b/lib/autoproj/test.rb @@ -271,7 +271,7 @@ def ws_create_os_package_resolver os_package_manager: 'os') end - def ws_create(dir = make_tmpdir) + def ws_create(dir = make_tmpdir, partial_config: false) require 'autoproj/ops/main_config_switcher' FileUtils.cp_r Ops::MainConfigSwitcher::MAIN_CONFIGURATION_TEMPLATE, File.join(dir, 'autoproj') FileUtils.mkdir_p File.join(dir, '.autoproj') @@ -280,8 +280,11 @@ def ws_create(dir = make_tmpdir) @ws = Workspace.new( dir, os_package_resolver: ws_os_package_resolver, package_managers: ws_package_managers) - ws.config.set 'osdeps_mode', 'all' - ws.config.set 'apt_dpkg_update', true + + if !partial_config + ws.config.set 'osdeps_mode', 'all' + ws.config.set 'apt_dpkg_update', true + end ws.config.set 'GITHUB', 'http,ssh', true ws.config.set 'GITORIOUS', 'http,ssh', true ws.config.set 'gems_install_path', File.join(dir, 'gems') diff --git a/lib/autoproj/workspace.rb b/lib/autoproj/workspace.rb index 8743eccb..111c708b 100644 --- a/lib/autoproj/workspace.rb +++ b/lib/autoproj/workspace.rb @@ -236,10 +236,16 @@ def overrides_dir File.join(config_dir, OVERRIDES_DIR) end + # Load the configuration for this workspace from + # config_file_path + # + # @param [Boolean] reset Set to true to replace the configuration object, + # set to false to load into the existing + # @return [Configuration] configuration object def load_config(reconfigure = false) - @config = Configuration.new(config_file_path) if File.file?(config_file_path) - config.load(reconfigure: reconfigure) + config.reset + config.load(path: config_file_path, reconfigure: reconfigure) if raw_vcs = config.get('manifest_source', nil) manifest.vcs = VCSDefinition.from_raw(raw_vcs) else diff --git a/test/cli/test_reconfigure.rb b/test/cli/test_reconfigure.rb new file mode 100644 index 00000000..7a73e237 --- /dev/null +++ b/test/cli/test_reconfigure.rb @@ -0,0 +1,42 @@ +require 'autoproj/test' +require 'autoproj/cli/main' +require 'autoproj/cli/reconfigure' +require 'timeout' + +module Autoproj + module CLI + describe Reconfigure do + attr_reader :ws + before do + option_name = "custom-configuration-option" + default_value = "option-defaultvalue" + + @ws = ws_create(make_tmpdir, partial_config: true) + end + after do + Autoproj.verbose = false + end + describe "#reconfigure" do + def run_command(*args) + capture_subprocess_io do + ENV['AUTOPROJ_CURRENT_ROOT'] = ws.root_path.to_s + in_ws do + Main.start([*args,"--debug"], debug: true) + end + end + end + + it "reconfigure should run interactively" do + assert_raises Timeout::Error do + Timeout.timeout(3) do + run_command 'reconfigure' + end + end + end + it "reconfigure should run non interactively" do + run_command 'reconfigure','--no-interactive' + end + end + end + end +end diff --git a/test/test_configuration.rb b/test/test_configuration.rb index 93cff179..4aca13fc 100644 --- a/test/test_configuration.rb +++ b/test/test_configuration.rb @@ -346,6 +346,85 @@ module Autoproj refute @config.modified? end end + describe "#interactive" do + it "set interactive mode" do + @config.interactive = false + assert !@config.interactive? + + @config.interactive = true + assert @config.interactive? + end + + it "disables interactive configuration setting through config option" do + option_name = "custom-configuration-option" + default_value = "option-defaultvalue" + @config.declare(option_name,"string",default: default_value) + + @config.interactive = false + + Timeout.timeout(3) do + @config.configure(option_name) + end + assert @config.get(option_name) == default_value + end + + it "disables interactive configuration setting through ENV" do + option_name = "custom-configuration-option" + default_value = "option-defaultvalue" + @config.declare(option_name,"string",default: default_value) + + ENV['AUTOPROJ_NONINTERACTIVE'] = '1' + + assert !@config.interactive? + begin + Timeout.timeout(3) do + @config.configure(option_name) + end + assert @config.get(option_name) == default_value + ensure + ENV.delete('AUTOPROJ_NONINTERACTIVE') + end + end + it "use interactive configuration by default" do + option_name = "custom-configuration-option" + default_value = "option-defaultvalue" + @config.declare(option_name,"string",default: default_value) + assert @config.interactive? + assert_raises Timeout::Error do + Timeout.timeout(3) do + @config.configure(option_name) + end + end + end + it "skip saving default value" do + option_a_name = "custom-configuration-option-a" + default_a_value = "option-a-defaultvalue" + + option_b_name = "custom-configuration-option-b" + default_b_value = "option-b-default-value" + b_value = "option-b-value" + + @config.declare(option_a_name,"string",default: default_a_value) + @config.declare(option_b_name,"string",default: default_b_value) + + @config.interactive = false + @config.configure(option_a_name) + @config.set(option_b_name, b_value) + @config.configure(option_b_name) + + assert !@config.has_value_for?(option_a_name) + assert @config.has_value_for?(option_b_name) + + tempfile = Tempfile.new("skip-saving-config") + @config.save(tempfile) + + loaded_config = Configuration.new(tempfile) + loaded_config.load + assert !loaded_config.has_value_for?(option_a_name) + assert loaded_config.has_value_for?(option_b_name) + assert loaded_config.get(option_b_name) == b_value + end + end end end diff --git a/test/test_workspace.rb b/test/test_workspace.rb index efbc2c3d..3a1bd6aa 100644 --- a/test/test_workspace.rb +++ b/test/test_workspace.rb @@ -486,6 +486,7 @@ def create_test_executable(path = target_test_path) flexmock(Autoproj::Configuration).new_instances. should_receive(:source_dir).and_return(ws.root_dir) assert_raises(ConfigError) do + ws.config = Configuration.new(ws.root_dir) ws.load_config end end