Add Rubocop

- Pull in the 37signals house style
- Autofix violations
- Add to CI
This commit is contained in:
Donal McBreen
2024-03-05 11:40:08 +00:00
parent c985fa33d1
commit 3ecfb3744f
51 changed files with 229 additions and 164 deletions

View File

@@ -5,6 +5,21 @@ on:
- main - main
pull_request: pull_request:
jobs: jobs:
rubocop:
name: RuboCop
runs-on: ubuntu-latest
env:
BUNDLE_ONLY: rubocop
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Ruby and install gems
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.3.0
bundler-cache: true
- name: Run Rubocop
run: bundle exec rubocop --parallel
tests: tests:
strategy: strategy:
matrix: matrix:

1
.rubocop.yml Normal file
View File

@@ -0,0 +1 @@
inherit_gem: { rubocop-37signals: rubocop.yml }

View File

@@ -1,4 +1,8 @@
source 'https://rubygems.org' source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" } git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gemspec gemspec
group :rubocop do
gem "rubocop-37signals", github: "basecamp/house-style", require: false
end

View File

@@ -1,3 +1,13 @@
GIT
remote: https://github.com/basecamp/house-style.git
revision: a9ca7e4ab80b72c1a10053c50efefe8cd275e3b8
specs:
rubocop-37signals (1.0.0)
rubocop
rubocop-minitest
rubocop-performance
rubocop-rails
PATH PATH
remote: . remote: .
specs: specs:
@@ -42,6 +52,7 @@ GEM
minitest (>= 5.1) minitest (>= 5.1)
mutex_m mutex_m
tzinfo (~> 2.0) tzinfo (~> 2.0)
ast (2.4.2)
base64 (0.2.0) base64 (0.2.0)
bcrypt_pbkdf (1.1.0) bcrypt_pbkdf (1.1.0)
bigdecimal (3.1.5) bigdecimal (3.1.5)
@@ -63,6 +74,8 @@ GEM
irb (1.11.0) irb (1.11.0)
rdoc rdoc
reline (>= 0.3.8) reline (>= 0.3.8)
json (2.7.1)
language_server-protocol (3.17.0.3)
loofah (2.22.0) loofah (2.22.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.12.0) nokogiri (>= 1.12.0)
@@ -79,6 +92,10 @@ GEM
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.16.0-x86_64-linux) nokogiri (1.16.0-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
parallel (1.24.0)
parser (3.3.0.5)
ast (~> 2.4.1)
racc
psych (5.1.2) psych (5.1.2)
stringio stringio
racc (1.7.3) racc (1.7.3)
@@ -105,11 +122,39 @@ GEM
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0, >= 1.2.2) thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6) zeitwerk (~> 2.6)
rainbow (3.1.1)
rake (13.1.0) rake (13.1.0)
rdoc (6.6.2) rdoc (6.6.2)
psych (>= 4.0.0) psych (>= 4.0.0)
regexp_parser (2.9.0)
reline (0.4.2) reline (0.4.2)
io-console (~> 0.5) io-console (~> 0.5)
rexml (3.2.6)
rubocop (1.61.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.30.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.31.1)
parser (>= 3.3.0.4)
rubocop-minitest (0.34.5)
rubocop (>= 1.39, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-performance (1.20.2)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-rails (2.24.0)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
sshkit (1.21.7) sshkit (1.21.7)
mutex_m mutex_m
@@ -119,6 +164,7 @@ GEM
thor (1.3.0) thor (1.3.0)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
webrick (1.8.1) webrick (1.8.1)
zeitwerk (2.6.12) zeitwerk (2.6.12)
@@ -132,6 +178,7 @@ DEPENDENCIES
kamal! kamal!
mocha mocha
railties railties
rubocop-37signals!
BUNDLED WITH BUNDLED WITH
2.4.3 2.4.3

View File

@@ -177,7 +177,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
if name == "all" if name == "all"
KAMAL.accessory_names.each { |accessory_name| remove(accessory_name) } KAMAL.accessory_names.each { |accessory_name| remove(accessory_name) }
else else
if options[:confirmed] || ask("This will remove all containers, images and data directories for #{name}. Are you sure?", limited_to: %w( y N ), default: "N") == "y" if options[:confirmed] || ask("This will remove all containers, images and data directories for #{name}. Are you sure?", limited_to: %w[ y N ], default: "N") == "y"
with_accessory(name) do with_accessory(name) do
stop(name) stop(name)
remove_container(name) remove_container(name)

View File

@@ -207,7 +207,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
run_locally do run_locally do
info "Following logs on #{KAMAL.primary_host}..." info "Following logs on #{KAMAL.primary_host}..."
KAMAL.specific_roles ||= ["web"] KAMAL.specific_roles ||= [ "web" ]
role = KAMAL.roles_on(KAMAL.primary_host).first role = KAMAL.roles_on(KAMAL.primary_host).first
info KAMAL.app(role: role).follow_logs(host: KAMAL.primary_host, lines: lines, grep: grep) info KAMAL.app(role: role).follow_logs(host: KAMAL.primary_host, lines: lines, grep: grep)

View File

@@ -73,7 +73,7 @@ module Kamal::Cli
def print_runtime def print_runtime
started_at = Time.now started_at = Time.now
yield yield
return Time.now - started_at Time.now - started_at
ensure ensure
runtime = Time.now - started_at runtime = Time.now - started_at
puts " Finished all in #{sprintf("%.1f seconds", runtime)}" puts " Finished all in #{sprintf("%.1f seconds", runtime)}"
@@ -181,5 +181,5 @@ module Kamal::Cli
execute(*KAMAL.server.ensure_run_directory) execute(*KAMAL.server.ensure_run_directory)
end end
end end
end end
end end

View File

@@ -197,7 +197,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
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
mutating do mutating do
if options[:confirmed] || ask("This will remove all containers and images. Are you sure?", limited_to: %w( y N ), default: "N") == "y" if options[:confirmed] || ask("This will remove all containers and images. Are you sure?", limited_to: %w[ y N ], default: "N") == "y"
invoke "kamal:cli:traefik:remove", [], options.without(:confirmed) invoke "kamal:cli:traefik:remove", [], options.without(:confirmed)
invoke "kamal:cli:app:remove", [], options.without(:confirmed) invoke "kamal:cli:app:remove", [], options.without(:confirmed)
invoke "kamal:cli:accessory:remove", [ "all" ], options invoke "kamal:cli:accessory:remove", [ "all" ], options

View File

@@ -13,7 +13,7 @@ class Kamal::Cli::Traefik < Kamal::Cli::Base
option :rolling, type: :boolean, default: false, desc: "Reboot traefik on hosts in sequence, rather than in parallel" option :rolling, type: :boolean, default: false, desc: "Reboot traefik on hosts in sequence, rather than in parallel"
def reboot def reboot
mutating do mutating do
host_groups = options[:rolling] ? KAMAL.traefik_hosts : [KAMAL.traefik_hosts] host_groups = options[:rolling] ? KAMAL.traefik_hosts : [ KAMAL.traefik_hosts ]
host_groups.each do |hosts| host_groups.each do |hosts|
host_list = Array(hosts).join(",") host_list = Array(hosts).join(",")
run_hook "pre-traefik-reboot", hosts: host_list run_hook "pre-traefik-reboot", hosts: host_list

View File

@@ -103,7 +103,7 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
end end
def remove_env_file def remove_env_file
[:rm, "-f", accessory_config.host_env_file_path] [ :rm, "-f", accessory_config.host_env_file_path ]
end end
private private

View File

@@ -15,7 +15,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
"--detach", "--detach",
"--restart unless-stopped", "--restart unless-stopped",
"--name", container_name, "--name", container_name,
*(["--hostname", hostname] if hostname), *([ "--hostname", hostname ] if hostname),
"-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"", "-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
"-e", "KAMAL_VERSION=\"#{config.version}\"", "-e", "KAMAL_VERSION=\"#{config.version}\"",
*role.env_args, *role.env_args,

View File

@@ -4,7 +4,7 @@ module Kamal::Commands::App::Assets
combine \ combine \
make_directory(role.asset_extracted_path), make_directory(role.asset_extracted_path),
[*docker(:stop, "-t 1", asset_container, "2> /dev/null"), "|| true"], [ *docker(:stop, "-t 1", asset_container, "2> /dev/null"), "|| true" ],
docker(:run, "--name", asset_container, "--detach", "--rm", config.latest_image, "sleep 1000000"), docker(:run, "--name", asset_container, "--detach", "--rm", config.latest_image, "sleep 1000000"),
docker(:cp, "-L", "#{asset_container}:#{role.asset_path}/.", role.asset_extracted_path), docker(:cp, "-L", "#{asset_container}:#{role.asset_path}/.", role.asset_extracted_path),
docker(:stop, "-t 1", asset_container), docker(:stop, "-t 1", asset_container),
@@ -17,7 +17,7 @@ module Kamal::Commands::App::Assets
old_extracted_path, old_volume_path = role.asset_extracted_path(old_version), role.asset_volume(old_version).host_path old_extracted_path, old_volume_path = role.asset_extracted_path(old_version), role.asset_volume(old_version).host_path
end end
commands = [make_directory(new_volume_path), copy_contents(new_extracted_path, new_volume_path)] commands = [ make_directory(new_volume_path), copy_contents(new_extracted_path, new_volume_path) ]
if old_version.present? if old_version.present?
commands << copy_contents(new_extracted_path, old_volume_path, continue_on_error: true) commands << copy_contents(new_extracted_path, old_volume_path, continue_on_error: true)
@@ -46,6 +46,6 @@ module Kamal::Commands::App::Assets
end end
def copy_contents(source, destination, continue_on_error: false) def copy_contents(source, destination, continue_on_error: false)
[ :cp, "-rnT", "#{source}", destination, *("|| true" if continue_on_error)] [ :cp, "-rnT", "#{source}", destination, *("|| true" if continue_on_error) ]
end end
end end

View File

@@ -2,7 +2,7 @@ module Kamal::Commands::App::Cord
def cord(version:) def cord(version:)
pipe \ pipe \
docker(:inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", container_name(version)), docker(:inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", container_name(version)),
[:awk, "'$2 == \"#{role.cord_volume.container_path}\" {print $1}'"] [ :awk, "'$2 == \"#{role.cord_volume.container_path}\" {print $1}'" ]
end end
def tie_cord(cord) def tie_cord(cord)
@@ -17,6 +17,6 @@ module Kamal::Commands::App::Cord
def create_empty_file(file) def create_empty_file(file)
chain \ chain \
make_directory_for(file), make_directory_for(file),
[:touch, file] [ :touch, file ]
end end
end end

View File

@@ -25,7 +25,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
pipe \ pipe \
docker(:inspect, "-f", "'{{ .Config.Labels.service }}'", config.absolute_image), docker(:inspect, "-f", "'{{ .Config.Labels.service }}'", config.absolute_image),
any( any(
[:grep, "-x", config.service], [ :grep, "-x", config.service ],
"(echo \"Image #{config.absolute_image} is missing the 'service' label\" && exit 1)" "(echo \"Image #{config.absolute_image} is missing the 'service' label\" && exit 1)"
) )
end end
@@ -38,8 +38,8 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
def build_cache def build_cache
if cache_to && cache_from if cache_to && cache_from
["--cache-to", cache_to, [ "--cache-to", cache_to,
"--cache-from", cache_from] "--cache-from", cache_from ]
end end
end end

View File

@@ -1,5 +1,4 @@
class Kamal::Commands::Healthcheck < Kamal::Commands::Base class Kamal::Commands::Healthcheck < Kamal::Commands::Base
def run def run
primary = config.role(config.primary_role) primary = config.role(config.primary_role)

View File

@@ -5,14 +5,14 @@ require "base64"
class Kamal::Commands::Lock < Kamal::Commands::Base class Kamal::Commands::Lock < Kamal::Commands::Base
def acquire(message, version) def acquire(message, version)
combine \ combine \
[:mkdir, lock_dir], [ :mkdir, lock_dir ],
write_lock_details(message, version) write_lock_details(message, version)
end end
def release def release
combine \ combine \
[:rm, lock_details_file], [ :rm, lock_details_file ],
[:rm, "-r", lock_dir] [ :rm, "-r", lock_dir ]
end end
def status def status
@@ -24,19 +24,19 @@ class Kamal::Commands::Lock < Kamal::Commands::Base
private private
def write_lock_details(message, version) def write_lock_details(message, version)
write \ write \
[:echo, "\"#{Base64.encode64(lock_details(message, version))}\""], [ :echo, "\"#{Base64.encode64(lock_details(message, version))}\"" ],
lock_details_file lock_details_file
end end
def read_lock_details def read_lock_details
pipe \ pipe \
[:cat, lock_details_file], [ :cat, lock_details_file ],
[:base64, "-d"] [ :base64, "-d" ]
end end
def stat_lock_dir def stat_lock_dir
write \ write \
[:stat, lock_dir], [ :stat, lock_dir ],
"/dev/null" "/dev/null"
end end
@@ -45,7 +45,7 @@ class Kamal::Commands::Lock < Kamal::Commands::Base
end end
def lock_details_file def lock_details_file
[lock_dir, :details].join("/") [ lock_dir, :details ].join("/")
end end
def lock_details(message, version) def lock_details(message, version)

View File

@@ -26,7 +26,7 @@ class Kamal::Commands::Prune < Kamal::Commands::Base
private private
def stopped_containers_filters def stopped_containers_filters
[ "created", "exited", "dead" ].flat_map { |status| ["--filter", "status=#{status}"] } [ "created", "exited", "dead" ].flat_map { |status| [ "--filter", "status=#{status}" ] }
end end
def active_image_list def active_image_list
@@ -43,4 +43,4 @@ class Kamal::Commands::Prune < Kamal::Commands::Base
def healthcheck_service_filter def healthcheck_service_filter
[ "--filter", "label=service=#{config.healthcheck_service}" ] [ "--filter", "label=service=#{config.healthcheck_service}" ]
end end
end end

View File

@@ -1,5 +1,5 @@
class Kamal::Commands::Server < Kamal::Commands::Base class Kamal::Commands::Server < Kamal::Commands::Base
def ensure_run_directory def ensure_run_directory
[:mkdir, "-p", config.run_directory] [ :mkdir, "-p", config.run_directory ]
end end
end end

View File

@@ -4,7 +4,7 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
DEFAULT_IMAGE = "traefik:v2.10" DEFAULT_IMAGE = "traefik:v2.10"
CONTAINER_PORT = 80 CONTAINER_PORT = 80
DEFAULT_ARGS = { DEFAULT_ARGS = {
'log.level' => 'DEBUG' "log.level" => "DEBUG"
} }
DEFAULT_LABELS = { DEFAULT_LABELS = {
# These ensure we serve a 502 rather than a 404 if no containers are available # These ensure we serve a 502 rather than a 404 if no containers are available
@@ -84,7 +84,7 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
end end
def remove_env_file def remove_env_file
[:rm, "-f", host_env_file_path] [ :rm, "-f", host_env_file_path ]
end end
private private

View File

@@ -16,7 +16,7 @@ class Kamal::Configuration::Accessory
end end
def hosts def hosts
if (specifics.keys & ["host", "hosts", "roles"]).size != 1 if (specifics.keys & [ "host", "hosts", "roles" ]).size != 1
raise ArgumentError, "Specify one of `host`, `hosts` or `roles` for accessory `#{name}`" raise ArgumentError, "Specify one of `host`, `hosts` or `roles` for accessory `#{name}`"
end end
@@ -159,7 +159,7 @@ class Kamal::Configuration::Accessory
if specifics.key?("host") if specifics.key?("host")
host = specifics["host"] host = specifics["host"]
if host if host
[host] [ host ]
else else
raise ArgumentError, "Missing host for accessory `#{name}`" raise ArgumentError, "Missing host for accessory `#{name}`"
end end

View File

@@ -8,7 +8,7 @@ class Kamal::Configuration::Boot
limit = @options["limit"] limit = @options["limit"]
if limit.to_s.end_with?("%") if limit.to_s.end_with?("%")
[@host_count * limit.to_i / 100, 1].max [ @host_count * limit.to_i / 100, 1 ].max
else else
limit limit
end end

View File

@@ -88,7 +88,7 @@ class Kamal::Configuration::Builder
private private
def valid? def valid?
if @options["cache"] && @options["cache"]["type"] if @options["cache"] && @options["cache"]["type"]
raise ArgumentError, "Invalid cache type: #{@options["cache"]["type"]}" unless ["gha", "registry"].include?(@options["cache"]["type"]) raise ArgumentError, "Invalid cache type: #{@options["cache"]["type"]}" unless [ "gha", "registry" ].include?(@options["cache"]["type"])
end end
end end
@@ -109,7 +109,7 @@ class Kamal::Configuration::Builder
end end
def cache_to_config_for_gha def cache_to_config_for_gha
[ "type=gha", @options["cache"]&.fetch("options", nil)].compact.join(",") [ "type=gha", @options["cache"]&.fetch("options", nil) ].compact.join(",")
end end
def cache_to_config_for_registry def cache_to_config_for_registry

View File

@@ -67,7 +67,7 @@ class Kamal::Configuration::Role
end end
def host_env_file_path def host_env_file_path
File.join host_env_directory, "#{[config.service, name, config.destination].compact.join("-")}.env" File.join host_env_directory, "#{[ config.service, name, config.destination ].compact.join("-")}.env"
end end
def env_args def env_args
@@ -123,13 +123,13 @@ class Kamal::Configuration::Role
end end
def cord_host_directory def cord_host_directory
File.join config.run_directory_as_docker_volume, "cords", [container_prefix, config.run_id].join("-") File.join config.run_directory_as_docker_volume, "cords", [ container_prefix, config.run_id ].join("-")
end end
def cord_volume def cord_volume
if (cord = health_check_options["cord"]) if (cord = health_check_options["cord"])
@cord_volume ||= Kamal::Configuration::Volume.new \ @cord_volume ||= Kamal::Configuration::Volume.new \
host_path: File.join(config.run_directory, "cords", [container_prefix, config.run_id].join("-")), host_path: File.join(config.run_directory, "cords", [ container_prefix, config.run_id ].join("-")),
container_path: cord container_path: cord
end end
end end
@@ -229,7 +229,7 @@ class Kamal::Configuration::Role
def specializations def specializations
if config.servers.is_a?(Array) || config.servers[name].is_a?(Array) if config.servers.is_a?(Array) || config.servers[name].is_a?(Array)
{ } {}
else else
config.servers[name].except("hosts") config.servers[name].except("hosts")
end end

View File

@@ -3,7 +3,7 @@ class Kamal::EnvFile
def initialize(env) def initialize(env)
@env = env @env = env
end end
def to_s def to_s
env_file = StringIO.new.tap do |contents| env_file = StringIO.new.tap do |contents|
if (secrets = @env["secret"]).present? if (secrets = @env["secret"]).present?
@@ -26,10 +26,10 @@ class Kamal::EnvFile
end end
alias to_str to_s alias to_str to_s
private private
def docker_env_file_line(key, value) def docker_env_file_line(key, value)
"#{key.to_s}=#{escape_docker_env_file_value(value)}\n" "#{key}=#{escape_docker_env_file_value(value)}\n"
end end
# Escape a value to make it safe to dump in a docker file. # Escape a value to make it safe to dump in a docker file.

View File

@@ -9,7 +9,7 @@ module Kamal::Utils
if value.present? if value.present?
attr = "#{key}=#{escape_shell_value(value)}" attr = "#{key}=#{escape_shell_value(value)}"
attr = self.sensitive(attr, redaction: "#{key}=[REDACTED]") if sensitive attr = self.sensitive(attr, redaction: "#{key}=[REDACTED]") if sensitive
[ argument, attr] [ argument, attr ]
else else
[ argument, key ] [ argument, key ]
end end
@@ -29,7 +29,7 @@ module Kamal::Utils
# Flattens a one-to-many structure into an array of two-element arrays each containing a key-value pair # Flattens a one-to-many structure into an array of two-element arrays each containing a key-value pair
def flatten_args(args) def flatten_args(args)
args.flat_map { |key, value| value.try(:map) { |entry| [key, entry] } || [ [ key, value ] ] } args.flat_map { |key, value| value.try(:map) { |entry| [ key, entry ] } || [ [ key, value ] ] }
end end
# Marks sensitive values for redaction in logs and human-visible output. # Marks sensitive values for redaction in logs and human-visible output.
@@ -66,7 +66,7 @@ module Kamal::Utils
Array(filters).select do |filter| Array(filters).select do |filter|
matches += Array(items).select do |item| matches += Array(items).select do |item|
# Only allow * for a wildcard # Only allow * for a wildcard
pattern = Regexp.escape(filter).gsub('\*', '.*') pattern = Regexp.escape(filter).gsub('\*', ".*")
# items are roles or hosts # items are roles or hosts
(item.respond_to?(:name) ? item.name : item).match(/^#{pattern}$/) (item.respond_to?(:name) ? item.name : item).match(/^#{pattern}$/)
end end

View File

@@ -154,9 +154,9 @@ class CliAccessoryTest < CliTestCase
run_command("boot", "redis", "--hosts", "1.1.1.1").tap do |output| run_command("boot", "redis", "--hosts", "1.1.1.1").tap do |output|
assert_match /docker login.*on 1.1.1.1/, output assert_match /docker login.*on 1.1.1.1/, output
refute_match /docker login.*on 1.1.1.2/, output assert_no_match /docker login.*on 1.1.1.2/, output
assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.1", output assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.1", output
refute_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.2", output assert_no_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.2", output
end end
end end
@@ -166,14 +166,14 @@ class CliAccessoryTest < CliTestCase
run_command("boot", "redis", "--hosts", "1.1.1.1,1.1.1.3").tap do |output| run_command("boot", "redis", "--hosts", "1.1.1.1,1.1.1.3").tap do |output|
assert_match /docker login.*on 1.1.1.1/, output assert_match /docker login.*on 1.1.1.1/, output
refute_match /docker login.*on 1.1.1.3/, output assert_no_match /docker login.*on 1.1.1.3/, output
assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.1", output assert_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.1", output
refute_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.3", output assert_no_match "docker run --name app-redis --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 6379:6379 --env-file .kamal/env/accessories/app-redis.env --volume $PWD/app-redis/data:/data --label service=\"app-redis\" redis:latest on 1.1.1.3", output
end end
end end
private private
def run_command(*command) def run_command(*command)
stdouted { Kamal::Cli::Accessory.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) } stdouted { Kamal::Cli::Accessory.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }
end end
end end

View File

@@ -27,7 +27,7 @@ class CliAppTest < CliTestCase
.returns("123") # old version .returns("123") # old version
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", "app-web-123", "|", :awk, "'$2 == \"/tmp/kamal-cord\" {print $1}'", :raise_on_non_zero_exit => false) .with(:docker, :inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", "app-web-123", "|", :awk, "'$2 == \"/tmp/kamal-cord\" {print $1}'", raise_on_non_zero_exit: false)
.returns("cordfile") # old version .returns("cordfile") # old version
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
@@ -57,7 +57,7 @@ class CliAppTest < CliTestCase
test "boot errors leave lock in place" do test "boot errors leave lock in place" do
Kamal::Cli::App.any_instance.expects(:using_version).raises(RuntimeError) Kamal::Cli::App.any_instance.expects(:using_version).raises(RuntimeError)
assert !KAMAL.holding_lock? assert_not KAMAL.holding_lock?
assert_raises(RuntimeError) do assert_raises(RuntimeError) do
stderred { run_command("boot") } stderred { run_command("boot") }
end end
@@ -79,7 +79,7 @@ class CliAppTest < CliTestCase
.returns("123").twice # old version .returns("123").twice # old version
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", "app-web-123", "|", :awk, "'$2 == \"/tmp/kamal-cord\" {print $1}'", :raise_on_non_zero_exit => false) .with(:docker, :inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", "app-web-123", "|", :awk, "'$2 == \"/tmp/kamal-cord\" {print $1}'", raise_on_non_zero_exit: false)
.returns("") # old version .returns("") # old version
run_command("boot", config: :with_assets).tap do |output| run_command("boot", config: :with_assets).tap do |output|
@@ -223,14 +223,14 @@ class CliAppTest < CliTestCase
test "version through main" do test "version through main" do
stdouted { Kamal::Cli::Main.start(["app", "version", "-c", "test/fixtures/deploy_with_accessories.yml", "--hosts", "1.1.1.1"]) }.tap do |output| stdouted { Kamal::Cli::Main.start([ "app", "version", "-c", "test/fixtures/deploy_with_accessories.yml", "--hosts", "1.1.1.1" ]) }.tap do |output|
assert_match "docker ps --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest --format \"{{.Names}}\" | while read line; do echo ${line#app-web-}; done", output assert_match "docker ps --filter label=service=app --filter label=role=web --filter status=running --filter status=restarting --latest --format \"{{.Names}}\" | while read line; do echo ${line#app-web-}; done", output
end end
end end
private private
def run_command(*command, config: :with_accessories) def run_command(*command, config: :with_accessories)
stdouted { Kamal::Cli::App.start([*command, "-c", "test/fixtures/deploy_#{config}.yml", "--hosts", "1.1.1.1"]) } stdouted { Kamal::Cli::App.start([ *command, "-c", "test/fixtures/deploy_#{config}.yml", "--hosts", "1.1.1.1" ]) }
end end
def stub_running def stub_running

View File

@@ -25,7 +25,7 @@ class CliBuildTest < CliTestCase
.with(:docker, "--version", "&&", :docker, :buildx, "version") .with(:docker, "--version", "&&", :docker, :buildx, "version")
SSHKit::Backend::Abstract.any_instance.stubs(:execute) SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*args| args[0..1] == [:docker, :buildx] } .with { |*args| args[0..1] == [ :docker, :buildx ] }
.raises(SSHKit::Command::Failed.new("no builder")) .raises(SSHKit::Command::Failed.new("no builder"))
.then .then
.returns(true) .returns(true)
@@ -50,7 +50,7 @@ class CliBuildTest < CliTestCase
assert_raises(Kamal::Cli::HookError) { run_command("push") } assert_raises(Kamal::Cli::HookError) { run_command("push") }
assert @executions.none? { |args| args[0..2] == [:docker, :buildx, :build] } assert @executions.none? { |args| args[0..2] == [ :docker, :buildx, :build ] }
end end
test "pull" do test "pull" do
@@ -105,13 +105,13 @@ class CliBuildTest < CliTestCase
private private
def run_command(*command, fixture: :with_accessories) def run_command(*command, fixture: :with_accessories)
stdouted { Kamal::Cli::Build.start([*command, "-c", "test/fixtures/deploy_#{fixture}.yml"]) } stdouted { Kamal::Cli::Build.start([ *command, "-c", "test/fixtures/deploy_#{fixture}.yml" ]) }
end end
def stub_dependency_checks def stub_dependency_checks
SSHKit::Backend::Abstract.any_instance.stubs(:execute) SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with(:docker, "--version", "&&", :docker, :buildx, "version") .with(:docker, "--version", "&&", :docker, :buildx, "version")
SSHKit::Backend::Abstract.any_instance.stubs(:execute) SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*args| args[0..1] == [:docker, :buildx] } .with { |*args| args[0..1] == [ :docker, :buildx ] }
end end
end end

