kamal 1.8.3 → 2.7.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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/kamal/cli/accessory.rb +92 -38
  4. data/lib/kamal/cli/alias/command.rb +10 -0
  5. data/lib/kamal/cli/app/{prepare_assets.rb → assets.rb} +1 -1
  6. data/lib/kamal/cli/app/boot.rb +23 -16
  7. data/lib/kamal/cli/app/error_pages.rb +33 -0
  8. data/lib/kamal/cli/app/ssl_certificates.rb +28 -0
  9. data/lib/kamal/cli/app.rb +132 -30
  10. data/lib/kamal/cli/base.rb +57 -53
  11. data/lib/kamal/cli/build.rb +81 -38
  12. data/lib/kamal/cli/healthcheck/barrier.rb +2 -0
  13. data/lib/kamal/cli/healthcheck/poller.rb +18 -39
  14. data/lib/kamal/cli/lock.rb +2 -3
  15. data/lib/kamal/cli/main.rb +60 -59
  16. data/lib/kamal/cli/proxy.rb +290 -0
  17. data/lib/kamal/cli/prune.rb +0 -1
  18. data/lib/kamal/cli/registry.rb +2 -0
  19. data/lib/kamal/cli/secrets.rb +49 -0
  20. data/lib/kamal/cli/server.rb +6 -5
  21. data/lib/kamal/cli/templates/deploy.yml +53 -53
  22. data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +2 -12
  23. data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +3 -0
  24. data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +1 -1
  25. data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
  26. data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
  27. data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +1 -1
  28. data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +1 -1
  29. data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +19 -6
  30. data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +3 -0
  31. data/lib/kamal/cli/templates/secrets +17 -0
  32. data/lib/kamal/cli.rb +2 -0
  33. data/lib/kamal/commander/specifics.rb +19 -6
  34. data/lib/kamal/commander.rb +39 -32
  35. data/lib/kamal/commands/accessory/proxy.rb +16 -0
  36. data/lib/kamal/commands/accessory.rb +19 -19
  37. data/lib/kamal/commands/app/assets.rb +10 -10
  38. data/lib/kamal/commands/app/containers.rb +2 -2
  39. data/lib/kamal/commands/app/error_pages.rb +9 -0
  40. data/lib/kamal/commands/app/execution.rb +7 -4
  41. data/lib/kamal/commands/app/images.rb +1 -1
  42. data/lib/kamal/commands/app/logging.rb +16 -6
  43. data/lib/kamal/commands/app/proxy.rb +32 -0
  44. data/lib/kamal/commands/app.rb +25 -24
  45. data/lib/kamal/commands/auditor.rb +12 -3
  46. data/lib/kamal/commands/base.rb +54 -8
  47. data/lib/kamal/commands/builder/base.rb +46 -16
  48. data/lib/kamal/commands/builder/clone.rb +16 -14
  49. data/lib/kamal/commands/builder/cloud.rb +22 -0
  50. data/lib/kamal/commands/builder/hybrid.rb +21 -0
  51. data/lib/kamal/commands/builder/local.rb +14 -0
  52. data/lib/kamal/commands/builder/pack.rb +46 -0
  53. data/lib/kamal/commands/builder/remote.rb +63 -0
  54. data/lib/kamal/commands/builder.rb +21 -45
  55. data/lib/kamal/commands/docker.rb +4 -0
  56. data/lib/kamal/commands/hook.rb +8 -2
  57. data/lib/kamal/commands/lock.rb +2 -6
  58. data/lib/kamal/commands/proxy.rb +127 -0
  59. data/lib/kamal/commands/prune.rb +1 -9
  60. data/lib/kamal/commands/registry.rb +9 -7
  61. data/lib/kamal/commands/server.rb +11 -1
  62. data/lib/kamal/configuration/accessory.rb +89 -12
  63. data/lib/kamal/configuration/alias.rb +15 -0
  64. data/lib/kamal/configuration/builder.rb +73 -15
  65. data/lib/kamal/configuration/docs/accessory.yml +53 -15
  66. data/lib/kamal/configuration/docs/alias.yml +26 -0
  67. data/lib/kamal/configuration/docs/boot.yml +3 -3
  68. data/lib/kamal/configuration/docs/builder.yml +63 -38
  69. data/lib/kamal/configuration/docs/configuration.yml +62 -46
  70. data/lib/kamal/configuration/docs/env.yml +61 -17
  71. data/lib/kamal/configuration/docs/logging.yml +3 -3
  72. data/lib/kamal/configuration/docs/proxy.yml +168 -0
  73. data/lib/kamal/configuration/docs/registry.yml +20 -13
  74. data/lib/kamal/configuration/docs/role.yml +14 -13
  75. data/lib/kamal/configuration/docs/servers.yml +2 -2
  76. data/lib/kamal/configuration/docs/ssh.yml +23 -19
  77. data/lib/kamal/configuration/docs/sshkit.yml +4 -4
  78. data/lib/kamal/configuration/env/tag.rb +4 -3
  79. data/lib/kamal/configuration/env.rb +19 -17
  80. data/lib/kamal/configuration/proxy/boot.rb +129 -0
  81. data/lib/kamal/configuration/proxy.rb +124 -0
  82. data/lib/kamal/configuration/registry.rb +7 -6
  83. data/lib/kamal/configuration/role.rb +69 -98
  84. data/lib/kamal/configuration/servers.rb +8 -1
  85. data/lib/kamal/configuration/validator/accessory.rb +6 -2
  86. data/lib/kamal/configuration/validator/alias.rb +15 -0
  87. data/lib/kamal/configuration/validator/builder.rb +6 -0
  88. data/lib/kamal/configuration/validator/proxy.rb +25 -0
  89. data/lib/kamal/configuration/validator/role.rb +3 -1
  90. data/lib/kamal/configuration/validator/servers.rb +1 -1
  91. data/lib/kamal/configuration/validator.rb +62 -24
  92. data/lib/kamal/configuration.rb +96 -50
  93. data/lib/kamal/docker.rb +30 -0
  94. data/lib/kamal/env_file.rb +7 -1
  95. data/lib/kamal/git.rb +10 -0
  96. data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +51 -0
  97. data/lib/kamal/secrets/adapters/base.rb +33 -0
  98. data/lib/kamal/secrets/adapters/bitwarden.rb +81 -0
  99. data/lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb +66 -0
  100. data/lib/kamal/secrets/adapters/doppler.rb +57 -0
  101. data/lib/kamal/secrets/adapters/enpass.rb +71 -0
  102. data/lib/kamal/secrets/adapters/gcp_secret_manager.rb +112 -0
  103. data/lib/kamal/secrets/adapters/last_pass.rb +40 -0
  104. data/lib/kamal/secrets/adapters/one_password.rb +104 -0
  105. data/lib/kamal/secrets/adapters/passbolt.rb +130 -0
  106. data/lib/kamal/secrets/adapters/test.rb +14 -0
  107. data/lib/kamal/secrets/adapters.rb +16 -0
  108. data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +33 -0
  109. data/lib/kamal/secrets.rb +42 -0
  110. data/lib/kamal/sshkit_with_ext.rb +1 -0
  111. data/lib/kamal/utils.rb +30 -0
  112. data/lib/kamal/version.rb +1 -1
  113. data/lib/kamal.rb +3 -1
  114. metadata +63 -36
  115. data/lib/kamal/cli/env.rb +0 -54
  116. data/lib/kamal/cli/templates/sample_hooks/post-traefik-reboot.sample +0 -3
  117. data/lib/kamal/cli/templates/sample_hooks/pre-traefik-reboot.sample +0 -3
  118. data/lib/kamal/cli/templates/template.env +0 -2
  119. data/lib/kamal/cli/traefik.rb +0 -122
  120. data/lib/kamal/commands/app/cord.rb +0 -22
  121. data/lib/kamal/commands/builder/multiarch/remote.rb +0 -65
  122. data/lib/kamal/commands/builder/multiarch.rb +0 -41
  123. data/lib/kamal/commands/builder/native/cached.rb +0 -25
  124. data/lib/kamal/commands/builder/native/remote.rb +0 -67
  125. data/lib/kamal/commands/builder/native.rb +0 -20
  126. data/lib/kamal/commands/traefik.rb +0 -85
  127. data/lib/kamal/configuration/docs/healthcheck.yml +0 -59
  128. data/lib/kamal/configuration/docs/traefik.yml +0 -62
  129. data/lib/kamal/configuration/healthcheck.rb +0 -63
  130. data/lib/kamal/configuration/traefik.rb +0 -60
