kamal 1.4.0 → 1.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kamal/cli/accessory.rb +3 -2
  3. data/lib/kamal/cli/app/boot.rb +67 -0
  4. data/lib/kamal/cli/app/prepare_assets.rb +24 -0
  5. data/lib/kamal/cli/app.rb +20 -61
  6. data/lib/kamal/cli/base.rb +21 -7
  7. data/lib/kamal/cli/env.rb +3 -3
  8. data/lib/kamal/cli/main.rb +1 -1
  9. data/lib/kamal/cli/templates/deploy.yml +1 -1
  10. data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +0 -0
  11. data/lib/kamal/cli/traefik.rb +15 -12
  12. data/lib/kamal/commander/specifics.rb +49 -0
  13. data/lib/kamal/commander.rb +9 -33
  14. data/lib/kamal/commands/accessory.rb +2 -2
  15. data/lib/kamal/commands/app/assets.rb +4 -4
  16. data/lib/kamal/commands/app/cord.rb +2 -2
  17. data/lib/kamal/commands/app/execution.rb +8 -6
  18. data/lib/kamal/commands/app/images.rb +1 -1
  19. data/lib/kamal/commands/app.rb +29 -8
  20. data/lib/kamal/commands/auditor.rb +1 -1
  21. data/lib/kamal/commands/base.rb +5 -1
  22. data/lib/kamal/commands/builder/base.rb +14 -4
  23. data/lib/kamal/commands/builder/multiarch.rb +9 -9
  24. data/lib/kamal/commands/builder/native/cached.rb +7 -6
  25. data/lib/kamal/commands/builder/native/remote.rb +9 -9
  26. data/lib/kamal/commands/builder/native.rb +8 -7
  27. data/lib/kamal/commands/healthcheck.rb +0 -1
  28. data/lib/kamal/commands/hook.rb +1 -1
  29. data/lib/kamal/commands/lock.rb +19 -9
  30. data/lib/kamal/commands/prune.rb +2 -2
  31. data/lib/kamal/commands/server.rb +1 -1
  32. data/lib/kamal/commands/traefik.rb +8 -14
  33. data/lib/kamal/configuration/accessory.rb +9 -19
  34. data/lib/kamal/configuration/boot.rb +1 -1
  35. data/lib/kamal/configuration/builder.rb +7 -3
  36. data/lib/kamal/configuration/env.rb +40 -0
  37. data/lib/kamal/configuration/role.rb +12 -42
  38. data/lib/kamal/configuration.rb +20 -8
  39. data/lib/kamal/env_file.rb +12 -15
  40. data/lib/kamal/utils.rb +7 -3
  41. data/lib/kamal/version.rb +1 -1
  42. data/lib/kamal.rb +1 -1
  43. metadata +6 -2
@@ -15,7 +15,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
15
15
  "--detach",
16
16
  "--restart unless-stopped",
17
17
  "--name", container_name,
18
- *(["--hostname", hostname] if hostname),
18
+ *([ "--hostname", hostname ] if hostname),
19
19
  "-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
20
20
  "-e", "KAMAL_VERSION=\"#{config.version}\"",
21
21
  *role.env_args,
@@ -49,7 +49,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
49
49
 
50
50
 
51
51
  def current_running_container_id
52
- docker :ps, "--quiet", *filter_args(statuses: ACTIVE_DOCKER_STATUSES), "--latest"
52
+ current_running_container(format: "--quiet")
53
53
  end
54
54
 
55
55
  def container_id_for_version(version, only_running: false)
@@ -57,22 +57,24 @@ class Kamal::Commands::App < Kamal::Commands::Base
57
57
  end
58
58
 
59
59
  def current_running_version
60
- list_versions("--latest", statuses: ACTIVE_DOCKER_STATUSES)
60
+ pipe \
61
+ current_running_container(format: "--format '{{.Names}}'"),
62
+ extract_version_from_name
61
63
  end
62
64
 
