kamal 2.3.0 → 2.5.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kamal/cli/accessory.rb +24 -9
  3. data/lib/kamal/cli/alias/command.rb +1 -0
  4. data/lib/kamal/cli/app/boot.rb +2 -2
  5. data/lib/kamal/cli/app.rb +28 -8
  6. data/lib/kamal/cli/base.rb +16 -1
  7. data/lib/kamal/cli/build.rb +36 -14
  8. data/lib/kamal/cli/main.rb +4 -3
  9. data/lib/kamal/cli/proxy.rb +2 -4
  10. data/lib/kamal/cli/registry.rb +2 -0
  11. data/lib/kamal/cli/secrets.rb +9 -3
  12. data/lib/kamal/cli/templates/deploy.yml +6 -3
  13. data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +3 -0
  14. data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
  15. data/lib/kamal/cli.rb +1 -0
  16. data/lib/kamal/commander.rb +16 -25
  17. data/lib/kamal/commands/accessory/proxy.rb +16 -0
  18. data/lib/kamal/commands/accessory.rb +4 -4
  19. data/lib/kamal/commands/app/assets.rb +4 -4
  20. data/lib/kamal/commands/app/containers.rb +2 -2
  21. data/lib/kamal/commands/app/execution.rb +4 -2
  22. data/lib/kamal/commands/app/images.rb +1 -1
  23. data/lib/kamal/commands/app/logging.rb +14 -4
  24. data/lib/kamal/commands/app.rb +15 -7
  25. data/lib/kamal/commands/base.rb +25 -1
  26. data/lib/kamal/commands/builder/base.rb +17 -6
  27. data/lib/kamal/commands/builder/cloud.rb +22 -0
  28. data/lib/kamal/commands/builder.rb +6 -20
  29. data/lib/kamal/commands/registry.rb +9 -7
  30. data/lib/kamal/configuration/accessory.rb +41 -9
  31. data/lib/kamal/configuration/builder.rb +8 -0
  32. data/lib/kamal/configuration/docs/accessory.yml +26 -3
  33. data/lib/kamal/configuration/docs/alias.yml +2 -2
  34. data/lib/kamal/configuration/docs/builder.yml +9 -0
  35. data/lib/kamal/configuration/docs/proxy.yml +13 -10
  36. data/lib/kamal/configuration/docs/registry.yml +4 -0
  37. data/lib/kamal/configuration/registry.rb +6 -6
  38. data/lib/kamal/configuration/role.rb +6 -6
  39. data/lib/kamal/configuration/validator/role.rb +1 -1
  40. data/lib/kamal/configuration.rb +31 -14
  41. data/lib/kamal/docker.rb +30 -0
  42. data/lib/kamal/git.rb +10 -0
  43. data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +50 -0
  44. data/lib/kamal/secrets/adapters/base.rb +13 -3
  45. data/lib/kamal/secrets/adapters/bitwarden.rb +2 -2
  46. data/lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb +72 -0
  47. data/lib/kamal/secrets/adapters/doppler.rb +57 -0
  48. data/lib/kamal/secrets/adapters/enpass.rb +71 -0
  49. data/lib/kamal/secrets/adapters/gcp_secret_manager.rb +112 -0
  50. data/lib/kamal/secrets/adapters/last_pass.rb +3 -2
  51. data/lib/kamal/secrets/adapters/one_password.rb +2 -2
  52. data/lib/kamal/secrets/adapters/test.rb +2 -2
  53. data/lib/kamal/secrets/adapters.rb +2 -0
  54. data/lib/kamal/secrets.rb +1 -1
  55. data/lib/kamal/version.rb +1 -1
  56. metadata +13 -3
@@ -4,10 +4,10 @@ module Kamal::Commands::App::Assets
4
4
 
5
5
  combine \
6
6
  make_directory(role.asset_extracted_directory),
7
- [ *docker(:stop, "-t 1", asset_container, "2> /dev/null"), "|| true" ],
8
- docker(:run, "--name", asset_container, "--detach", "--rm", "--entrypoint", "sleep", config.absolute_image, "1000000"),
9
- docker(:cp, "-L", "#{asset_container}:#{role.asset_path}/.", role.asset_extracted_directory),
10
- docker(:stop, "-t 1", asset_container),
7
+ [ *docker(:container, :rm, asset_container, "2> /dev/null"), "|| true" ],
8
+ docker(:container, :create, "--name", asset_container, config.absolute_image),
9
+ docker(:container, :cp, "-L", "#{asset_container}:#{role.asset_path}/.", role.asset_extracted_directory),
10
+ docker(:container, :rm, asset_container),
11
11
  by: "&&"
