Compare commits

..

1 Commits

Author SHA1 Message Date
Donal McBreen
70096160c9 Replace .env* with .kamal/env*
By default look for the env file in .kamal/env to avoid clashes with
other tools using .env.

For now we'll still load .env and issue a deprecation warning, but in
future we'll stop reading those.
2024-07-31 10:18:59 +01:00
21 changed files with 180 additions and 77 deletions

View File

@@ -22,48 +22,63 @@ module Kamal::Cli
class_option :skip_hooks, aliases: "-H", type: :boolean, default: false, desc: "Don't run hooks" 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(*) def initialize(*)
super super
@original_env = ENV.to_h.dup @original_env = ENV.to_h.dup
run_pre_init_hook
load_env load_env
initialize_commander(options_with_subcommand_class_options) initialize_commander(options_with_subcommand_class_options)
end end
private private
def reload_env
reset_env
load_env
end
def load_env def load_env
if destination = options[:destination] if destination = options[:destination]
if File.exist?(".kamal/.env.#{destination}") || File.exist?(".kamal/.env") if File.exist?(".kamal/env.#{destination}") || File.exist?(".kamal/env")
Dotenv.load(".kamal/.env.#{destination}", ".kamal/.env") Dotenv.load(".kamal/env.#{destination}", ".kamal/env")
else else
loading_files = [ (".env" if File.exist?(".env")), (".env.#{destination}" if File.exist?(".env.#{destination}")) ].compact loading_files = [ (".env" if File.exist?(".env")), (".env.#{destination}" if File.exist?(".env.#{destination}")) ].compact
if loading_files.any? if loading_files.any?
warn "Loading #{loading_files.join(" and ")} from the project root, in future they will be loaded from .kamal/" warn "Loading #{loading_files.join(" and ")} from the project root, use .kamal/env* instead"
Dotenv.load(".env.#{destination}", ".env") Dotenv.load(".env.#{destination}", ".env")
end end
end end
else else
if File.exist?(".kamal/.env") if File.exist?(".kamal/env")
Dotenv.load(".kamal/.env") Dotenv.load(".kamal/env")
elsif File.exist?(".env") elsif File.exist?(".env")
$stderr.puts caller warn "Loading .env from the project root is deprecated, use .kamal/env instead"
warn "Loading .env from the project root, in future it will be loaded then from .kamal/.env"
Dotenv.load(".env") Dotenv.load(".env")
end end
end end
end end
def reset_env
replace_env @original_env
end
def replace_env(env)
ENV.clear
ENV.update(env)
end
def with_original_env
keeping_current_env do
reset_env
yield
end
end
def keeping_current_env
current_env = ENV.to_h.dup
yield
ensure
replace_env(current_env)
end
def options_with_subcommand_class_options def options_with_subcommand_class_options
options.merge(@_initializer.last[:class_options] || {}) options.merge(@_initializer.last[:class_options] || {})
end end
@@ -160,23 +175,8 @@ module Kamal::Cli
end end
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) def run_hook(hook, **extra_details)
if run_hook?(hook) if !options[:skip_hooks] && KAMAL.hook.hook_exists?(hook)
details = { hosts: KAMAL.hosts.join(","), command: command, subcommand: subcommand } details = { hosts: KAMAL.hosts.join(","), command: command, subcommand: subcommand }
say "Running the #{hook} hook...", :magenta say "Running the #{hook} hook...", :magenta
@@ -188,10 +188,6 @@ module Kamal::Cli
end end
end end
def run_hook?(hook)
!options[:skip_hooks] && Kamal::Hooks.exists?(hook)
end
def on(*args, &block) def on(*args, &block)
if !KAMAL.connected? if !KAMAL.connected?
run_hook "pre-connect" run_hook "pre-connect"

View File

@@ -10,6 +10,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
invoke "kamal:cli:server:bootstrap", [], invoke_options invoke "kamal:cli:server:bootstrap", [], invoke_options
say "Evaluate and push env files...", :magenta say "Evaluate and push env files...", :magenta
invoke "kamal:cli:main:envify", [], invoke_options
invoke "kamal:cli:env:push", [], invoke_options invoke "kamal:cli:env:push", [], invoke_options
invoke "kamal:cli:accessory:boot", [ "all" ], invoke_options invoke "kamal:cli:accessory:boot", [ "all" ], invoke_options
@@ -178,6 +179,45 @@ class Kamal::Cli::Main < Kamal::Cli::Base
end end
end end
desc "envify", "Create .env by evaluating .env.erb (or .env.staging.erb -> .env.staging when using -d staging)"
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip .env file push"
def envify
if destination = options[:destination]
env_template_path = ".kamal/env.#{destination}.erb"
env_path = ".kamal/env.#{destination}"
else
env_template_path = ".kamal/env.erb"
env_path = ".kamal/env"
end
unless Pathname.new(File.expand_path(env_template_path)).exist?
if destination = options[:destination]
env_template_path = ".env.#{destination}.erb"
env_path = ".env.#{destination}"
else
env_template_path = ".env.erb"
env_path = ".env"
end
if Pathname.new(File.expand_path(env_template_path)).exist?
warn "Loading #{env_template_path} from the project root is deprecated, use .kamal/env[.<DESTINATION>].erb instead"
end
end
if Pathname.new(File.expand_path(env_template_path)).exist?
# Ensure existing env doesn't pollute template evaluation
content = with_original_env { ERB.new(File.read(env_template_path), trim_mode: "-").result }
File.write(env_path, content, perm: 0600)
unless options[:skip_push]
reload_env
invoke "kamal:cli:env:push", options
end
else
puts "Skipping envify (no #{env_template_path} exist)"
end
end
desc "remove", "Remove Traefik, app, accessories, and registry session from servers" desc "remove", "Remove Traefik, app, accessories, and registry session from servers"
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question" option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
def remove def remove

View File

@@ -1,5 +1,14 @@
class Kamal::Commands::Hook < Kamal::Commands::Base class Kamal::Commands::Hook < Kamal::Commands::Base
def run(hook, **details) def run(hook, **details)
[ Kamal::Hooks.file(hook), env: tags(**details).env ] [ hook_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
end end

View File

@@ -7,7 +7,7 @@ require "erb"
require "net/ssh/proxy/jump" require "net/ssh/proxy/jump"
class Kamal::Configuration class Kamal::Configuration
delegate :service, :image, :labels, :stop_wait_time, to: :raw_config, allow_nil: true delegate :service, :image, :labels, :stop_wait_time, :hooks_path, to: :raw_config, allow_nil: true
delegate :argumentize, :optionize, to: Kamal::Utils delegate :argumentize, :optionize, to: Kamal::Utils
attr_reader :destination, :raw_config attr_reader :destination, :raw_config
@@ -208,6 +208,10 @@ class Kamal::Configuration
end end
end end
def hooks_path
raw_config.hooks_path || ".kamal/hooks"
end
def asset_path def asset_path
raw_config.asset_path raw_config.asset_path
end end

View File

@@ -74,6 +74,10 @@ env:
# To configure this, set the path to the assets: # To configure this, set the path to the assets:
asset_path: /path/to/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 # Require destinations
# #
# Whether deployments require a destination to be specified, defaults to `false` # Whether deployments require a destination to be specified, defaults to `false`

View File

@@ -24,14 +24,14 @@ env:
# KAMAL_REGISTRY_PASSWORD=pw # KAMAL_REGISTRY_PASSWORD=pw
# DB_PASSWORD=secret123 # DB_PASSWORD=secret123
# ``` # ```
# See https://kamal-deploy.org/docs/commands/env/ for how to use generated .env files. # See https://kamal-deploy.org/docs/commands/envify/ for how to use generated .env files.
# #
# To pass the secrets you should list them under the `secret` key. When you do this the # To pass the secrets you should list them under the `secret` key. When you do this the
# other variables need to be moved under the `clear` key. # other variables need to be moved under the `clear` key.
# #
# Unlike clear values, secrets are not passed directly to the container, # Unlike clear valies, secrets are not passed directly to the container,
# but are stored in an env file on the host # but are stored in an env file on the host
# The file is not updated when deploying, only when running `kamal env push`. # The file is not updated when deploying, only when running `kamal envify` or `kamal env push`.
env: env:
clear: clear:
DB_USER: app DB_USER: app

View File

@@ -10,7 +10,7 @@ class CliBuildTest < CliTestCase
test "push" do test "push" do
with_build_directory do |build_directory| with_build_directory do |build_directory|
Kamal::Hooks.stubs(:exists?).returns(true) Kamal::Commands::Hook.any_instance.stubs(:hook_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" } 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) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
@@ -70,7 +70,7 @@ class CliBuildTest < CliTestCase
end end
test "push without clone" do test "push without clone" do
Kamal::Hooks.stubs(:exists?).returns(true) Kamal::Commands::Hook.any_instance.stubs(:hook_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" } 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| run_command("push", "--verbose", fixture: :without_clone).tap do |output|

View File

@@ -18,7 +18,7 @@ class CliTestCase < ActiveSupport::TestCase
private private
def fail_hook(hook) def fail_hook(hook)
@executions = [] @executions = []
Kamal::Hooks.stubs(:exists?).returns(true) Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
SSHKit::Backend::Abstract.any_instance.stubs(:execute) SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*args| @executions << args; args != [ ".kamal/hooks/#{hook}" ] } .with { |*args| @executions << args; args != [ ".kamal/hooks/#{hook}" ] }

View File

@@ -8,6 +8,7 @@ class CliMainTest < CliTestCase
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "version" => "999", "skip_hooks" => false } invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "version" => "999", "skip_hooks" => false }
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:server:bootstrap", [], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:server:bootstrap", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:main:envify", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:env:push", [], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:env:push", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:boot", [ "all" ], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:boot", [ "all" ], invoke_options)
Kamal::Cli::Main.any_instance.expects(:deploy) Kamal::Cli::Main.any_instance.expects(:deploy)
@@ -23,6 +24,7 @@ class CliMainTest < CliTestCase
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:server:bootstrap", [], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:server:bootstrap", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:env:push", [], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:env:push", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:main:envify", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:boot", [ "all" ], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:boot", [ "all" ], invoke_options)
# deploy # deploy
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:registry:login", [], invoke_options.merge(skip_local: true)) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:registry:login", [], invoke_options.merge(skip_local: true))
@@ -56,11 +58,10 @@ 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:app:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:prune:all", [], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:prune:all", [], invoke_options)
Kamal::Hooks.stubs(:exists?).returns(true) Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2", command: "deploy" } 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| run_command("deploy", "--verbose").tap do |output|
assert_match "Running the pre-init hook...", output
assert_hook_ran "pre-connect", output, **hook_variables assert_hook_ran "pre-connect", output, **hook_variables
assert_match /Log into image registry/, output assert_match /Log into image registry/, output
assert_match /Build and push app image/, output assert_match /Build and push app image/, output
@@ -236,7 +237,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:stale_containers", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options)
Kamal::Hooks.stubs(:exists?).returns(true) Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2", command: "redeploy" } hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2", command: "redeploy" }
@@ -297,7 +298,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}}'") .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 .returns("unhealthy").at_least_once # health check
Kamal::Hooks.stubs(:exists?).returns(true) Kamal::Commands::Hook.any_instance.stubs(:hook_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" } 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| run_command("rollback", "--verbose", "123", config_file: "deploy_with_accessories").tap do |output|
@@ -395,7 +396,7 @@ class CliMainTest < CliTestCase
end end
test "init" do test "init" do
Pathname.any_instance.expects(:exist?).returns(false).times(4) Pathname.any_instance.expects(:exist?).returns(false).times(3)
Pathname.any_instance.stubs(:mkpath) Pathname.any_instance.stubs(:mkpath)
FileUtils.stubs(:mkdir_p) FileUtils.stubs(:mkdir_p)
FileUtils.stubs(:cp_r) FileUtils.stubs(:cp_r)
@@ -408,7 +409,7 @@ class CliMainTest < CliTestCase
end end
test "init with existing config" do test "init with existing config" do
Pathname.any_instance.expects(:exist?).returns(true).times(4) Pathname.any_instance.expects(:exist?).returns(true).times(3)
run_command("init").tap do |output| run_command("init").tap do |output|
assert_match /Config file already exists in config\/deploy.yml \(remove first to create a new one\)/, output assert_match /Config file already exists in config\/deploy.yml \(remove first to create a new one\)/, output
@@ -416,7 +417,7 @@ class CliMainTest < CliTestCase
end end
test "init with bundle option" do test "init with bundle option" do
Pathname.any_instance.expects(:exist?).returns(false).times(5) Pathname.any_instance.expects(:exist?).returns(false).times(4)
Pathname.any_instance.stubs(:mkpath) Pathname.any_instance.stubs(:mkpath)
FileUtils.stubs(:mkdir_p) FileUtils.stubs(:mkdir_p)
FileUtils.stubs(:cp_r) FileUtils.stubs(:cp_r)
@@ -433,7 +434,7 @@ class CliMainTest < CliTestCase
end end
test "init with bundle option and existing binstub" do test "init with bundle option and existing binstub" do
Pathname.any_instance.expects(:exist?).returns(true).times(5) Pathname.any_instance.expects(:exist?).returns(true).times(4)
Pathname.any_instance.stubs(:mkpath) Pathname.any_instance.stubs(:mkpath)
FileUtils.stubs(:mkdir_p) FileUtils.stubs(:mkdir_p)
FileUtils.stubs(:cp_r) FileUtils.stubs(:cp_r)
@@ -445,6 +446,50 @@ class CliMainTest < CliTestCase
end end
end end
test "envify" do
with_test_env_files("env.erb": "HELLO=<%= 'world' %>") do
run_command("envify")
assert_equal("HELLO=world", File.read(".kamal/env"))
end
end
test "envify with blank line trimming" do
file = <<~EOF
HELLO=<%= 'world' %>
<% if true -%>
KEY=value
<% end -%>
EOF
with_test_env_files("env.erb": file) do
run_command("envify")
assert_equal("HELLO=world\nKEY=value\n", File.read(".kamal/env"))
end
end
test "envify with destination" do
with_test_env_files("env.world.erb": "HELLO=<%= 'world' %>") do
run_command("envify", "-d", "world", config_file: "deploy_for_dest")
assert_equal "HELLO=world", File.read(".kamal/env.world")
end
end
test "envify with skip_push" do
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)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:env:push").never
run_command("envify", "--skip-push")
end
test "envify with clean env" do
with_test_env_files("env": "HELLO=already", "env.erb": "HELLO=<%= ENV.fetch 'HELLO', 'never' %>") do
run_command("envify", "--skip-push")
assert_equal "HELLO=never", File.read(".kamal/env")
end
end
test "remove with confirmation" do test "remove with confirmation" do
run_command("remove", "-y", config_file: "deploy_with_accessories").tap do |output| run_command("remove", "-y", config_file: "deploy_with_accessories").tap do |output|
assert_match /docker container stop traefik/, output assert_match /docker container stop traefik/, output
@@ -497,7 +542,7 @@ class CliMainTest < CliTestCase
stdouted { Kamal::Cli::Main.start([ *command, "-c", "test/fixtures/#{config_file}.yml" ]) } stdouted { Kamal::Cli::Main.start([ *command, "-c", "test/fixtures/#{config_file}.yml" ]) }
end end
def with_test_dotenv(**files) def with_test_env_files(**files)
Dir.mktmpdir do |dir| Dir.mktmpdir do |dir|
fixtures_dup = File.join(dir, "test") fixtures_dup = File.join(dir, "test")
FileUtils.mkdir_p(fixtures_dup) FileUtils.mkdir_p(fixtures_dup)