View File

@@ -21,7 +21,7 @@ class CliTestCase < ActiveSupport::TestCase
Kamal::Commands::Hook.any_instance.stubs(:hook_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}" ] }
SSHKit::Backend::Abstract.any_instance.stubs(:execute) SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*args| args.first == ".kamal/hooks/#{hook}" } .with { |*args| args.first == ".kamal/hooks/#{hook}" }
.raises(SSHKit::Command::Failed.new("failed")) .raises(SSHKit::Command::Failed.new("failed"))

View File

@@ -13,7 +13,6 @@ class CliEnvTest < CliTestCase
assert_match ".kamal/env/roles/app-workers.env", output assert_match ".kamal/env/roles/app-workers.env", output
assert_match ".kamal/env/traefik/traefik.env", output assert_match ".kamal/env/traefik/traefik.env", output
assert_match ".kamal/env/accessories/app-redis.env", output assert_match ".kamal/env/accessories/app-redis.env", output
end end
end end
@@ -33,6 +32,6 @@ class CliEnvTest < CliTestCase
private private
def run_command(*command) def run_command(*command)
stdouted { Kamal::Cli::Env.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) } stdouted { Kamal::Cli::Env.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }
end end
end end

View File

@@ -64,10 +64,10 @@ class CliHealthcheckTest < CliTestCase
end end
assert_match "container not ready (unhealthy)", exception.message assert_match "container not ready (unhealthy)", exception.message
end end
test "raises an exception if primary does not have traefik" do test "raises an exception if primary does not have traefik" do
SSHKit::Backend::Abstract.any_instance.expects(:execute).never SSHKit::Backend::Abstract.any_instance.expects(:execute).never
exception = assert_raises do exception = assert_raises do
run_command("perform", config_file: "test/fixtures/deploy_workers_only.yml") run_command("perform", config_file: "test/fixtures/deploy_workers_only.yml")
end end
@@ -77,6 +77,6 @@ class CliHealthcheckTest < CliTestCase
private private
def run_command(*command, config_file: "test/fixtures/deploy_with_accessories.yml") def run_command(*command, config_file: "test/fixtures/deploy_with_accessories.yml")
stdouted { Kamal::Cli::Healthcheck.start([*command, "-c", config_file]) } stdouted { Kamal::Cli::Healthcheck.start([ *command, "-c", config_file ]) }
end end
end end