@@ -5,15 +5,22 @@ class Kamal::Cli::Build < Kamal::Cli::Base
5
5
 
6
6
  desc "deliver", "Build app and push app image to registry then pull image on servers"
7
7
  def deliver
8
- push
9
- pull
8
+ invoke :push
9
+ invoke :pull
10
10
  end
11
11
 
12
12
  desc "push", "Build and push app image to registry"
13
+ option :output, type: :string, default: "registry", banner: "export_type", desc: "Exported type for the build result, and may be any exported type supported by 'buildx --output'."
13
14
  def push
14
15
  cli = self
15
16
 
16
- verify_local_dependencies
17
+ # Ensure pre-connect hooks run before the build, they may needed for a remote builder
18
+ # or the pre-build hooks.
19
+ pre_connect_if_required
20
+
21
+ ensure_docker_installed
22
+ login_to_registry_locally
23
+
17
24
  run_hook "pre-build"
18
25
 
19
26
  uncommitted_changes = Kamal::Git.uncommitted_changes
@@ -30,49 +37,52 @@ class Kamal::Cli::Build < Kamal::Cli::Base
30
37
  say "Building with uncommitted changes:\n #{uncommitted_changes}", :yellow
31
38
  end
32
39
 
33
- # Get the command here to ensure the Dir.chdir doesn't interfere with it
34
- push = KAMAL.builder.push
40
+ with_env(KAMAL.config.builder.secrets) do
41
+ run_locally do
42
+ begin
43
+ execute *KAMAL.builder.inspect_builder
44
+ rescue SSHKit::Command::Failed => e
45
+ if e.message =~ /(context not found|no builder|no compatible builder|does not exist)/
46
+ warn "Missing compatible builder, so creating a new one first"
47
+ begin
48
+ cli.remove
49
+ rescue SSHKit::Command::Failed
50
+ raise unless e.message =~ /(context not found|no builder|does not exist)/
51
+ end
52
+ cli.create
53
+ else
54
+ raise
55
+ end
56
+ end
35
57
 
