kamal 1.8.3 → 2.0.0.beta1

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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/kamal/cli/accessory.rb +44 -20
  4. data/lib/kamal/cli/alias/command.rb +9 -0
  5. data/lib/kamal/cli/app/boot.rb +22 -16
  6. data/lib/kamal/cli/app.rb +40 -5
  7. data/lib/kamal/cli/base.rb +19 -51
  8. data/lib/kamal/cli/build.rb +12 -13
  9. data/lib/kamal/cli/healthcheck/barrier.rb +2 -0
  10. data/lib/kamal/cli/healthcheck/poller.rb +18 -39
  11. data/lib/kamal/cli/lock.rb +2 -3
  12. data/lib/kamal/cli/main.rb +54 -51
  13. data/lib/kamal/cli/proxy.rb +224 -0
  14. data/lib/kamal/cli/prune.rb +0 -1
  15. data/lib/kamal/cli/secrets.rb +36 -0
  16. data/lib/kamal/cli/server.rb +2 -3
  17. data/lib/kamal/cli/templates/deploy.yml +4 -21
  18. data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
  19. data/lib/kamal/cli/templates/secrets +16 -0
  20. data/lib/kamal/cli.rb +1 -0
  21. data/lib/kamal/commander/specifics.rb +3 -3
  22. data/lib/kamal/commander.rb +24 -8
  23. data/lib/kamal/commands/accessory.rb +7 -7
  24. data/lib/kamal/commands/app/assets.rb +8 -8
  25. data/lib/kamal/commands/app/proxy.rb +16 -0
  26. data/lib/kamal/commands/app.rb +7 -15
  27. data/lib/kamal/commands/auditor.rb +6 -3
  28. data/lib/kamal/commands/base.rb +8 -0
  29. data/lib/kamal/commands/builder/base.rb +26 -13
  30. data/lib/kamal/commands/builder/hybrid.rb +21 -0
  31. data/lib/kamal/commands/builder/local.rb +14 -0
  32. data/lib/kamal/commands/builder/remote.rb +63 -0
  33. data/lib/kamal/commands/builder.rb +13 -29
  34. data/lib/kamal/commands/docker.rb +4 -0
  35. data/lib/kamal/commands/hook.rb +5 -2
  36. data/lib/kamal/commands/lock.rb +2 -6
  37. data/lib/kamal/commands/proxy.rb +77 -0
  38. data/lib/kamal/commands/prune.rb +1 -9
  39. data/lib/kamal/commands/server.rb +11 -1
  40. data/lib/kamal/configuration/accessory.rb +14 -2
  41. data/lib/kamal/configuration/alias.rb +15 -0
  42. data/lib/kamal/configuration/builder.rb +52 -18
  43. data/lib/kamal/configuration/docs/alias.yml +26 -0
  44. data/lib/kamal/configuration/docs/builder.yml +26 -23
  45. data/lib/kamal/configuration/docs/configuration.yml +22 -16
  46. data/lib/kamal/configuration/docs/env.yml +10 -11
  47. data/lib/kamal/configuration/docs/proxy.yml +100 -0
  48. data/lib/kamal/configuration/docs/registry.yml +4 -2
  49. data/lib/kamal/configuration/docs/role.yml +3 -5
  50. data/lib/kamal/configuration/env/tag.rb +4 -3
  51. data/lib/kamal/configuration/env.rb +10 -17
  52. data/lib/kamal/configuration/proxy.rb +66 -0
  53. data/lib/kamal/configuration/registry.rb +3 -2
  54. data/lib/kamal/configuration/role.rb +63 -94
  55. data/lib/kamal/configuration/validator/alias.rb +15 -0
  56. data/lib/kamal/configuration/validator/builder.rb +4 -0
  57. data/lib/kamal/configuration/validator/proxy.rb +11 -0
  58. data/lib/kamal/configuration/validator.rb +42 -24
  59. data/lib/kamal/configuration.rb +91 -33
  60. data/lib/kamal/env_file.rb +4 -0
  61. data/lib/kamal/secrets/adapters/base.rb +18 -0
  62. data/lib/kamal/secrets/adapters/bitwarden.rb +64 -0
  63. data/lib/kamal/secrets/adapters/last_pass.rb +30 -0
  64. data/lib/kamal/secrets/adapters/one_password.rb +61 -0
  65. data/lib/kamal/secrets/adapters/test.rb +10 -0
  66. data/lib/kamal/secrets/adapters.rb +14 -0
  67. data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +32 -0
  68. data/lib/kamal/secrets.rb +37 -0
  69. data/lib/kamal/sshkit_with_ext.rb +1 -0
  70. data/lib/kamal/utils.rb +28 -0
  71. data/lib/kamal/version.rb +1 -1
  72. data/lib/kamal.rb +3 -1
  73. metadata +32 -23
  74. data/lib/kamal/cli/env.rb +0 -54
  75. data/lib/kamal/cli/templates/sample_hooks/post-traefik-reboot.sample +0 -3
  76. data/lib/kamal/cli/templates/template.env +0 -2
  77. data/lib/kamal/cli/traefik.rb +0 -122
  78. data/lib/kamal/commands/app/cord.rb +0 -22
  79. data/lib/kamal/commands/builder/multiarch/remote.rb +0 -65
  80. data/lib/kamal/commands/builder/multiarch.rb +0 -41
  81. data/lib/kamal/commands/builder/native/cached.rb +0 -25
  82. data/lib/kamal/commands/builder/native/remote.rb +0 -67
  83. data/lib/kamal/commands/builder/native.rb +0 -20
  84. data/lib/kamal/commands/traefik.rb +0 -85
  85. data/lib/kamal/configuration/docs/healthcheck.yml +0 -59
  86. data/lib/kamal/configuration/docs/traefik.yml +0 -62
  87. data/lib/kamal/configuration/healthcheck.rb +0 -63
  88. data/lib/kamal/configuration/traefik.rb +0 -60
  89. /data/lib/kamal/cli/templates/sample_hooks/{pre-traefik-reboot.sample → pre-proxy-reboot.sample} +0 -0