12
12
  end
13
13
 
@@ -2,7 +2,7 @@ module Kamal::Commands::App::Containers
2
2
  DOCKER_HEALTH_LOG_FORMAT = "'{{json .State.Health}}'"
3
3
 
4
4
  def list_containers
5
- docker :container, :ls, "--all", *filter_args
5
+ docker :container, :ls, "--all", *container_filter_args
6
6
  end
7
7
 
8
8
  def list_container_names
@@ -20,7 +20,7 @@ module Kamal::Commands::App::Containers
20
20
  end
21
21
 
22
22
  def remove_containers
23
- docker :container, :prune, "--force", *filter_args
23
+ docker :container, :prune, "--force", *container_filter_args
24
24
  end
25
25
 
26
26
  def container_health_log(version:)
@@ -7,13 +7,15 @@ module Kamal::Commands::App::Execution
7
7
  *command
8
8
  end
9
9
 
10
- def execute_in_new_container(*command, interactive: false, env:)
10
+ def execute_in_new_container(*command, interactive: false, detach: false, env:)
11
11
  docker :run,
12
12
  ("-it" if interactive),
13
- "--rm",
13
+ ("--detach" if detach),
14
+ ("--rm" unless detach),
14
15
  "--network", "kamal",
15
16
  *role&.env_args(host),
16
17
  *argumentize("--env", env),
18
+ *role.logging_args,
17
19
  *config.volume_args,
18
20
  *role&.option_args,
19
21
  config.absolute_image,
@@ -4,7 +4,7 @@ module Kamal::Commands::App::Images
4
4
  end
5
5
 
6
6
  def remove_images
7
- docker :image, :prune, "--all", "--force", *filter_args
7
+ docker :image, :prune, "--all", "--force", *image_filter_args
8
8
  end
9
9
 
10
10
  def tag_latest_image
@@ -1,18 +1,28 @@
1
1
  module Kamal::Commands::App::Logging
2
- def logs(version: nil, timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil)
2
+ def logs(container_id: nil, timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil)
3
3
  pipe \
4
- version ? container_id_for_version(version) : current_running_container_id,
4
+ container_id_command(container_id),
5
5
  "xargs docker logs#{" --timestamps" if timestamps}#{" --since #{since}" if since}#{" --tail #{lines}" if lines} 2>&1",