36
- run_locally do
37
- begin
38
- context_hosts = capture_with_info(*KAMAL.builder.context_hosts).split("\n")
58
+ # Get the command here to ensure the Dir.chdir doesn't interfere with it
59
+ push = KAMAL.builder.push(cli.options[:output])
39
60
 
40
- if context_hosts != KAMAL.builder.config_context_hosts
41
- warn "Context hosts have changed, so re-creating builder, was: #{context_hosts.join(", ")}], now: #{KAMAL.builder.config_context_hosts.join(", ")}"
42
- cli.remove
43
- cli.create
44
- end
45
- rescue SSHKit::Command::Failed => e
46
- if e.message =~ /(context not found|no builder|does not exist)/
47
- warn "Missing compatible builder, so creating a new one first"
48
- cli.create
49
- else
50
- raise
61
+ KAMAL.with_verbosity(:debug) do
62
+ Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
51
63
  end
52
64
  end
53
-
54
- KAMAL.with_verbosity(:debug) do
55
- Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
56
- end
57
65
  end
58
66
  end
59
67
 
60
68
  desc "pull", "Pull app image from registry onto servers"
61
69
  def pull
70
+ login_to_registry_remotely
71
+
62
72
  if (first_hosts = mirror_hosts).any?
63
73
  #  Pull on a single host per mirror first to seed them
64
74
  say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
65
75
  pull_on_hosts(first_hosts)
66
76
  say "Pulling image on remaining hosts...", :magenta
