kamal 1.8.2 → 2.0.0.beta1

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