View File

@@ -15,6 +15,6 @@ class CliLockTest < CliTestCase
private private
def run_command(*command) def run_command(*command)
stdouted { Kamal::Cli::Lock.start([*command, "-v", "-c", "test/fixtures/deploy_with_accessories.yml"]) } stdouted { Kamal::Cli::Lock.start([ *command, "-v", "-c", "test/fixtures/deploy_with_accessories.yml" ]) }
end end
end end

View File

@@ -3,7 +3,7 @@ require_relative "cli_test_case"
class CliMainTest < CliTestCase class CliMainTest < CliTestCase
test "setup" do test "setup" do
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: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)
@@ -102,7 +102,7 @@ class CliMainTest < CliTestCase
.with { |*args| args == [ :mkdir, "-p", ".kamal" ] } .with { |*args| args == [ :mkdir, "-p", ".kamal" ] }
SSHKit::Backend::Abstract.any_instance.stubs(:execute) SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*arg| arg[0..1] == [:mkdir, ".kamal/lock-app"] } .with { |*arg| arg[0..1] == [ :mkdir, ".kamal/lock-app" ] }
.raises(RuntimeError, "mkdir: cannot create directory kamal_lock-app: File exists") .raises(RuntimeError, "mkdir: cannot create directory kamal_lock-app: File exists")
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_debug) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_debug)
@@ -120,7 +120,7 @@ class CliMainTest < CliTestCase
.with { |*args| args == [ :mkdir, "-p", ".kamal" ] } .with { |*args| args == [ :mkdir, "-p", ".kamal" ] }
SSHKit::Backend::Abstract.any_instance.stubs(:execute) SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*arg| arg[0..1] == [:mkdir, ".kamal/lock-app"] } .with { |*arg| arg[0..1] == [ :mkdir, ".kamal/lock-app" ] }
.raises(SocketError, "getaddrinfo: nodename nor servname provided, or not known") .raises(SocketError, "getaddrinfo: nodename nor servname provided, or not known")
assert_raises(SSHKit::Runner::ExecuteError) do assert_raises(SSHKit::Runner::ExecuteError) do
@@ -135,11 +135,11 @@ class CliMainTest < CliTestCase
.with("kamal:cli:registry:login", [], invoke_options) .with("kamal:cli:registry:login", [], invoke_options)
.raises(RuntimeError) .raises(RuntimeError)
assert !KAMAL.holding_lock? assert_not KAMAL.holding_lock?
assert_raises(RuntimeError) do assert_raises(RuntimeError) do
stderred { run_command("deploy") } stderred { run_command("deploy") }
end end
assert !KAMAL.holding_lock? assert_not KAMAL.holding_lock?
end end
test "deploy with skipped hooks" do test "deploy with skipped hooks" do
@@ -154,15 +154,15 @@ class CliMainTest < CliTestCase
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)
run_command("deploy", "--skip_hooks") do run_command("deploy", "--skip_hooks") do
refute_match /Running the post-deploy hook.../, output assert_no_match /Running the post-deploy hook.../, output
end end
end end
test "deploy without healthcheck if primary host doesn't have traefik" do test "deploy without healthcheck if primary host doesn't have traefik" do
invoke_options = { "config_file" => "test/fixtures/deploy_workers_only.yml", "version" => "999", "skip_hooks" => false } invoke_options = { "config_file" => "test/fixtures/deploy_workers_only.yml", "version" => "999", "skip_hooks" => false }
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:healthcheck:perform", [], invoke_options).never Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:healthcheck:perform", [], invoke_options).never
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:registry:login", [], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:registry:login", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:deliver", [], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:deliver", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:traefik:boot", [], invoke_options) Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:traefik:boot", [], invoke_options)
@@ -252,7 +252,7 @@ class CliMainTest < CliTestCase
end end
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", "app-web-version-to-rollback", "|", :awk, "'$2 == \"/tmp/kamal-cord\" {print $1}'", :raise_on_non_zero_exit => false) .with(:docker, :inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", "app-web-version-to-rollback", "|", :awk, "'$2 == \"/tmp/kamal-cord\" {print $1}'", raise_on_non_zero_exit: false)
.returns("corddirectory").at_least_once # health check .returns("corddirectory").at_least_once # health check
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info) SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
@@ -311,8 +311,8 @@ class CliMainTest < CliTestCase
run_command("config", config_file: "deploy_simple").tap do |output| run_command("config", config_file: "deploy_simple").tap do |output|
config = YAML.load(output) config = YAML.load(output)
assert_equal ["web"], config[:roles] assert_equal [ "web" ], config[:roles]
assert_equal ["1.1.1.1", "1.1.1.2"], config[:hosts] assert_equal [ "1.1.1.1", "1.1.1.2" ], config[:hosts]
assert_equal "999", config[:version] assert_equal "999", config[:version]
assert_equal "dhh/app", config[:repository] assert_equal "dhh/app", config[:repository]
assert_equal "dhh/app:999", config[:absolute_image] assert_equal "dhh/app:999", config[:absolute_image]
@@ -324,8 +324,8 @@ class CliMainTest < CliTestCase
run_command("config", config_file: "deploy_with_roles").tap do |output| run_command("config", config_file: "deploy_with_roles").tap do |output|
config = YAML.load(output) config = YAML.load(output)
assert_equal ["web", "workers"], config[:roles] assert_equal [ "web", "workers" ], config[:roles]
assert_equal ["1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4"], config[:hosts] assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4" ], config[:hosts]
assert_equal "999", config[:version] assert_equal "999", config[:version]
assert_equal "registry.digitalocean.com/dhh/app", config[:repository] assert_equal "registry.digitalocean.com/dhh/app", config[:repository]
assert_equal "registry.digitalocean.com/dhh/app:999", config[:absolute_image] assert_equal "registry.digitalocean.com/dhh/app:999", config[:absolute_image]
@@ -337,8 +337,8 @@ class CliMainTest < CliTestCase
run_command("config", config_file: "deploy_primary_web_role_override").tap do |output| run_command("config", config_file: "deploy_primary_web_role_override").tap do |output|
config = YAML.load(output) config = YAML.load(output)
assert_equal ["web_chicago", "web_tokyo"], config[:roles] assert_equal [ "web_chicago", "web_tokyo" ], config[:roles]
assert_equal ["1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4"], config[:hosts] assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4" ], config[:hosts]
assert_equal "1.1.1.3", config[:primary_host] assert_equal "1.1.1.3", config[:primary_host]
end end
end end
@@ -347,8 +347,8 @@ class CliMainTest < CliTestCase
run_command("config", "-d", "world", config_file: "deploy_for_dest").tap do |output| run_command("config", "-d", "world", config_file: "deploy_for_dest").tap do |output|
config = YAML.load(output) config = YAML.load(output)
assert_equal ["web"], config[:roles] assert_equal [ "web" ], config[:roles]
assert_equal ["1.1.1.1", "1.1.1.2"], config[:hosts] assert_equal [ "1.1.1.1", "1.1.1.2" ], config[:hosts]
assert_equal "999", config[:version] assert_equal "999", config[:version]
assert_equal "registry.digitalocean.com/dhh/app", config[:repository] assert_equal "registry.digitalocean.com/dhh/app", config[:repository]
assert_equal "registry.digitalocean.com/dhh/app:999", config[:absolute_image] assert_equal "registry.digitalocean.com/dhh/app:999", config[:absolute_image]
@@ -360,8 +360,8 @@ class CliMainTest < CliTestCase
run_command("config", config_file: "deploy_with_aliases").tap do |output| run_command("config", config_file: "deploy_with_aliases").tap do |output|
config = YAML.load(output) config = YAML.load(output)
assert_equal ["web", "web_tokyo", "workers", "workers_tokyo"], config[:roles] assert_equal [ "web", "web_tokyo", "workers", "workers_tokyo" ], config[:roles]
assert_equal ["1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4"], config[:hosts] assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3", "1.1.1.4" ], config[:hosts]
assert_equal "999", config[:version] assert_equal "999", config[:version]
assert_equal "registry.digitalocean.com/dhh/app", config[:repository] assert_equal "registry.digitalocean.com/dhh/app", config[:repository]
assert_equal "registry.digitalocean.com/dhh/app:999", config[:absolute_image] assert_equal "registry.digitalocean.com/dhh/app:999", config[:absolute_image]
@@ -487,6 +487,6 @@ class CliMainTest < CliTestCase
private private
def run_command(*command, config_file: "deploy_simple") def run_command(*command, config_file: "deploy_simple")
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
end end