67
- pull_on_hosts(KAMAL.hosts - first_hosts)
77
+ pull_on_hosts(KAMAL.app_hosts - first_hosts)
68
78
  else
69
- pull_on_hosts(KAMAL.hosts)
79
+ pull_on_hosts(KAMAL.app_hosts)
70
80
  end
71
81
  end
72
82
 
73
83
  desc "create", "Create a build setup"
74
84
  def create
75
- if (remote_host = KAMAL.config.builder.remote_host)
85
+ if (remote_host = KAMAL.config.builder.remote)
76
86
  connect_to_remote_host(remote_host)
77
87
  end
78
88
 
@@ -107,21 +117,42 @@ class Kamal::Cli::Build < Kamal::Cli::Base
107
117
  end
108
118
  end
109
119
 
110
- private
111
- def verify_local_dependencies
112
- run_locally do
113
- begin
114
- execute *KAMAL.builder.ensure_local_dependencies_installed
115
- rescue SSHKit::Command::Failed => e
116
- build_error = e.message =~ /command not found/ ?
117
- "Docker is not installed locally" :
118
- "Docker buildx plugin is not installed locally"
120
+ desc "dev", "Build using the working directory, tag it as dirty, and push to local image store."
121
+ option :output, type: :string, default: "docker", banner: "export_type", desc: "Exported type for the build result, and may be any exported type supported by 'buildx --output'."
122
+ def dev
123
+ cli = self
124
+
125
+ ensure_docker_installed
119
126
 
120
- raise BuildError, build_error
127
+ docker_included_files = Set.new(Kamal::Docker.included_files)
128
+ git_uncommitted_files = Set.new(Kamal::Git.uncommitted_files)
129
+ git_untracked_files = Set.new(Kamal::Git.untracked_files)
130
+
131
+ docker_uncommitted_files = docker_included_files & git_uncommitted_files
132
+ if docker_uncommitted_files.any?
133
+ say "WARNING: Files with uncommitted changes will be present in the dev container:", :yellow
134
+ docker_uncommitted_files.sort.each { |f| say " #{f}", :yellow }
135
+ say
136
+ end
137
+
138
+ docker_untracked_files = docker_included_files & git_untracked_files
139
+ if docker_untracked_files.any?
140
+ say "WARNING: Untracked files will be present in the dev container:", :yellow
141
+ docker_untracked_files.sort.each { |f| say " #{f}", :yellow }
142
+ say
143
+ end
144
+
145
+ with_env(KAMAL.config.builder.secrets) do
146
+ run_locally do
147
+ build = KAMAL.builder.push(cli.options[:output], tag_as_dirty: true)
148
+ KAMAL.with_verbosity(:debug) do
149
+ execute(*build)
121
150
  end
122
151
  end
123
152
  end
153
+ end
124
154
 
155
+ private
125
156
  def connect_to_remote_host(remote_host)
126
157
  remote_uri = URI.parse(remote_host)
127
158
  if remote_uri.scheme == "ssh"
@@ -136,9 +167,9 @@ class Kamal::Cli::Build < Kamal::Cli::Base
136
167
  end
137
168
 
138
169
  def mirror_hosts
139
- if KAMAL.hosts.many?
170
+ if KAMAL.app_hosts.many?
140
171
  mirror_hosts = Concurrent::Hash.new
141
- on(KAMAL.hosts) do |host|
172
+ on(KAMAL.app_hosts) do |host|
142
173
  first_mirror = capture_with_info(*KAMAL.builder.first_mirror).strip.presence
143
174
  mirror_hosts[first_mirror] ||= host.to_s if first_mirror
144
175
  rescue SSHKit::Command::Failed => e
@@ -158,4 +189,16 @@ class Kamal::Cli::Build < Kamal::Cli::Base
158
189
  execute *KAMAL.builder.validate_image
159
190
  end
160
191
  end
192
+
193
+ def login_to_registry_locally
194
+ run_locally do
195
+ execute *KAMAL.registry.login
196
+ end
197
+ end
198
+
199
+ def login_to_registry_remotely
200
+ on(KAMAL.app_hosts) do
201
+ execute *KAMAL.registry.login
202
+ end
203
+ end
161
204
  end
