diff --git a/lib/kamal/cli/base.rb b/lib/kamal/cli/base.rb index 2463f585..3129eaab 100644 --- a/lib/kamal/cli/base.rb +++ b/lib/kamal/cli/base.rb @@ -22,9 +22,21 @@ module Kamal::Cli class_option :skip_hooks, aliases: "-H", type: :boolean, default: false, desc: "Don't run hooks" + @@ran_pre_init_hook = false + class << self + def ran_pre_init_hook + @@ran_pre_init_hook + end + + def ran_pre_init_hook=(value) + @@ran_pre_init_hook = value + end + end + def initialize(*) super @original_env = ENV.to_h.dup + run_pre_init_hook load_env initialize_commander(options_with_subcommand_class_options) end @@ -176,8 +188,23 @@ module Kamal::Cli end end + def run_pre_init_hook + unless self.class.ran_pre_init_hook + hook = "pre-init" + if run_hook?(hook) + say "Running the #{hook} hook...", :magenta + run_locally do + execute *Kamal::Hooks.file(hook), verbosity: :debug + rescue SSHKit::Command::Failed => e + raise HookError.new("Hook `#{hook}` failed:\n#{e.message}") + end + end + self.class.ran_pre_init_hook = true + end + end + def run_hook(hook, **extra_details) - if !options[:skip_hooks] && KAMAL.hook.hook_exists?(hook) + if run_hook?(hook) details = { hosts: KAMAL.hosts.join(","), command: command, subcommand: subcommand } say "Running the #{hook} hook...", :magenta @@ -189,6 +216,10 @@ module Kamal::Cli end end + def run_hook?(hook) + !options[:skip_hooks] && Kamal::Hooks.exists?(hook) + end + def on(*args, &block) if !KAMAL.connected? run_hook "pre-connect" diff --git a/lib/kamal/commands/hook.rb b/lib/kamal/commands/hook.rb index 66fe8b8c..50cb6d35 100644 --- a/lib/kamal/commands/hook.rb +++ b/lib/kamal/commands/hook.rb @@ -1,14 +1,5 @@ class Kamal::Commands::Hook < Kamal::Commands::Base def run(hook, **details) - [ hook_file(hook), env: tags(**details).env ] + [ Kamal::Hooks.file(hook), env: tags(**details).env ] end - - def hook_exists?(hook) - Pathname.new(hook_file(hook)).exist? - end - - private - def hook_file(hook) - File.join(config.hooks_path, hook) - end end diff --git a/lib/kamal/configuration.rb b/lib/kamal/configuration.rb index 8d989464..48bf536c 100644 --- a/lib/kamal/configuration.rb +++ b/lib/kamal/configuration.rb @@ -7,7 +7,7 @@ require "erb" require "net/ssh/proxy/jump" class Kamal::Configuration - delegate :service, :image, :labels, :stop_wait_time, :hooks_path, to: :raw_config, allow_nil: true + delegate :service, :image, :labels, :stop_wait_time, to: :raw_config, allow_nil: true delegate :argumentize, :optionize, to: Kamal::Utils attr_reader :destination, :raw_config @@ -208,10 +208,6 @@ class Kamal::Configuration end end - def hooks_path - raw_config.hooks_path || ".kamal/hooks" - end - def asset_path raw_config.asset_path end diff --git a/lib/kamal/configuration/docs/configuration.yml b/lib/kamal/configuration/docs/configuration.yml index fc9245c5..4522bb94 100644 --- a/lib/kamal/configuration/docs/configuration.yml +++ b/lib/kamal/configuration/docs/configuration.yml @@ -74,10 +74,6 @@ env: # To configure this, set the path to the assets: asset_path: /path/to/assets -# Path to hooks, defaults to `.kamal/hooks` -# See https://kamal-deploy.org/docs/hooks for more information -hooks_path: /user_home/kamal/hooks - # Require destinations # # Whether deployments require a destination to be specified, defaults to `false` diff --git a/test/cli/build_test.rb b/test/cli/build_test.rb index 18ff254b..be0e7e10 100644 --- a/test/cli/build_test.rb +++ b/test/cli/build_test.rb @@ -10,7 +10,7 @@ class CliBuildTest < CliTestCase test "push" do with_build_directory do |build_directory| - Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true) + Kamal::Hooks.stubs(:exists?).returns(true) hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "build", subcommand: "push" } SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) @@ -70,7 +70,7 @@ class CliBuildTest < CliTestCase end test "push without clone" do - Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true) + Kamal::Hooks.stubs(:exists?).returns(true) hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "build", subcommand: "push" } run_command("push", "--verbose", fixture: :without_clone).tap do |output| diff --git a/test/cli/cli_test_case.rb b/test/cli/cli_test_case.rb index 4c6b491d..8b32e1c7 100644 --- a/test/cli/cli_test_case.rb +++ b/test/cli/cli_test_case.rb @@ -18,7 +18,7 @@ class CliTestCase < ActiveSupport::TestCase private def fail_hook(hook) @executions = [] - Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true) + Kamal::Hooks.stubs(:exists?).returns(true) SSHKit::Backend::Abstract.any_instance.stubs(:execute) .with { |*args| @executions << args; args != [ ".kamal/hooks/#{hook}" ] } diff --git a/test/cli/main_test.rb b/test/cli/main_test.rb index 82d4e2ba..61f2e7d2 100644 --- a/test/cli/main_test.rb +++ b/test/cli/main_test.rb @@ -58,10 +58,11 @@ class CliMainTest < CliTestCase Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:prune:all", [], invoke_options) - Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true) + Kamal::Hooks.stubs(:exists?).returns(true) hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2", command: "deploy" } run_command("deploy", "--verbose").tap do |output| + assert_match "Running /usr/bin/env .kamal/hooks/pre-init", output assert_hook_ran "pre-connect", output, **hook_variables assert_match /Log into image registry/, output assert_match /Build and push app image/, output @@ -237,7 +238,7 @@ class CliMainTest < CliTestCase Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options) - Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true) + Kamal::Hooks.stubs(:exists?).returns(true) hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2", command: "redeploy" } @@ -298,7 +299,7 @@ class CliMainTest < CliTestCase .with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-version-to-rollback$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'") .returns("unhealthy").at_least_once # health check - Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true) + Kamal::Hooks.stubs(:exists?).returns(true) hook_variables = { version: 123, service_version: "app@123", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "rollback" } run_command("rollback", "--verbose", "123", config_file: "deploy_with_accessories").tap do |output| @@ -396,7 +397,7 @@ class CliMainTest < CliTestCase end test "init" do - Pathname.any_instance.expects(:exist?).returns(false).times(3) + Pathname.any_instance.expects(:exist?).returns(false).times(4) Pathname.any_instance.stubs(:mkpath) FileUtils.stubs(:mkdir_p) FileUtils.stubs(:cp_r) @@ -409,7 +410,7 @@ class CliMainTest < CliTestCase end test "init with existing config" do - Pathname.any_instance.expects(:exist?).returns(true).times(3) + Pathname.any_instance.expects(:exist?).returns(true).times(4) run_command("init").tap do |output| assert_match /Config file already exists in config\/deploy.yml \(remove first to create a new one\)/, output @@ -417,7 +418,7 @@ class CliMainTest < CliTestCase end test "init with bundle option" do - Pathname.any_instance.expects(:exist?).returns(false).times(4) + Pathname.any_instance.expects(:exist?).returns(false).times(5) Pathname.any_instance.stubs(:mkpath) FileUtils.stubs(:mkdir_p) FileUtils.stubs(:cp_r) @@ -434,7 +435,7 @@ class CliMainTest < CliTestCase end test "init with bundle option and existing binstub" do - Pathname.any_instance.expects(:exist?).returns(true).times(4) + Pathname.any_instance.expects(:exist?).returns(true).times(5) Pathname.any_instance.stubs(:mkpath) FileUtils.stubs(:mkdir_p) FileUtils.stubs(:cp_r) @@ -475,7 +476,7 @@ class CliMainTest < CliTestCase end test "envify with skip_push" do - Pathname.any_instance.expects(:exist?).returns(true).times(1) + Pathname.any_instance.expects(:exist?).returns(true).times(2) File.expects(:read).with(".kamal/.env.erb").returns("HELLO=<%= 'world' %>") File.expects(:write).with(".kamal/.env", "HELLO=world", perm: 0600) diff --git a/test/cli/server_test.rb b/test/cli/server_test.rb index 5d9fec4d..f4ecf650 100644 --- a/test/cli/server_test.rb +++ b/test/cli/server_test.rb @@ -40,7 +40,8 @@ class CliServerTest < CliTestCase SSHKit::Backend::Abstract.any_instance.expects(:execute).with('[ "${EUID:-$(id -u)}" -eq 0 ] || command -v sudo >/dev/null || command -v su >/dev/null', raise_on_non_zero_exit: false).returns(true).at_least_once SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:sh, "-c", "'curl -fsSL https://get.docker.com || wget -O - https://get.docker.com || echo \"exit 1\"'", "|", :sh).at_least_once SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:mkdir, "-p", ".kamal").returns("").at_least_once - Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true) + Kamal::Hooks.stubs(:exists?).returns(true) + SSHKit::Backend::Abstract.any_instance.expects(:execute).with(".kamal/hooks/pre-init", anything).at_least_once SSHKit::Backend::Abstract.any_instance.expects(:execute).with(".kamal/hooks/pre-connect", anything).at_least_once SSHKit::Backend::Abstract.any_instance.expects(:execute).with(".kamal/hooks/docker-setup", anything).at_least_once diff --git a/test/commands/hook_test.rb b/test/commands/hook_test.rb index c0e6e98f..ec4ae583 100644 --- a/test/commands/hook_test.rb +++ b/test/commands/hook_test.rb @@ -28,6 +28,7 @@ class CommandsHookTest < ActiveSupport::TestCase end test "run with custom hooks_path" do + ENV["KAMAL_HOOKS_PATH"] = "custom/hooks/path" assert_equal [ "custom/hooks/path/foo", { env: { @@ -36,7 +37,9 @@ class CommandsHookTest < ActiveSupport::TestCase "KAMAL_VERSION" => "123", "KAMAL_SERVICE_VERSION" => "app@123", "KAMAL_SERVICE" => "app" } } - ], new_command(hooks_path: "custom/hooks/path").run("foo") + ], new_command.run("foo") + ensure + ENV.delete("KAMAL_HOOKS_PATH") end private diff --git a/test/configuration/validation_test.rb b/test/configuration/validation_test.rb index b7bd6b6a..dd2c0a29 100644 --- a/test/configuration/validation_test.rb +++ b/test/configuration/validation_test.rb @@ -6,7 +6,7 @@ class ConfigurationValidationTest < ActiveSupport::TestCase end test "wrong root types" do - [ :service, :image, :asset_path, :hooks_path, :primary_role, :minimum_version, :run_directory ].each do |key| + [ :service, :image, :asset_path, :primary_role, :minimum_version, :run_directory ].each do |key| assert_error "#{key}: should be a string", **{ key => [] } end diff --git a/test/integration/main_test.rb b/test/integration/main_test.rb index 4d4513ad..854e4396 100644 --- a/test/integration/main_test.rb +++ b/test/integration/main_test.rb @@ -12,19 +12,19 @@ class MainTest < IntegrationTest kamal :deploy assert_app_is_up version: first_version - assert_hooks_ran "pre-connect", "pre-build", "pre-deploy", "post-deploy" + assert_hooks_ran "pre-init", "pre-connect", "pre-build", "pre-deploy", "post-deploy" assert_envs version: first_version second_version = update_app_rev kamal :redeploy assert_app_is_up version: second_version - assert_hooks_ran "pre-connect", "pre-build", "pre-deploy", "post-deploy" + assert_hooks_ran "pre-init", "pre-connect", "pre-build", "pre-deploy", "post-deploy" assert_accumulated_assets first_version, second_version kamal :rollback, first_version - assert_hooks_ran "pre-connect", "pre-deploy", "post-deploy" + assert_hooks_ran "pre-init", "pre-connect", "pre-deploy", "post-deploy" assert_app_is_up version: first_version details = kamal :details, capture: true @@ -54,7 +54,7 @@ class MainTest < IntegrationTest kamal :deploy assert_app_is_up version: version - assert_hooks_ran "pre-connect", "pre-build", "pre-deploy", "post-deploy" + assert_hooks_ran "pre-init", "pre-connect", "pre-build", "pre-deploy", "post-deploy" assert_container_running host: :vm3, name: "app-workers-#{version}" second_version = update_app_rev @@ -65,7 +65,7 @@ class MainTest < IntegrationTest end test "config" do - config = YAML.load(kamal(:config, capture: true)) + config = YAML.load(kamal(:config, "-q", capture: true)) version = latest_app_version assert_equal [ "web" ], config[:roles] diff --git a/test/test_helper.rb b/test/test_helper.rb index 5f7a25c4..5647c382 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -26,6 +26,10 @@ end class ActiveSupport::TestCase include ActiveSupport::Testing::Stream + setup do + Kamal::Cli::Base.ran_pre_init_hook = false + end + private def stdouted capture(:stdout) { yield }.strip