63
65
  def list_versions(*docker_args, statuses: nil)
64
66
  pipe \
65
67
  docker(:ps, *filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
66
- %(while read line; do echo ${line##{role.container_prefix}-}; done) # Extract SHA from "service-role-dest-SHA"
68
+ extract_version_from_name
67
69
  end
68
70
 
69
71
 
70
72
  def make_env_directory
71
- make_directory role.host_env_directory
73
+ make_directory role.env.secrets_directory
72
74
  end
73
75
 
74
76
  def remove_env_file
75
- [ :rm, "-f", role.host_env_file_path ]
77
+ [ :rm, "-f", role.env.secrets_file ]
76
78
  end
77
79
 
78
80
 
@@ -81,12 +83,31 @@ class Kamal::Commands::App < Kamal::Commands::Base
81
83
  [ role.container_prefix, version || config.version ].compact.join("-")
82
84
  end
83
85
 
86
+ def latest_image_id
87
+ docker :image, :ls, *argumentize("--filter", "reference=#{config.latest_image}"), "--format", "'{{.ID}}'"
88
+ end
89
+
90
+ def current_running_container(format:)
91
+ pipe \
92
+ shell(chain(latest_image_container(format: format), latest_container(format: format))),
93
+ [ :head, "-1" ]
94
+ end
95
+
96
+ def latest_image_container(format:)
97
+ latest_container format: format, filters: [ "ancestor=$(#{latest_image_id.join(" ")})" ]
98
+ end
99
+
100
+ def latest_container(format:, filters: nil)
101
+ docker :ps, "--latest", *format, *filter_args(statuses: ACTIVE_DOCKER_STATUSES), argumentize("--filter", filters)
102
+ end
103
+
84
104
  def filter_args(statuses: nil)
85
105
  argumentize "--filter", filters(statuses: statuses)
86
106
  end
87
107
 
88
- def service_role_dest
89
- [ config.service, role, config.destination ].compact.join("-")
108
+ def extract_version_from_name
109
+ # Extract SHA from "service-role-dest-SHA"
110
+ %(while read line; do echo ${line##{role.container_prefix}-}; done)
90
111
  end
91
112
 
92
113
  def filters(statuses: nil)
@@ -21,7 +21,7 @@ class Kamal::Commands::Auditor < Kamal::Commands::Base
21
21
  def audit_log_file
22
22
  file = [ config.service, config.destination, "audit.log" ].compact.join("-")
23
23
 
24
- "#{config.run_directory}/#{file}"
24
+ File.join(config.run_directory, file)
25
25
  end
26
26
 
27
27
  def audit_tags(**details)
@@ -71,13 +71,17 @@ module Kamal::Commands
71
71
  end
72
72
 
73
73
  def shell(command)
74
- [ :sh, "-c", "'#{command.flatten.join(" ").gsub("'", "'\\''")}'" ]
74
+ [ :sh, "-c", "'#{command.flatten.join(" ").gsub("'", "'\\\\''")}'" ]
75
75
  end
76
76
 
77
77
  def docker(*args)
78
78
  args.compact.unshift :docker
79
79
  end
80
80
 
81
+ def git(*args)
82
+ args.compact.unshift :git
83
+ end
84
+
81
85
  def tags(**details)
82
86
  Kamal::Tags.from_config(config, **details)
83
87
  end
@@ -3,7 +3,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
3
3
  class BuilderError < StandardError; end
4
4
 
5
5
  delegate :argumentize, to: Kamal::Utils
6
- delegate :args, :secrets, :dockerfile, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, :ssh, to: :builder_config
6
+ delegate :args, :secrets, :dockerfile, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, :ssh, :git_archive?, to: :builder_config
7
7
 
8
8
  def clean
9
9
  docker :image, :rm, "--force", config.absolute_image
@@ -13,6 +13,16 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
13
13
  docker :pull, config.absolute_image
14
14
  end
15
15
 
16
+ def push
17
+ if git_archive?
18
+ pipe \
19
+ git(:archive, "--format=tar", :HEAD),
20
+ build_and_push
21
+ else
22
+ build_and_push
23
+ end
24
+ end
25
+
16
26
  def build_options
17
27
  [ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_ssh ]
18
28
  end
@@ -25,7 +35,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
25
35
  pipe \
26
36
  docker(:inspect, "-f", "'{{ .Config.Labels.service }}'", config.absolute_image),
27
37
  any(
28
- [:grep, "-x", config.service],
38
+ [ :grep, "-x", config.service ],
29
39
  "(echo \"Image #{config.absolute_image} is missing the 'service' label\" && exit 1)"
30
40
  )
31
41
  end
@@ -38,8 +48,8 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
38
48
 
39
49
  def build_cache
40
50
  if cache_to && cache_from
41
- ["--cache-to", cache_to,
42
- "--cache-from", cache_from]
51
+ [ "--cache-to", cache_to,
52
+ "--cache-from", cache_from ]
43
53
  end
44
54
  end
45
55
 
@@ -7,15 +7,6 @@ class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
7
7
  docker :buildx, :rm, builder_name
8
8
  end
9
9
 
10
- def push
11
- docker :buildx, :build,
12
- "--push",
13
- "--platform", platform_names,
14
- "--builder", builder_name,
15
- *build_options,
16
- build_context
17
- end
18
-
19
10
  def info
20
11
  combine \
21
12
  docker(:context, :ls),
@@ -34,4 +25,13 @@ class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
34
25
  "linux/amd64,linux/arm64"
35
26
  end
36
27
  end
28
+
29
+ def build_and_push
30
+ docker :buildx, :build,
31
+ "--push",
32
+ "--platform", platform_names,
33
+ "--builder", builder_name,
34
+ *build_options,
35
+ build_context
36
+ end
37
37
  end
@@ -7,10 +7,11 @@ class Kamal::Commands::Builder::Native::Cached < Kamal::Commands::Builder::Nativ
7
7
  docker :buildx, :rm, builder_name
8
8
  end
9
9
 
10
- def push
11
- docker :buildx, :build,
12
- "--push",
13
- *build_options,
14
- build_context
15
- end
10
+ private
11
+ def build_and_push
12
+ docker :buildx, :build,
13
+ "--push",
14
+ *build_options,
15
+ build_context
16
+ end
16
17
  end
@@ -11,15 +11,6 @@ class Kamal::Commands::Builder::Native::Remote < Kamal::Commands::Builder::Nativ
11
11
  remove_buildx
12
12
  end
13
13
 
14
- def push
15
- docker :buildx, :build,
16
- "--push",
17
- "--platform", platform,
18
- "--builder", builder_name,
19
- *build_options,
20
- build_context
21
- end
22
-
23
14
  def info
24
15
  chain \
25
16
  docker(:context, :ls),
@@ -56,4 +47,13 @@ class Kamal::Commands::Builder::Native::Remote < Kamal::Commands::Builder::Nativ
56
47
  def remove_buildx
57
48
  docker :buildx, :rm, builder_name
58
49
  end
50
+
51
+ def build_and_push
52
+ docker :buildx, :build,
53
+ "--push",
54
+ "--platform", platform,
55
+ "--builder", builder_name,
56
+ *build_options,
57
+ build_context
58
+ end
59
59
  end
@@ -7,14 +7,15 @@ class Kamal::Commands::Builder::Native < Kamal::Commands::Builder::Base
7
7
  # No-op on native without cache
8
8
  end
9
9
 
10
- def push
11
- combine \
12
- docker(:build, *build_options, build_context),
13
- docker(:push, config.absolute_image),
14
- docker(:push, config.latest_image)
15
- end
16
-
17
10
  def info
18
11
  # No-op on native
19
12
  end
13
+
14
+ private
15
+ def build_and_push
16
+ combine \
17
+ docker(:build, *build_options, build_context),
18
+ docker(:push, config.absolute_image),
19
+ docker(:push, config.latest_image)
20
+ end
20
21
  end
@@ -1,5 +1,4 @@
1
1
  class Kamal::Commands::Healthcheck < Kamal::Commands::Base
2
-
3
2
  def run
4
3
  primary = config.role(config.primary_role)
5
4
 
@@ -9,6 +9,6 @@ class Kamal::Commands::Hook < Kamal::Commands::Base
9
9
 
10
10
  private
11
11
  def hook_file(hook)
12
- "#{config.hooks_path}/#{hook}"
12
+ File.join(config.hooks_path, hook)
13
13
  end
14
14
  end
@@ -5,14 +5,14 @@ require "base64"
5
5
  class Kamal::Commands::Lock < Kamal::Commands::Base
6
6
  def acquire(message, version)
7
7
  combine \
8
- [:mkdir, lock_dir],
8
+ [ :mkdir, lock_dir ],
9
9
  write_lock_details(message, version)
10
10
  end
11
11
 
12
12
  def release
13
13
  combine \
14
- [:rm, lock_details_file],
15
- [:rm, "-r", lock_dir]
14
+ [ :rm, lock_details_file ],
15
+ [ :rm, "-r", lock_dir ]
16
16
  end
17
17
 
18
18
  def status
@@ -21,31 +21,41 @@ class Kamal::Commands::Lock < Kamal::Commands::Base
21
21
  read_lock_details
22
22
  end
23
23
 
24
+ def ensure_locks_directory
25
+ [ :mkdir, "-p", locks_dir ]
26
+ end
27
+
24
28
  private
25
29
  def write_lock_details(message, version)
26
30
  write \
27
- [:echo, "\"#{Base64.encode64(lock_details(message, version))}\""],
31
+ [ :echo, "\"#{Base64.encode64(lock_details(message, version))}\"" ],
28
32
  lock_details_file
29
33
  end
30
34
 
31
35
  def read_lock_details
32
36
  pipe \
33
- [:cat, lock_details_file],
34
- [:base64, "-d"]
37
+ [ :cat, lock_details_file ],
38
+ [ :base64, "-d" ]
35
39
  end
36
40
 
37
41
  def stat_lock_dir
38
42
  write \
39
- [:stat, lock_dir],
43
+ [ :stat, lock_dir ],
40
44
  "/dev/null"
41
45
  end
42
46
 
47
+ def locks_dir
48
+ File.join(config.run_directory, "locks")
49
+ end
50
+
43
51
  def lock_dir
44
- "#{config.run_directory}/lock-#{config.service}"
52
+ dir_name = [ config.service, config.destination ].compact.join("-")
53
+
54
+ File.join(locks_dir, dir_name)
45
55
  end
46
56
 
47
57
  def lock_details_file
48
- [lock_dir, :details].join("/")
58
+ File.join(lock_dir, "details")
49
59
  end
50
60
 
51
61
  def lock_details(message, version)
@@ -26,7 +26,7 @@ class Kamal::Commands::Prune < Kamal::Commands::Base
26
26
 
27
27
  private
28
28
  def stopped_containers_filters
29
- [ "created", "exited", "dead" ].flat_map { |status| ["--filter", "status=#{status}"] }
29
+ [ "created", "exited", "dead" ].flat_map { |status| [ "--filter", "status=#{status}" ] }
30
30
  end
31
31
 
32
32
  def active_image_list
@@ -43,4 +43,4 @@ class Kamal::Commands::Prune < Kamal::Commands::Base
43
43
  def healthcheck_service_filter
44
44
  [ "--filter", "label=service=#{config.healthcheck_service}" ]
45
45
  end
46
- end
46
+ end
@@ -1,5 +1,5 @@
1
1
  class Kamal::Commands::Server < Kamal::Commands::Base
2
2
  def ensure_run_directory
3
- [:mkdir, "-p", config.run_directory]
3
+ [ :mkdir, "-p", config.run_directory ]
4
4
  end
5
5
  end
@@ -4,7 +4,7 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
4
4
  DEFAULT_IMAGE = "traefik:v2.10"
5
5
  CONTAINER_PORT = 80
6
6
  DEFAULT_ARGS = {
7
- 'log.level' => 'DEBUG'
7
+ "log.level" => "DEBUG"
8
8
  }
9
9
  DEFAULT_LABELS = {
10
10
  # These ensure we serve a 502 rather than a 404 if no containers are available
@@ -71,20 +71,18 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
71
71
  "#{host_port}:#{CONTAINER_PORT}"
72
72
  end
73
73
 
74
- def env_file
75
- Kamal::EnvFile.new(config.traefik.fetch("env", {}))
76
- end
77
-
78
- def host_env_file_path
79
- File.join host_env_directory, "traefik.env"
74
+ def env
75
+ Kamal::Configuration::Env.from_config \
76
+ config: config.traefik.fetch("env", {}),
77
+ secrets_file: File.join(config.host_env_directory, "traefik", "traefik.env")
80
78
  end
81
79
 
82
80
  def make_env_directory
83
- make_directory(host_env_directory)
81
+ make_directory(env.secrets_directory)
84
82
  end
85
83
 
86
84
  def remove_env_file
87
- [:rm, "-f", host_env_file_path]
85
+ [ :rm, "-f", env.secrets_file ]
88
86
  end
89
87
 
90
88
  private
@@ -97,11 +95,7 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
97
95
  end
98
96
 
99
97
  def env_args
100
- argumentize "--env-file", host_env_file_path
101
- end
102
-
103
- def host_env_directory
104
- File.join config.host_env_directory, "traefik"
98
+ env.args
105
99
  end
106
100
 
107
101
  def labels
@@ -16,7 +16,7 @@ class Kamal::Configuration::Accessory
16
16
  end
17
17
 
18
18
  def hosts
19
- if (specifics.keys & ["host", "hosts", "roles"]).size != 1
19
+ if (specifics.keys & [ "host", "hosts", "roles" ]).size != 1
20
20
  raise ArgumentError, "Specify one of `host`, `hosts` or `roles` for accessory `#{name}`"
21
21
  end
22
22
 
@@ -42,23 +42,13 @@ class Kamal::Configuration::Accessory
42
42
  end
43
43
 
44
44
  def env
45
- specifics["env"] || {}
46
- end
47
-
48
- def env_file
49
- Kamal::EnvFile.new(env)
50
- end
51
-
52
- def host_env_directory
53
- File.join config.host_env_directory, "accessories"
54
- end
55
-
56
- def host_env_file_path
57
- File.join host_env_directory, "#{service_name}.env"
45
+ Kamal::Configuration::Env.from_config \
46
+ config: specifics.fetch("env", {}),
47
+ secrets_file: File.join(config.host_env_directory, "accessories", "#{service_name}.env")
58
48
  end
59
49
 
60
50
  def env_args
61
- argumentize "--env-file", host_env_file_path
51
+ env.args
62
52
  end
63
53
 
64
54
  def files
@@ -111,10 +101,10 @@ class Kamal::Configuration::Accessory
111
101
  end
112
102
 
113
103
  def with_clear_env_loaded
114
- (env["clear"] || env).each { |k, v| ENV[k] = v }
104
+ env.clear.each { |k, v| ENV[k] = v }
115
105
  yield
116
106
  ensure
117
- (env["clear"] || env).each { |k, v| ENV.delete(k) }
107
+ env.clear.each { |k, v| ENV.delete(k) }
118
108
  end
119
109
 
120
110
  def read_dynamic_file(local_file)
@@ -144,7 +134,7 @@ class Kamal::Configuration::Accessory
144
134
  end
145
135
 
146
136
  def expand_host_path(host_path)
147
- absolute_path?(host_path) ? host_path : "#{service_data_directory}/#{host_path}"
137
+ absolute_path?(host_path) ? host_path : File.join(service_data_directory, host_path)
148
138
  end
149
139
 
150
140
  def absolute_path?(path)
@@ -159,7 +149,7 @@ class Kamal::Configuration::Accessory
159
149
  if specifics.key?("host")
160
150
  host = specifics["host"]
161
151
  if host
162
- [host]
152
+ [ host ]
163
153
  else
164
154
  raise ArgumentError, "Missing host for accessory `#{name}`"
165
155
  end
@@ -8,7 +8,7 @@ class Kamal::Configuration::Boot
8
8
  limit = @options["limit"]
9
9
 
10
10
  if limit.to_s.end_with?("%")
11
- [@host_count * limit.to_i / 100, 1].max
11
+ [ @host_count * limit.to_i / 100, 1 ].max
12
12
  else
13
13
  limit
14
14
  end
@@ -40,7 +40,7 @@ class Kamal::Configuration::Builder
40
40
  end
41
41
 
42
42
  def context
43
- @options["context"] || "."
43
+ @options["context"] || (git_archive? ? "-" : ".")
44
44
  end
45
45
 
46
46
  def local_arch
@@ -85,10 +85,14 @@ class Kamal::Configuration::Builder
85
85
  @options["ssh"]
86
86
  end
87
87
 
88
+ def git_archive?
89
+ Kamal::Git.used? && @options["context"].nil?
90
+ end
91
+
88
92
  private
89
93
  def valid?
90
94
  if @options["cache"] && @options["cache"]["type"]
91
- raise ArgumentError, "Invalid cache type: #{@options["cache"]["type"]}" unless ["gha", "registry"].include?(@options["cache"]["type"])
95
+ raise ArgumentError, "Invalid cache type: #{@options["cache"]["type"]}" unless [ "gha", "registry" ].include?(@options["cache"]["type"])
92
96
  end
93
97
  end
94
98
 
@@ -109,7 +113,7 @@ class Kamal::Configuration::Builder
109
113
  end
110
114
 
111
115
  def cache_to_config_for_gha
112
- [ "type=gha", @options["cache"]&.fetch("options", nil)].compact.join(",")
116
+ [ "type=gha", @options["cache"]&.fetch("options", nil) ].compact.join(",")
113
117
  end
114
118
 
115
119
  def cache_to_config_for_registry
@@ -0,0 +1,40 @@
1
+ class Kamal::Configuration::Env
2
+ attr_reader :secrets_keys, :clear, :secrets_file
3
+ delegate :argumentize, to: Kamal::Utils
4
+
5
+ def self.from_config(config:, secrets_file: nil)
6
+ secrets_keys = config.fetch("secret", [])
7
+ clear = config.fetch("clear", config.key?("secret") ? {} : config)
8
+
9
+ new clear: clear, secrets_keys: secrets_keys, secrets_file: secrets_file
10
+ end
11
+
12
+ def initialize(clear:, secrets_keys:, secrets_file:)
13
+ @clear = clear
14
+ @secrets_keys = secrets_keys
15
+ @secrets_file = secrets_file
16
+ end
17
+
18
+ def args
19
+ [ "--env-file", secrets_file, *argumentize("--env", clear) ]
20
+ end
21
+
22
+ def secrets_io
23
+ StringIO.new(Kamal::EnvFile.new(secrets).to_s)
24
+ end
25
+
26
+ def secrets
27
+ @secrets ||= secrets_keys.to_h { |key| [ key, ENV.fetch(key) ] }
28
+ end
29
+
30
+ def secrets_directory
31
+ File.dirname(secrets_file)
32
+ end
33
+
34
+ def merge(other)
35
+ self.class.new \
36
+ clear: @clear.merge(other.clear),
37
+ secrets_keys: @secrets_keys | other.secrets_keys,
38
+ secrets_file: secrets_file
39
+ end
40
+ end
@@ -51,27 +51,11 @@ class Kamal::Configuration::Role
51
51
 
52
52
 
53
53
  def env
54
- if config.env && config.env["secret"]
55
- merged_env_with_secrets
56
- else
57
- merged_env
58
- end
59
- end
60
-
61
- def env_file
62
- Kamal::EnvFile.new(env)
63
- end
64
-
65
- def host_env_directory
66
- File.join config.host_env_directory, "roles"
67
- end
68
-
69
- def host_env_file_path
70
- File.join host_env_directory, "#{[config.service, name, config.destination].compact.join("-")}.env"
54
+ @env ||= base_env.merge(specialized_env)
71
55
  end
72
56
 
73
57
  def env_args
74
- argumentize "--env-file", host_env_file_path
58
+ env.args
75
59
  end
76
60
 
77
61
  def asset_volume_args
@@ -123,13 +107,13 @@ class Kamal::Configuration::Role
123
107
  end
124
108
 
125
109
  def cord_host_directory
126
- File.join config.run_directory_as_docker_volume, "cords", [container_prefix, config.run_id].join("-")
110
+ File.join config.run_directory_as_docker_volume, "cords", [ container_prefix, config.run_id ].join("-")
127
111
  end
128
112
 
129
113
  def cord_volume
130
114
  if (cord = health_check_options["cord"])
131
115
  @cord_volume ||= Kamal::Configuration::Volume.new \
132
- host_path: File.join(config.run_directory, "cords", [container_prefix, config.run_id].join("-")),
116
+ host_path: File.join(config.run_directory, "cords", [ container_prefix, config.run_id ].join("-")),
133
117
  container_path: cord
134
118
  end
135
119
  end
@@ -192,11 +176,7 @@ class Kamal::Configuration::Role
192
176
  end
193
177
 
194
178
  def default_labels
195
- if config.destination
196
- { "service" => config.service, "role" => name, "destination" => config.destination }
197
- else
198
- { "service" => config.service, "role" => name }
199
- end
179
+ { "service" => config.service, "role" => name, "destination" => config.destination }
200
180
  end
201
181
 
202
182
  def traefik_labels
@@ -217,7 +197,7 @@ class Kamal::Configuration::Role
217
197
  end
218
198
 
219
199
  def traefik_service
220
- [ config.service, name, config.destination ].compact.join("-")
200
+ container_prefix
221
201
  end
222
202
 
223
203
  def custom_labels
@@ -229,31 +209,21 @@ class Kamal::Configuration::Role
229
209
 
230
210
  def specializations
231
211
  if config.servers.is_a?(Array) || config.servers[name].is_a?(Array)
232
- { }
212
+ {}
233
213
  else
234
214
  config.servers[name].except("hosts")
235
215
  end
236
216
  end
237
217
 
238
218
  def specialized_env
239
- specializations["env"] || {}
240
- end
241
-
242
- def merged_env
243
- config.env&.merge(specialized_env) || {}
219
+ Kamal::Configuration::Env.from_config config: specializations.fetch("env", {})
244
220
  end
245
221
 
246
222
  # Secrets are stored in an array, which won't merge by default, so have to do it by hand.
247
- def merged_env_with_secrets
248
- merged_env.tap do |new_env|
249
- new_env["secret"] = Array(config.env["secret"]) + Array(specialized_env["secret"])
250
-
251
- # If there's no secret/clear split, everything is clear
252
- clear_app_env = config.env["secret"] ? Array(config.env["clear"]) : Array(config.env["clear"] || config.env)
253
- clear_role_env = specialized_env["secret"] ? Array(specialized_env["clear"]) : Array(specialized_env["clear"] || specialized_env)
254
-
255
- new_env["clear"] = clear_app_env.to_h.merge(clear_role_env.to_h)
256
- end
223
+ def base_env
224
+ Kamal::Configuration::Env.from_config \
225
+ config: config.env,
226
+ secrets_file: File.join(config.host_env_directory, "roles", "#{container_prefix}.env")
257
227
  end
258
228
 
259
229
  def http_health_check(port:, path:)