diff --git a/lib/kamal/cli/base.rb b/lib/kamal/cli/base.rb index 9a38d97e5..7cd69fc6c 100644 --- a/lib/kamal/cli/base.rb +++ b/lib/kamal/cli/base.rb @@ -14,8 +14,8 @@ def self.exit_on_failure?() true end class_option :version, desc: "Run commands against a specific app version" class_option :primary, type: :boolean, aliases: "-p", desc: "Run commands only on primary host instead of all" - class_option :hosts, aliases: "-h", desc: "Run commands on these hosts instead of all (separate by comma)" - class_option :roles, aliases: "-r", desc: "Run commands on these roles instead of all (separate by comma)" + class_option :hosts, aliases: "-h", desc: "Run commands on these hosts instead of all (separate by comma, supports wildcards with *)" + class_option :roles, aliases: "-r", desc: "Run commands on these roles instead of all (separate by comma, supports wildcards with *)" class_option :config_file, aliases: "-c", default: "config/deploy.yml", desc: "Path to config file" class_option :destination, aliases: "-d", desc: "Specify destination to be used for config file (staging -> deploy.staging.yml)" diff --git a/lib/kamal/commander.rb b/lib/kamal/commander.rb index 9ba3be122..d97f19917 100644 --- a/lib/kamal/commander.rb +++ b/lib/kamal/commander.rb @@ -28,11 +28,11 @@ def specific_primary! end def specific_roles=(role_names) - @specific_roles = config.roles.select { |r| role_names.include?(r.name) } if role_names.present? + @specific_roles = Kamal::Utils.filter_specific_items(role_names, config.roles) if role_names.present? end def specific_hosts=(hosts) - @specific_hosts = config.all_hosts & hosts if hosts.present? + @specific_hosts = Kamal::Utils.filter_specific_items(hosts, config.all_hosts) if hosts.present? end def primary_host diff --git a/lib/kamal/utils.rb b/lib/kamal/utils.rb index da16633e4..54185cf47 100644 --- a/lib/kamal/utils.rb +++ b/lib/kamal/utils.rb @@ -58,4 +58,20 @@ def escape_shell_value(value) .gsub(/`/, '\\\\`') .gsub(DOLLAR_SIGN_WITHOUT_SHELL_EXPANSION_REGEX, '\$') end + + # Apply a list of host or role filters, including wildcard matches + def filter_specific_items(filters, items) + matches = [] + + Array(filters).select do |filter| + matches += Array(items).select do |item| + # Only allow * for a wildcard + pattern = Regexp.escape(filter).gsub('\*', '.*') + # items are roles or hosts + (item.respond_to?(:name) ? item.name : item).match(/^#{pattern}$/) + end + end + + matches + end end diff --git a/test/commander_test.rb b/test/commander_test.rb index 2290e0f00..06cc5bbb1 100644 --- a/test/commander_test.rb +++ b/test/commander_test.rb @@ -14,6 +14,18 @@ class CommanderTest < ActiveSupport::TestCase @kamal.specific_hosts = [ "1.1.1.1", "1.1.1.2" ] assert_equal [ "1.1.1.1", "1.1.1.2" ], @kamal.hosts + + @kamal.specific_hosts = [ "1.1.1.1*" ] + assert_equal [ "1.1.1.1" ], @kamal.hosts + + @kamal.specific_hosts = [ "1.1.1.*", "*.1.2.*" ] + assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4" ], @kamal.hosts + + @kamal.specific_hosts = [ "*" ] + assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4" ], @kamal.hosts + + @kamal.specific_hosts = [ "*miss" ] + assert_equal [], @kamal.hosts end test "filtering hosts by filtering roles" do @@ -28,6 +40,18 @@ class CommanderTest < ActiveSupport::TestCase @kamal.specific_roles = [ "workers" ] assert_equal [ "workers" ], @kamal.roles.map(&:name) + + @kamal.specific_roles = [ "w*" ] + assert_equal [ "web", "workers" ], @kamal.roles.map(&:name) + + @kamal.specific_roles = [ "we*", "*orkers" ] + assert_equal [ "web", "workers" ], @kamal.roles.map(&:name) + + @kamal.specific_roles = [ "*" ] + assert_equal [ "web", "workers" ], @kamal.roles.map(&:name) + + @kamal.specific_roles = [ "*miss" ] + assert_equal [], @kamal.roles.map(&:name) end test "filtering roles by filtering hosts" do