@@ -1,3 +1,5 @@
1
+ require "concurrent/ivar"
2
+
1
3
  class Kamal::Cli::Healthcheck::Barrier
2
4
  def initialize
3
5
  @ivar = Concurrent::IVar.new
@@ -1,51 +1,30 @@
1
1
  module Kamal::Cli::Healthcheck::Poller
2
2
  extend self
3
3
 
4
- TRAEFIK_UPDATE_DELAY = 5
5
-
6
-
7
- def wait_for_healthy(pause_after_ready: false, &block)
4
+ def wait_for_healthy(role, &block)
8
5
  attempt = 1
9
- max_attempts = KAMAL.config.healthcheck.max_attempts
6
+ timeout_at = Time.now + KAMAL.config.deploy_timeout
7
+ readiness_delay = KAMAL.config.readiness_delay
10
8
 
11
9
  begin
12
- case status = block.call
13
- when "healthy"
14
- sleep TRAEFIK_UPDATE_DELAY if pause_after_ready
15
- when "running" # No health check configured
16
- sleep KAMAL.config.readiness_delay if pause_after_ready
17
- else
18
- raise Kamal::Cli::Healthcheck::Error, "container not ready (#{status})"
19
- end
20
- rescue Kamal::Cli::Healthcheck::Error => e
21
- if attempt <= max_attempts
22
- info "#{e.message}, retrying in #{attempt}s (attempt #{attempt}/#{max_attempts})..."
23
- sleep attempt
24
- attempt += 1
25
- retry
26
- else
27
- raise
10
+ status = block.call
11
+
12
+ if status == "running"
13
+ # Wait for the readiness delay and confirm it is still running
14
+ if readiness_delay > 0
15
+ info "Container is running, waiting for readiness delay of #{readiness_delay} seconds"
16
+ sleep readiness_delay
17
+ status = block.call
18
+ end
28
19
  end
29
- end
30
20
 
31
- info "Container is healthy!"
32
- end
33
-
34
- def wait_for_unhealthy(pause_after_ready: false, &block)
35
- attempt = 1
36
- max_attempts = KAMAL.config.healthcheck.max_attempts
37
-
38
- begin
39
- case status = block.call
40
- when "unhealthy"
41
- sleep TRAEFIK_UPDATE_DELAY if pause_after_ready
42
- else
43
- raise Kamal::Cli::Healthcheck::Error, "container not unhealthy (#{status})"
21
+ unless %w[ running healthy ].include?(status)
22
+ raise Kamal::Cli::Healthcheck::Error, "container not ready after #{KAMAL.config.deploy_timeout} seconds (#{status})"
44
23
  end
45
24
  rescue Kamal::Cli::Healthcheck::Error => e
46
- if attempt <= max_attempts
47
- info "#{e.message}, retrying in #{attempt}s (attempt #{attempt}/#{max_attempts})..."
48
- sleep attempt
25
+ time_left = timeout_at - Time.now
26
+ if time_left > 0
27
+ sleep [ attempt, time_left ].min
49
28
  attempt += 1
50
29
  retry
51
30
  else
@@ -53,7 +32,7 @@ module Kamal::Cli::Healthcheck::Poller
53
32
  end
54
33
  end
55
34
 
56
- info "Container is unhealthy!"
35
+ info "Container is healthy!"
57
36
  end
58
37
 
59
38
  private
@@ -3,7 +3,6 @@ class Kamal::Cli::Lock < Kamal::Cli::Base
3
3
  def status
4
4
  handle_missing_lock do
5
5
  on(KAMAL.primary_host) do
6
- execute *KAMAL.server.ensure_run_directory
7
6
  puts capture_with_debug(*KAMAL.lock.status)
8
7
  end
9
8
  end
@@ -13,9 +12,10 @@ class Kamal::Cli::Lock < Kamal::Cli::Base
13
12
  option :message, aliases: "-m", type: :string, desc: "A lock message", required: true