@@ -0,0 +1,30 @@
1
+ class Kamal::Secrets::Adapters::LastPass < Kamal::Secrets::Adapters::Base
2
+ private
3
+ def login(account)
4
+ unless loggedin?(account)
5
+ `lpass login #{account.shellescape}`
6
+ raise RuntimeError, "Failed to login to 1Password" unless $?.success?
7
+ end
8
+ end
9
+
10
+ def loggedin?(account)
11
+ `lpass status --color never`.strip == "Logged in as #{account}."
12
+ end
13
+
14
+ def fetch_secrets(secrets, account:, session:)
15
+ items = `lpass show #{secrets.map(&:shellescape).join(" ")} --json`
16
+ raise RuntimeError, "Could not read #{secrets} from 1Password" unless $?.success?
17
+
18
+ items = JSON.parse(items)
19
+
20
+ {}.tap do |results|
21
+ items.each do |item|
22
+ results[item["fullname"]] = item["password"]
23
+ end
24
+
25
+ if (missing_items = secrets - results.keys).any?
26
+ raise RuntimeError, "Could not find #{missing_items.join(", ")} in LassPass"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,61 @@
1
+ class Kamal::Secrets::Adapters::OnePassword < Kamal::Secrets::Adapters::Base
2
+ delegate :optionize, to: Kamal::Utils
3
+
4
+ private
5
+ def login(account)
6
+ unless loggedin?(account)
7
+ `op signin #{to_options(account: account, force: true, raw: true)}`.tap do
8
+ raise RuntimeError, "Failed to login to 1Password" unless $?.success?
9
+ end
10
+ end
11
+ end
12
+
13
+ def loggedin?(account)
14
+ `op account get --account #{account.shellescape} 2> /dev/null`
15
+ $?.success?
16
+ end
17
+
18
+ def fetch_secrets(secrets, account:, session:)
19
+ {}.tap do |results|
20
+ vaults_items_fields(secrets).map do |vault, items|
21
+ items.each do |item, fields|
22
+ fields_json = JSON.parse(op_item_get(vault, item, fields, account: account, session: session))
23
+ fields_json = [ fields_json ] if fields.one?
24
+
25
+ fields_json.each do |field_json|
26
+ # The reference is in the form `op://vault/item/field[/field]`
27
+ field = field_json["reference"].delete_prefix("op://").delete_suffix("/password")
28
+ results[field] = field_json["value"]
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def to_options(**options)
36
+ optionize(options.compact).join(" ")
37
+ end
38
+
39
+ def vaults_items_fields(secrets)
40
+ {}.tap do |vaults|
41
+ secrets.each do |secret|
42
+ secret = secret.delete_prefix("op://")
43
+ vault, item, *fields = secret.split("/")
44
+ fields << "password" if fields.empty?
45
+
46
+ vaults[vault] ||= {}
47
+ vaults[vault][item] ||= []
48
+ vaults[vault][item] << fields.join(".")
49
+ end
50
+ end
51
+ end
52
+
53
+ def op_item_get(vault, item, fields, account:, session:)
54
+ labels = fields.map { |field| "label=#{field}" }.join(",")
55
+ options = to_options(vault: vault, fields: labels, format: "json", account: account, session: session.presence)
56
+
57
+ `op item get #{item.shellescape} #{options}`.tap do
58
+ raise RuntimeError, "Could not read #{fields.join(", ")} from #{item} in the #{vault} 1Password vault" unless $?.success?
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,10 @@
1
+ class Kamal::Secrets::Adapters::Test < Kamal::Secrets::Adapters::Base
2
+ private
3
+ def login(account)
4
+ true
5
+ end
6
+
7
+ def fetch_secrets(secrets, account:, session:)
8
+ secrets.to_h { |secret| [ secret, secret.reverse ] }
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ require "active_support/core_ext/string/inflections"
2
+ module Kamal::Secrets::Adapters
3
+ def self.lookup(name)
4
+ name = "one_password" if name.downcase == "1password"
5
+ name = "last_pass" if name.downcase == "lastpass"
6
+ adapter_class(name)
7
+ end
8
+
9
+ def self.adapter_class(name)
10
+ Object.const_get("Kamal::Secrets::Adapters::#{name.camelize}").new
11
+ rescue NameError => e
12
+ raise RuntimeError, "Unknown secrets adapter: #{name}"
13
+ end
14
+ end
@@ -0,0 +1,32 @@
1
+ class Kamal::Secrets::Dotenv::InlineCommandSubstitution
2
+ class << self
3
+ def install!
4
+ ::Dotenv::Parser.substitutions.map! { |sub| sub == ::Dotenv::Substitutions::Command ? self : sub }
5
+ end
6
+
7
+ def call(value, _env, overwrite: false)
8
+ # Process interpolated shell commands
9
+ value.gsub(Dotenv::Substitutions::Command.singleton_class::INTERPOLATED_SHELL_COMMAND) do |*|
10
+ # Eliminate opening and closing parentheses
11
+ command = $LAST_MATCH_INFO[:cmd][1..-2]
12
+
13
+ if $LAST_MATCH_INFO[:backslash]
14
+ # Command is escaped, don't replace it.
15
+ $LAST_MATCH_INFO[0][1..]
16
+ else
17
+ if command =~ /\A\s*kamal\s*secrets\s+/
18
+ # Inline the command
19
+ inline_secrets_command(command)
20
+ else
21
+ # Execute the command and return the value
22
+ `#{command}`.chomp
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def inline_secrets_command(command)
29
+ Kamal::Cli::Main.start(command.shellsplit[1..] + [ "--inline" ]).chomp
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,37 @@
1
+ require "dotenv"
2
+
3
+ class Kamal::Secrets
4
+ attr_reader :secrets_files
5
+
6
+ Kamal::Secrets::Dotenv::InlineCommandSubstitution.install!
7
+
8
+ def initialize(destination: nil)
9
+ @secrets_files = \
10
+ [ ".kamal/secrets-common", ".kamal/secrets#{(".#{destination}" if destination)}" ].select { |f| File.exist?(f) }
11
+ @mutex = Mutex.new
12
+ end
13
+
14
+ def [](key)
15
+ # Fetching secrets may ask the user for input, so ensure only one thread does that
16
+ @mutex.synchronize do
17
+ secrets.fetch(key)
18
+ end
19
+ rescue KeyError
20
+ if secrets_files
21
+ raise Kamal::ConfigurationError, "Secret '#{key}' not found in #{secrets_files.join(", ")}"
22
+ else
23
+ raise Kamal::ConfigurationError, "Secret '#{key}' not found, no secret files provided"
24
+ end
25
+ end
26
+
27
+ def to_h
28
+ secrets
29
+ end
30
+
31
+ private
32
+ def secrets
33
+ @secrets ||= secrets_files.inject({}) do |secrets, secrets_file|
34
+ secrets.merge!(::Dotenv.parse(secrets_file))
35
+ end
36
+ end
37
+ end
@@ -3,6 +3,7 @@ require "sshkit/dsl"
3
3
  require "net/scp"
4
4
  require "active_support/core_ext/hash/deep_merge"
5
5
  require "json"
6
+ require "concurrent/atomic/semaphore"
6
7
 
7
8
  class SSHKit::Backend::Abstract
8
9
  def capture_with_info(*args, **kwargs)
data/lib/kamal/utils.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require "active_support/core_ext/object/try"
2
+
1
3
  module Kamal::Utils
2
4
  extend self
3
5
 
@@ -54,6 +56,12 @@ module Kamal::Utils
54
56
 
55
57
  # Escape a value to make it safe for shell use.
56
58
  def escape_shell_value(value)
59
+ value.to_s.scan(/[\x00-\x7F]+|[^\x00-\x7F]+/) \
60
+ .map { |part| part.ascii_only? ? escape_ascii_shell_value(part) : part }
61
+ .join
62
+ end
63
+
64
+ def escape_ascii_shell_value(value)
57
65
  value.to_s.dump
58
66
  .gsub(/`/, '\\\\`')
59
67
  .gsub(DOLLAR_SIGN_WITHOUT_SHELL_EXPANSION_REGEX, '\$')
@@ -77,4 +85,24 @@ module Kamal::Utils
77
85
  def stable_sort!(elements, &block)
78
86
  elements.sort_by!.with_index { |element, index| [ block.call(element), index ] }
79
87
  end
88
+
89
+ def join_commands(commands)
90
+ commands.map(&:strip).join(" ")
91
+ end
92
+
93
+ def docker_arch
94
+ arch = `docker info --format '{{.Architecture}}'`.strip
95
+ case arch
96
+ when /aarch64/
97
+ "arm64"
98
+ when /x86_64/
99
+ "amd64"
100
+ else
101
+ arch
102
+ end
103
+ end
104
+
105
+ def older_version?(version, other_version)
106
+ Gem::Version.new(version.delete_prefix("v")) < Gem::Version.new(other_version.delete_prefix("v"))
107
+ end
80
108
  end
data/lib/kamal/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kamal
2
- VERSION = "1.8.3"
2
+ VERSION = "2.0.0.beta1"
3
3
  end
data/lib/kamal.rb CHANGED
@@ -5,8 +5,10 @@ end
5
5
  require "active_support"
6
6
  require "zeitwerk"
7
7
  require "yaml"
8
+ require "tmpdir"
9
+ require "pathname"
8
10
 
9
11
  loader = Zeitwerk::Loader.for_gem
10
12
  loader.ignore(File.join(__dir__, "kamal", "sshkit_with_ext.rb"))
11
13
  loader.setup
12
- loader.eager_load # We need all commands loaded.
14
+ loader.eager_load_namespace(Kamal::Cli) # We need all commands loaded.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kamal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.3
4
+ version: 2.0.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-02 00:00:00.000000000 Z
11
+ date: 2024-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -64,28 +64,28 @@ dependencies:
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '1.2'
67
+ version: '1.3'
68
68
  type: :runtime
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '1.2'
74
+ version: '1.3'
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: dotenv
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '2.8'
81
+ version: '3.1'
82
82
  type: :runtime
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: '2.8'
88
+ version: '3.1'
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: zeitwerk
91
91
  requirement: !ruby/object:Gem::Requirement
@@ -211,31 +211,32 @@ files:
211
211
  - lib/kamal.rb
212
212
  - lib/kamal/cli.rb
213
213
  - lib/kamal/cli/accessory.rb
214
+ - lib/kamal/cli/alias/command.rb
214
215
  - lib/kamal/cli/app.rb
215
216
  - lib/kamal/cli/app/boot.rb
216
217
  - lib/kamal/cli/app/prepare_assets.rb
217
218
  - lib/kamal/cli/base.rb
218
219
  - lib/kamal/cli/build.rb
219
220
  - lib/kamal/cli/build/clone.rb
220
- - lib/kamal/cli/env.rb
221
221
  - lib/kamal/cli/healthcheck/barrier.rb
222
222
  - lib/kamal/cli/healthcheck/error.rb
223
223
  - lib/kamal/cli/healthcheck/poller.rb
224
224
  - lib/kamal/cli/lock.rb
225
225
  - lib/kamal/cli/main.rb
226
+ - lib/kamal/cli/proxy.rb
226
227
  - lib/kamal/cli/prune.rb
227
228
  - lib/kamal/cli/registry.rb
229
+ - lib/kamal/cli/secrets.rb
228
230
  - lib/kamal/cli/server.rb
229
231
  - lib/kamal/cli/templates/deploy.yml
230
232
  - lib/kamal/cli/templates/sample_hooks/docker-setup.sample
231
233
  - lib/kamal/cli/templates/sample_hooks/post-deploy.sample
232
- - lib/kamal/cli/templates/sample_hooks/post-traefik-reboot.sample
234
+ - lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample
233
235
  - lib/kamal/cli/templates/sample_hooks/pre-build.sample
234
236
  - lib/kamal/cli/templates/sample_hooks/pre-connect.sample
235
237
  - lib/kamal/cli/templates/sample_hooks/pre-deploy.sample
236
- - lib/kamal/cli/templates/sample_hooks/pre-traefik-reboot.sample
237
- - lib/kamal/cli/templates/template.env
238
- - lib/kamal/cli/traefik.rb
238
+ - lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample
239
+ - lib/kamal/cli/templates/secrets
239
240
  - lib/kamal/commander.rb
240
241
  - lib/kamal/commander/specifics.rb
241
242
  - lib/kamal/commands.rb
@@ -243,66 +244,74 @@ files:
243
244
  - lib/kamal/commands/app.rb
244
245
  - lib/kamal/commands/app/assets.rb
245
246
  - lib/kamal/commands/app/containers.rb
246
- - lib/kamal/commands/app/cord.rb
247
247
  - lib/kamal/commands/app/execution.rb
248
248
  - lib/kamal/commands/app/images.rb
249
249
  - lib/kamal/commands/app/logging.rb
250
+ - lib/kamal/commands/app/proxy.rb
250
251
  - lib/kamal/commands/auditor.rb
251
252
  - lib/kamal/commands/base.rb
252
253
  - lib/kamal/commands/builder.rb
253
254
  - lib/kamal/commands/builder/base.rb
254
255
  - lib/kamal/commands/builder/clone.rb
255
- - lib/kamal/commands/builder/multiarch.rb
256
- - lib/kamal/commands/builder/multiarch/remote.rb
257
- - lib/kamal/commands/builder/native.rb
258
- - lib/kamal/commands/builder/native/cached.rb
259
- - lib/kamal/commands/builder/native/remote.rb
256
+ - lib/kamal/commands/builder/hybrid.rb
257
+ - lib/kamal/commands/builder/local.rb
258
+ - lib/kamal/commands/builder/remote.rb
260
259
  - lib/kamal/commands/docker.rb
261
260
  - lib/kamal/commands/hook.rb
262
261
  - lib/kamal/commands/lock.rb
262
+ - lib/kamal/commands/proxy.rb
263
263
  - lib/kamal/commands/prune.rb
264
264
  - lib/kamal/commands/registry.rb
265
265
  - lib/kamal/commands/server.rb
266
- - lib/kamal/commands/traefik.rb
267
266
  - lib/kamal/configuration.rb
268
267
  - lib/kamal/configuration/accessory.rb
268
+ - lib/kamal/configuration/alias.rb
269
269
  - lib/kamal/configuration/boot.rb
270
270
  - lib/kamal/configuration/builder.rb
271
271
  - lib/kamal/configuration/docs/accessory.yml
272
+ - lib/kamal/configuration/docs/alias.yml
272
273
  - lib/kamal/configuration/docs/boot.yml
273
274
  - lib/kamal/configuration/docs/builder.yml
274
275
  - lib/kamal/configuration/docs/configuration.yml
275
276
  - lib/kamal/configuration/docs/env.yml
276
- - lib/kamal/configuration/docs/healthcheck.yml
277
277
  - lib/kamal/configuration/docs/logging.yml
278
+ - lib/kamal/configuration/docs/proxy.yml
278
279
  - lib/kamal/configuration/docs/registry.yml
279
280
  - lib/kamal/configuration/docs/role.yml
280
281
  - lib/kamal/configuration/docs/servers.yml
281
282
  - lib/kamal/configuration/docs/ssh.yml
282
283
  - lib/kamal/configuration/docs/sshkit.yml
283
- - lib/kamal/configuration/docs/traefik.yml
284
284
  - lib/kamal/configuration/env.rb
285
285
  - lib/kamal/configuration/env/tag.rb
286
- - lib/kamal/configuration/healthcheck.rb
287
286
  - lib/kamal/configuration/logging.rb
287
+ - lib/kamal/configuration/proxy.rb
288
288
  - lib/kamal/configuration/registry.rb
289
289
  - lib/kamal/configuration/role.rb
290
290
  - lib/kamal/configuration/servers.rb
291
291
  - lib/kamal/configuration/ssh.rb
292
292
  - lib/kamal/configuration/sshkit.rb
293
- - lib/kamal/configuration/traefik.rb
294
293
  - lib/kamal/configuration/validation.rb
295
294
  - lib/kamal/configuration/validator.rb
296
295
  - lib/kamal/configuration/validator/accessory.rb
296
+ - lib/kamal/configuration/validator/alias.rb
297
297
  - lib/kamal/configuration/validator/builder.rb
298
298
  - lib/kamal/configuration/validator/configuration.rb
299
299
  - lib/kamal/configuration/validator/env.rb
300
+ - lib/kamal/configuration/validator/proxy.rb
300
301
  - lib/kamal/configuration/validator/registry.rb
301
302
  - lib/kamal/configuration/validator/role.rb
302
303
  - lib/kamal/configuration/validator/servers.rb
303
304
  - lib/kamal/configuration/volume.rb
304
305
  - lib/kamal/env_file.rb
305
306
  - lib/kamal/git.rb
307
+ - lib/kamal/secrets.rb
308
+ - lib/kamal/secrets/adapters.rb
309
+ - lib/kamal/secrets/adapters/base.rb
310
+ - lib/kamal/secrets/adapters/bitwarden.rb
311
+ - lib/kamal/secrets/adapters/last_pass.rb
312
+ - lib/kamal/secrets/adapters/one_password.rb
313
+ - lib/kamal/secrets/adapters/test.rb
314
+ - lib/kamal/secrets/dotenv/inline_command_substitution.rb
306
315
  - lib/kamal/sshkit_with_ext.rb
307
316
  - lib/kamal/tags.rb
308
317
  - lib/kamal/utils.rb
@@ -327,7 +336,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
327
336
  - !ruby/object:Gem::Version
328
337
  version: '0'
329
338
  requirements: []
330
- rubygems_version: 3.5.11
339
+ rubygems_version: 3.5.16
331
340
  signing_key:
332
341
  specification_version: 4
333
342
  summary: Deploy web apps in containers to servers running Docker with zero downtime.
data/lib/kamal/cli/env.rb DELETED
@@ -1,54 +0,0 @@
1
- require "tempfile"
2
-
3
- class Kamal::Cli::Env < Kamal::Cli::Base
4
- desc "push", "Push the env file to the remote hosts"
5
- def push
6
- with_lock do
7
- on(KAMAL.hosts) do
8
- execute *KAMAL.auditor.record("Pushed env files"), verbosity: :debug
9
-
10
- KAMAL.roles_on(host).each do |role|
11
- execute *KAMAL.app(role: role, host: host).make_env_directory
12
- upload! role.env(host).secrets_io, role.env(host).secrets_file, mode: 400
13
- end
14
- end
15
-
16
- on(KAMAL.traefik_hosts) do
17
- execute *KAMAL.traefik.make_env_directory
18
- upload! KAMAL.traefik.env.secrets_io, KAMAL.traefik.env.secrets_file, mode: 400
19
- end
20
-
21
- on(KAMAL.accessory_hosts) do
22
- KAMAL.accessories_on(host).each do |accessory|
23
- accessory_config = KAMAL.config.accessory(accessory)
24
- execute *KAMAL.accessory(accessory).make_env_directory
25
- upload! accessory_config.env.secrets_io, accessory_config.env.secrets_file, mode: 400
26
- end
27
- end
28
- end
29
- end
30
-
31
- desc "delete", "Delete the env file from the remote hosts"
32
- def delete
33
- with_lock do
34
- on(KAMAL.hosts) do
35
- execute *KAMAL.auditor.record("Deleted env files"), verbosity: :debug
36
-
37
- KAMAL.roles_on(host).each do |role|
38
- execute *KAMAL.app(role: role, host: host).remove_env_file
39
- end
40
- end
41
-
42
- on(KAMAL.traefik_hosts) do
43
- execute *KAMAL.traefik.remove_env_file
44
- end
45
-
46
- on(KAMAL.accessory_hosts) do
47
- KAMAL.accessories_on(host).each do |accessory|
48
- accessory_config = KAMAL.config.accessory(accessory)
49
- execute *KAMAL.accessory(accessory).remove_env_file
50
- end
51
- end
52
- end
53
- end
54
- end
@@ -1,3 +0,0 @@
1
- #!/bin/sh
2
-
3
- echo "Rebooted Traefik on $KAMAL_HOSTS"
@@ -1,2 +0,0 @@
1
- KAMAL_REGISTRY_PASSWORD=change-this
2
- RAILS_MASTER_KEY=another-env
@@ -1,122 +0,0 @@
1
- class Kamal::Cli::Traefik < Kamal::Cli::Base
2
- desc "boot", "Boot Traefik on servers"
3
- def boot
4
- with_lock do
5
- on(KAMAL.traefik_hosts) do
6
- execute *KAMAL.registry.login
7
- execute *KAMAL.traefik.start_or_run
8
- end
9
- end
10
- end
11
-
12
- desc "reboot", "Reboot Traefik on servers (stop container, remove container, start new container)"
13
- option :rolling, type: :boolean, default: false, desc: "Reboot traefik on hosts in sequence, rather than in parallel"
14
- option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
15
- def reboot
16
- confirming "This will cause a brief outage on each host. Are you sure?" do
17
- with_lock do
18
- host_groups = options[:rolling] ? KAMAL.traefik_hosts : [ KAMAL.traefik_hosts ]
19
- host_groups.each do |hosts|
20
- host_list = Array(hosts).join(",")
21
- run_hook "pre-traefik-reboot", hosts: host_list
22
- on(hosts) do
23
- execute *KAMAL.auditor.record("Rebooted traefik"), verbosity: :debug
24
- execute *KAMAL.registry.login
25
- execute *KAMAL.traefik.stop, raise_on_non_zero_exit: false
26
- execute *KAMAL.traefik.remove_container
27
- execute *KAMAL.traefik.run
28
- end
29
- run_hook "post-traefik-reboot", hosts: host_list
30
- end
31
- end
32
- end
33
- end
34
-
35
- desc "start", "Start existing Traefik container on servers"
36
- def start
37
- with_lock do
38
- on(KAMAL.traefik_hosts) do
39
- execute *KAMAL.auditor.record("Started traefik"), verbosity: :debug
40
- execute *KAMAL.traefik.start
41
- end
42
- end
43
- end
44
-
45
- desc "stop", "Stop existing Traefik container on servers"
46
- def stop
47
- with_lock do
48
- on(KAMAL.traefik_hosts) do
49
- execute *KAMAL.auditor.record("Stopped traefik"), verbosity: :debug
50
- execute *KAMAL.traefik.stop, raise_on_non_zero_exit: false
51
- end
52
- end
53
- end
54
-
55
- desc "restart", "Restart existing Traefik container on servers"
56
- def restart
57
- with_lock do
58
- stop
59
- start
60
- end
61
- end
62
-
63
- desc "details", "Show details about Traefik container from servers"
64
- def details
65
- on(KAMAL.traefik_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.traefik.info), type: "Traefik" }
66
- end
67
-
68
- desc "logs", "Show log lines from Traefik on servers"
69
- option :since, aliases: "-s", desc: "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
70
- option :lines, type: :numeric, aliases: "-n", desc: "Number of log lines to pull from each server"
71
- option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
72
- option :grep_options, aliases: "-o", desc: "Additional options supplied to grep"
73
- option :follow, aliases: "-f", desc: "Follow logs on primary server (or specific host set by --hosts)"
74
- def logs
75
- grep = options[:grep]
76
- grep_options = options[:grep_options]
77
-
78
- if options[:follow]
79
- run_locally do
80
- info "Following logs on #{KAMAL.primary_host}..."
81
- info KAMAL.traefik.follow_logs(host: KAMAL.primary_host, grep: grep, grep_options: grep_options)
82
- exec KAMAL.traefik.follow_logs(host: KAMAL.primary_host, grep: grep, grep_options: grep_options)
83
- end
84
- else
85
- since = options[:since]
86
- lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
87
-
88
- on(KAMAL.traefik_hosts) do |host|
89
- puts_by_host host, capture(*KAMAL.traefik.logs(since: since, lines: lines, grep: grep, grep_options: grep_options)), type: "Traefik"
90
- end
91
- end
92
- end
93
-
94
- desc "remove", "Remove Traefik container and image from servers"
95
- def remove
96
- with_lock do
97
- stop
98
- remove_container
99
- remove_image
100
- end
101
- end
102
-
103
- desc "remove_container", "Remove Traefik container from servers", hide: true
104
- def remove_container
105
- with_lock do
106
- on(KAMAL.traefik_hosts) do
107
- execute *KAMAL.auditor.record("Removed traefik container"), verbosity: :debug
108
- execute *KAMAL.traefik.remove_container
109
- end
110
- end
111
- end
112
-
113
- desc "remove_image", "Remove Traefik image from servers", hide: true
114
- def remove_image
115
- with_lock do
116
- on(KAMAL.traefik_hosts) do
117
- execute *KAMAL.auditor.record("Removed traefik image"), verbosity: :debug
118
- execute *KAMAL.traefik.remove_image
119
- end
120
- end
121
- end
122
- end
@@ -1,22 +0,0 @@
1
- module Kamal::Commands::App::Cord
2
- def cord(version:)
3
- pipe \
4
- docker(:inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", container_name(version)),
5
- [ :awk, "'$2 == \"#{role.cord_volume.container_path}\" {print $1}'" ]
6
- end
7
-
8
- def tie_cord(cord)
9
- create_empty_file(cord)
10
- end
11
-
12
- def cut_cord(cord)
13
- remove_directory(cord)
14
- end
15
-
16
- private
17
- def create_empty_file(file)
18
- chain \
19
- make_directory_for(file),
20
- [ :touch, file ]
21
- end
22
- end