View File

@@ -33,6 +33,6 @@ class CliPruneTest < CliTestCase
private private
def run_command(*command) def run_command(*command)
stdouted { Kamal::Cli::Prune.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) } stdouted { Kamal::Cli::Prune.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }
end end
end end

View File

@@ -16,6 +16,6 @@ class CliRegistryTest < CliTestCase
private private
def run_command(*command) def run_command(*command)
stdouted { Kamal::Cli::Registry.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) } stdouted { Kamal::Cli::Registry.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }
end end
end end

View File

@@ -36,6 +36,6 @@ class CliServerTest < CliTestCase
private private
def run_command(*command) def run_command(*command)
stdouted { Kamal::Cli::Server.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) } stdouted { Kamal::Cli::Server.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }
end end
end end

View File

@@ -91,6 +91,6 @@ class CliTraefikTest < CliTestCase
private private
def run_command(*command) def run_command(*command)
stdouted { Kamal::Cli::Traefik.start([*command, "-c", "test/fixtures/deploy_with_accessories.yml"]) } stdouted { Kamal::Cli::Traefik.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }
end end
end end

View File

@@ -103,14 +103,14 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
test "execute in new container over ssh" do test "execute in new container over ssh" do
new_command(:mysql).stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do new_command(:mysql).stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do
assert_match %r|docker run -it --rm --env-file .kamal/env/accessories/app-mysql.env private.registry/mysql:8.0 mysql -u root|, assert_match %r{docker run -it --rm --env-file .kamal/env/accessories/app-mysql.env private.registry/mysql:8.0 mysql -u root},
new_command(:mysql).execute_in_new_container_over_ssh("mysql", "-u", "root") new_command(:mysql).execute_in_new_container_over_ssh("mysql", "-u", "root")
end end
end end
test "execute in existing container over ssh" do test "execute in existing container over ssh" do
new_command(:mysql).stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do new_command(:mysql).stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do
assert_match %r|docker exec -it app-mysql mysql -u root|, assert_match %r{docker exec -it app-mysql mysql -u root},
new_command(:mysql).execute_in_existing_container_over_ssh("mysql", "-u", "root") new_command(:mysql).execute_in_existing_container_over_ssh("mysql", "-u", "root")
end end
end end

