diff --git a/lib/mrsk/cli/base.rb b/lib/mrsk/cli/base.rb index 567f19b3..96fa434e 100644 --- a/lib/mrsk/cli/base.rb +++ b/lib/mrsk/cli/base.rb @@ -9,6 +9,8 @@ module Mrsk::Cli class_option :verbose, type: :boolean, aliases: "-v", desc: "Detailed logging" + 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)" @@ -27,6 +29,7 @@ module Mrsk::Cli commander.config_file = Pathname.new(File.expand_path(options[:config_file])) commander.destination = options[:destination] commander.verbose = options[:verbose] + commander.version = options[:version] commander.specific_hosts = options[:hosts]&.split(",") commander.specific_roles = options[:roles]&.split(",") diff --git a/lib/mrsk/commander.rb b/lib/mrsk/commander.rb index 8ea7f9de..209aa803 100644 --- a/lib/mrsk/commander.rb +++ b/lib/mrsk/commander.rb @@ -8,14 +8,17 @@ require "mrsk/commands/traefik" require "mrsk/commands/registry" class Mrsk::Commander - attr_accessor :config_file, :destination, :verbose + attr_accessor :config_file, :destination, :verbose, :version def initialize(config_file: nil, destination: nil, verbose: false) @config_file, @destination, @verbose = config_file, destination, verbose end def config - @config ||= Mrsk::Configuration.create_from(config_file, destination: destination).tap { |config| setup_with(config) } + @config ||= \ + Mrsk::Configuration + .create_from(config_file, destination: destination, version: cascading_version) + .tap { |config| configure_sshkit_with(config) } end attr_accessor :specific_hosts @@ -71,8 +74,12 @@ class Mrsk::Commander end private + def cascading_version + version.presence || ENV["VERSION"] || `git rev-parse HEAD`.strip + end + # Lazy setup of SSHKit - def setup_with(config) + def configure_sshkit_with(config) SSHKit::Backend::Netssh.configure { |ssh| ssh.ssh_options = config.ssh_options } SSHKit.config.command_map[:docker] = "docker" # No need to use /usr/bin/env, just clogs up the logs SSHKit.config.output_verbosity = :debug if verbose diff --git a/lib/mrsk/configuration.rb b/lib/mrsk/configuration.rb index 40c50fe1..914948f1 100644 --- a/lib/mrsk/configuration.rb +++ b/lib/mrsk/configuration.rb @@ -10,13 +10,13 @@ class Mrsk::Configuration delegate :argumentize, to: Mrsk::Utils class << self - def create_from(base_config_file, destination: nil) + def create_from(base_config_file, destination: nil, version: "missing") new(load_config_file(base_config_file).tap do |config| if destination config.merge! \ load_config_file destination_config_file(base_config_file, destination) end - end) + end, version: version) end private @@ -34,8 +34,9 @@ class Mrsk::Configuration end end - def initialize(config, validate: true) + def initialize(config, version: "missing", validate: true) @config = ActiveSupport::InheritableOptions.new(config) + @version = version ensure_required_keys_present if validate end @@ -62,7 +63,7 @@ class Mrsk::Configuration def version - @version ||= ENV["VERSION"] || `git rev-parse HEAD`.strip + @version end def repository diff --git a/test/commands/app_test.rb b/test/commands/app_test.rb index c86c6a4b..5297b6fa 100644 --- a/test/commands/app_test.rb +++ b/test/commands/app_test.rb @@ -2,7 +2,6 @@ require "test_helper" require "mrsk/configuration" require "mrsk/commands/app" -ENV["VERSION"] = "123" ENV["RAILS_MASTER_KEY"] = "456" class CommandsAppTest < ActiveSupport::TestCase @@ -13,12 +12,12 @@ class CommandsAppTest < ActiveSupport::TestCase test "run" do assert_equal \ - [:docker, :run, "-d", "--restart unless-stopped", "--name", "app-123", "-e", "RAILS_MASTER_KEY=456", "--label", "service=app", "--label", "role=web", "--label", "traefik.http.routers.app.rule='PathPrefix(`/`)'", "--label", "traefik.http.services.app.loadbalancer.healthcheck.path=/up", "--label", "traefik.http.services.app.loadbalancer.healthcheck.interval=1s", "--label", "traefik.http.middlewares.app.retry.attempts=3", "--label", "traefik.http.middlewares.app.retry.initialinterval=500ms", "dhh/app:123"], @app.run + [:docker, :run, "-d", "--restart unless-stopped", "--name", "app-missing", "-e", "RAILS_MASTER_KEY=456", "--label", "service=app", "--label", "role=web", "--label", "traefik.http.routers.app.rule='PathPrefix(`/`)'", "--label", "traefik.http.services.app.loadbalancer.healthcheck.path=/up", "--label", "traefik.http.services.app.loadbalancer.healthcheck.interval=1s", "--label", "traefik.http.middlewares.app.retry.attempts=3", "--label", "traefik.http.middlewares.app.retry.initialinterval=500ms", "dhh/app:missing"], @app.run end test "run with" do assert_equal \ - [ :docker, :run, "--rm", "-e", "RAILS_MASTER_KEY=456", "dhh/app:123", "bin/rails", "db:setup" ], + [ :docker, :run, "--rm", "-e", "RAILS_MASTER_KEY=456", "dhh/app:missing", "bin/rails", "db:setup" ], @app.run_exec("bin/rails", "db:setup") end end diff --git a/test/commands/builder_test.rb b/test/commands/builder_test.rb index 1f2df502..16c8f1ca 100644 --- a/test/commands/builder_test.rb +++ b/test/commands/builder_test.rb @@ -8,42 +8,44 @@ class CommandsBuilderTest < ActiveSupport::TestCase end test "target multiarch by default" do - builder = Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config)) - assert builder.multiarch? + assert new_builder_command.multiarch? end test "target native when multiarch is off" do - builder = Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config.merge({ builder: { "multiarch" => false } }))) - assert builder.native? + assert new_builder_command(builder: { "multiarch" => false }).native? end test "target multiarch remote when local and remote is set" do - builder = Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config.merge({ builder: { "local" => { }, "remote" => { } } }))) - assert builder.remote? + assert new_builder_command(builder: { "local" => { }, "remote" => { } }).remote? end test "build args" do - builder = Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config.merge({ builder: { "args" => { "a" => 1, "b" => 2 } } }))) + builder = new_builder_command(builder: { "args" => { "a" => 1, "b" => 2 } }) assert_equal [ "--build-arg", "a=1", "--build-arg", "b=2" ], builder.target.build_args end test "build secrets" do - builder = Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config.merge({ builder: { "secrets" => ["token_a", "token_b"] } }))) + builder = new_builder_command(builder: { "secrets" => ["token_a", "token_b"] }) assert_equal [ "--secret", "id=token_a", "--secret", "id=token_b" ], builder.target.build_secrets end test "native push with build args" do - builder = Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config.merge({ builder: { "multiarch" => false, "args" => { "a" => 1, "b" => 2 } } }))) + builder = new_builder_command(builder: { "multiarch" => false, "args" => { "a" => 1, "b" => 2 } }) assert_equal [ :docker, :build, "-t", "--build-arg", "a=1", "--build-arg", "b=2", "dhh/app:123", ".", "&&", :docker, :push, "dhh/app:123" ], builder.push end test "multiarch push with build args" do - builder = Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config.merge({ builder: { "args" => { "a" => 1, "b" => 2 } } }))) + builder = new_builder_command(builder: { "args" => { "a" => 1, "b" => 2 } }) assert_equal [ :docker, :buildx, :build, "--push", "--platform linux/amd64,linux/arm64", "-t", "dhh/app:123", "--build-arg", "a=1", "--build-arg", "b=2", "." ], builder.push end test "native push with with build secrets" do - builder = Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config.merge({ builder: { "multiarch" => false, "secrets" => [ "a", "b" ] } }))) + builder = new_builder_command(builder: { "multiarch" => false, "secrets" => [ "a", "b" ] }) assert_equal [ :docker, :build, "-t", "--secret", "id=a", "--secret", "id=b", "dhh/app:123", ".", "&&", :docker, :push, "dhh/app:123" ], builder.push end + + private + def new_builder_command(additional_config = {}) + Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config.merge(additional_config), version: "123")) + end end diff --git a/test/configuration_role_test.rb b/test/configuration_role_test.rb index f954ebff..590805aa 100644 --- a/test/configuration_role_test.rb +++ b/test/configuration_role_test.rb @@ -1,8 +1,6 @@ require "test_helper" require "mrsk/configuration" -ENV["VERSION"] = "123" - class ConfigurationRoleTest < ActiveSupport::TestCase setup do @deploy = { diff --git a/test/configuration_test.rb b/test/configuration_test.rb index b61df46c..71fb3061 100644 --- a/test/configuration_test.rb +++ b/test/configuration_test.rb @@ -1,7 +1,6 @@ require "test_helper" require "mrsk/configuration" -ENV["VERSION"] = "123" ENV["RAILS_MASTER_KEY"] = "456" class ConfigurationTest < ActiveSupport::TestCase @@ -63,7 +62,8 @@ class ConfigurationTest < ActiveSupport::TestCase end test "version" do - assert_equal "123", @config.version + assert_equal "missing", @config.version + assert_equal "123", Mrsk::Configuration.new(@deploy, version: "123").version end test "repository" do @@ -74,14 +74,14 @@ class ConfigurationTest < ActiveSupport::TestCase end test "absolute image" do - assert_equal "dhh/app:123", @config.absolute_image + assert_equal "dhh/app:missing", @config.absolute_image config = Mrsk::Configuration.new(@deploy.tap { |c| c[:registry].merge!({ "server" => "ghcr.io" }) }) - assert_equal "ghcr.io/dhh/app:123", config.absolute_image + assert_equal "ghcr.io/dhh/app:missing", config.absolute_image end test "service with version" do - assert_equal "app-123", @config.service_with_version + assert_equal "app-missing", @config.service_with_version end @@ -125,6 +125,6 @@ class ConfigurationTest < ActiveSupport::TestCase end test "to_h" do - assert_equal({ :roles=>["web"], :hosts=>["1.1.1.1", "1.1.1.2"], :primary_host=>"1.1.1.1", :version=>"123", :repository=>"dhh/app", :absolute_image=>"dhh/app:123", :service_with_version=>"app-123", :env_args=>["-e", "REDIS_URL=redis://x/y"], :ssh_options=>{:user=>"root", :auth_methods=>["publickey"]} }, @config.to_h) + assert_equal({ :roles=>["web"], :hosts=>["1.1.1.1", "1.1.1.2"], :primary_host=>"1.1.1.1", :version=>"missing", :repository=>"dhh/app", :absolute_image=>"dhh/app:missing", :service_with_version=>"app-missing", :env_args=>["-e", "REDIS_URL=redis://x/y"], :ssh_options=>{:user=>"root", :auth_methods=>["publickey"]} }, @config.to_h) end end