14
13
  def acquire
15
14
  message = options[:message]
15
+ ensure_run_directory
16
+
16
17
  raise_if_locked do
17
18
  on(KAMAL.primary_host) do
18
- execute *KAMAL.server.ensure_run_directory
19
19
  execute *KAMAL.lock.acquire(message, KAMAL.config.version), verbosity: :debug
20
20
  end
21
21
  say "Acquired the deploy lock"
@@ -26,7 +26,6 @@ class Kamal::Cli::Lock < Kamal::Cli::Base
26
26
  def release
27
27
  handle_missing_lock do
28
28
  on(KAMAL.primary_host) do
29
- execute *KAMAL.server.ensure_run_directory
30
29
  execute *KAMAL.lock.release, verbosity: :debug
31
30
  end
32
31
  say "Released the deploy lock"
@@ -9,25 +9,17 @@ class Kamal::Cli::Main < Kamal::Cli::Base
9
9
  say "Ensure Docker is installed...", :magenta
10
10
  invoke "kamal:cli:server:bootstrap", [], invoke_options
11
11
 
12
- say "Evaluate and push env files...", :magenta
13
- invoke "kamal:cli:main:envify", [], invoke_options
14
- invoke "kamal:cli:env:push", [], invoke_options
15
-
16
- invoke "kamal:cli:accessory:boot", [ "all" ], invoke_options
17
- deploy
12
+ deploy(boot_accessories: true)
18
13
  end
19
14
  end
20
15
  end
21
16
 
22
17
  desc "deploy", "Deploy app to servers"
23
18
  option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
24
- def deploy
19
+ def deploy(boot_accessories: false)
25
20
  runtime = print_runtime do
26
21
  invoke_options = deploy_options
27
22
 
28
- say "Log into image registry...", :magenta
29
- invoke "kamal:cli:registry:login", [], invoke_options.merge(skip_local: options[:skip_push])
30
-
31
23
  if options[:skip_push]
32
24
  say "Pull app image...", :magenta
33
25
  invoke "kamal:cli:build:pull", [], invoke_options
@@ -37,10 +29,12 @@ class Kamal::Cli::Main < Kamal::Cli::Base
37
29
  end
38
30
 
39
31
  with_lock do
40
- run_hook "pre-deploy"
32
+ run_hook "pre-deploy", secrets: true
41
33
 
42
- say "Ensure Traefik is running...", :magenta
43
- invoke "kamal:cli:traefik:boot", [], invoke_options
34
+ say "Ensure kamal-proxy is running...", :magenta
35
+ invoke "kamal:cli:proxy:boot", [], invoke_options
36
+
37
+ invoke "kamal:cli:accessory:boot", [ "all" ], invoke_options if boot_accessories
44
38
 
45
39
  say "Detect stale containers...", :magenta
46
40
  invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)
@@ -52,10 +46,10 @@ class Kamal::Cli::Main < Kamal::Cli::Base
52
46
  end
53
47
  end
54
48
 
55
- run_hook "post-deploy", runtime: runtime.round
49
+ run_hook "post-deploy", secrets: true, runtime: runtime.round.to_s
56
50
  end
57
51
 
58
- desc "redeploy", "Deploy app to servers without bootstrapping servers, starting Traefik, pruning, and registry login"
52
+ desc "redeploy", "Deploy app to servers without bootstrapping servers, starting kamal-proxy and pruning"
59
53
  option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
60
54
  def redeploy
61
55
  runtime = print_runtime do
@@ -70,7 +64,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
70
64
  end
71
65
 
72
66
  with_lock do
73
- run_hook "pre-deploy"
67
+ run_hook "pre-deploy", secrets: true
74
68
 
75
69
  say "Detect stale containers...", :magenta
76
70
  invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)
@@ -79,7 +73,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
79
73
  end
80
74
  end
81
75
 
82
- run_hook "post-deploy", runtime: runtime.round
76
+ run_hook "post-deploy", secrets: true, runtime: runtime.round.to_s
83
77
  end
84
78
 