View File

@@ -25,7 +25,7 @@ class CommandsAppTest < ActiveSupport::TestCase
end end
test "run with volumes" do test "run with volumes" do
@config[:volumes] = ["/local/path:/container/path" ] @config[:volumes] = [ "/local/path:/container/path" ]
assert_equal \ assert_equal \
"docker run --detach --restart unless-stopped --name app-web-999 -e KAMAL_CONTAINER_NAME=\"app-web-999\" -e KAMAL_VERSION=\"999\" --env-file .kamal/env/roles/app-web.env --health-cmd \"(curl -f http://localhost:3000/up || exit 1) && (stat /tmp/kamal-cord/cord > /dev/null || exit 1)\" --health-interval \"1s\" --volume $(pwd)/.kamal/cords/app-web-12345678901234567890123456789012:/tmp/kamal-cord --log-opt max-size=\"10m\" --volume /local/path:/container/path --label service=\"app\" --label role=\"web\" --label traefik.http.services.app-web.loadbalancer.server.scheme=\"http\" --label traefik.http.routers.app-web.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.app-web.priority=\"2\" --label traefik.http.middlewares.app-web-retry.retry.attempts=\"5\" --label traefik.http.middlewares.app-web-retry.retry.initialinterval=\"500ms\" --label traefik.http.routers.app-web.middlewares=\"app-web-retry@docker\" dhh/app:999", "docker run --detach --restart unless-stopped --name app-web-999 -e KAMAL_CONTAINER_NAME=\"app-web-999\" -e KAMAL_VERSION=\"999\" --env-file .kamal/env/roles/app-web.env --health-cmd \"(curl -f http://localhost:3000/up || exit 1) && (stat /tmp/kamal-cord/cord > /dev/null || exit 1)\" --health-interval \"1s\" --volume $(pwd)/.kamal/cords/app-web-12345678901234567890123456789012:/tmp/kamal-cord --log-opt max-size=\"10m\" --volume /local/path:/container/path --label service=\"app\" --label role=\"web\" --label traefik.http.services.app-web.loadbalancer.server.scheme=\"http\" --label traefik.http.routers.app-web.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.app-web.priority=\"2\" --label traefik.http.middlewares.app-web-retry.retry.attempts=\"5\" --label traefik.http.middlewares.app-web-retry.retry.initialinterval=\"500ms\" --label traefik.http.routers.app-web.middlewares=\"app-web-retry@docker\" dhh/app:999",
@@ -191,18 +191,18 @@ class CommandsAppTest < ActiveSupport::TestCase
end end
test "execute in new container over ssh" do test "execute in new container over ssh" do
assert_match %r|docker run -it --rm --env-file .kamal/env/roles/app-web.env dhh/app:999 bin/rails c|, assert_match %r{docker run -it --rm --env-file .kamal/env/roles/app-web.env dhh/app:999 bin/rails c},
new_command.execute_in_new_container_over_ssh("bin/rails", "c", host: "app-1") new_command.execute_in_new_container_over_ssh("bin/rails", "c", host: "app-1")
end end
test "execute in new container with custom options over ssh" do test "execute in new container with custom options over ssh" do
@config[:servers] = { "web" => { "hosts" => [ "1.1.1.1" ], "options" => { "mount" => "somewhere", "cap-add" => true } } } @config[:servers] = { "web" => { "hosts" => [ "1.1.1.1" ], "options" => { "mount" => "somewhere", "cap-add" => true } } }
assert_match %r|docker run -it --rm --env-file .kamal/env/roles/app-web.env --mount \"somewhere\" --cap-add dhh/app:999 bin/rails c|, assert_match %r{docker run -it --rm --env-file .kamal/env/roles/app-web.env --mount \"somewhere\" --cap-add dhh/app:999 bin/rails c},
new_command.execute_in_new_container_over_ssh("bin/rails", "c", host: "app-1") new_command.execute_in_new_container_over_ssh("bin/rails", "c", host: "app-1")
end end
test "execute in existing container over ssh" do test "execute in existing container over ssh" do
assert_match %r|docker exec -it app-web-999 bin/rails c|, assert_match %r{docker exec -it app-web-999 bin/rails c},
new_command.execute_in_existing_container_over_ssh("bin/rails", "c", host: "app-1") new_command.execute_in_existing_container_over_ssh("bin/rails", "c", host: "app-1")
end end
@@ -387,7 +387,7 @@ class CommandsAppTest < ActiveSupport::TestCase
:mkdir, "-p", ".kamal/assets/volumes/app-web-999", ";", :mkdir, "-p", ".kamal/assets/volumes/app-web-999", ";",
:cp, "-rnT", ".kamal/assets/extracted/app-web-999", ".kamal/assets/volumes/app-web-999", ";", :cp, "-rnT", ".kamal/assets/extracted/app-web-999", ".kamal/assets/volumes/app-web-999", ";",
:cp, "-rnT", ".kamal/assets/extracted/app-web-999", ".kamal/assets/volumes/app-web-998", "|| true", ";", :cp, "-rnT", ".kamal/assets/extracted/app-web-999", ".kamal/assets/volumes/app-web-998", "|| true", ";",
:cp, "-rnT", ".kamal/assets/extracted/app-web-998", ".kamal/assets/volumes/app-web-999", "|| true", :cp, "-rnT", ".kamal/assets/extracted/app-web-998", ".kamal/assets/volumes/app-web-999", "|| true"
], new_command(asset_path: "/public/assets").sync_asset_volumes(old_version: 998) ], new_command(asset_path: "/public/assets").sync_asset_volumes(old_version: 998)
end end

