kamal 2.5.3 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kamal/cli/accessory.rb +16 -5
  3. data/lib/kamal/cli/app/{prepare_assets.rb → assets.rb} +1 -1
  4. data/lib/kamal/cli/app/boot.rb +1 -0
  5. data/lib/kamal/cli/app/error_pages.rb +33 -0
  6. data/lib/kamal/cli/app.rb +66 -22
  7. data/lib/kamal/cli/base.rb +13 -3
  8. data/lib/kamal/cli/build.rb +20 -4
  9. data/lib/kamal/cli/main.rb +4 -7
  10. data/lib/kamal/cli/proxy.rb +57 -10
  11. data/lib/kamal/cli/server.rb +4 -2
  12. data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +1 -1
  13. data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +1 -1
  14. data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +1 -1
  15. data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +6 -5
  16. data/lib/kamal/commander/specifics.rb +4 -0
  17. data/lib/kamal/commander.rb +2 -2
  18. data/lib/kamal/commands/accessory/proxy.rb +1 -1
  19. data/lib/kamal/commands/accessory.rb +2 -3
  20. data/lib/kamal/commands/app/error_pages.rb +9 -0
  21. data/lib/kamal/commands/app/proxy.rb +13 -1
  22. data/lib/kamal/commands/app.rb +1 -1
  23. data/lib/kamal/commands/auditor.rb +11 -5
  24. data/lib/kamal/commands/base.rb +4 -0
  25. data/lib/kamal/commands/builder/base.rb +2 -1
  26. data/lib/kamal/commands/proxy.rb +55 -15
  27. data/lib/kamal/configuration/accessory.rb +24 -2
  28. data/lib/kamal/configuration/docs/accessory.yml +6 -1
  29. data/lib/kamal/configuration/docs/configuration.yml +6 -0
  30. data/lib/kamal/configuration/docs/env.yml +31 -0
  31. data/lib/kamal/configuration/docs/proxy.yml +7 -0
  32. data/lib/kamal/configuration/env.rb +13 -4
  33. data/lib/kamal/configuration/proxy/boot.rb +121 -0
  34. data/lib/kamal/configuration/proxy.rb +18 -1
  35. data/lib/kamal/configuration/servers.rb +8 -1
  36. data/lib/kamal/configuration/validator/accessory.rb +4 -2
  37. data/lib/kamal/configuration/validator/role.rb +1 -0
  38. data/lib/kamal/configuration/validator/servers.rb +1 -1
  39. data/lib/kamal/configuration/validator.rb +6 -0
  40. data/lib/kamal/configuration.rb +36 -74
  41. data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +1 -0
  42. data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +2 -1
  43. data/lib/kamal/version.rb +1 -1
  44. metadata +9 -10
@@ -10,15 +10,10 @@ class Kamal::Configuration
10
10
  delegate :argumentize, :optionize, to: Kamal::Utils
11
11
 
12
12
  attr_reader :destination, :raw_config, :secrets
13
- attr_reader :accessories, :aliases, :boot, :builder, :env, :logging, :proxy, :servers, :ssh, :sshkit, :registry
13
+ attr_reader :accessories, :aliases, :boot, :builder, :env, :logging, :proxy, :proxy_boot, :servers, :ssh, :sshkit, :registry
14
14
 
15
15
  include Validation
16
16
 
17
- PROXY_MINIMUM_VERSION = "v0.8.4"
18
- PROXY_HTTP_PORT = 80
19
- PROXY_HTTPS_PORT = 443
20
- PROXY_LOG_MAX_SIZE = "10m"
21
-
22
17
  class << self
23
18
  def create_from(config_file:, destination: nil, version: nil)
24
19
  ENV["KAMAL_DESTINATION"] = destination
@@ -68,7 +63,8 @@ class Kamal::Configuration
68
63
  @env = Env.new(config: @raw_config.env || {}, secrets: secrets)
69
64
 
70
65
  @logging = Logging.new(logging_config: @raw_config.logging)