85
79
  desc "rollback [VERSION]", "Rollback app to VERSION"
@@ -93,7 +87,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
93
87
  old_version = nil
94
88
 
95
89
  if container_available?(version)
96
- run_hook "pre-deploy"
90
+ run_hook "pre-deploy", secrets: true
97
91
 
98
92
  invoke "kamal:cli:app:boot", [], invoke_options.merge(version: version)
99
93
  rolled_back = true
@@ -103,12 +97,12 @@ class Kamal::Cli::Main < Kamal::Cli::Base
103
97
  end
104
98
  end
105
99
 
106
- run_hook "post-deploy", runtime: runtime.round if rolled_back
100
+ run_hook "post-deploy", secrets: true, runtime: runtime.round.to_s if rolled_back
107
101
  end
108
102
 
109
103
  desc "details", "Show details about all containers"
110
104
  def details
111
- invoke "kamal:cli:traefik:details"
105
+ invoke "kamal:cli:proxy:details"
112
106
  invoke "kamal:cli:app:details"
113
107
  invoke "kamal:cli:accessory:details", [ "all" ]
114
108
  end
@@ -127,7 +121,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
127
121
  end
128
122
  end
129
123
 
130
- desc "docs", "Show Kamal documentation for configuration setting"
124
+ desc "docs [SECTION]", "Show Kamal configuration documentation"
131
125
  def docs(section = nil)
132
126
  case section
133
127
  when NilClass
@@ -139,7 +133,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
139
133
  puts "No documentation found for #{section}"
140
134
  end
141
135
 
142
- desc "init", "Create config stub in config/deploy.yml and env stub in .env"
136
+ desc "init", "Create config stub in config/deploy.yml and secrets stub in .kamal"
143
137
  option :bundle, type: :boolean, default: false, desc: "Add Kamal to the Gemfile and create a bin/kamal binstub"
144
138
  def init
145
139
  require "fileutils"
@@ -152,9 +146,10 @@ class Kamal::Cli::Main < Kamal::Cli::Base
152
146
  puts "Created configuration file in config/deploy.yml"
153
147
  end
154
148
 
155
- unless (deploy_file = Pathname.new(File.expand_path(".env"))).exist?
156
- FileUtils.cp_r Pathname.new(File.expand_path("templates/template.env", __dir__)), deploy_file
157
- puts "Created .env file"
149
+ unless (secrets_file = Pathname.new(File.expand_path(".kamal/secrets"))).exist?
150
+ FileUtils.mkdir_p secrets_file.dirname
151
+ FileUtils.cp_r Pathname.new(File.expand_path("templates/secrets", __dir__)), secrets_file
152
+ puts "Created .kamal/secrets file"
158
153
  end
159
154
 
160
155
  unless (hooks_dir = Pathname.new(File.expand_path(".kamal/hooks"))).exist?
@@ -179,44 +174,50 @@ class Kamal::Cli::Main < Kamal::Cli::Base
179
174
  end
180
175
  end
181
176
 
182
- desc "envify", "Create .env by evaluating .env.erb (or .env.staging.erb -> .env.staging when using -d staging)"
183
- option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip .env file push"
184
- def envify
185
- if destination = options[:destination]
186
- env_template_path = ".env.#{destination}.erb"
187
- env_path = ".env.#{destination}"
188
- else
189
- env_template_path = ".env.erb"
190
- env_path = ".env"
191
- end
192
-
193
- if Pathname.new(File.expand_path(env_template_path)).exist?
194
- # Ensure existing env doesn't pollute template evaluation
195
- content = with_original_env { ERB.new(File.read(env_template_path), trim_mode: "-").result }
196
- File.write(env_path, content, perm: 0600)
197
-
198
- unless options[:skip_push]
199
- reload_env
200
- invoke "kamal:cli:env:push", options
201
- end
202
- else
203
- puts "Skipping envify (no #{env_template_path} exist)"
204
- end
205
- end
206
-
207
- desc "remove", "Remove Traefik, app, accessories, and registry session from servers"
177
+ desc "remove", "Remove kamal-proxy, app, accessories, and registry session from servers"
208
178
  option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