View File

@@ -6,7 +6,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
end end
test "target multiarch by default" do test "target multiarch by default" do
builder = new_builder_command(builder: { "cache" => { "type" => "gha" }}) builder = new_builder_command(builder: { "cache" => { "type" => "gha" } })
assert_equal "multiarch", builder.name assert_equal "multiarch", builder.name
assert_equal \ assert_equal \
"docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .", "docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
@@ -22,7 +22,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
end end
test "target native cached when multiarch is off and cache is set" do test "target native cached when multiarch is off and cache is set" do
builder = new_builder_command(builder: { "multiarch" => false, "cache" => { "type" => "gha" }}) builder = new_builder_command(builder: { "multiarch" => false, "cache" => { "type" => "gha" } })
assert_equal "native/cached", builder.name assert_equal "native/cached", builder.name
assert_equal \ assert_equal \
"docker buildx build --push -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .", "docker buildx build --push -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
@@ -30,7 +30,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
end end
test "target multiarch remote when local and remote is set" do test "target multiarch remote when local and remote is set" do
builder = new_builder_command(builder: { "local" => { }, "remote" => { }, "cache" => { "type" => "gha" } }) builder = new_builder_command(builder: { "local" => {}, "remote" => {}, "cache" => { "type" => "gha" } })
assert_equal "multiarch/remote", builder.name assert_equal "multiarch/remote", builder.name
assert_equal \ assert_equal \
"docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch-remote -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .", "docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch-remote -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
@@ -61,7 +61,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
end end
test "build secrets" do test "build secrets" do
builder = new_builder_command(builder: { "secrets" => ["token_a", "token_b"] }) builder = new_builder_command(builder: { "secrets" => [ "token_a", "token_b" ] })
assert_equal \ assert_equal \
"-t dhh/app:123 -t dhh/app:latest --label service=\"app\" --secret id=\"token_a\" --secret id=\"token_b\" --file Dockerfile", "-t dhh/app:123 -t dhh/app:latest --label service=\"app\" --secret id=\"token_a\" --secret id=\"token_b\" --file Dockerfile",
builder.target.build_options.join(" ") builder.target.build_options.join(" ")
@@ -112,7 +112,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
end end
test "build with ssh agent socket" do test "build with ssh agent socket" do
builder = new_builder_command(builder: { "ssh" => 'default=$SSH_AUTH_SOCK' }) builder = new_builder_command(builder: { "ssh" => "default=$SSH_AUTH_SOCK" })
assert_equal \ assert_equal \
"-t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile --ssh default=$SSH_AUTH_SOCK", "-t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile --ssh default=$SSH_AUTH_SOCK",

View File

@@ -37,7 +37,7 @@ class CommandsTraefikTest < ActiveSupport::TestCase
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"", "docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ") new_command.run.join(" ")
@config[:traefik]["options"] = {"publish" => %w[9000:9000 9001:9001]} @config[:traefik]["options"] = { "publish" => %w[9000:9000 9001:9001] }
assert_equal \ assert_equal \
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" --publish \"9000:9000\" --publish \"9001:9001\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"", "docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" --publish \"9000:9000\" --publish \"9001:9001\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ") new_command.run.join(" ")
@@ -48,7 +48,7 @@ class CommandsTraefikTest < ActiveSupport::TestCase
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"", "docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ") new_command.run.join(" ")
@config[:traefik]["options"] = {"volume" => %w[./letsencrypt/acme.json:/letsencrypt/acme.json] } @config[:traefik]["options"] = { "volume" => %w[./letsencrypt/acme.json:/letsencrypt/acme.json] }
assert_equal \ assert_equal \
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" --volume \"./letsencrypt/acme.json:/letsencrypt/acme.json\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"", "docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" --volume \"./letsencrypt/acme.json:/letsencrypt/acme.json\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ") new_command.run.join(" ")
@@ -59,7 +59,7 @@ class CommandsTraefikTest < ActiveSupport::TestCase
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"", "docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ") new_command.run.join(" ")
@config[:traefik]["options"] = {"volume" => %w[./letsencrypt/acme.json:/letsencrypt/acme.json], "publish" => %w[8080:8080], "memory" => "512m"} @config[:traefik]["options"] = { "volume" => %w[./letsencrypt/acme.json:/letsencrypt/acme.json], "publish" => %w[8080:8080], "memory" => "512m" }
assert_equal \ assert_equal \
"docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" --volume \"./letsencrypt/acme.json:/letsencrypt/acme.json\" --publish \"8080:8080\" --memory \"512m\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"", "docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" --volume \"./letsencrypt/acme.json:/letsencrypt/acme.json\" --publish \"8080:8080\" --memory \"512m\" #{@image} --providers.docker --log.level=\"DEBUG\" --accesslog.format=\"json\" --api.insecure --metrics.prometheus.buckets=\"0.1,0.3,1.2,5.0\"",
new_command.run.join(" ") new_command.run.join(" ")
@@ -138,7 +138,7 @@ class CommandsTraefikTest < ActiveSupport::TestCase
test "traefik logs since 2h" do test "traefik logs since 2h" do
assert_equal \ assert_equal \
"docker logs traefik --since 2h --timestamps 2>&1", "docker logs traefik --since 2h --timestamps 2>&1",
new_command.logs(since: '2h').join(" ") new_command.logs(since: "2h").join(" ")
end end
test "traefik logs last 10 lines" do test "traefik logs last 10 lines" do
@@ -150,7 +150,7 @@ class CommandsTraefikTest < ActiveSupport::TestCase
test "traefik logs with grep hello!" do test "traefik logs with grep hello!" do
assert_equal \ assert_equal \
"docker logs traefik --timestamps 2>&1 | grep 'hello!'", "docker logs traefik --timestamps 2>&1 | grep 'hello!'",
new_command.logs(grep: 'hello!').join(" ") new_command.logs(grep: "hello!").join(" ")
end end
test "traefik remove container" do test "traefik remove container" do
@@ -174,7 +174,7 @@ class CommandsTraefikTest < ActiveSupport::TestCase
test "traefik follow logs with grep hello!" do test "traefik follow logs with grep hello!" do
assert_equal \ assert_equal \
"ssh -t root@1.1.1.1 -p 22 'docker logs traefik --timestamps --tail 10 --follow 2>&1 | grep \"hello!\"'", "ssh -t root@1.1.1.1 -p 22 'docker logs traefik --timestamps --tail 10 --follow 2>&1 | grep \"hello!\"'",
new_command.follow_logs(host: @config[:servers].first, grep: 'hello!') new_command.follow_logs(host: @config[:servers].first, grep: "hello!")
end end
test "env_file" do test "env_file" do

View File

