From 2f7feaf59d9a9b9d340ac25c1fcf66daa81ea890 Mon Sep 17 00:00:00 2001 From: Ryan Ahearn Date: Sat, 26 Apr 2025 12:17:59 -0400 Subject: [PATCH 01/23] Update name of KAMAL_ROLES in sample hooks files --- lib/kamal/cli/templates/sample_hooks/post-deploy.sample | 2 +- lib/kamal/cli/templates/sample_hooks/pre-build.sample | 2 +- lib/kamal/cli/templates/sample_hooks/pre-connect.sample | 2 +- lib/kamal/cli/templates/sample_hooks/pre-deploy.sample | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/kamal/cli/templates/sample_hooks/post-deploy.sample b/lib/kamal/cli/templates/sample_hooks/post-deploy.sample index 75efafc1..fd364c2a 100755 --- a/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +++ b/lib/kamal/cli/templates/sample_hooks/post-deploy.sample @@ -7,7 +7,7 @@ # KAMAL_PERFORMER # KAMAL_VERSION # KAMAL_HOSTS -# KAMAL_ROLE (if set) +# KAMAL_ROLES (if set) # KAMAL_DESTINATION (if set) # KAMAL_RUNTIME diff --git a/lib/kamal/cli/templates/sample_hooks/pre-build.sample b/lib/kamal/cli/templates/sample_hooks/pre-build.sample index f87d8113..c5a55678 100755 --- a/lib/kamal/cli/templates/sample_hooks/pre-build.sample +++ b/lib/kamal/cli/templates/sample_hooks/pre-build.sample @@ -13,7 +13,7 @@ # KAMAL_PERFORMER # KAMAL_VERSION # KAMAL_HOSTS -# KAMAL_ROLE (if set) +# KAMAL_ROLES (if set) # KAMAL_DESTINATION (if set) if [ -n "$(git status --porcelain)" ]; then diff --git a/lib/kamal/cli/templates/sample_hooks/pre-connect.sample b/lib/kamal/cli/templates/sample_hooks/pre-connect.sample index 18e61d7e..77744bdc 100755 --- a/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +++ b/lib/kamal/cli/templates/sample_hooks/pre-connect.sample @@ -9,7 +9,7 @@ # KAMAL_PERFORMER # KAMAL_VERSION # KAMAL_HOSTS -# KAMAL_ROLE (if set) +# KAMAL_ROLES (if set) # KAMAL_DESTINATION (if set) # KAMAL_RUNTIME diff --git a/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample b/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample index 06b86aa1..665197f1 100755 --- a/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +++ b/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample @@ -13,7 +13,7 @@ # KAMAL_HOSTS # KAMAL_COMMAND # KAMAL_SUBCOMMAND -# KAMAL_ROLE (if set) +# KAMAL_ROLES (if set) # KAMAL_DESTINATION (if set) # Only check the build status for production deployments From f5f1bab8bf3d6a53a0608c2eb30362e8a3d57963 Mon Sep 17 00:00:00 2001 From: Luke Freeman Date: Mon, 5 May 2025 08:29:50 -0700 Subject: [PATCH 02/23] bumping ed25519 dependency to fix compile errors --- Gemfile.lock | 4 ++-- kamal.gemspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b85c77fa..f6328178 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,7 +7,7 @@ PATH bcrypt_pbkdf (~> 1.0) concurrent-ruby (~> 1.2) dotenv (~> 3.1) - ed25519 (~> 1.2) + ed25519 (~> 1.4) net-ssh (~> 7.3) sshkit (>= 1.23.0, < 2.0) thor (~> 1.3) @@ -60,7 +60,7 @@ GEM reline (>= 0.3.8) dotenv (3.1.5) drb (2.2.1) - ed25519 (1.3.0) + ed25519 (1.4.0) erubi (1.13.0) i18n (1.14.6) concurrent-ruby (~> 1.0) diff --git a/kamal.gemspec b/kamal.gemspec index 7218f865..2d66d09a 100644 --- a/kamal.gemspec +++ b/kamal.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |spec| spec.add_dependency "thor", "~> 1.3" spec.add_dependency "dotenv", "~> 3.1" spec.add_dependency "zeitwerk", ">= 2.6.18", "< 3.0" - spec.add_dependency "ed25519", "~> 1.2" + spec.add_dependency "ed25519", "~> 1.4" spec.add_dependency "bcrypt_pbkdf", "~> 1.0" spec.add_dependency "concurrent-ruby", "~> 1.2" spec.add_dependency "base64", "~> 0.2" From 1ea5d0bd8664f1e8697d745d4d6bf05dfa27df3d Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Thu, 17 Apr 2025 14:44:19 +0100 Subject: [PATCH 03/23] Allow kamal-proxy run command options to be set Allow --metrics_port and --debug options to be set via the boot config. --metrics_port support will come in kamal-proxy v0.8.8, so this option doesn't work right now. This will be updated before the next Kamal release though and we can add integration tests for the metrics at that point. --- lib/kamal/cli/proxy.rb | 12 ++++++ lib/kamal/commands/proxy.rb | 10 ++++- test/cli/proxy_test.rb | 40 ++++++++++++++----- test/commands/proxy_test.rb | 16 +++++++- .../.kamal/hooks/pre-deploy | 4 ++ 5 files changed, 70 insertions(+), 12 deletions(-) create mode 100755 test/integration/docker/deployer/app_with_proxied_accessory/.kamal/hooks/pre-deploy diff --git a/lib/kamal/cli/proxy.rb b/lib/kamal/cli/proxy.rb index cd4e45d9..91bcdfc1 100644 --- a/lib/kamal/cli/proxy.rb +++ b/lib/kamal/cli/proxy.rb @@ -31,6 +31,8 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base option :registry, type: :string, default: nil, desc: "Registry to use for the proxy image" option :repository, type: :string, default: nil, desc: "Repository for the proxy image" option :image_version, type: :string, default: nil, desc: "Version of the proxy to run" + option :metrics_port, type: :numeric, default: nil, desc: "Port to report prometheus metrics on" + option :debug, type: :boolean, default: false, desc: "Whether to run the proxy in debug mode" option :docker_options, type: :array, default: [], desc: "Docker options to pass to the proxy container", banner: "option=value option2=value2" def boot_config(subcommand) proxy_boot_config = KAMAL.config.proxy_boot @@ -51,6 +53,9 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base image_version = options[:image_version] + run_command_options = { debug: options[:debug] || nil, "metrics-port": options[:metrics_port] }.compact + run_command = "kamal-proxy run #{Kamal::Utils.optionize(run_command_options).join(" ")}" if run_command_options.any? + on(KAMAL.proxy_hosts) do |host| execute(*KAMAL.proxy.ensure_proxy_directory) if boot_options != proxy_boot_config.default_boot_options @@ -70,6 +75,12 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base else execute *KAMAL.proxy.reset_image_version, raise_on_non_zero_exit: false end + + if run_command + upload! StringIO.new(run_command), KAMAL.config.proxy_run_command_file + else + execute *KAMAL.proxy.reset_run_command, raise_on_non_zero_exit: false + end end when "get" @@ -81,6 +92,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base execute *KAMAL.proxy.reset_boot_options, raise_on_non_zero_exit: false execute *KAMAL.proxy.reset_image, raise_on_non_zero_exit: false execute *KAMAL.proxy.reset_image_version, raise_on_non_zero_exit: false + execute *KAMAL.proxy.reset_run_command, raise_on_non_zero_exit: false end else raise ArgumentError, "Unknown boot_config subcommand #{subcommand}" diff --git a/lib/kamal/commands/proxy.rb b/lib/kamal/commands/proxy.rb index 2193fff7..b9b255f5 100644 --- a/lib/kamal/commands/proxy.rb +++ b/lib/kamal/commands/proxy.rb @@ -70,7 +70,7 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base end def boot_config - [ :echo, "#{substitute(read_boot_options)} #{substitute(read_image)}:#{substitute(read_image_version)}" ] + [ :echo, "#{substitute(read_boot_options)} #{substitute(read_image)}:#{substitute(read_image_version)} #{substitute(read_run_command)}" ] end def read_boot_options @@ -85,6 +85,10 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base read_file(config.proxy_boot.image_version_file, default: Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION) end + def read_run_command + read_file(config.proxy_run_command_file) + end + def reset_boot_options remove_file config.proxy_boot.options_file end @@ -97,6 +101,10 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base remove_file config.proxy_boot.image_version_file end + def reset_run_command + remove_file config.proxy_run_command_file + end + private def container_name config.proxy_boot.container_name diff --git a/test/cli/proxy_test.rb b/test/cli/proxy_test.rb index 428fc515..fea95406 100644 --- a/test/cli/proxy_test.rb +++ b/test/cli/proxy_test.rb @@ -5,7 +5,7 @@ class CliProxyTest < CliTestCase run_command("boot").tap do |output| assert_match "docker login", output assert_match "mkdir -p .kamal/proxy/apps-config", output - assert_match "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy", output + assert_match "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") $(cat .kamal/proxy/run_command 2> /dev/null || echo \"\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy", output end end @@ -19,7 +19,7 @@ class CliProxyTest < CliTestCase exception = assert_raises do run_command("boot").tap do |output| assert_match "docker login", output - assert_match "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy", output + assert_match "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") $(cat .kamal/proxy/run_command 2> /dev/null || echo \"\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy", output end end @@ -37,7 +37,7 @@ class CliProxyTest < CliTestCase run_command("boot").tap do |output| assert_match "docker login", output - assert_match "docker container start kamal-proxy || echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy", output + assert_match "docker container start kamal-proxy || echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") $(cat .kamal/proxy/run_command 2> /dev/null || echo \"\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy", output end ensure Thread.report_on_exception = false @@ -58,13 +58,13 @@ class CliProxyTest < CliTestCase assert_match "docker container stop kamal-proxy on 1.1.1.1", output assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.1", output assert_match "mkdir -p .kamal/proxy/apps-config on 1.1.1.1", output - assert_match "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $(pwd)/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config on 1.1.1.1", output + assert_match "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") $(cat .kamal/proxy/run_command 2> /dev/null || echo \"\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $(pwd)/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config on 1.1.1.1", output assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target=\"abcdefabcdef:80\" --deploy-timeout=\"6s\" --drain-timeout=\"30s\" --buffer-requests --buffer-responses --log-request-header=\"Cache-Control\" --log-request-header=\"Last-Modified\" --log-request-header=\"User-Agent\" on 1.1.1.1", output assert_match "docker container stop kamal-proxy on 1.1.1.2", output assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.2", output assert_match "mkdir -p .kamal/proxy/apps-config on 1.1.1.1", output - assert_match "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $(pwd)/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config on 1.1.1.2", output + assert_match "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") $(cat .kamal/proxy/run_command 2> /dev/null || echo \"\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $(pwd)/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config on 1.1.1.2", output assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target=\"abcdefabcdef:80\" --deploy-timeout=\"6s\" --drain-timeout=\"30s\" --buffer-requests --buffer-responses --log-request-header=\"Cache-Control\" --log-request-header=\"Last-Modified\" --log-request-header=\"User-Agent\" on 1.1.1.2", output end end @@ -199,7 +199,7 @@ class CliProxyTest < CliTestCase assert_match "/usr/bin/env mkdir -p .kamal", output assert_match "docker network create kamal", output assert_match "docker login -u [REDACTED] -p [REDACTED]", output - assert_match "docker container start kamal-proxy || echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy", output + assert_match "docker container start kamal-proxy || echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") $(cat .kamal/proxy/run_command 2> /dev/null || echo \"\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy", output assert_match "/usr/bin/env mkdir -p .kamal", output assert_match %r{docker rename app-web-latest app-web-latest_replaced_.*}, output assert_match "/usr/bin/env mkdir -p .kamal/apps/app/env/roles", output @@ -243,7 +243,9 @@ class CliProxyTest < CliTestCase assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/options on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image on #{host}", output - assert_match "Running /usr/bin/env rm .kamal/proxy/image_version on #{host}", output end + assert_match "Running /usr/bin/env rm .kamal/proxy/image_version on #{host}", output + assert_match "Running /usr/bin/env rm .kamal/proxy/run_command on #{host}", output + end end end @@ -254,6 +256,7 @@ class CliProxyTest < CliTestCase assert_match "Uploading \"--log-opt max-size=10m\" to .kamal/proxy/options on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image_version on #{host}", output + assert_match "Running /usr/bin/env rm .kamal/proxy/run_command on #{host}", output end end end @@ -265,6 +268,7 @@ class CliProxyTest < CliTestCase assert_match "Uploading \"--publish 80:80 --publish 443:443 --log-opt max-size=100m\" to .kamal/proxy/options on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image_version on #{host}", output + assert_match "Running /usr/bin/env rm .kamal/proxy/run_command on #{host}", output end end end @@ -276,6 +280,7 @@ class CliProxyTest < CliTestCase assert_match "Uploading \"--publish 80:80 --publish 443:443\" to .kamal/proxy/options on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image_version on #{host}", output + assert_match "Running /usr/bin/env rm .kamal/proxy/run_command on #{host}", output end end end @@ -322,6 +327,7 @@ class CliProxyTest < CliTestCase assert_match "Uploading \"--publish 80:80 --publish 443:443 --log-opt max-size=10m --label=foo=bar --add_host=thishost:thathost\" to .kamal/proxy/options on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image_version on #{host}", output + assert_match "Running /usr/bin/env rm .kamal/proxy/run_command on #{host}", output end end end @@ -333,6 +339,7 @@ class CliProxyTest < CliTestCase assert_match "Running /usr/bin/env rm .kamal/proxy/options on #{host}", output assert_match "Uploading \"myreg/basecamp/kamal-proxy\" to .kamal/proxy/image on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image_version on #{host}", output + assert_match "Running /usr/bin/env rm .kamal/proxy/run_command on #{host}", output end end end @@ -344,6 +351,7 @@ class CliProxyTest < CliTestCase assert_match "Running /usr/bin/env rm .kamal/proxy/options on #{host}", output assert_match "Uploading \"myrepo/kamal-proxy\" to .kamal/proxy/image on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image_version on #{host}", output + assert_match "Running /usr/bin/env rm .kamal/proxy/run_command on #{host}", output end end end @@ -355,23 +363,37 @@ class CliProxyTest < CliTestCase assert_match "Running /usr/bin/env rm .kamal/proxy/options on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image on #{host}", output assert_match "Uploading \"0.9.9\" to .kamal/proxy/image_version on #{host}", output + assert_match "Running /usr/bin/env rm .kamal/proxy/run_command on #{host}", output + end + end + end + + test "boot_config set run_command" do + run_command("boot_config", "set", "--metrics_port", "9000", "--debug", "true").tap do |output| + %w[ 1.1.1.1 1.1.1.2 ].each do |host| + assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output + assert_match "Running /usr/bin/env rm .kamal/proxy/options on #{host}", output + assert_match "Running /usr/bin/env rm .kamal/proxy/image on #{host}", output + assert_match "Running /usr/bin/env rm .kamal/proxy/image_version on #{host}", output + assert_match "Uploading \"kamal-proxy run --debug --metrics-port \\\"9000\\\"\" to .kamal/proxy/run_command on #{host}", output end end end test "boot_config set all" do - run_command("boot_config", "set", "--docker_options", "label=foo=bar", "--registry", "myreg", "--repository", "myrepo", "--image_version", "0.9.9").tap do |output| + run_command("boot_config", "set", "--docker_options", "label=foo=bar", "--registry", "myreg", "--repository", "myrepo", "--image_version", "0.9.9", "--metrics_port", "9000", "--debug", "true").tap do |output| %w[ 1.1.1.1 1.1.1.2 ].each do |host| assert_match "Uploading \"--publish 80:80 --publish 443:443 --log-opt max-size=10m --label=foo=bar\" to .kamal/proxy/options on #{host}", output assert_match "Uploading \"myreg/myrepo/kamal-proxy\" to .kamal/proxy/image on #{host}", output assert_match "Uploading \"0.9.9\" to .kamal/proxy/image_version on #{host}", output + assert_match "Uploading \"kamal-proxy run --debug --metrics-port \\\"9000\\\"\" to .kamal/proxy/run_command on #{host}", output end end end test "boot_config get" do SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) - .with(:echo, "$(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"v0.8.7\")") + .with(:echo, "$(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") $(cat .kamal/proxy/run_command 2> /dev/null || echo \"\")") .returns("--publish 80:80 --publish 8443:443 --label=foo=bar basecamp/kamal-proxy:v1.0.0") .twice diff --git a/test/commands/proxy_test.rb b/test/commands/proxy_test.rb index e141725a..2bb2dadb 100644 --- a/test/commands/proxy_test.rb +++ b/test/commands/proxy_test.rb @@ -15,7 +15,7 @@ class CommandsProxyTest < ActiveSupport::TestCase test "run" do assert_equal \ - "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $(pwd)/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config", + "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") $(cat .kamal/proxy/run_command 2> /dev/null || echo \"\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $(pwd)/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config", new_command.run.join(" ") end @@ -23,7 +23,7 @@ class CommandsProxyTest < ActiveSupport::TestCase @config.delete(:proxy) assert_equal \ - "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $(pwd)/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config", + "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") $(cat .kamal/proxy/run_command 2> /dev/null || echo \"\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $(pwd)/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config", new_command.run.join(" ") end @@ -129,6 +129,12 @@ class CommandsProxyTest < ActiveSupport::TestCase new_command.read_image_version.join(" ") end + test "read_run_command" do + assert_equal \ + "cat .kamal/proxy/run_command 2> /dev/null || echo \"\"", + new_command.read_run_command.join(" ") + end + test "reset_boot_options" do assert_equal \ "rm .kamal/proxy/options", @@ -153,6 +159,12 @@ class CommandsProxyTest < ActiveSupport::TestCase new_command.ensure_apps_config_directory.join(" ") end + test "reset_run_command" do + assert_equal \ + "rm .kamal/proxy/run_command", + new_command.reset_run_command.join(" ") + end + private def new_command Kamal::Commands::Proxy.new(Kamal::Configuration.new(@config, version: "123")) diff --git a/test/integration/docker/deployer/app_with_proxied_accessory/.kamal/hooks/pre-deploy b/test/integration/docker/deployer/app_with_proxied_accessory/.kamal/hooks/pre-deploy new file mode 100755 index 00000000..af8d0b4a --- /dev/null +++ b/test/integration/docker/deployer/app_with_proxied_accessory/.kamal/hooks/pre-deploy @@ -0,0 +1,4 @@ +#!/bin/sh +set -e + +kamal proxy boot_config set --registry registry:4443 From e32ea2e2764ca88a6127d71fafc14ad65f643ace Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Thu, 17 Apr 2025 16:09:01 +0100 Subject: [PATCH 04/23] Expose the metrics port --- lib/kamal/cli/proxy.rb | 1 + test/cli/proxy_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/kamal/cli/proxy.rb b/lib/kamal/cli/proxy.rb index 91bcdfc1..a65caf52 100644 --- a/lib/kamal/cli/proxy.rb +++ b/lib/kamal/cli/proxy.rb @@ -42,6 +42,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base boot_options = [ *(proxy_boot_config.publish_args(options[:http_port], options[:https_port], options[:publish_host_ip]) if options[:publish]), *(proxy_boot_config.logging_args(options[:log_max_size])), + *("--expose=#{options[:metrics_port]}" if options[:metrics_port]), *options[:docker_options].map { |option| "--#{option}" } ] diff --git a/test/cli/proxy_test.rb b/test/cli/proxy_test.rb index fea95406..fd131984 100644 --- a/test/cli/proxy_test.rb +++ b/test/cli/proxy_test.rb @@ -372,7 +372,7 @@ class CliProxyTest < CliTestCase run_command("boot_config", "set", "--metrics_port", "9000", "--debug", "true").tap do |output| %w[ 1.1.1.1 1.1.1.2 ].each do |host| assert_match "Running /usr/bin/env mkdir -p .kamal/proxy on #{host}", output - assert_match "Running /usr/bin/env rm .kamal/proxy/options on #{host}", output + assert_match "Uploading \"--publish 80:80 --publish 443:443 --log-opt max-size=10m --expose=9000\" to .kamal/proxy/options on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image on #{host}", output assert_match "Running /usr/bin/env rm .kamal/proxy/image_version on #{host}", output assert_match "Uploading \"kamal-proxy run --debug --metrics-port \\\"9000\\\"\" to .kamal/proxy/run_command on #{host}", output @@ -383,7 +383,7 @@ class CliProxyTest < CliTestCase test "boot_config set all" do run_command("boot_config", "set", "--docker_options", "label=foo=bar", "--registry", "myreg", "--repository", "myrepo", "--image_version", "0.9.9", "--metrics_port", "9000", "--debug", "true").tap do |output| %w[ 1.1.1.1 1.1.1.2 ].each do |host| - assert_match "Uploading \"--publish 80:80 --publish 443:443 --log-opt max-size=10m --label=foo=bar\" to .kamal/proxy/options on #{host}", output + assert_match "Uploading \"--publish 80:80 --publish 443:443 --log-opt max-size=10m --expose=9000 --label=foo=bar\" to .kamal/proxy/options on #{host}", output assert_match "Uploading \"myreg/myrepo/kamal-proxy\" to .kamal/proxy/image on #{host}", output assert_match "Uploading \"0.9.9\" to .kamal/proxy/image_version on #{host}", output assert_match "Uploading \"kamal-proxy run --debug --metrics-port \\\"9000\\\"\" to .kamal/proxy/run_command on #{host}", output From 226e7091dbe196260868459cc73758473bd7650d Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Tue, 6 May 2025 12:14:58 +0100 Subject: [PATCH 05/23] Add run_command_file to proxy boot --- lib/kamal/cli/proxy.rb | 2 +- lib/kamal/commands/proxy.rb | 4 ++-- lib/kamal/configuration/proxy/boot.rb | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/kamal/cli/proxy.rb b/lib/kamal/cli/proxy.rb index a65caf52..73707572 100644 --- a/lib/kamal/cli/proxy.rb +++ b/lib/kamal/cli/proxy.rb @@ -78,7 +78,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base end if run_command - upload! StringIO.new(run_command), KAMAL.config.proxy_run_command_file + upload! StringIO.new(run_command), proxy_boot_config.run_command_file else execute *KAMAL.proxy.reset_run_command, raise_on_non_zero_exit: false end diff --git a/lib/kamal/commands/proxy.rb b/lib/kamal/commands/proxy.rb index b9b255f5..7699dde2 100644 --- a/lib/kamal/commands/proxy.rb +++ b/lib/kamal/commands/proxy.rb @@ -86,7 +86,7 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base end def read_run_command - read_file(config.proxy_run_command_file) + read_file(config.proxy_boot.run_command_file) end def reset_boot_options @@ -102,7 +102,7 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base end def reset_run_command - remove_file config.proxy_run_command_file + remove_file config.proxy_boot.run_command_file end private diff --git a/lib/kamal/configuration/proxy/boot.rb b/lib/kamal/configuration/proxy/boot.rb index 668c0cea..9f8c6f5c 100644 --- a/lib/kamal/configuration/proxy/boot.rb +++ b/lib/kamal/configuration/proxy/boot.rb @@ -66,6 +66,10 @@ class Kamal::Configuration::Proxy::Boot File.join host_directory, "image_version" end + def run_command_file + File.join host_directory, "run_command" + end + def apps_directory File.join host_directory, "apps-config" end From c351c2d2ded7f7b1f17ad79751cd823e279aee92 Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Fri, 9 May 2025 08:40:41 +0100 Subject: [PATCH 06/23] Set minimum proxy version to 0.9.0 --- lib/kamal/configuration/proxy/boot.rb | 2 +- test/integration/docker/deployer/setup.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/kamal/configuration/proxy/boot.rb b/lib/kamal/configuration/proxy/boot.rb index 9f8c6f5c..7cdd6a06 100644 --- a/lib/kamal/configuration/proxy/boot.rb +++ b/lib/kamal/configuration/proxy/boot.rb @@ -1,5 +1,5 @@ class Kamal::Configuration::Proxy::Boot - MINIMUM_VERSION = "v0.8.7" + MINIMUM_VERSION = "v0.9.0" DEFAULT_HTTP_PORT = 80 DEFAULT_HTTPS_PORT = 443 DEFAULT_LOG_MAX_SIZE = "10m" diff --git a/test/integration/docker/deployer/setup.sh b/test/integration/docker/deployer/setup.sh index 24f39d7f..1ad7998b 100755 --- a/test/integration/docker/deployer/setup.sh +++ b/test/integration/docker/deployer/setup.sh @@ -20,7 +20,7 @@ push_image_to_registry_4443() { install_kamal push_image_to_registry_4443 nginx 1-alpine-slim push_image_to_registry_4443 busybox 1.36.0 -push_image_to_registry_4443 basecamp/kamal-proxy v0.8.7 +push_image_to_registry_4443 basecamp/kamal-proxy v0.9.0 # .ssh is on a shared volume that persists between runs. Clean it up as the # churn of temporary vm IPs can eventually create conflicts. From 83a5636e27ccd4da2a6856a5690e879aef6fb8d8 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 9 May 2025 18:14:47 +0200 Subject: [PATCH 07/23] Pin accessories to tags --- lib/kamal/configuration/accessory.rb | 8 +++++++- lib/kamal/configuration/docs/accessory.yml | 4 +++- lib/kamal/configuration/validator/accessory.rb | 4 ++-- test/configuration/accessory_test.rb | 10 +++++++--- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/kamal/configuration/accessory.rb b/lib/kamal/configuration/accessory.rb index 62979627..819f309b 100644 --- a/lib/kamal/configuration/accessory.rb +++ b/lib/kamal/configuration/accessory.rb @@ -32,7 +32,7 @@ class Kamal::Configuration::Accessory end def hosts - hosts_from_host || hosts_from_hosts || hosts_from_roles + hosts_from_host || hosts_from_hosts || hosts_from_roles || hosts_from_tags end def port @@ -206,6 +206,12 @@ class Kamal::Configuration::Accessory end end + def hosts_from_tags + if accessory_config.key?("tags") + accessory_config["tags"].flat_map { |tag| config.tag(tag)&.hosts } + end + end + def network accessory_config["network"] || DEFAULT_NETWORK end diff --git a/lib/kamal/configuration/docs/accessory.yml b/lib/kamal/configuration/docs/accessory.yml index 1852681e..7a77fd46 100644 --- a/lib/kamal/configuration/docs/accessory.yml +++ b/lib/kamal/configuration/docs/accessory.yml @@ -46,13 +46,15 @@ accessories: # Accessory hosts # - # Specify one of `host`, `hosts`, or `roles`: + # Specify one of `host`, `hosts`, `roles`, or `tags`: host: mysql-db1 hosts: - mysql-db1 - mysql-db2 roles: - mysql + tags: + - writer # Custom command # diff --git a/lib/kamal/configuration/validator/accessory.rb b/lib/kamal/configuration/validator/accessory.rb index 8b3d5b71..f870a27f 100644 --- a/lib/kamal/configuration/validator/accessory.rb +++ b/lib/kamal/configuration/validator/accessory.rb @@ -2,8 +2,8 @@ class Kamal::Configuration::Validator::Accessory < Kamal::Configuration::Validat def validate! super - if (config.keys & [ "host", "hosts", "roles" ]).size != 1 - error "specify one of `host`, `hosts` or `roles`" + if (config.keys & [ "host", "hosts", "roles", "tags" ]).size != 1 + error "specify one of `host`, `hosts`, `roles` or `tags`" end validate_docker_options!(config["options"]) diff --git a/test/configuration/accessory_test.rb b/test/configuration/accessory_test.rb index a652c2ac..ee6d6856 100644 --- a/test/configuration/accessory_test.rb +++ b/test/configuration/accessory_test.rb @@ -7,7 +7,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" }, servers: { - "web" => [ "1.1.1.1", "1.1.1.2" ], + "web" => [ { "1.1.1.1" => "writer" }, { "1.1.1.2" => "reader" } ], "workers" => [ "1.1.1.3", "1.1.1.4" ] }, builder: { "arch" => "amd64" }, @@ -70,6 +70,10 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase "proxy" => { "host" => "monitoring.example.com" } + }, + "proxy" => { + "image" => "proxy:latest", + "tags" => [ "writer" ] } } } @@ -117,14 +121,14 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase end end - test "setting host, hosts and roles" do + test "setting host, hosts, roles and tags" do @deploy[:accessories]["mysql"]["hosts"] = [ "mysql-db1" ] @deploy[:accessories]["mysql"]["roles"] = [ "db" ] exception = assert_raises(Kamal::ConfigurationError) do Kamal::Configuration.new(@deploy) end - assert_equal "accessories/mysql: specify one of `host`, `hosts` or `roles`", exception.message + assert_equal "accessories/mysql: specify one of `host`, `hosts`, `roles` or `tags`", exception.message end test "all hosts" do From 9aac51bbd0f59f07a09ce2d701e9575b3fc069e4 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 9 May 2025 21:11:28 +0200 Subject: [PATCH 08/23] Extract hosts for accessories by tags --- lib/kamal/configuration/accessory.rb | 16 +++++++++++++++- test/configuration/accessory_test.rb | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/kamal/configuration/accessory.rb b/lib/kamal/configuration/accessory.rb index 819f309b..50f70e58 100644 --- a/lib/kamal/configuration/accessory.rb +++ b/lib/kamal/configuration/accessory.rb @@ -208,7 +208,21 @@ class Kamal::Configuration::Accessory def hosts_from_tags if accessory_config.key?("tags") - accessory_config["tags"].flat_map { |tag| config.tag(tag)&.hosts } + accessory_config["tags"].flat_map { |tag| extract_hosts_from_config_with_tag(tag) } + end + end + + def extract_hosts_from_config_with_tag(tag) + if config.raw_config.servers.is_a?(Hash) + config.raw_config.servers.flat_map do |(role, servers_in_role)| + servers_in_role.collect do |host| + if host.is_a?(Hash) && host.values.first.include?(tag) + host.keys.first + end + end + end.compact + else + [] end end diff --git a/test/configuration/accessory_test.rb b/test/configuration/accessory_test.rb index ee6d6856..0e75c629 100644 --- a/test/configuration/accessory_test.rb +++ b/test/configuration/accessory_test.rb @@ -111,6 +111,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase assert_equal [ "1.1.1.5" ], @config.accessory(:mysql).hosts assert_equal [ "1.1.1.6", "1.1.1.7" ], @config.accessory(:redis).hosts assert_equal [ "1.1.1.1", "1.1.1.2" ], @config.accessory(:monitoring).hosts + assert_equal [ "1.1.1.1" ], @config.accessory(:proxy).hosts end test "missing host" do From 1ca2b4d394af052e687938e4bfe3863e342af6e8 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 9 May 2025 21:15:44 +0200 Subject: [PATCH 09/23] Test with multiple host matches across roles --- test/configuration/accessory_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/configuration/accessory_test.rb b/test/configuration/accessory_test.rb index 0e75c629..fa3a8005 100644 --- a/test/configuration/accessory_test.rb +++ b/test/configuration/accessory_test.rb @@ -8,7 +8,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase registry: { "username" => "dhh", "password" => "secret" }, servers: { "web" => [ { "1.1.1.1" => "writer" }, { "1.1.1.2" => "reader" } ], - "workers" => [ "1.1.1.3", "1.1.1.4" ] + "workers" => [ { "1.1.1.3" => "writer" }, "1.1.1.4" ] }, builder: { "arch" => "amd64" }, env: { "REDIS_URL" => "redis://x/y" }, @@ -111,7 +111,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase assert_equal [ "1.1.1.5" ], @config.accessory(:mysql).hosts assert_equal [ "1.1.1.6", "1.1.1.7" ], @config.accessory(:redis).hosts assert_equal [ "1.1.1.1", "1.1.1.2" ], @config.accessory(:monitoring).hosts - assert_equal [ "1.1.1.1" ], @config.accessory(:proxy).hosts + assert_equal [ "1.1.1.1", "1.1.1.3" ], @config.accessory(:proxy).hosts end test "missing host" do From 5ad000a08e0e480c76cdbf0362aa1033e3b8cba5 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 9 May 2025 21:16:31 +0200 Subject: [PATCH 10/23] Unnecessary parenthesis --- lib/kamal/configuration/accessory.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/kamal/configuration/accessory.rb b/lib/kamal/configuration/accessory.rb index 50f70e58..1317501e 100644 --- a/lib/kamal/configuration/accessory.rb +++ b/lib/kamal/configuration/accessory.rb @@ -214,7 +214,7 @@ class Kamal::Configuration::Accessory def extract_hosts_from_config_with_tag(tag) if config.raw_config.servers.is_a?(Hash) - config.raw_config.servers.flat_map do |(role, servers_in_role)| + config.raw_config.servers.flat_map do |role, servers_in_role| servers_in_role.collect do |host| if host.is_a?(Hash) && host.values.first.include?(tag) host.keys.first From 9d5a534ef86e5bf7beb8edabce5f36d374fb8e12 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 9 May 2025 21:26:41 +0200 Subject: [PATCH 11/23] Refactored for clarity and style --- lib/kamal/configuration/accessory.rb | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/kamal/configuration/accessory.rb b/lib/kamal/configuration/accessory.rb index 1317501e..f7edb81b 100644 --- a/lib/kamal/configuration/accessory.rb +++ b/lib/kamal/configuration/accessory.rb @@ -213,16 +213,12 @@ class Kamal::Configuration::Accessory end def extract_hosts_from_config_with_tag(tag) - if config.raw_config.servers.is_a?(Hash) - config.raw_config.servers.flat_map do |role, servers_in_role| + if (servers_with_roles = config.raw_config.servers).is_a?(Hash) + servers_with_roles.flat_map do |role, servers_in_role| servers_in_role.collect do |host| - if host.is_a?(Hash) && host.values.first.include?(tag) - host.keys.first - end - end - end.compact - else - [] + host.keys.first if host.is_a?(Hash) && host.values.first.include?(tag) + end.compact + end end end From fb82d04aafb30f43b67309e19a9a3d835b60767b Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 9 May 2025 21:30:33 +0200 Subject: [PATCH 12/23] Use #filter_map instead of #collect + #compact --- lib/kamal/configuration/accessory.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/kamal/configuration/accessory.rb b/lib/kamal/configuration/accessory.rb index f7edb81b..a510acba 100644 --- a/lib/kamal/configuration/accessory.rb +++ b/lib/kamal/configuration/accessory.rb @@ -215,9 +215,9 @@ class Kamal::Configuration::Accessory def extract_hosts_from_config_with_tag(tag) if (servers_with_roles = config.raw_config.servers).is_a?(Hash) servers_with_roles.flat_map do |role, servers_in_role| - servers_in_role.collect do |host| + servers_in_role.filter_map do |host| host.keys.first if host.is_a?(Hash) && host.values.first.include?(tag) - end.compact + end end end end From 299c741c1b872a96fbce90815522c8b8e5e69658 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 9 May 2025 21:47:26 +0200 Subject: [PATCH 13/23] More natural api when you are just applying accessory to a single tag --- lib/kamal/configuration/accessory.rb | 4 +++- lib/kamal/configuration/docs/accessory.yml | 2 ++ lib/kamal/configuration/validator/accessory.rb | 4 ++-- test/configuration/accessory_test.rb | 10 +++++++--- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/kamal/configuration/accessory.rb b/lib/kamal/configuration/accessory.rb index a510acba..c15501a9 100644 --- a/lib/kamal/configuration/accessory.rb +++ b/lib/kamal/configuration/accessory.rb @@ -207,7 +207,9 @@ class Kamal::Configuration::Accessory end def hosts_from_tags - if accessory_config.key?("tags") + if accessory_config.key?("tag") + extract_hosts_from_config_with_tag(accessory_config["tag"]) + elsif accessory_config.key?("tags") accessory_config["tags"].flat_map { |tag| extract_hosts_from_config_with_tag(tag) } end end diff --git a/lib/kamal/configuration/docs/accessory.yml b/lib/kamal/configuration/docs/accessory.yml index 7a77fd46..1033edaa 100644 --- a/lib/kamal/configuration/docs/accessory.yml +++ b/lib/kamal/configuration/docs/accessory.yml @@ -53,8 +53,10 @@ accessories: - mysql-db2 roles: - mysql + tag: writer tags: - writer + - reader # Custom command # diff --git a/lib/kamal/configuration/validator/accessory.rb b/lib/kamal/configuration/validator/accessory.rb index f870a27f..96c5aafc 100644 --- a/lib/kamal/configuration/validator/accessory.rb +++ b/lib/kamal/configuration/validator/accessory.rb @@ -2,8 +2,8 @@ class Kamal::Configuration::Validator::Accessory < Kamal::Configuration::Validat def validate! super - if (config.keys & [ "host", "hosts", "roles", "tags" ]).size != 1 - error "specify one of `host`, `hosts`, `roles` or `tags`" + if (config.keys & [ "host", "hosts", "roles", "tag", "tags" ]).size != 1 + error "specify one of `host`, `hosts`, `roles`, `tag` or `tags`" end validate_docker_options!(config["options"]) diff --git a/test/configuration/accessory_test.rb b/test/configuration/accessory_test.rb index fa3a8005..e8af6556 100644 --- a/test/configuration/accessory_test.rb +++ b/test/configuration/accessory_test.rb @@ -73,7 +73,11 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase }, "proxy" => { "image" => "proxy:latest", - "tags" => [ "writer" ] + "tags" => [ "writer", "reader" ] + }, + "logger" => { + "image" => "logger:latest", + "tag" => "writer" } } } @@ -111,7 +115,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase assert_equal [ "1.1.1.5" ], @config.accessory(:mysql).hosts assert_equal [ "1.1.1.6", "1.1.1.7" ], @config.accessory(:redis).hosts assert_equal [ "1.1.1.1", "1.1.1.2" ], @config.accessory(:monitoring).hosts - assert_equal [ "1.1.1.1", "1.1.1.3" ], @config.accessory(:proxy).hosts + assert_equal [ "1.1.1.1", "1.1.1.3", "1.1.1.2" ], @config.accessory(:proxy).hosts end test "missing host" do @@ -129,7 +133,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase exception = assert_raises(Kamal::ConfigurationError) do Kamal::Configuration.new(@deploy) end - assert_equal "accessories/mysql: specify one of `host`, `hosts`, `roles` or `tags`", exception.message + assert_equal "accessories/mysql: specify one of `host`, `hosts`, `roles`, `tag` or `tags`", exception.message end test "all hosts" do From 7bfb2ed9f2777db7b87293121ddc0359350a795e Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 9 May 2025 21:50:07 +0200 Subject: [PATCH 14/23] Actually test the fixture for singular --- test/configuration/accessory_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/configuration/accessory_test.rb b/test/configuration/accessory_test.rb index e8af6556..179a5f5d 100644 --- a/test/configuration/accessory_test.rb +++ b/test/configuration/accessory_test.rb @@ -116,6 +116,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase assert_equal [ "1.1.1.6", "1.1.1.7" ], @config.accessory(:redis).hosts assert_equal [ "1.1.1.1", "1.1.1.2" ], @config.accessory(:monitoring).hosts assert_equal [ "1.1.1.1", "1.1.1.3", "1.1.1.2" ], @config.accessory(:proxy).hosts + assert_equal [ "1.1.1.1", "1.1.1.3" ], @config.accessory(:logger).hosts end test "missing host" do From 78c9d610cf61e2dbe45d1899a7f47a3b6e879a96 Mon Sep 17 00:00:00 2001 From: Krzysztof Duda Date: Mon, 12 May 2025 10:55:15 +0200 Subject: [PATCH 15/23] Add a singular role --- lib/kamal/configuration/accessory.rb | 6 +++++- lib/kamal/configuration/docs/accessory.yml | 3 ++- lib/kamal/configuration/validator/accessory.rb | 4 ++-- test/configuration/accessory_test.rb | 4 ++-- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/kamal/configuration/accessory.rb b/lib/kamal/configuration/accessory.rb index c15501a9..c1dbad57 100644 --- a/lib/kamal/configuration/accessory.rb +++ b/lib/kamal/configuration/accessory.rb @@ -201,7 +201,9 @@ class Kamal::Configuration::Accessory end def hosts_from_roles - if accessory_config.key?("roles") + if accessory_config.key?("role") + config.role(accessory_config["role"])&.hosts + elsif accessory_config.key?("roles") accessory_config["roles"].flat_map { |role| config.role(role)&.hosts } end end @@ -231,6 +233,8 @@ class Kamal::Configuration::Accessory def ensure_valid_roles if accessory_config["roles"] && (missing_roles = accessory_config["roles"] - config.roles.map(&:name)).any? raise Kamal::ConfigurationError, "accessories/#{name}: unknown roles #{missing_roles.join(", ")}" + elsif accessory_config["role"] && !config.role(accessory_config["role"]) + raise Kamal::ConfigurationError, "accessories/#{name}: unknown role #{accessory_config["role"]}" end end end diff --git a/lib/kamal/configuration/docs/accessory.yml b/lib/kamal/configuration/docs/accessory.yml index 1033edaa..3983211a 100644 --- a/lib/kamal/configuration/docs/accessory.yml +++ b/lib/kamal/configuration/docs/accessory.yml @@ -46,11 +46,12 @@ accessories: # Accessory hosts # - # Specify one of `host`, `hosts`, `roles`, or `tags`: + # Specify one of `host`, `hosts`, `role`, `roles`, `tag` or `tags`: host: mysql-db1 hosts: - mysql-db1 - mysql-db2 + role: mysql roles: - mysql tag: writer diff --git a/lib/kamal/configuration/validator/accessory.rb b/lib/kamal/configuration/validator/accessory.rb index 96c5aafc..564cfc7e 100644 --- a/lib/kamal/configuration/validator/accessory.rb +++ b/lib/kamal/configuration/validator/accessory.rb @@ -2,8 +2,8 @@ class Kamal::Configuration::Validator::Accessory < Kamal::Configuration::Validat def validate! super - if (config.keys & [ "host", "hosts", "roles", "tag", "tags" ]).size != 1 - error "specify one of `host`, `hosts`, `roles`, `tag` or `tags`" + if (config.keys & [ "host", "hosts", "role", "roles", "tag", "tags" ]).size != 1 + error "specify one of `host`, `hosts`, `role`, `roles`, `tag` or `tags`" end validate_docker_options!(config["options"]) diff --git a/test/configuration/accessory_test.rb b/test/configuration/accessory_test.rb index 179a5f5d..0f13b33e 100644 --- a/test/configuration/accessory_test.rb +++ b/test/configuration/accessory_test.rb @@ -55,7 +55,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase "service" => "custom-monitoring", "image" => "monitoring:latest", "registry" => { "server" => "other.registry", "username" => "user", "password" => "pw" }, - "roles" => [ "web" ], + "role" => "web", "port" => "4321:4321", "labels" => { "cache" => "true" @@ -134,7 +134,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase exception = assert_raises(Kamal::ConfigurationError) do Kamal::Configuration.new(@deploy) end - assert_equal "accessories/mysql: specify one of `host`, `hosts`, `roles`, `tag` or `tags`", exception.message + assert_equal "accessories/mysql: specify one of `host`, `hosts`, `role`, `roles`, `tag` or `tags`", exception.message end test "all hosts" do From 031f55ecf745bd7a3aec173d360bd96dc64b643c Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Tue, 13 May 2025 09:50:52 +0100 Subject: [PATCH 16/23] Bump version for 2.6.0 --- Gemfile.lock | 2 +- lib/kamal/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f6328178..bcc38f0a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - kamal (2.5.3) + kamal (2.6.0) activesupport (>= 7.0) base64 (~> 0.2) bcrypt_pbkdf (~> 1.0) diff --git a/lib/kamal/version.rb b/lib/kamal/version.rb index 3099a7cc..9525c872 100644 --- a/lib/kamal/version.rb +++ b/lib/kamal/version.rb @@ -1,3 +1,3 @@ module Kamal - VERSION = "2.5.3" + VERSION = "2.6.0" end From 2c1d6ed8916e9f93a5a2639092f8e28f9214d1b8 Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Wed, 14 May 2025 15:55:54 +0100 Subject: [PATCH 17/23] Run pre-connect hooks before building They might be needed for remote builds or the pre-build hook. --- lib/kamal/cli/build.rb | 4 ++++ test/cli/build_test.rb | 1 + 2 files changed, 5 insertions(+) diff --git a/lib/kamal/cli/build.rb b/lib/kamal/cli/build.rb index b9b6a5d5..2327943e 100644 --- a/lib/kamal/cli/build.rb +++ b/lib/kamal/cli/build.rb @@ -14,6 +14,10 @@ class Kamal::Cli::Build < Kamal::Cli::Base def push cli = self + # Ensure pre-connect hooks run before the build, they may needed for a remote builder + # or the pre-build hooks. + pre_connect_if_required + ensure_docker_installed login_to_registry_locally diff --git a/test/cli/build_test.rb b/test/cli/build_test.rb index c0a236ad..0bdd6b65 100644 --- a/test/cli/build_test.rb +++ b/test/cli/build_test.rb @@ -21,6 +21,7 @@ class CliBuildTest < CliTestCase .returns("") run_command("push", "--verbose").tap do |output| + assert_hook_ran "pre-connect", output assert_hook_ran "pre-build", output assert_match /Cloning repo into build directory/, output assert_match /git -C #{Dir.tmpdir}\/kamal-clones\/app-#{pwd_sha} clone #{Dir.pwd}/, output From b9e5ce7ca7a93d1b37d8bd37365a251b27ea684b Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Thu, 15 May 2025 09:51:40 +0100 Subject: [PATCH 18/23] Ensure primary_role app hosts are sorted first When booting non-primary role hosts we will always wait for a primary role host to boor first. So when booting in groups, if there are no primary role hosts in the first batch, then booting will stall. Sort primary role app_hosts first to avoid this. Fixes: https://github.com/basecamp/kamal/issues/1553 --- lib/kamal/commander/specifics.rb | 8 ++++++-- test/commander_test.rb | 6 ++++++ .../deploy_with_roles_workers_primary.yml | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 test/fixtures/deploy_with_roles_workers_primary.yml diff --git a/lib/kamal/commander/specifics.rb b/lib/kamal/commander/specifics.rb index e609baba..d61f6b28 100644 --- a/lib/kamal/commander/specifics.rb +++ b/lib/kamal/commander/specifics.rb @@ -11,7 +11,7 @@ class Kamal::Commander::Specifics @primary_role = primary_or_first_role(roles_on(primary_host)) stable_sort!(roles) { |role| role == primary_role ? 0 : 1 } - stable_sort!(hosts) { |host| roles_on(host).any? { |role| role == primary_role } ? 0 : 1 } + sort_primary_role_hosts_first!(hosts) end def roles_on(host) @@ -19,7 +19,7 @@ class Kamal::Commander::Specifics end def app_hosts - config.app_hosts & specified_hosts + @app_hosts ||= sort_primary_role_hosts_first!(config.app_hosts & specified_hosts) end def proxy_hosts @@ -55,4 +55,8 @@ class Kamal::Commander::Specifics specified_hosts end end + + def sort_primary_role_hosts_first!(hosts) + stable_sort!(hosts) { |host| roles_on(host).any? { |role| role == primary_role } ? 0 : 1 } + end end diff --git a/test/commander_test.rb b/test/commander_test.rb index 4a3aa2e2..84115f3a 100644 --- a/test/commander_test.rb +++ b/test/commander_test.rb @@ -149,6 +149,12 @@ class CommanderTest < ActiveSupport::TestCase assert_equal [], @kamal.accessory_hosts end + test "primary role hosts are first" do + configure_with(:deploy_with_roles_workers_primary) + assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4" ], @kamal.hosts + assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4" ], @kamal.app_hosts + end + private def configure_with(variant) @kamal = Kamal::Commander.new.tap do |kamal| diff --git a/test/fixtures/deploy_with_roles_workers_primary.yml b/test/fixtures/deploy_with_roles_workers_primary.yml new file mode 100644 index 00000000..4e208c6b --- /dev/null +++ b/test/fixtures/deploy_with_roles_workers_primary.yml @@ -0,0 +1,19 @@ +service: app +image: dhh/app +servers: + workers: + - 1.1.1.1 + - 1.1.1.2 + web: + - 1.1.1.3 + - 1.1.1.4 +env: + REDIS_URL: redis://x/y +registry: + server: registry.digitalocean.com + username: user + password: pw +builder: + arch: amd64 +deploy_timeout: 1 +primary_role: workers From 7b1439c3c645ea856e8b8f407409fa1adf176d36 Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Thu, 15 May 2025 10:14:52 +0100 Subject: [PATCH 19/23] Update per-role proxy docs Clarify that proxy: true/proxy: false only belong in the role config, not at the root level. --- lib/kamal/configuration/docs/proxy.yml | 32 ++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/kamal/configuration/docs/proxy.yml b/lib/kamal/configuration/docs/proxy.yml index 49c11ac8..a127bfea 100644 --- a/lib/kamal/configuration/docs/proxy.yml +++ b/lib/kamal/configuration/docs/proxy.yml @@ -10,11 +10,6 @@ # They are application-specific, so they are not shared when multiple applications # run on the same proxy. # -# The proxy is enabled by default on the primary role but can be disabled by -# setting `proxy: false`. -# -# It is disabled by default on all other roles but can be enabled by setting -# `proxy: true` or providing a proxy configuration. proxy: # Hosts @@ -113,3 +108,30 @@ proxy: response_headers: - X-Request-ID - X-Request-Start + +# Enabling/disabling the proxy on roles +# +# The proxy is enabled by default on the primary role but can be disabled by +# setting `proxy: false` in the primary role's configuration. +# +# ```yaml +# servers: +# web: +# hosts: +# - ... +# proxy: false +# ``` +# +# It is disabled by default on all other roles but can be enabled by setting +# `proxy: true` or providing a proxy configuration for that role. +# +# ```yaml +# servers: +# web: +# hosts: +# - ... +# web2: +# hosts: +# - ... +# proxy: true +# ``` From 87965281a3abf036110a667e7a3e13b9b0e79ac8 Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Thu, 15 May 2025 14:33:05 +0100 Subject: [PATCH 20/23] Default the proxy config is it is nil Instead of checking for the proxy key, we'll set the config to {} if it is nil in the Kamal::Configuration::Proxy initializer. This is a bit cleaner, and maybe it will help with https://github.com/basecamp/kamal/issues/1555 if somehow @raw_config.key?(:proxy) is false but @raw_config.proxy is not nil. --- lib/kamal/configuration.rb | 2 +- lib/kamal/configuration/proxy.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/kamal/configuration.rb b/lib/kamal/configuration.rb index 26450170..6435fa57 100644 --- a/lib/kamal/configuration.rb +++ b/lib/kamal/configuration.rb @@ -63,7 +63,7 @@ class Kamal::Configuration @env = Env.new(config: @raw_config.env || {}, secrets: secrets) @logging = Logging.new(logging_config: @raw_config.logging) - @proxy = Proxy.new(config: self, proxy_config: @raw_config.key?(:proxy) ? @raw_config.proxy : {}) + @proxy = Proxy.new(config: self, proxy_config: @raw_config.proxy) @proxy_boot = Proxy::Boot.new(config: self) @ssh = Ssh.new(config: self) @sshkit = Sshkit.new(config: self) diff --git a/lib/kamal/configuration/proxy.rb b/lib/kamal/configuration/proxy.rb index b5afbaae..ccb4ac42 100644 --- a/lib/kamal/configuration/proxy.rb +++ b/lib/kamal/configuration/proxy.rb @@ -11,6 +11,7 @@ class Kamal::Configuration::Proxy def initialize(config:, proxy_config:, context: "proxy") @config = config @proxy_config = proxy_config + @proxy_config = {} if @proxy_config.nil? validate! @proxy_config, with: Kamal::Configuration::Validator::Proxy, context: context end From ad21c7e984bde535b25b651d89a9da7b14863a75 Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Thu, 15 May 2025 14:45:19 +0100 Subject: [PATCH 21/23] Don't deploy on proxy reboot It shouldn't be necessary to deploy the app on proxy reboot. When there are multiple apps using the same proxy we'll only deploy the one we run the reboot command from, so we don't always reboot anyway. --- lib/kamal/cli/proxy.rb | 12 ------------ test/cli/proxy_test.rb | 22 ---------------------- 2 files changed, 34 deletions(-) diff --git a/lib/kamal/cli/proxy.rb b/lib/kamal/cli/proxy.rb index 73707572..0e00af6c 100644 --- a/lib/kamal/cli/proxy.rb +++ b/lib/kamal/cli/proxy.rb @@ -120,18 +120,6 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base execute *KAMAL.proxy.ensure_apps_config_directory execute *KAMAL.proxy.run - - KAMAL.roles_on(host).select(&:running_proxy?).each do |role| - app = KAMAL.app(role: role, host: host) - - version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip - endpoint = capture_with_info(*app.container_id_for_version(version)).strip - - if endpoint.present? - info "Deploying #{endpoint} for role `#{role}` on #{host}..." - execute *app.deploy(target: endpoint) - end - end end run_hook "post-proxy-reboot", hosts: host_list end diff --git a/test/cli/proxy_test.rb b/test/cli/proxy_test.rb index fd131984..e6b0eaea 100644 --- a/test/cli/proxy_test.rb +++ b/test/cli/proxy_test.rb @@ -44,42 +44,20 @@ class CliProxyTest < CliTestCase end test "reboot" do - SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) - .with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-123$", "--quiet") - .returns("abcdefabcdef") - .at_least_once - - SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) - .with { |*args| args[0..1] == [ :sh, "-c" ] } - .returns("123") - .at_least_once - run_command("reboot", "-y").tap do |output| assert_match "docker container stop kamal-proxy on 1.1.1.1", output assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.1", output assert_match "mkdir -p .kamal/proxy/apps-config on 1.1.1.1", output assert_match "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") $(cat .kamal/proxy/run_command 2> /dev/null || echo \"\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $(pwd)/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config on 1.1.1.1", output - assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target=\"abcdefabcdef:80\" --deploy-timeout=\"6s\" --drain-timeout=\"30s\" --buffer-requests --buffer-responses --log-request-header=\"Cache-Control\" --log-request-header=\"Last-Modified\" --log-request-header=\"User-Agent\" on 1.1.1.1", output assert_match "docker container stop kamal-proxy on 1.1.1.2", output assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.2", output assert_match "mkdir -p .kamal/proxy/apps-config on 1.1.1.1", output assert_match "echo $(cat .kamal/proxy/options 2> /dev/null || echo \"--publish 80:80 --publish 443:443 --log-opt max-size=10m\") $(cat .kamal/proxy/image 2> /dev/null || echo \"basecamp/kamal-proxy\"):$(cat .kamal/proxy/image_version 2> /dev/null || echo \"#{Kamal::Configuration::Proxy::Boot::MINIMUM_VERSION}\") $(cat .kamal/proxy/run_command 2> /dev/null || echo \"\") | xargs docker run --name kamal-proxy --network kamal --detach --restart unless-stopped --volume kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy --volume $(pwd)/.kamal/proxy/apps-config:/home/kamal-proxy/.apps-config on 1.1.1.2", output - assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target=\"abcdefabcdef:80\" --deploy-timeout=\"6s\" --drain-timeout=\"30s\" --buffer-requests --buffer-responses --log-request-header=\"Cache-Control\" --log-request-header=\"Last-Modified\" --log-request-header=\"User-Agent\" on 1.1.1.2", output end end test "reboot --rolling" do - SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) - .with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-123$", "--quiet") - .returns("abcdefabcdef") - .at_least_once - - SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) - .with { |*args| args[0..1] == [ :sh, "-c" ] } - .returns("123") - .at_least_once - run_command("reboot", "--rolling", "-y").tap do |output| assert_match "Running docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy on 1.1.1.1", output end From 22e7243b100a0a5d2f36486d139e18842a67d345 Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Thu, 15 May 2025 15:15:29 +0100 Subject: [PATCH 22/23] Bump version for 2.6.1 --- Gemfile.lock | 2 +- lib/kamal/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index bcc38f0a..3fb94ec5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - kamal (2.6.0) + kamal (2.6.1) activesupport (>= 7.0) base64 (~> 0.2) bcrypt_pbkdf (~> 1.0) diff --git a/lib/kamal/version.rb b/lib/kamal/version.rb index 9525c872..d9029c54 100644 --- a/lib/kamal/version.rb +++ b/lib/kamal/version.rb @@ -1,3 +1,3 @@ module Kamal - VERSION = "2.6.0" + VERSION = "2.6.1" end From 30d630ce4df2f2815a494db6be6d91cc7bccc216 Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Thu, 15 May 2025 15:21:13 +0100 Subject: [PATCH 23/23] Drop Ruby 3.1 from the test matrix It is EOL since 2025-03-26. --- .github/workflows/ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8da7d39c..4e0358b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,16 +26,12 @@ jobs: fail-fast: false matrix: ruby-version: - - "3.1" - "3.2" - "3.3" - "3.4" gemfile: - Gemfile - gemfiles/rails_edge.gemfile - exclude: - - ruby-version: "3.1" - gemfile: gemfiles/rails_edge.gemfile name: ${{ format('Tests (Ruby {0})', matrix.ruby-version) }} runs-on: ubuntu-latest env: