kamal 2.3.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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