@@ -20,7 +20,7 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
}, },
"secret" => [ "secret" => [
"MYSQL_ROOT_PASSWORD" "MYSQL_ROOT_PASSWORD"
], ]
}, },
"files" => [ "files" => [
"config/mysql/my.cnf:/etc/mysql/my.cnf", "config/mysql/my.cnf:/etc/mysql/my.cnf",
@@ -82,9 +82,9 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
end end
test "host" do test "host" do
assert_equal ["1.1.1.5"], @config.accessory(:mysql).hosts 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.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.2" ], @config.accessory(:monitoring).hosts
end end
test "missing host" do test "missing host" do
@@ -108,13 +108,13 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
end end
test "label args" do test "label args" do
assert_equal ["--label", "service=\"app-mysql\""], @config.accessory(:mysql).label_args assert_equal [ "--label", "service=\"app-mysql\"" ], @config.accessory(:mysql).label_args
assert_equal ["--label", "service=\"app-redis\"", "--label", "cache=\"true\""], @config.accessory(:redis).label_args assert_equal [ "--label", "service=\"app-redis\"", "--label", "cache=\"true\"" ], @config.accessory(:redis).label_args
end end
test "env args" do test "env args" do
assert_equal ["--env-file", ".kamal/env/accessories/app-mysql.env"], @config.accessory(:mysql).env_args assert_equal [ "--env-file", ".kamal/env/accessories/app-mysql.env" ], @config.accessory(:mysql).env_args
assert_equal ["--env-file", ".kamal/env/accessories/app-redis.env"], @config.accessory(:redis).env_args assert_equal [ "--env-file", ".kamal/env/accessories/app-redis.env" ], @config.accessory(:redis).env_args
end end
test "env file with secret" do test "env file with secret" do
@@ -139,8 +139,8 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
end end
test "volume args" do test "volume args" do
assert_equal ["--volume", "$PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf", "--volume", "$PWD/app-mysql/docker-entrypoint-initdb.d/structure.sql:/docker-entrypoint-initdb.d/structure.sql", "--volume", "$PWD/app-mysql/data:/var/lib/mysql"], @config.accessory(:mysql).volume_args assert_equal [ "--volume", "$PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf", "--volume", "$PWD/app-mysql/docker-entrypoint-initdb.d/structure.sql:/docker-entrypoint-initdb.d/structure.sql", "--volume", "$PWD/app-mysql/data:/var/lib/mysql" ], @config.accessory(:mysql).volume_args
assert_equal ["--volume", "/var/lib/redis:/data"], @config.accessory(:redis).volume_args assert_equal [ "--volume", "/var/lib/redis:/data" ], @config.accessory(:redis).volume_args
end end
test "dynamic file expansion" do test "dynamic file expansion" do
@@ -153,15 +153,15 @@ class ConfigurationAccessoryTest < ActiveSupport::TestCase
test "directory with a relative path" do test "directory with a relative path" do
@deploy[:accessories]["mysql"]["directories"] = [ "data:/var/lib/mysql" ] @deploy[:accessories]["mysql"]["directories"] = [ "data:/var/lib/mysql" ]
assert_equal({"$PWD/app-mysql/data"=>"/var/lib/mysql"}, @config.accessory(:mysql).directories) assert_equal({ "$PWD/app-mysql/data"=>"/var/lib/mysql" }, @config.accessory(:mysql).directories)
end end
test "directory with an absolute path" do test "directory with an absolute path" do
@deploy[:accessories]["mysql"]["directories"] = [ "/var/data/mysql:/var/lib/mysql" ] @deploy[:accessories]["mysql"]["directories"] = [ "/var/data/mysql:/var/lib/mysql" ]
assert_equal({"/var/data/mysql"=>"/var/lib/mysql"}, @config.accessory(:mysql).directories) assert_equal({ "/var/data/mysql"=>"/var/lib/mysql" }, @config.accessory(:mysql).directories)
end end
test "options" do test "options" do
assert_equal ["--cpus", "\"4\"", "--memory", "\"2GB\""], @config.accessory(:redis).option_args assert_equal [ "--cpus", "\"4\"", "--memory", "\"2GB\"" ], @config.accessory(:redis).option_args
end end
end end

View File

@@ -124,9 +124,9 @@ class ConfigurationBuilderTest < ActiveSupport::TestCase
end end
test "setting secrets" do test "setting secrets" do
@deploy_with_builder_option[:builder] = { "secrets" => ["GITHUB_TOKEN"] } @deploy_with_builder_option[:builder] = { "secrets" => [ "GITHUB_TOKEN" ] }
assert_equal ["GITHUB_TOKEN"], @config_with_builder_option.builder.secrets assert_equal [ "GITHUB_TOKEN" ], @config_with_builder_option.builder.secrets
end end
test "dockerfile" do test "dockerfile" do
@@ -154,8 +154,8 @@ class ConfigurationBuilderTest < ActiveSupport::TestCase
end end
test "setting ssh params" do test "setting ssh params" do
@deploy_with_builder_option[:builder] = { "ssh" => 'default=$SSH_AUTH_SOCK' } @deploy_with_builder_option[:builder] = { "ssh" => "default=$SSH_AUTH_SOCK" }
assert_equal 'default=$SSH_AUTH_SOCK', @config_with_builder_option.builder.ssh assert_equal "default=$SSH_AUTH_SOCK", @config_with_builder_option.builder.ssh
end end
end end

View File

@@ -90,7 +90,7 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
end end
test "env args" do test "env args" do
assert_equal ["--env-file", ".kamal/env/roles/app-workers.env"], @config_with_roles.role(:workers).env_args assert_equal [ "--env-file", ".kamal/env/roles/app-workers.env" ], @config_with_roles.role(:workers).env_args
end end
test "env secret overwritten by role" do test "env secret overwritten by role" do
@@ -188,8 +188,8 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
@deploy_with_roles[:servers]["workers"]["env"] = { @deploy_with_roles[:servers]["workers"]["env"] = {
"clear" => { "clear" => {
"REDIS_URL" => "redis://c/d", "REDIS_URL" => "redis://c/d"
}, }
} }
ENV["REDIS_PASSWORD"] = "secret456" ENV["REDIS_PASSWORD"] = "secret456"
@@ -214,7 +214,7 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
test "uses cord" do test "uses cord" do
assert @config_with_roles.role(:web).uses_cord? assert @config_with_roles.role(:web).uses_cord?
assert !@config_with_roles.role(:workers).uses_cord? assert_not @config_with_roles.role(:workers).uses_cord?
end end
test "cord host file" do test "cord host file" do
@@ -238,28 +238,28 @@ class ConfigurationRoleTest < ActiveSupport::TestCase
assert_nil @config_with_roles.role(:workers).asset_volume_args assert_nil @config_with_roles.role(:workers).asset_volume_args
assert_nil @config_with_roles.role(:web).asset_path assert_nil @config_with_roles.role(:web).asset_path
assert_nil @config_with_roles.role(:workers).asset_path assert_nil @config_with_roles.role(:workers).asset_path
assert !@config_with_roles.role(:web).assets? assert_not @config_with_roles.role(:web).assets?
assert !@config_with_roles.role(:workers).assets? assert_not @config_with_roles.role(:workers).assets?
config_with_assets = Kamal::Configuration.new(@deploy_with_roles.dup.tap { |c| config_with_assets = Kamal::Configuration.new(@deploy_with_roles.dup.tap { |c|
c[:asset_path] = "foo" c[:asset_path] = "foo"
}) })
assert_equal "foo", config_with_assets.role(:web).asset_path assert_equal "foo", config_with_assets.role(:web).asset_path
assert_equal "foo", config_with_assets.role(:workers).asset_path assert_equal "foo", config_with_assets.role(:workers).asset_path
assert_equal ["--volume", "$(pwd)/.kamal/assets/volumes/app-web-12345:foo"], config_with_assets.role(:web).asset_volume_args assert_equal [ "--volume", "$(pwd)/.kamal/assets/volumes/app-web-12345:foo" ], config_with_assets.role(:web).asset_volume_args
assert_nil config_with_assets.role(:workers).asset_volume_args assert_nil config_with_assets.role(:workers).asset_volume_args
assert config_with_assets.role(:web).assets? assert config_with_assets.role(:web).assets?
assert !config_with_assets.role(:workers).assets? assert_not config_with_assets.role(:workers).assets?
config_with_assets = Kamal::Configuration.new(@deploy_with_roles.dup.tap { |c| config_with_assets = Kamal::Configuration.new(@deploy_with_roles.dup.tap { |c|
c[:servers]["web"] = { "hosts" => [ "1.1.1.1", "1.1.1.2" ], "asset_path" => "bar" } c[:servers]["web"] = { "hosts" => [ "1.1.1.1", "1.1.1.2" ], "asset_path" => "bar" }
}) })
assert_equal "bar", config_with_assets.role(:web).asset_path assert_equal "bar", config_with_assets.role(:web).asset_path
assert_nil config_with_assets.role(:workers).asset_path assert_nil config_with_assets.role(:workers).asset_path
assert_equal ["--volume", "$(pwd)/.kamal/assets/volumes/app-web-12345:bar"], config_with_assets.role(:web).asset_volume_args assert_equal [ "--volume", "$(pwd)/.kamal/assets/volumes/app-web-12345:bar" ], config_with_assets.role(:web).asset_volume_args
assert_nil config_with_assets.role(:workers).asset_volume_args assert_nil config_with_assets.role(:workers).asset_volume_args
assert config_with_assets.role(:web).assets? assert config_with_assets.role(:web).assets?
assert !config_with_assets.role(:workers).assets? assert_not config_with_assets.role(:workers).assets?
ensure ensure
ENV.delete("VERSION") ENV.delete("VERSION")

View File

@@ -7,7 +7,7 @@ class ConfigurationSshTest < ActiveSupport::TestCase
registry: { "username" => "dhh", "password" => "secret" }, registry: { "username" => "dhh", "password" => "secret" },
env: { "REDIS_URL" => "redis://x/y" }, env: { "REDIS_URL" => "redis://x/y" },
servers: [ "1.1.1.1", "1.1.1.2" ], servers: [ "1.1.1.1", "1.1.1.2" ],
volumes: ["/local/path:/container/path"] volumes: [ "/local/path:/container/path" ]
} }
@config = Kamal::Configuration.new(@deploy) @config = Kamal::Configuration.new(@deploy)