6
6
  ("grep '#{grep}'#{" #{grep_options}" if grep_options}" if grep)
7
7
  end
8
8
 
9
- def follow_logs(host:, timestamps: true, lines: nil, grep: nil, grep_options: nil)
9
+ def follow_logs(host:, container_id: nil, timestamps: true, lines: nil, grep: nil, grep_options: nil)
10
10
  run_over_ssh \
11
11
  pipe(
12
- current_running_container_id,
12
+ container_id_command(container_id),
13
13
  "xargs docker logs#{" --timestamps" if timestamps}#{" --tail #{lines}" if lines} --follow 2>&1",
14
14
  (%(grep "#{grep}"#{" #{grep_options}" if grep_options}) if grep)
15
15
  ),
16
16
  host: host
17
17
  end
18
+
19
+ private
20
+
21
+ def container_id_command(container_id)
22
+ case container_id
23
+ when Array then container_id
24
+ when String, Symbol then "echo #{container_id}"
25
+ else current_running_container_id
26
+ end
27
+ end
18
28
  end
@@ -47,7 +47,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
47
47
  end
48
48
 
49
49
  def info
50
- docker :ps, *filter_args
50
+ docker :ps, *container_filter_args
51
51
  end
52
52
 
53
53
 
@@ -67,7 +67,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
67
67
 
68
68
  def list_versions(*docker_args, statuses: nil)
69
69
  pipe \
70
- docker(:ps, *filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
70
+ docker(:ps, *container_filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
71
71
  extract_version_from_name
72
72
  end
73
73
 
@@ -91,11 +91,15 @@ class Kamal::Commands::App < Kamal::Commands::Base
91
91
  end
92
92
 
93
93
  def latest_container(format:, filters: nil)
94
- docker :ps, "--latest", *format, *filter_args(statuses: ACTIVE_DOCKER_STATUSES), argumentize("--filter", filters)
94
+ docker :ps, "--latest", *format, *container_filter_args(statuses: ACTIVE_DOCKER_STATUSES), argumentize("--filter", filters)
95
95
  end
96
96
 
97
- def filter_args(statuses: nil)
98
- argumentize "--filter", filters(statuses: statuses)
97
+ def container_filter_args(statuses: nil)
98
+ argumentize "--filter", container_filters(statuses: statuses)
99
+ end
100
+
101
+ def image_filter_args
102
+ argumentize "--filter", image_filters
99
103
  end
100
104
 
101
105
  def extract_version_from_name
@@ -103,13 +107,17 @@ class Kamal::Commands::App < Kamal::Commands::Base
103
107
  %(while read line; do echo ${line##{role.container_prefix}-}; done)
104
108
  end
105
109
 
106
- def filters(statuses: nil)
110
+ def container_filters(statuses: nil)
107
111
  [ "label=service=#{config.service}" ].tap do |filters|
108
- filters << "label=destination=#{config.destination}" if config.destination
112
+ filters << "label=destination=#{config.destination}"
109
113
  filters << "label=role=#{role}" if role
110
114
  statuses&.each do |status|
111
115
  filters << "status=#{status}"
112
116
  end
113
117
  end
114
118
  end
119
+
120
+ def image_filters
121
+ [ "label=service=#{config.service}" ]
122
+ end
115
123
  end
@@ -11,7 +11,7 @@ module Kamal::Commands
11
11
  end
12
12
 
13
13
  def run_over_ssh(*command, host:)
14
- "ssh#{ssh_proxy_args} -t #{config.ssh.user}@#{host} -p #{config.ssh.port} '#{command.join(" ").gsub("'", "'\\\\''")}'"
14
+ "ssh#{ssh_proxy_args}#{ssh_keys_args} -t #{config.ssh.user}@#{host} -p #{config.ssh.port} '#{command.join(" ").gsub("'", "'\\\\''")}'"
15
15
  end
16
16
 
17
17
  def container_id_for(container_name:, only_running: false)
@@ -34,6 +34,12 @@ module Kamal::Commands
34
34
  [ :rm, path ]
35
35
  end
36
36
 
37
+ def ensure_docker_installed
38
+ combine \
39
+ ensure_local_docker_installed,
40
+ ensure_local_buildx_installed
41
+ end
42
+
37
43
  private
38
44
  def combine(*commands, by: "&&")
39
45
  commands
@@ -94,5 +100,23 @@ module Kamal::Commands
94
100
  " -o ProxyCommand='#{config.ssh.proxy.command_line_template}'"
95
101
  end
96
102
  end
103
+
104
+ def ssh_keys_args
105
+ "#{ ssh_keys.join("") if ssh_keys}" + "#{" -o IdentitiesOnly=yes" if config.ssh&.keys_only}"
106
+ end
107
+
108
+ def ssh_keys
109
+ config.ssh.keys&.map do |key|
110
+ " -i #{key}"
111
+ end
112
+ end
113
+
114
+ def ensure_local_docker_installed
115
+ docker "--version"
116
+ end
117
+
118
+ def ensure_local_buildx_installed
119
+ docker :buildx, "version"
120
+ end
97
121
  end
98
122
  end
@@ -6,18 +6,19 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
6
6
  delegate :argumentize, to: Kamal::Utils
7
7
  delegate \
8
8
  :args, :secrets, :dockerfile, :target, :arches, :local_arches, :remote_arches, :remote,
9
- :cache_from, :cache_to, :ssh, :provenance, :driver, :docker_driver?,
9
+ :cache_from, :cache_to, :ssh, :provenance, :sbom, :driver, :docker_driver?,
10
10
  to: :builder_config
11
11
 
12
12
  def clean
13
13
  docker :image, :rm, "--force", config.absolute_image
14
14
  end
15
15
 
16
- def push
16
+ def push(export_action = "registry", tag_as_dirty: false)
17
17
  docker :buildx, :build,
18
- "--push",
18
+ "--output=type=#{export_action}",
19
19
  *platform_options(arches),
20
20
  *([ "--builder", builder_name ] unless docker_driver?),
21
+ *build_tag_options(tag_as_dirty: tag_as_dirty),
21
22
  *build_options,
22
23
  build_context
23
24
  end
@@ -37,7 +38,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
37
38
  end
38
39
 
39
40
  def build_options
40
- [ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_target, *build_ssh, *builder_provenance ]
41
+ [ *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_target, *build_ssh, *builder_provenance, *builder_sbom ]
41
42
  end
42
43
 
43
44
  def build_context
@@ -58,8 +59,14 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
58
59
  end
59
60
 
60
61
  private
61
- def build_tags
62
- [ "-t", config.absolute_image, "-t", config.latest_image ]
62
+ def build_tag_names(tag_as_dirty: false)
63
+ tag_names = [ config.absolute_image, config.latest_image ]
64
+ tag_names.map! { |t| "#{t}-dirty" } if tag_as_dirty
65
+ tag_names
66
+ end
67
+
68
+ def build_tag_options(tag_as_dirty: false)
69
+ build_tag_names(tag_as_dirty: tag_as_dirty).flat_map { |name| [ "-t", name ] }
63
70
  end
64
71
 
65
72
  def build_cache
@@ -101,6 +108,10 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
101
108
  argumentize "--provenance", provenance unless provenance.nil?
102
109
  end
103
110
 
111
+ def builder_sbom
112
+ argumentize "--sbom", sbom unless sbom.nil?
113
+ end
114
+
104
115
  def builder_config
105
116
  config.builder
106
117
  end
@@ -0,0 +1,22 @@
1
+ class Kamal::Commands::Builder::Cloud < Kamal::Commands::Builder::Base
2
+ # Expects `driver` to be of format "cloud docker-org-name/builder-name"
3
+
4
+ def create
5
+ docker :buildx, :create, "--driver", driver
6
+ end
7
+
8
+ def remove
9
+ docker :buildx, :rm, builder_name
10
+ end
11
+
12
+ private
13
+ def builder_name
14
+ driver.gsub(/[ \/]/, "-")
15
+ end
16
+
17
+ def inspect_buildx
18
+ pipe \
19
+ docker(:buildx, :inspect, builder_name),
20
+ grep("-q", "Endpoint:.*cloud://.*")
21
+ end
22
+ end
@@ -1,8 +1,8 @@
1
1
  require "active_support/core_ext/string/filters"
2
2
 
3
3
  class Kamal::Commands::Builder < Kamal::Commands::Base
4
- delegate :create, :remove, :push, :clean, :pull, :info, :inspect_builder, :validate_image, :first_mirror, to: :target
5
- delegate :local?, :remote?, to: "config.builder"
4
+ delegate :create, :remove, :dev, :push, :clean, :pull, :info, :inspect_builder, :validate_image, :first_mirror, to: :target
5
+ delegate :local?, :remote?, :cloud?, to: "config.builder"
6
6
 
7
7
  include Clone
8
8
 
@@ -17,6 +17,8 @@ class Kamal::Commands::Builder < Kamal::Commands::Base
17
17
  else
18
18
  remote
19
19
  end
20
+ elsif cloud?
21
+ cloud
20
22
  else
21
23
  local
22
24
  end
@@ -34,23 +36,7 @@ class Kamal::Commands::Builder < Kamal::Commands::Base
34
36
  @hybrid ||= Kamal::Commands::Builder::Hybrid.new(config)
35
37
  end
36
38
 
37
-
38
- def ensure_local_dependencies_installed
39
- if name.native?
40
- ensure_local_docker_installed
41
- else
42
- combine \
43
- ensure_local_docker_installed,
44
- ensure_local_buildx_installed
45
- end
39
+ def cloud
40
+ @cloud ||= Kamal::Commands::Builder::Cloud.new(config)
46
41
  end
47
-
48
- private
49
- def ensure_local_docker_installed
50
- docker "--version"
51
- end
52
-
53
- def ensure_local_buildx_installed
54
- docker :buildx, "version"
55
- end
56
42
  end
@@ -1,14 +1,16 @@
1
1
  class Kamal::Commands::Registry < Kamal::Commands::Base
2
- delegate :registry, to: :config
2
+ def login(registry_config: nil)
3
+ registry_config ||= config.registry
3
4
 
4
- def login
5
5
  docker :login,
6
- registry.server,
7
- "-u", sensitive(Kamal::Utils.escape_shell_value(registry.username)),
8
- "-p", sensitive(Kamal::Utils.escape_shell_value(registry.password))
6
+ registry_config.server,
7
+ "-u", sensitive(Kamal::Utils.escape_shell_value(registry_config.username)),
8
+ "-p", sensitive(Kamal::Utils.escape_shell_value(registry_config.password))
9
9
  end
10
10
 
11
- def logout
12
- docker :logout, registry.server
11
+ def logout(registry_config: nil)
12
+ registry_config ||= config.registry
13
+
14
+ docker :logout, registry_config.server
13
15
  end
14
16
  end
@@ -5,7 +5,7 @@ class Kamal::Configuration::Accessory
5
5
 
6
6
  delegate :argumentize, :optionize, to: Kamal::Utils
7
7
 
8
- attr_reader :name, :accessory_config, :env
8
+ attr_reader :name, :env, :proxy, :registry
9
9
 
10
10
  def initialize(name, config:)
11
11
  @name, @config, @accessory_config = name.inquiry, config, config.raw_config["accessories"][name]
@@ -16,10 +16,11 @@ class Kamal::Configuration::Accessory
16
16
  context: "accessories/#{name}",
17
17
  with: Kamal::Configuration::Validator::Accessory
18
18
 
19
- @env = Kamal::Configuration::Env.new \
20
- config: accessory_config.fetch("env", {}),
21
- secrets: config.secrets,
22
- context: "accessories/#{name}/env"
19
+ ensure_valid_roles
20
+
21
+ @env = initialize_env
22
+ @proxy = initialize_proxy if running_proxy?
23
+ @registry = initialize_registry if accessory_config["registry"].present?
23
24
  end
24
25
 
25
26
  def service_name
@@ -27,7 +28,7 @@ class Kamal::Configuration::Accessory
27
28
  end
28
29
 
29
30
  def image
30
- accessory_config["image"]
31
+ [ registry&.server, accessory_config["image"] ].compact.join("/")
31
32
  end
32
33
 
33
34
  def hosts
@@ -106,8 +107,33 @@ class Kamal::Configuration::Accessory
106
107
  accessory_config["cmd"]
107
108
  end
108
109
 
110
+ def running_proxy?
111
+ accessory_config["proxy"].present?
112
+ end
113
+
109
114
  private
110
- attr_accessor :config
115
+ attr_reader :config, :accessory_config
116
+
117
+ def initialize_env
118
+ Kamal::Configuration::Env.new \
119
+ config: accessory_config.fetch("env", {}),
120
+ secrets: config.secrets,
121
+ context: "accessories/#{name}/env"
122
+ end
123
+
124
+ def initialize_proxy
125
+ Kamal::Configuration::Proxy.new \
126
+ config: config,
127
+ proxy_config: accessory_config["proxy"],
128
+ context: "accessories/#{name}/proxy"
129
+ end
130
+
131
+ def initialize_registry
132
+ Kamal::Configuration::Registry.new \
133
+ config: accessory_config,
134
+ secrets: config.secrets,
135
+ context: "accessories/#{name}/registry"
136
+ end
111
137
 
112
138
  def default_labels
113
139
  { "service" => service_name }
@@ -129,7 +155,7 @@ class Kamal::Configuration::Accessory
129
155
  end
130
156
 
131
157
  def read_dynamic_file(local_file)
132
- StringIO.new(ERB.new(IO.read(local_file)).result)
158
+ StringIO.new(ERB.new(File.read(local_file)).result)
133
159
  end
134
160
 
135
161
  def expand_remote_file(remote_file)
@@ -176,11 +202,17 @@ class Kamal::Configuration::Accessory
176
202
 
177
203
  def hosts_from_roles
178
204
  if accessory_config.key?("roles")
179
- accessory_config["roles"].flat_map { |role| config.role(role).hosts }
205
+ accessory_config["roles"].flat_map { |role| config.role(role)&.hosts }
180
206
  end
181
207
  end
182
208
 
183
209
  def network
184
210
  accessory_config["network"] || DEFAULT_NETWORK
185
211
  end
212
+
213
+ def ensure_valid_roles
214
+ if accessory_config["roles"] && (missing_roles = accessory_config["roles"] - config.roles.map(&:name)).any?
215
+ raise Kamal::ConfigurationError, "accessories/#{name}: unknown roles #{missing_roles.join(", ")}"
216
+ end
217
+ end
186
218
  end
@@ -53,6 +53,10 @@ class Kamal::Configuration::Builder
53
53
  !local_disabled? && (arches.empty? || local_arches.any?)
54
54
  end
55
55
 
56
+ def cloud?
57
+ driver.start_with? "cloud"
58
+ end
59
+
56
60
  def cached?
57
61
  !!builder_config["cache"]
58
62
  end
@@ -115,6 +119,10 @@ class Kamal::Configuration::Builder
115
119
  builder_config["provenance"]
116
120
  end
117
121
 
122
+ def sbom
123
+ builder_config["sbom"]
124
+ end
125
+
118
126
  def git_clone?
119
127
  Kamal::Git.used? && builder_config["context"].nil?
120
128
  end
@@ -23,9 +23,27 @@ accessories:
23
23
 
24
24
  # Image
25
25
  #
26
- # The Docker image to use, prefix it with a registry if not using Docker Hub:
26
+ # The Docker image to use.
27
+ # Prefix it with its server when using root level registry different from Docker Hub.
28
+ # Define registry directly or via anchors when it differs from root level registry.
27
29
  image: mysql:8.0
28
30
 
31
+ # Registry
32
+ #
33
+ # By default accessories use Docker Hub registry.
34
+ # You can specify different registry per accessory with this option.
35
+ # Don't prefix image with this registry server.
36
+ # Use anchors if you need to set the same specific registry for several accessories.
37
+ #
38
+ # ```yml
39
+ # registry:
40
+ # <<: *specific-registry
41
+ # ```
42
+ #
43
+ # See kamal docs registry for more information:
44
+ registry:
45
+ ...
46
+
29
47
  # Accessory hosts
30
48
  #
31
49
  # Specify one of `host`, `hosts`, or `roles`:
@@ -43,8 +61,8 @@ accessories:
43
61
 
44
62
  # Port mappings
45
63
  #
46
- # See https://docs.docker.com/network/, and especially note the warning about the security
47
- # implications of exposing ports publicly.
64
+ # See [https://docs.docker.com/network/](https://docs.docker.com/network/), and
65
+ # especially note the warning about the security implications of exposing ports publicly.
48
66
  port: "127.0.0.1:3306:3306"
49
67
 
50
68
  # Labels
@@ -98,3 +116,8 @@ accessories:
98
116
  # Defaults to kamal:
99
117
  network: custom
100
118
 
119
+ # Proxy
120
+ #
121
+ # You can run your accessory behind the Kamal proxy. See kamal docs proxy for more information
122
+ proxy:
123
+ ...
@@ -5,12 +5,12 @@
5
5
  # For example, for a Rails app, you might open a console with:
6
6
  #
7
7
  # ```shell
8
- # kamal app exec -i -r console "rails console"
8
+ # kamal app exec -i --reuse "bin/rails console"
9
9
  # ```
10
10
  #
11
11
  # By defining an alias, like this:
12
12
  aliases:
13
- console: app exec -r console -i "rails console"
13
+ console: app exec -i --reuse "bin/rails console"
14
14
  # You can now open the console with:
15
15
  #
16
16
  # ```shell
@@ -102,9 +102,18 @@ builder:
102
102
  #
103
103
  # The build driver to use, defaults to `docker-container`:
104
104
  driver: docker
105
+ #
106
+ # If you want to use Docker Build Cloud (https://www.docker.com/products/build-cloud/), you can set the driver to:
107
+ driver: cloud org-name/builder-name
105
108
 
106
109
  # Provenance
107
110
  #
108
111
  # It is used to configure provenance attestations for the build result.
109
112
  # The value can also be a boolean to enable or disable provenance attestations.
110
113
  provenance: mode=max
114
+
115
+ # SBOM (Software Bill of Materials)
116
+ #
117
+ # It is used to configure SBOM generation for the build result.
118
+ # The value can also be a boolean to enable or disable SBOM generation.
119
+ sbom: true
@@ -46,9 +46,22 @@ proxy:
46
46
  # The host value must point to the server we are deploying to, and port 443 must be
47
47
  # open for the Let's Encrypt challenge to succeed.
48
48
  #
49
+ # If you set `ssl` to `true`, `kamal-proxy` will stop forwarding headers to your app,
50
+ # unless you explicitly set `forward_headers: true`
51
+ #
49
52
  # Defaults to `false`:
50
53
  ssl: true
51
54
 
55
+ # Forward headers
56
+ #
57
+ # Whether to forward the `X-Forwarded-For` and `X-Forwarded-Proto` headers.
58
+ #
59
+ # If you are behind a trusted proxy, you can set this to `true` to forward the headers.
60
+ #
61
+ # By default, kamal-proxy will not forward the headers if the `ssl` option is set to `true`, and
62
+ # will forward them if it is set to `false`.
63
+ forward_headers: true
64
+
52
65
  # Response timeout
53
66
  #
54
67
  # How long to wait for requests to complete before timing out, defaults to 30 seconds:
@@ -93,13 +106,3 @@ proxy:
93
106
  response_headers:
94
107
  - X-Request-ID
95
108
  - X-Request-Start
96
-
97
- # Forward headers
98
- #
99
- # Whether to forward the `X-Forwarded-For` and `X-Forwarded-Proto` headers.
100
- #
101
- # If you are behind a trusted proxy, you can set this to `true` to forward the headers.
102
- #
103
- # By default, kamal-proxy will not forward the headers if the `ssl` option is set to `true`, and
104
- # will forward them if it is set to `false`.
105
- forward_headers: true
@@ -2,6 +2,10 @@
2
2
  #
3
3
  # The default registry is Docker Hub, but you can change it using `registry/server`.
4
4
  #
5
+ # By default, Docker Hub creates public repositories. To avoid making your images public,
6
+ # set up a private repository before deploying, or change the default repository privacy
7
+ # settings to private in your [Docker Hub settings](https://hub.docker.com/repository-settings/default-privacy).
8
+ #
5
9
  # A reference to a secret (in this case, `DOCKER_REGISTRY_TOKEN`) will look up the secret
6
10
  # in the local environment:
7
11
  registry:
@@ -1,12 +1,10 @@
1
1
  class Kamal::Configuration::Registry
2
2
  include Kamal::Configuration::Validation
3
3
 
4
- attr_reader :registry_config, :secrets
5
-
6
- def initialize(config:)
7
- @registry_config = config.raw_config.registry || {}
8
- @secrets = config.secrets
9
- validate! registry_config, with: Kamal::Configuration::Validator::Registry
4
+ def initialize(config:, secrets:, context: "registry")
5
+ @registry_config = config["registry"] || {}
6
+ @secrets = secrets
7
+ validate! registry_config, context: context, with: Kamal::Configuration::Validator::Registry
10
8
  end
11
9
 
12
10
  def server
@@ -22,6 +20,8 @@ class Kamal::Configuration::Registry
22
20
  end
23
21
 
24
22
  private
23
+ attr_reader :registry_config, :secrets
24
+
25
25
  def lookup(key)
26
26
  if registry_config[key].is_a?(Array)
27
27
  secrets[registry_config[key].first]
@@ -10,7 +10,7 @@ class Kamal::Configuration::Role
10
10
  def initialize(name, config:)
11
11
  @name, @config = name.inquiry, config
12
12
  validate! \
13
- specializations,
13
+ role_config,
14
14
  example: validation_yml["servers"]["workers"],
15
15
  context: "servers/#{name}",
16
16
  with: Kamal::Configuration::Validator::Role
@@ -204,11 +204,11 @@ class Kamal::Configuration::Role
204
204
  end
205
205
 
206
206
  def specializations
207
- if config.raw_config.servers.is_a?(Array) || config.raw_config.servers[name].is_a?(Array)
208
- {}
209
- else
210
- config.raw_config.servers[name]
211
- end
207
+ @specializations ||= role_config.is_a?(Array) ? {} : role_config
208
+ end
209
+
210
+ def role_config
211
+ @role_config ||= config.raw_config.servers.is_a?(Array) ? {} : config.raw_config.servers[name]
212
212
  end
213
213
 
214
214
  def custom_labels
@@ -3,7 +3,7 @@ class Kamal::Configuration::Validator::Role < Kamal::Configuration::Validator
3
3
  validate_type! config, Array, Hash
4
4
 
5
5
  if config.is_a?(Array)
6
- validate_servers! "servers", config
6
+ validate_servers!(config)
7
7
  else
8
8
  super
9
9
  end