View File

@@ -40,8 +40,7 @@ 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('[ "${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(: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 SSHKit::Backend::Abstract.any_instance.expects(:execute).with(:mkdir, "-p", ".kamal").returns("").at_least_once
Kamal::Hooks.stubs(:exists?).returns(true) Kamal::Commands::Hook.any_instance.stubs(:hook_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/pre-connect", anything).at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:execute).with(".kamal/hooks/docker-setup", anything).at_least_once SSHKit::Backend::Abstract.any_instance.expects(:execute).with(".kamal/hooks/docker-setup", anything).at_least_once

View File

@@ -28,7 +28,6 @@ class CommandsHookTest < ActiveSupport::TestCase
end end
test "run with custom hooks_path" do test "run with custom hooks_path" do
ENV["KAMAL_HOOKS_PATH"] = "custom/hooks/path"
assert_equal [ assert_equal [
"custom/hooks/path/foo", "custom/hooks/path/foo",
{ env: { { env: {
@@ -37,9 +36,7 @@ class CommandsHookTest < ActiveSupport::TestCase
"KAMAL_VERSION" => "123", "KAMAL_VERSION" => "123",
"KAMAL_SERVICE_VERSION" => "app@123", "KAMAL_SERVICE_VERSION" => "app@123",
"KAMAL_SERVICE" => "app" } } "KAMAL_SERVICE" => "app" } }
], new_command.run("foo") ], new_command(hooks_path: "custom/hooks/path").run("foo")
ensure
ENV.delete("KAMAL_HOOKS_PATH")
end end
private private

View File

@@ -6,7 +6,7 @@ class ConfigurationValidationTest < ActiveSupport::TestCase
end end
test "wrong root types" do test "wrong root types" do
[ :service, :image, :asset_path, :primary_role, :minimum_version, :run_directory ].each do |key| [ :service, :image, :asset_path, :hooks_path, :primary_role, :minimum_version, :run_directory ].each do |key|
assert_error "#{key}: should be a string", **{ key => [] } assert_error "#{key}: should be a string", **{ key => [] }
end end

View File

@@ -2,6 +2,8 @@ require_relative "integration_test"
class AccessoryTest < IntegrationTest class AccessoryTest < IntegrationTest
test "boot, stop, start, restart, logs, remove" do test "boot, stop, start, restart, logs, remove" do
kamal :envify
kamal :accessory, :boot, :busybox kamal :accessory, :boot, :busybox
assert_accessory_running :busybox assert_accessory_running :busybox

View File

@@ -2,6 +2,8 @@ require_relative "integration_test"
class AppTest < IntegrationTest class AppTest < IntegrationTest
test "stop, start, boot, logs, images, containers, exec, remove" do test "stop, start, boot, logs, images, containers, exec, remove" do
kamal :envify
kamal :deploy kamal :deploy
assert_app_is_up assert_app_is_up

View File

@@ -4,6 +4,8 @@ class BrokenDeployTest < IntegrationTest
test "deploying a bad image" do test "deploying a bad image" do
@app = "app_with_roles" @app = "app_with_roles"
kamal :envify
first_version = latest_app_version first_version = latest_app_version
kamal :deploy kamal :deploy

View File

@@ -0,0 +1,2 @@
SECRET_TOKEN='1234 with "中文"'
SECRET_TAG='TAGME'

View File

@@ -0,0 +1 @@
SECRET_TOKEN='1234 with "中文"'

View File

@@ -2,6 +2,8 @@ require_relative "integration_test"
class LockTest < IntegrationTest class LockTest < IntegrationTest
test "acquire, release, status" do test "acquire, release, status" do
kamal :envify
kamal :lock, :acquire, "-m 'Integration Tests'" kamal :lock, :acquire, "-m 'Integration Tests'"
status = kamal :lock, :status, capture: true status = kamal :lock, :status, capture: true

View File

@@ -1,8 +1,8 @@
require_relative "integration_test" require_relative "integration_test"
class MainTest < IntegrationTest class MainTest < IntegrationTest
test "env push, deploy, redeploy, rollback, details and audit" do test "envify, deploy, redeploy, rollback, details and audit" do
kamal :env, :push kamal :envify
assert_env_files assert_env_files
remove_local_env_file remove_local_env_file
@@ -12,19 +12,19 @@ class MainTest < IntegrationTest
kamal :deploy kamal :deploy
assert_app_is_up version: first_version assert_app_is_up version: first_version
assert_hooks_ran "pre-init", "pre-connect", "pre-build", "pre-deploy", "post-deploy" assert_hooks_ran "pre-connect", "pre-build", "pre-deploy", "post-deploy"
assert_envs version: first_version assert_envs version: first_version
second_version = update_app_rev second_version = update_app_rev
kamal :redeploy kamal :redeploy
assert_app_is_up version: second_version assert_app_is_up version: second_version
assert_hooks_ran "pre-init", "pre-connect", "pre-build", "pre-deploy", "post-deploy" assert_hooks_ran "pre-connect", "pre-build", "pre-deploy", "post-deploy"
assert_accumulated_assets first_version, second_version assert_accumulated_assets first_version, second_version
kamal :rollback, first_version kamal :rollback, first_version
assert_hooks_ran "pre-init", "pre-connect", "pre-deploy", "post-deploy" assert_hooks_ran "pre-connect", "pre-deploy", "post-deploy"
assert_app_is_up version: first_version assert_app_is_up version: first_version
details = kamal :details, capture: true details = kamal :details, capture: true
@@ -45,7 +45,7 @@ class MainTest < IntegrationTest
test "app with roles" do test "app with roles" do
@app = "app_with_roles" @app = "app_with_roles"
kamal :env, :push kamal :envify
version = latest_app_version version = latest_app_version
@@ -54,7 +54,7 @@ class MainTest < IntegrationTest
kamal :deploy kamal :deploy
assert_app_is_up version: version assert_app_is_up version: version
assert_hooks_ran "pre-init", "pre-connect", "pre-build", "pre-deploy", "post-deploy" assert_hooks_ran "pre-connect", "pre-build", "pre-deploy", "post-deploy"
assert_container_running host: :vm3, name: "app-workers-#{version}" assert_container_running host: :vm3, name: "app-workers-#{version}"
second_version = update_app_rev second_version = update_app_rev
@@ -87,7 +87,7 @@ class MainTest < IntegrationTest
kamal :remove, "-y" kamal :remove, "-y"
assert_no_images_or_containers assert_no_images_or_containers
kamal :env, :push kamal :envify
kamal :setup kamal :setup
assert_images_and_containers assert_images_and_containers
@@ -97,7 +97,7 @@ class MainTest < IntegrationTest
private private
def assert_local_env_file(contents) def assert_local_env_file(contents)
assert_equal contents, deployer_exec("cat .kamal/.env", capture: true) assert_equal contents, deployer_exec("cat .kamal/env", capture: true)
end end
def assert_envs(version:) def assert_envs(version:)
@@ -127,7 +127,7 @@ class MainTest < IntegrationTest
end end
def remove_local_env_file def remove_local_env_file
deployer_exec("rm .kamal/.env") deployer_exec("rm .kamal/env")
end end
def assert_remote_env_file(contents, vm:) def assert_remote_env_file(contents, vm:)

View File

@@ -2,6 +2,8 @@ require_relative "integration_test"
class TraefikTest < IntegrationTest class TraefikTest < IntegrationTest
test "boot, reboot, stop, start, restart, logs, remove" do test "boot, reboot, stop, start, restart, logs, remove" do
kamal :envify
kamal :traefik, :boot kamal :traefik, :boot
assert_traefik_running assert_traefik_running

View File

@@ -26,10 +26,6 @@ end
class ActiveSupport::TestCase class ActiveSupport::TestCase
include ActiveSupport::Testing::Stream include ActiveSupport::Testing::Stream
setup do
Kamal::Cli::Base.ran_pre_init_hook = false
end
private private
def stdouted def stdouted
capture(:stdout) { yield }.strip capture(:stdout) { yield }.strip