View File

@@ -7,7 +7,7 @@ class ConfigurationSshkitTest < ActiveSupport::TestCase
registry: { "username" => "dhh", "password" => "secret" }, registry: { "username" => "dhh", "password" => "secret" },
env: { "REDIS_URL" => "redis://x/y" }, env: { "REDIS_URL" => "redis://x/y" },
servers: [ "1.1.1.1", "1.1.1.2" ], servers: [ "1.1.1.1", "1.1.1.2" ],
volumes: ["/local/path:/container/path"] volumes: [ "/local/path:/container/path" ]
} }
@config = Kamal::Configuration.new(@deploy) @config = Kamal::Configuration.new(@deploy)

View File

@@ -3,11 +3,11 @@ require "test_helper"
class ConfigurationVolumeTest < ActiveSupport::TestCase class ConfigurationVolumeTest < ActiveSupport::TestCase
test "docker args absolute" do test "docker args absolute" do
volume = Kamal::Configuration::Volume.new(host_path: "/root/foo/bar", container_path: "/assets") volume = Kamal::Configuration::Volume.new(host_path: "/root/foo/bar", container_path: "/assets")
assert_equal ["--volume", "/root/foo/bar:/assets"], volume.docker_args assert_equal [ "--volume", "/root/foo/bar:/assets" ], volume.docker_args
end end
test "docker args relative" do test "docker args relative" do
volume = Kamal::Configuration::Volume.new(host_path: "foo/bar", container_path: "/assets") volume = Kamal::Configuration::Volume.new(host_path: "foo/bar", container_path: "/assets")
assert_equal ["--volume", "$(pwd)/foo/bar:/assets"], volume.docker_args assert_equal [ "--volume", "$(pwd)/foo/bar:/assets" ], volume.docker_args
end end
end end

View File

@@ -10,7 +10,7 @@ class ConfigurationTest < ActiveSupport::TestCase
registry: { "username" => "dhh", "password" => "secret" }, registry: { "username" => "dhh", "password" => "secret" },
env: { "REDIS_URL" => "redis://x/y" }, env: { "REDIS_URL" => "redis://x/y" },
servers: [ "1.1.1.1", "1.1.1.2" ], servers: [ "1.1.1.1", "1.1.1.2" ],
volumes: ["/local/path:/container/path"] volumes: [ "/local/path:/container/path" ]
} }
@config = Kamal::Configuration.new(@deploy) @config = Kamal::Configuration.new(@deploy)
@@ -64,7 +64,7 @@ class ConfigurationTest < ActiveSupport::TestCase
end end
test "all hosts" do test "all hosts" do
assert_equal [ "1.1.1.1", "1.1.1.2"], @config.all_hosts assert_equal [ "1.1.1.1", "1.1.1.2" ], @config.all_hosts
assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3" ], @config_with_roles.all_hosts assert_equal [ "1.1.1.1", "1.1.1.2", "1.1.1.3" ], @config_with_roles.all_hosts
end end
@@ -86,7 +86,7 @@ class ConfigurationTest < ActiveSupport::TestCase
ENV.delete("VERSION") ENV.delete("VERSION")
Kamal::Git.expects(:used?).returns(nil) Kamal::Git.expects(:used?).returns(nil)
error = assert_raises(RuntimeError) { @config.version} error = assert_raises(RuntimeError) { @config.version }
assert_match /no git repository found/, error.message assert_match /no git repository found/, error.message
end end
@@ -186,21 +186,21 @@ class ConfigurationTest < ActiveSupport::TestCase
end end
test "volume_args" do test "volume_args" do
assert_equal ["--volume", "/local/path:/container/path"], @config.volume_args assert_equal [ "--volume", "/local/path:/container/path" ], @config.volume_args
end end
test "logging args default" do test "logging args default" do
assert_equal ["--log-opt", "max-size=\"10m\""], @config.logging_args assert_equal [ "--log-opt", "max-size=\"10m\"" ], @config.logging_args
end end
test "logging args with configured options" do test "logging args with configured options" do
config = Kamal::Configuration.new(@deploy.tap { |c| c.merge!(logging: { "options" => { "max-size" => "100m", "max-file" => 5 } }) }) config = Kamal::Configuration.new(@deploy.tap { |c| c.merge!(logging: { "options" => { "max-size" => "100m", "max-file" => 5 } }) })
assert_equal ["--log-opt", "max-size=\"100m\"", "--log-opt", "max-file=\"5\""], @config.logging_args assert_equal [ "--log-opt", "max-size=\"100m\"", "--log-opt", "max-file=\"5\"" ], @config.logging_args
end end
test "logging args with configured driver and options" do test "logging args with configured driver and options" do
config = Kamal::Configuration.new(@deploy.tap { |c| c.merge!(logging: { "driver" => "local", "options" => { "max-size" => "100m", "max-file" => 5 } }) }) config = Kamal::Configuration.new(@deploy.tap { |c| c.merge!(logging: { "driver" => "local", "options" => { "max-size" => "100m", "max-file" => 5 } }) })
assert_equal ["--log-driver", "\"local\"", "--log-opt", "max-size=\"100m\"", "--log-opt", "max-file=\"5\""], @config.logging_args assert_equal [ "--log-driver", "\"local\"", "--log-opt", "max-size=\"100m\"", "--log-opt", "max-file=\"5\"" ], @config.logging_args
end end
test "erb evaluation of yml config" do test "erb evaluation of yml config" do
@@ -240,19 +240,19 @@ class ConfigurationTest < ActiveSupport::TestCase
test "to_h" do test "to_h" do
expected_config = \ expected_config = \
{ :roles=>["web"], { roles: [ "web" ],
:hosts=>["1.1.1.1", "1.1.1.2"], hosts: [ "1.1.1.1", "1.1.1.2" ],
:primary_host=>"1.1.1.1", primary_host: "1.1.1.1",
:version=>"missing", version: "missing",
:repository=>"dhh/app", repository: "dhh/app",
:absolute_image=>"dhh/app:missing", absolute_image: "dhh/app:missing",
:service_with_version=>"app-missing", service_with_version: "app-missing",
:ssh_options=>{ :user=>"root", port: 22, log_level: :fatal, keepalive: true, keepalive_interval: 30 }, ssh_options: { user: "root", port: 22, log_level: :fatal, keepalive: true, keepalive_interval: 30 },
:sshkit=>{}, sshkit: {},
:volume_args=>["--volume", "/local/path:/container/path"], volume_args: [ "--volume", "/local/path:/container/path" ],
:builder=>{}, builder: {},
:logging=>["--log-opt", "max-size=\"10m\""], logging: [ "--log-opt", "max-size=\"10m\"" ],
:healthcheck=>{ "path"=>"/up", "port"=>3000, "max_attempts" => 7, "exposed_port" => 3999, "cord" => "/tmp/kamal-cord", "log_lines" => 50 }} healthcheck: { "path"=>"/up", "port"=>3000, "max_attempts" => 7, "exposed_port" => 3999, "cord" => "/tmp/kamal-cord", "log_lines" => 50 } }
assert_equal expected_config, @config.to_h assert_equal expected_config, @config.to_h
end end
@@ -304,7 +304,7 @@ class ConfigurationTest < ActiveSupport::TestCase
config = Kamal::Configuration.new(@deploy_with_roles.deep_merge({ config = Kamal::Configuration.new(@deploy_with_roles.deep_merge({
servers: { "alternate_web" => { "hosts" => [ "1.1.1.4", "1.1.1.5" ] } }, servers: { "alternate_web" => { "hosts" => [ "1.1.1.4", "1.1.1.5" ] } },
primary_role: "alternate_web" } )) primary_role: "alternate_web" }))
assert_equal "alternate_web", config.primary_role.name assert_equal "alternate_web", config.primary_role.name

View File

@@ -31,7 +31,7 @@ class AccessoryTest < IntegrationTest
end end
def assert_accessory_not_running(name) def assert_accessory_not_running(name)
refute_match /registry:4443\/busybox:1.36.0 "sh -c 'echo \\"Start/, accessory_details(name) assert_no_match /registry:4443\/busybox:1.36.0 "sh -c 'echo \\"Start/, accessory_details(name)
end end
def accessory_details(name) def accessory_details(name)

View File

@@ -11,7 +11,7 @@ class IntegrationTest < ActiveSupport::TestCase
teardown do teardown do
unless passed? unless passed?
[:deployer, :vm1, :vm2, :shared, :load_balancer, :registry].each do |container| [ :deployer, :vm1, :vm2, :shared, :load_balancer, :registry ].each do |container|
puts puts
puts "Logs for #{container}:" puts "Logs for #{container}:"
docker_compose :logs, container docker_compose :logs, container

View File

@@ -56,7 +56,7 @@ class TraefikTest < IntegrationTest
end end
def assert_traefik_not_running def assert_traefik_not_running
refute_match /traefik:v2.10 "\/entrypoint.sh/, traefik_details assert_no_match /traefik:v2.10 "\/entrypoint.sh/, traefik_details
end end
def traefik_details def traefik_details