71
- @proxy = Proxy.new(config: self, proxy_config: @raw_config.proxy || {})
66
+ @proxy = Proxy.new(config: self, proxy_config: @raw_config.key?(:proxy) ? @raw_config.proxy : {})
67
+ @proxy_boot = Proxy::Boot.new(config: self)
72
68
  @ssh = Ssh.new(config: self)
73
69
  @sshkit = Sshkit.new(config: self)
74
70
 
@@ -105,6 +101,10 @@ class Kamal::Configuration
105
101
  raw_config.minimum_version
106
102
  end
107
103
 
104
+ def service_and_destination
105
+ [ service, destination ].compact.join("-")
106
+ end
107
+
108
108
  def roles
109
109
  servers.roles
110
110
  end
@@ -121,6 +121,10 @@ class Kamal::Configuration
121
121
  (roles + accessories).flat_map(&:hosts).uniq
122
122
  end
123
123
 
124
+ def app_hosts
125
+ roles.flat_map(&:hosts).uniq
126
+ end
127
+
124
128
  def primary_host
125
129
  primary_role&.primary_host
126
130
  end
@@ -145,8 +149,12 @@ class Kamal::Configuration
145
149
  proxy_roles.flat_map(&:name)
146
150
  end
147
151
 
152
+ def proxy_accessories
153
+ accessories.select(&:running_proxy?)
154
+ end
155
+
148
156
  def proxy_hosts
149
- proxy_roles.flat_map(&:hosts).uniq
157
+ (proxy_roles.flat_map(&:hosts) + proxy_accessories.flat_map(&:hosts)).uniq
150
158
  end
151
159
 
152
160
  def repository
@@ -210,7 +218,7 @@ class Kamal::Configuration
210
218
  end
211
219
 
212
220
  def app_directory
213
- File.join apps_directory, [ service, destination ].compact.join("-")
221
+ File.join apps_directory, service_and_destination
214
222
  end
215
223
 
216
224
  def env_directory
@@ -229,6 +237,10 @@ class Kamal::Configuration
229
237
  raw_config.asset_path
230
238
  end
231
239
 
240
+ def error_pages_path
241
+ raw_config.error_pages_path
242
+ end
243
+
232
244
  def env_tags
233
245
  @env_tags ||= if (tags = raw_config.env["tags"])
234
246
  tags.collect { |name, config| Env::Tag.new(name, config: config, secrets: secrets) }
@@ -241,42 +253,6 @@ class Kamal::Configuration
241
253
  env_tags.detect { |t| t.name == name.to_s }
242
254
  end
243
255
 
244
- def proxy_publish_args(http_port, https_port, bind_ips = nil)
245
- ensure_valid_bind_ips(bind_ips)
246
-
247
- (bind_ips || [ nil ]).map do |bind_ip|
248
- bind_ip = format_bind_ip(bind_ip)
249
- publish_http = [ bind_ip, http_port, PROXY_HTTP_PORT ].compact.join(":")
250
- publish_https = [ bind_ip, https_port, PROXY_HTTPS_PORT ].compact.join(":")
251
-
252
- argumentize "--publish", [ publish_http, publish_https ]
253
- end.join(" ")
254
- end
255
-
256
- def proxy_logging_args(max_size)
257
- argumentize "--log-opt", "max-size=#{max_size}" if max_size.present?
258
- end
259
-
260
- def proxy_options_default
261
- [ *proxy_publish_args(PROXY_HTTP_PORT, PROXY_HTTPS_PORT), *proxy_logging_args(PROXY_LOG_MAX_SIZE) ]
262
- end
263
-
264
- def proxy_image
265
- "basecamp/kamal-proxy:#{PROXY_MINIMUM_VERSION}"
266
- end
267
-
268
- def proxy_container_name
269
- "kamal-proxy"
270
- end
271
-
272
- def proxy_directory
273
- File.join run_directory, "proxy"
274
- end
275
-
276
- def proxy_options_file
277
- File.join proxy_directory, "options"
278
- end
279
-
280
256
  def to_h