209
179
  def remove
210
180
  confirming "This will remove all containers and images. Are you sure?" do
211
181
  with_lock do
212
- invoke "kamal:cli:traefik:remove", [], options.without(:confirmed)
213
182
  invoke "kamal:cli:app:remove", [], options.without(:confirmed)
183
+ invoke "kamal:cli:proxy:remove", [], options.without(:confirmed)
214
184
  invoke "kamal:cli:accessory:remove", [ "all" ], options
215
185
  invoke "kamal:cli:registry:logout", [], options.without(:confirmed).merge(skip_local: true)
216
186
  end
217
187
  end
218
188
  end
219
189
 
190
+ desc "upgrade", "Upgrade from Kamal 1.x to 2.0"
191
+ option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
192
+ option :rolling, type: :boolean, default: false, desc: "Upgrade one host at a time"
193
+ def upgrade
194
+ confirming "This will replace Traefik with kamal-proxy and restart all accessories" do
195
+ with_lock do
196
+ if options[:rolling]
197
+ KAMAL.hosts.each do |host|
198
+ KAMAL.with_specific_hosts(host) do
199
+ say "Upgrading #{host}...", :magenta
200
+ if KAMAL.app_hosts.include?(host)
201
+ invoke "kamal:cli:proxy:upgrade", [], options.merge(confirmed: true, rolling: false)
202
+ reset_invocation(Kamal::Cli::Proxy)
203
+ end
204
+ if KAMAL.accessory_hosts.include?(host)
205
+ invoke "kamal:cli:accessory:upgrade", [ "all" ], options.merge(confirmed: true, rolling: false)
206
+ reset_invocation(Kamal::Cli::Accessory)
207
+ end
208
+ say "Upgraded #{host}", :magenta
209
+ end
210
+ end
211
+ else
212
+ say "Upgrading all hosts...", :magenta
213
+ invoke "kamal:cli:proxy:upgrade", [], options.merge(confirmed: true)
214
+ invoke "kamal:cli:accessory:upgrade", [ "all" ], options.merge(confirmed: true)
215
+ say "Upgraded all hosts", :magenta
216
+ end
217
+ end
218
+ end
219
+ end
220
+
220
221
  desc "version", "Show Kamal version"
221
222
  def version
222
223
  puts Kamal::VERSION
@@ -231,28 +232,28 @@ class Kamal::Cli::Main < Kamal::Cli::Base
231
232
  desc "build", "Build application image"
232
233
  subcommand "build", Kamal::Cli::Build
233
234
 
234
- desc "env", "Manage environment files"
235
- subcommand "env", Kamal::Cli::Env
236
-
237
235
  desc "lock", "Manage the deploy lock"
238
236
  subcommand "lock", Kamal::Cli::Lock
239
237
 
238
+ desc "proxy", "Manage kamal-proxy"
239
+ subcommand "proxy", Kamal::Cli::Proxy
240
+
240
241
  desc "prune", "Prune old application images and containers"
241
242
  subcommand "prune", Kamal::Cli::Prune
242
243
 
243
244
  desc "registry", "Login and -out of the image registry"
244
245
  subcommand "registry", Kamal::Cli::Registry
245
246
 
247
+ desc "secrets", "Helpers for extracting secrets"
248
+ subcommand "secrets", Kamal::Cli::Secrets
249
+
246
250
  desc "server", "Bootstrap servers with curl and Docker"
247
251
  subcommand "server", Kamal::Cli::Server
248
252
 
249
- desc "traefik", "Manage Traefik load balancer"
250
- subcommand "traefik", Kamal::Cli::Traefik
251
-
252
253
  private
253
254
  def container_available?(version)
254
255
  begin
255
- on(KAMAL.hosts) do
256
+ on(KAMAL.app_hosts) do
256
257
  KAMAL.roles_on(host).each do |role|
257
258
  container_id = capture_with_info(*KAMAL.app(role: role, host: host).container_id_for_version(version))
258
259
  raise "Container not found" unless container_id.present?