281
257
  {
282
258
  roles: role_names,
@@ -306,22 +282,26 @@ class Kamal::Configuration
306
282
  end
307
283
 
308
284
  def ensure_required_keys_present
309
- %i[ service image registry servers ].each do |key|
285
+ %i[ service image registry ].each do |key|
310
286
  raise Kamal::ConfigurationError, "Missing required configuration for #{key}" unless raw_config[key].present?
311
287
  end
312
288
 
313
- unless role(primary_role_name).present?
314
- raise Kamal::ConfigurationError, "The primary_role #{primary_role_name} isn't defined"
315
- end
289
+ if raw_config.servers.nil?
290
+ raise Kamal::ConfigurationError, "No servers or accessories specified" unless raw_config.accessories.present?
291
+ else
292
+ unless role(primary_role_name).present?
293
+ raise Kamal::ConfigurationError, "The primary_role #{primary_role_name} isn't defined"
294
+ end
316
295
 
317
- if primary_role.hosts.empty?
318
- raise Kamal::ConfigurationError, "No servers specified for the #{primary_role.name} primary_role"
319
- end
296
+ if primary_role.hosts.empty?
297
+ raise Kamal::ConfigurationError, "No servers specified for the #{primary_role.name} primary_role"
298
+ end
320
299
 
321
- unless allow_empty_roles?
322
- roles.each do |role|
323
- if role.hosts.empty?
324
- raise Kamal::ConfigurationError, "No servers specified for the #{role.name} role. You can ignore this with allow_empty_roles: true"
300
+ unless allow_empty_roles?
301
+ roles.each do |role|
302
+ if role.hosts.empty?
303
+ raise Kamal::ConfigurationError, "No servers specified for the #{role.name} role. You can ignore this with allow_empty_roles: true"
304
+ end
325
305
  end
326
306
  end
327
307
  end
@@ -343,15 +323,6 @@ class Kamal::Configuration
343
323
  true
344
324
  end
345
325
 
346
- def ensure_valid_bind_ips(bind_ips)
347
- bind_ips.present? && bind_ips.each do |ip|
348
- next if ip =~ Resolv::IPv4::Regex || ip =~ Resolv::IPv6::Regex
349
- raise ArgumentError, "Invalid publish IP address: #{ip}"
350
- end
351
-
352
- true
353
- end
354
-
355
326
  def ensure_retain_containers_valid
356
327
  raise Kamal::ConfigurationError, "Must retain at least 1 container" if retain_containers < 1
357
328
 
@@ -383,15 +354,6 @@ class Kamal::Configuration
383
354
  true
384
355
  end
385
356
 
386
- def format_bind_ip(ip)
387
- # Ensure IPv6 address inside square brackets - e.g. [::1]
388
- if ip =~ Resolv::IPv6::Regex && ip !~ /\[.*\]/
389
- "[#{ip}]"
390
- else
391
- ip
392
- end
393
- end
394
-
395
357
  def role_names
396
358
  raw_config.servers.is_a?(Array) ? [ "web" ] : raw_config.servers.keys.sort
397
359
  end
@@ -26,6 +26,7 @@ class Kamal::Secrets::Adapters::AwsSecretsManager < Kamal::Secrets::Adapters::Ba
26
26
  def get_from_secrets_manager(secrets, account: nil)
27
27
  args = [ "aws", "secretsmanager", "batch-get-secret-value", "--secret-id-list" ] + secrets.map(&:shellescape)
28
28
  args += [ "--profile", account.shellescape ] if account
29
+ args += [ "--output", "json" ]
29
30
  cmd = args.join(" ")
30
31
 
31
32
  `#{cmd}`.tap do |secrets|
@@ -4,7 +4,7 @@ class Kamal::Secrets::Dotenv::InlineCommandSubstitution
4
4
  ::Dotenv::Parser.substitutions.map! { |sub| sub == ::Dotenv::Substitutions::Command ? self : sub }
5
5
  end
6
6
 
7
- def call(value, _env, overwrite: false)
7
+ def call(value, env, overwrite: false)
8
8
  # Process interpolated shell commands
9
9
  value.gsub(Dotenv::Substitutions::Command.singleton_class::INTERPOLATED_SHELL_COMMAND) do |*|
10
10
  # Eliminate opening and closing parentheses
@@ -14,6 +14,7 @@ class Kamal::Secrets::Dotenv::InlineCommandSubstitution
14
14
  # Command is escaped, don't replace it.
15
15
  $LAST_MATCH_INFO[0][1..]
16
16
  else
17
+ command = ::Dotenv::Substitutions::Variable.call(command, env)
17
18
  if command =~ /\A\s*kamal\s*secrets\s+/
18
19
  # Inline the command
19
20
  inline_secrets_command(command)
data/lib/kamal/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kamal
2
- VERSION = "2.5.3"
2
+ VERSION = "2.6.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kamal
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.3
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-02-27 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -112,14 +111,14 @@ dependencies:
112
111
  requirements:
113
112
  - - "~>"
114
113
  - !ruby/object:Gem::Version
115
- version: '1.2'
114
+ version: '1.4'
116
115
  type: :runtime
117
116
  prerelease: false
118
117
  version_requirements: !ruby/object:Gem::Requirement
119
118
  requirements:
120
119
  - - "~>"
121
120
  - !ruby/object:Gem::Version
122
- version: '1.2'
121
+ version: '1.4'
123
122
  - !ruby/object:Gem::Dependency
124
123
  name: bcrypt_pbkdf
125
124
  requirement: !ruby/object:Gem::Requirement
@@ -204,7 +203,6 @@ dependencies:
204
203
  - - ">="
205
204
  - !ruby/object:Gem::Version
206
205
  version: '0'
207
- description:
208
206
  email: dhh@hey.com
209
207
  executables:
210
208
  - kamal
@@ -219,8 +217,9 @@ files:
219
217
  - lib/kamal/cli/accessory.rb
220
218
  - lib/kamal/cli/alias/command.rb
221
219
  - lib/kamal/cli/app.rb
220
+ - lib/kamal/cli/app/assets.rb
222
221
  - lib/kamal/cli/app/boot.rb
223
- - lib/kamal/cli/app/prepare_assets.rb
222
+ - lib/kamal/cli/app/error_pages.rb
224
223
  - lib/kamal/cli/base.rb
225
224
  - lib/kamal/cli/build.rb
226
225
  - lib/kamal/cli/build/clone.rb
@@ -253,6 +252,7 @@ files:
253
252
  - lib/kamal/commands/app.rb
254
253
  - lib/kamal/commands/app/assets.rb
255
254
  - lib/kamal/commands/app/containers.rb
255
+ - lib/kamal/commands/app/error_pages.rb
256
256
  - lib/kamal/commands/app/execution.rb
257
257
  - lib/kamal/commands/app/images.rb
258
258
  - lib/kamal/commands/app/logging.rb
@@ -295,6 +295,7 @@ files:
295
295
  - lib/kamal/configuration/env/tag.rb
296
296
  - lib/kamal/configuration/logging.rb
297
297
  - lib/kamal/configuration/proxy.rb
298
+ - lib/kamal/configuration/proxy/boot.rb
298
299
  - lib/kamal/configuration/registry.rb
299
300
  - lib/kamal/configuration/role.rb
300
301
  - lib/kamal/configuration/servers.rb
@@ -337,7 +338,6 @@ homepage: https://github.com/basecamp/kamal
337
338
  licenses:
338
339
  - MIT
339
340
  metadata: {}
340
- post_install_message:
341
341
  rdoc_options: []
342
342
  require_paths:
343
343
  - lib
@@ -352,8 +352,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
352
352
  - !ruby/object:Gem::Version
353
353
  version: '0'
354
354
  requirements: []
355
- rubygems_version: 3.5.22
356
- signing_key:
355
+ rubygems_version: 3.6.7
357
356
  specification_version: 4
358
357
  summary: Deploy web apps in containers to servers running Docker with zero downtime.
359
358
  test_files: []