dash 3.2.0 → 3.2.1
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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/bin/dash +2 -2
- data/lib/{kamal → dash}/cli/accessory.rb +24 -24
- data/lib/dash/cli/alias/command.rb +10 -0
- data/lib/{kamal → dash}/cli/app/assets.rb +2 -2
- data/lib/{kamal → dash}/cli/app/boot.rb +15 -15
- data/lib/{kamal → dash}/cli/app/error_pages.rb +6 -6
- data/lib/{kamal → dash}/cli/app/rollout_boot.rb +6 -6
- data/lib/{kamal → dash}/cli/app/ssl_certificates.rb +3 -3
- data/lib/{kamal → dash}/cli/app.rb +86 -86
- data/lib/{kamal → dash}/cli/base.rb +71 -47
- data/lib/dash/cli/build/clone.rb +59 -0
- data/lib/{kamal → dash}/cli/build/port_forwarding.rb +1 -1
- data/lib/{kamal → dash}/cli/build.rb +45 -45
- data/lib/{kamal → dash}/cli/doctor/config_checks.rb +3 -3
- data/lib/{kamal → dash}/cli/doctor/endpoint_checks.rb +25 -7
- data/lib/{kamal → dash}/cli/doctor/host_checks.rb +18 -18
- data/lib/{kamal → dash}/cli/doctor.rb +9 -9
- data/lib/{kamal → dash}/cli/healthcheck/barrier.rb +2 -2
- data/lib/{kamal → dash}/cli/healthcheck/drift_error.rb +2 -2
- data/lib/dash/cli/healthcheck/error.rb +2 -0
- data/lib/{kamal → dash}/cli/healthcheck/poller.rb +7 -7
- data/lib/{kamal → dash}/cli/lock.rb +1 -1
- data/lib/dash/cli/main/migrate.rb +77 -0
- data/lib/{kamal → dash}/cli/main.rb +77 -66
- data/lib/{kamal → dash}/cli/proxy/drift.rb +3 -3
- data/lib/{kamal → dash}/cli/proxy/loadbalancer_claim.rb +3 -3
- data/lib/{kamal → dash}/cli/proxy/loadbalancer_reboot.rb +22 -22
- data/lib/{kamal → dash}/cli/proxy/reboot.rb +15 -15
- data/lib/{kamal → dash}/cli/proxy.rb +166 -154
- data/lib/{kamal → dash}/cli/prune.rb +10 -10
- data/lib/{kamal → dash}/cli/registry.rb +11 -11
- data/lib/{kamal → dash}/cli/secrets.rb +3 -3
- data/lib/{kamal → dash}/cli/server.rb +14 -14
- data/lib/{kamal → dash}/cli/templates/deploy.yml +4 -4
- data/lib/dash/cli/templates/sample_hooks/docker-setup.sample +3 -0
- data/lib/dash/cli/templates/sample_hooks/post-app-boot.sample +3 -0
- data/lib/dash/cli/templates/sample_hooks/post-app-stop.sample +9 -0
- data/lib/dash/cli/templates/sample_hooks/post-deploy.sample +14 -0
- data/lib/dash/cli/templates/sample_hooks/post-proxy-deploy.sample +3 -0
- data/lib/dash/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
- data/lib/dash/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
- data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-app-stop.sample +2 -2
- data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-build.sample +8 -8
- data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-connect.sample +8 -8
- data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-deploy.sample +9 -9
- data/lib/dash/cli/templates/sample_hooks/pre-proxy-deploy.sample +3 -0
- data/lib/dash/cli/templates/sample_hooks/pre-proxy-reboot.sample +3 -0
- data/lib/{kamal → dash}/cli/templates/secrets +6 -6
- data/lib/{kamal → dash}/cli.rb +2 -2
- data/lib/{kamal → dash}/commander/specifics.rb +2 -2
- data/lib/{kamal → dash}/commander.rb +31 -23
- data/lib/{kamal → dash}/commands/accessory/proxy.rb +1 -1
- data/lib/{kamal → dash}/commands/accessory.rb +2 -2
- data/lib/{kamal → dash}/commands/app/assets.rb +1 -1
- data/lib/{kamal → dash}/commands/app/containers.rb +1 -1
- data/lib/{kamal → dash}/commands/app/error_pages.rb +2 -2
- data/lib/{kamal → dash}/commands/app/execution.rb +1 -1
- data/lib/{kamal → dash}/commands/app/images.rb +1 -1
- data/lib/{kamal → dash}/commands/app/logging.rb +1 -1
- data/lib/{kamal → dash}/commands/app/proxy.rb +1 -1
- data/lib/{kamal → dash}/commands/app.rb +1 -1
- data/lib/{kamal → dash}/commands/auditor.rb +3 -7
- data/lib/{kamal → dash}/commands/base.rb +39 -3
- data/lib/{kamal → dash}/commands/builder/base.rb +2 -2
- data/lib/{kamal → dash}/commands/builder/clone.rb +3 -3
- data/lib/{kamal → dash}/commands/builder/cloud.rb +1 -1
- data/lib/{kamal → dash}/commands/builder/hybrid.rb +1 -1
- data/lib/{kamal → dash}/commands/builder/local.rb +1 -1
- data/lib/{kamal → dash}/commands/builder/pack.rb +1 -1
- data/lib/{kamal → dash}/commands/builder/remote.rb +1 -1
- data/lib/{kamal → dash}/commands/builder.rb +7 -7
- data/lib/{kamal → dash}/commands/docker.rb +1 -1
- data/lib/{kamal → dash}/commands/hook.rb +1 -1
- data/lib/{kamal → dash}/commands/loadbalancer.rb +9 -8
- data/lib/{kamal → dash}/commands/lock.rb +2 -2
- data/lib/{kamal → dash}/commands/proxy/cert_transfer.rb +2 -2
- data/lib/{kamal → dash}/commands/proxy.rb +20 -10
- data/lib/{kamal → dash}/commands/prune.rb +1 -1
- data/lib/dash/commands/registry.rb +44 -0
- data/lib/{kamal → dash}/commands/server.rb +1 -5
- data/lib/dash/commands.rb +2 -0
- data/lib/{kamal → dash}/configuration/accessory.rb +11 -11
- data/lib/{kamal → dash}/configuration/alias.rb +3 -3
- data/lib/{kamal → dash}/configuration/boot.rb +2 -2
- data/lib/{kamal → dash}/configuration/builder.rb +7 -7
- data/lib/{kamal → dash}/configuration/docs/builder.yml +3 -3
- data/lib/{kamal → dash}/configuration/docs/configuration.yml +12 -5
- data/lib/{kamal → dash}/configuration/docs/env.yml +10 -10
- data/lib/{kamal → dash}/configuration/docs/proxy.yml +31 -8
- data/lib/{kamal → dash}/configuration/docs/registry.yml +5 -5
- data/lib/{kamal → dash}/configuration/env/tag.rb +2 -2
- data/lib/{kamal → dash}/configuration/env.rb +5 -5
- data/lib/{kamal → dash}/configuration/loadbalancer.rb +3 -3
- data/lib/{kamal → dash}/configuration/logging.rb +3 -3
- data/lib/{kamal → dash}/configuration/output.rb +4 -4
- data/lib/{kamal → dash}/configuration/proxy/acme.rb +4 -3
- data/lib/{kamal → dash}/configuration/proxy/boot.rb +7 -7
- data/lib/{kamal → dash}/configuration/proxy/run.rb +11 -11
- data/lib/{kamal → dash}/configuration/proxy.rb +15 -15
- data/lib/{kamal → dash}/configuration/registry.rb +3 -3
- data/lib/{kamal → dash}/configuration/role/healthcheck.rb +3 -3
- data/lib/{kamal → dash}/configuration/role.rb +12 -12
- data/lib/{kamal → dash}/configuration/servers.rb +4 -4
- data/lib/{kamal → dash}/configuration/ssh.rb +2 -2
- data/lib/{kamal → dash}/configuration/sshkit.rb +2 -2
- data/lib/{kamal → dash}/configuration/validation.rb +2 -2
- data/lib/{kamal → dash}/configuration/validator/accessory.rb +1 -1
- data/lib/{kamal → dash}/configuration/validator/alias.rb +2 -2
- data/lib/{kamal → dash}/configuration/validator/builder.rb +1 -1
- data/lib/dash/configuration/validator/configuration.rb +6 -0
- data/lib/{kamal → dash}/configuration/validator/env.rb +2 -2
- data/lib/{kamal → dash}/configuration/validator/proxy.rb +8 -8
- data/lib/{kamal → dash}/configuration/validator/registry.rb +1 -1
- data/lib/{kamal → dash}/configuration/validator/role.rb +1 -1
- data/lib/{kamal → dash}/configuration/validator/servers.rb +1 -1
- data/lib/{kamal → dash}/configuration/validator.rb +4 -4
- data/lib/{kamal → dash}/configuration/volume.rb +2 -2
- data/lib/{kamal → dash}/configuration.rb +39 -29
- data/lib/{kamal → dash}/docker.rb +1 -1
- data/lib/{kamal → dash}/env_file.rb +1 -1
- data/lib/{kamal → dash}/git.rb +1 -1
- data/lib/{kamal → dash}/otel_shipper.rb +3 -3
- data/lib/{kamal → dash}/output/base_logger.rb +1 -1
- data/lib/{kamal → dash}/output/file_logger.rb +1 -1
- data/lib/{kamal → dash}/output/formatter.rb +1 -1
- data/lib/{kamal → dash}/output/otel_logger.rb +3 -3
- data/lib/dash/project_directory.rb +28 -0
- data/lib/{kamal → dash}/secrets/adapters/aws_secrets_manager.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/base.rb +2 -2
- data/lib/{kamal → dash}/secrets/adapters/bitwarden.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/bitwarden_secrets_manager.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/doppler.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/enpass.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/gcp_secret_manager.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/last_pass.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/one_password.rb +2 -2
- data/lib/{kamal → dash}/secrets/adapters/passbolt.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/test.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters.rb +2 -2
- data/lib/{kamal → dash}/secrets/dotenv/inline_command_substitution.rb +2 -2
- data/lib/{kamal → dash}/secrets.rb +6 -6
- data/lib/{kamal → dash}/tags.rb +8 -3
- data/lib/{kamal → dash}/utils/sensitive.rb +1 -1
- data/lib/{kamal → dash}/utils.rb +15 -5
- data/lib/dash/version.rb +3 -0
- data/lib/{kamal.rb → dash.rb} +4 -4
- metadata +156 -154
- data/lib/kamal/cli/alias/command.rb +0 -10
- data/lib/kamal/cli/build/clone.rb +0 -59
- data/lib/kamal/cli/healthcheck/error.rb +0 -2
- data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/post-app-stop.sample +0 -9
- data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +0 -14
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-deploy.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/pre-proxy-deploy.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +0 -3
- data/lib/kamal/commands/registry.rb +0 -38
- data/lib/kamal/commands.rb +0 -2
- data/lib/kamal/configuration/validator/configuration.rb +0 -6
- data/lib/kamal/version.rb +0 -3
- /data/lib/{kamal → dash}/configuration/docs/accessory.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/alias.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/boot.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/logging.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/output.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/role.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/servers.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/ssh.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/sshkit.yml +0 -0
- /data/lib/{kamal → dash}/sshkit_with_ext.rb +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require "thor"
|
|
2
|
-
require "
|
|
2
|
+
require "dash/sshkit_with_ext"
|
|
3
3
|
|
|
4
|
-
module
|
|
4
|
+
module Dash::Cli
|
|
5
5
|
class Base < Thor
|
|
6
6
|
include SSHKit::DSL
|
|
7
7
|
|
|
@@ -16,7 +16,13 @@ module Kamal::Cli
|
|
|
16
16
|
HOOK_MUTEX = Mutex.new
|
|
17
17
|
|
|
18
18
|
def self.exit_on_failure?() true end
|
|
19
|
-
def self.dynamic_command_class()
|
|
19
|
+
def self.dynamic_command_class() Dash::Cli::Alias::Command end
|
|
20
|
+
|
|
21
|
+
# One legacy-project-directory deprecation notice per process. Tests reset it
|
|
22
|
+
# in CliTestCase, the same way they reset the DASH singleton.
|
|
23
|
+
class << self
|
|
24
|
+
attr_accessor :legacy_project_directory_warned
|
|
25
|
+
end
|
|
20
26
|
|
|
21
27
|
class_option :verbose, type: :boolean, aliases: "-v", desc: "Detailed logging"
|
|
22
28
|
class_option :quiet, type: :boolean, aliases: "-q", desc: "Minimal logging"
|
|
@@ -37,7 +43,7 @@ module Kamal::Cli
|
|
|
37
43
|
class_option :lock_wait_interval, type: :numeric, default: 15, desc: "Seconds between deploy lock polls when --lock-wait is set"
|
|
38
44
|
|
|
39
45
|
def initialize(args = [], local_options = {}, config = {})
|
|
40
|
-
if config[:current_command].is_a?(
|
|
46
|
+
if config[:current_command].is_a?(Dash::Cli::Alias::Command)
|
|
41
47
|
# When Thor generates a dynamic command, it doesn't attempt to parse the arguments.
|
|
42
48
|
# For our purposes, it means the arguments are passed in args rather than local_options.
|
|
43
49
|
super([], args, config)
|
|
@@ -45,36 +51,39 @@ module Kamal::Cli
|
|
|
45
51
|
super
|
|
46
52
|
end
|
|
47
53
|
|
|
48
|
-
|
|
54
|
+
unless DASH.configured?
|
|
55
|
+
initialize_commander
|
|
56
|
+
warn_on_legacy_project_directory(config[:current_command]&.name)
|
|
57
|
+
end
|
|
49
58
|
end
|
|
50
59
|
|
|
51
|
-
# Public so collaborators like
|
|
60
|
+
# Public so collaborators like Dash::Cli::App::Boot can fire hooks, but not a Thor command.
|
|
52
61
|
no_commands do
|
|
53
62
|
def run_hook(hook, **extra_details)
|
|
54
|
-
if !options[:skip_hooks] &&
|
|
63
|
+
if !options[:skip_hooks] && DASH.hook.hook_exists?(hook)
|
|
55
64
|
details = {
|
|
56
|
-
hosts:
|
|
57
|
-
roles:
|
|
58
|
-
lock:
|
|
65
|
+
hosts: DASH.hosts.join(","),
|
|
66
|
+
roles: DASH.specific_roles&.join(","),
|
|
67
|
+
lock: DASH.holding_lock?.to_s,
|
|
59
68
|
command: command,
|
|
60
69
|
subcommand: subcommand
|
|
61
70
|
}.compact
|
|
62
71
|
|
|
63
|
-
hooks_output =
|
|
72
|
+
hooks_output = DASH.config.hooks_output_for(hook)
|
|
64
73
|
|
|
65
74
|
# CLI flags override config: -q hides all, -v shows all
|
|
66
75
|
# Config setting :verbose forces output, :quiet forces silence
|
|
67
|
-
hook_verbosity = if
|
|
76
|
+
hook_verbosity = if DASH.verbosity == :info && hooks_output
|
|
68
77
|
VERBOSITY.fetch(hooks_output)
|
|
69
78
|
else
|
|
70
|
-
|
|
79
|
+
DASH.verbosity
|
|
71
80
|
end
|
|
72
81
|
|
|
73
82
|
HOOK_MUTEX.synchronize do
|
|
74
|
-
with_env
|
|
75
|
-
|
|
83
|
+
with_env DASH.hook.env(**details, **extra_details) do
|
|
84
|
+
DASH.with_verbosity(hook_verbosity) do
|
|
76
85
|
run_locally do
|
|
77
|
-
execute *
|
|
86
|
+
execute *DASH.hook.run(hook)
|
|
78
87
|
end
|
|
79
88
|
end
|
|
80
89
|
rescue SSHKit::Command::Failed => e
|
|
@@ -90,8 +99,23 @@ module Kamal::Cli
|
|
|
90
99
|
options.merge(@_initializer.last[:class_options] || {})
|
|
91
100
|
end
|
|
92
101
|
|
|
102
|
+
# Process-scoped rather than commander-scoped: Dash::Cli::Alias::Command
|
|
103
|
+
# resets DASH and re-enters Dash::Cli::Main.start, so an aliased command
|
|
104
|
+
# builds a second commander and would otherwise warn twice. `dash migrate`
|
|
105
|
+
# is exempt - telling an operator to run the command they are running is noise.
|
|
106
|
+
def warn_on_legacy_project_directory(command_name)
|
|
107
|
+
return if command_name == "migrate"
|
|
108
|
+
return if Dash::Cli::Base.legacy_project_directory_warned
|
|
109
|
+
return unless Dash::ProjectDirectory.legacy?
|
|
110
|
+
|
|
111
|
+
Dash::Cli::Base.legacy_project_directory_warned = true
|
|
112
|
+
|
|
113
|
+
say "Using the legacy #{Dash::ProjectDirectory::LEGACY}/ project directory. Run `dash migrate` to move it to " \
|
|
114
|
+
"#{Dash::ProjectDirectory::CURRENT}/ (#{Dash::ProjectDirectory::LEGACY}/ support is removed in dash 5.0).", :yellow
|
|
115
|
+
end
|
|
116
|
+
|
|
93
117
|
def initialize_commander
|
|
94
|
-
|
|
118
|
+
DASH.tap do |commander|
|
|
95
119
|
if options[:verbose]
|
|
96
120
|
ENV["VERBOSE"] = "1" # For backtraces via cli/start
|
|
97
121
|
commander.verbosity = VERBOSITY[:verbose]
|
|
@@ -128,7 +152,7 @@ module Kamal::Cli
|
|
|
128
152
|
# Deploy lock outside, server lock inside. Every caller acquires in that
|
|
129
153
|
# order, and deploy locks are unique per destination, so no cycle forms.
|
|
130
154
|
def modify(lock: false, server_lock: false)
|
|
131
|
-
|
|
155
|
+
DASH.modify(command: command, subcommand: subcommand) do
|
|
132
156
|
guarded = -> { server_lock ? with_server_lock { yield } : yield }
|
|
133
157
|
|
|
134
158
|
lock ? with_lock(&guarded) : guarded.call
|
|
@@ -137,13 +161,13 @@ module Kamal::Cli
|
|
|
137
161
|
|
|
138
162
|
def say(message = "", *)
|
|
139
163
|
super unless options[:raw]
|
|
140
|
-
|
|
164
|
+
DASH.log(message.to_s)
|
|
141
165
|
end
|
|
142
166
|
|
|
143
167
|
# Raw output is written straight to stdout for piping, so silence SSHKit's
|
|
144
168
|
# command echoing that would otherwise corrupt the byte stream.
|
|
145
169
|
def with_raw_output(raw, &block)
|
|
146
|
-
raw ?
|
|
170
|
+
raw ? DASH.with_verbosity(:error, &block) : block.call
|
|
147
171
|
end
|
|
148
172
|
|
|
149
173
|
# kamal-proxy is one container per host, shared by every destination
|
|
@@ -151,7 +175,7 @@ module Kamal::Cli
|
|
|
151
175
|
# destinations deploying at once take different locks and both mutate the
|
|
152
176
|
# same proxy. Anything touching the proxy takes this lock as well.
|
|
153
177
|
def with_server_lock
|
|
154
|
-
if
|
|
178
|
+
if DASH.holding_server_lock?
|
|
155
179
|
yield
|
|
156
180
|
else
|
|
157
181
|
acquire_server_lock
|
|
@@ -173,8 +197,8 @@ module Kamal::Cli
|
|
|
173
197
|
def acquire_server_lock
|
|
174
198
|
ensure_run_directory
|
|
175
199
|
|
|
176
|
-
timeout =
|
|
177
|
-
interval =
|
|
200
|
+
timeout = DASH.lock_wait_timeout
|
|
201
|
+
interval = DASH.lock_wait_interval
|
|
178
202
|
deadline = Time.now + timeout
|
|
179
203
|
details_shown = false
|
|
180
204
|
|
|
@@ -187,7 +211,7 @@ module Kamal::Cli
|
|
|
187
211
|
|
|
188
212
|
begin
|
|
189
213
|
server_lock_hosts.each do |host|
|
|
190
|
-
execute_lock_acquire(AUTOMATIC_DEPLOY_LOCK_MESSAGE, lock:
|
|
214
|
+
execute_lock_acquire(AUTOMATIC_DEPLOY_LOCK_MESSAGE, lock: DASH.server_lock, hosts: host)
|
|
191
215
|
held << host
|
|
192
216
|
end
|
|
193
217
|
|
|
@@ -200,7 +224,7 @@ module Kamal::Cli
|
|
|
200
224
|
# That means the lock is free, not that anything went wrong, so
|
|
201
225
|
# it must never escape as LockMissingError and fail the deploy.
|
|
202
226
|
status = begin
|
|
203
|
-
capture_lock_status(lock:
|
|
227
|
+
capture_lock_status(lock: DASH.server_lock, hosts: server_lock_hosts.first)
|
|
204
228
|
rescue LockMissingError
|
|
205
229
|
nil
|
|
206
230
|
end
|
|
@@ -227,14 +251,14 @@ module Kamal::Cli
|
|
|
227
251
|
end
|
|
228
252
|
end
|
|
229
253
|
|
|
230
|
-
|
|
254
|
+
DASH.holding_server_lock = true
|
|
231
255
|
end
|
|
232
256
|
|
|
233
257
|
def release_server_lock
|
|
234
258
|
say "Releasing the server lock...", :magenta
|
|
235
259
|
release_server_lock_on(server_lock_hosts)
|
|
236
260
|
|
|
237
|
-
|
|
261
|
+
DASH.holding_server_lock = false
|
|
238
262
|
end
|
|
239
263
|
|
|
240
264
|
# Only ever called with hosts this process actually locked, so a missing
|
|
@@ -242,18 +266,18 @@ module Kamal::Cli
|
|
|
242
266
|
# another deploy's lock.
|
|
243
267
|
def release_server_lock_on(hosts)
|
|
244
268
|
Array(hosts).each do |host|
|
|
245
|
-
execute_lock_release(lock:
|
|
269
|
+
execute_lock_release(lock: DASH.server_lock, hosts: host)
|
|
246
270
|
rescue LockMissingError
|
|
247
271
|
nil
|
|
248
272
|
end
|
|
249
273
|
end
|
|
250
274
|
|
|
251
275
|
def server_lock_hosts
|
|
252
|
-
|
|
276
|
+
DASH.proxy_hosts.presence || DASH.hosts
|
|
253
277
|
end
|
|
254
278
|
|
|
255
279
|
def with_lock
|
|
256
|
-
if
|
|
280
|
+
if DASH.holding_lock?
|
|
257
281
|
yield
|
|
258
282
|
else
|
|
259
283
|
acquire_lock
|
|
@@ -286,7 +310,7 @@ module Kamal::Cli
|
|
|
286
310
|
def acquire_lock
|
|
287
311
|
ensure_run_directory
|
|
288
312
|
|
|
289
|
-
if
|
|
313
|
+
if DASH.lock_wait
|
|
290
314
|
acquire_lock_with_wait
|
|
291
315
|
else
|
|
292
316
|
raise_if_locked do
|
|
@@ -295,12 +319,12 @@ module Kamal::Cli
|
|
|
295
319
|
end
|
|
296
320
|
end
|
|
297
321
|
|
|
298
|
-
|
|
322
|
+
DASH.holding_lock = true
|
|
299
323
|
end
|
|
300
324
|
|
|
301
325
|
def acquire_lock_with_wait
|
|
302
|
-
timeout =
|
|
303
|
-
interval =
|
|
326
|
+
timeout = DASH.lock_wait_timeout
|
|
327
|
+
interval = DASH.lock_wait_interval
|
|
304
328
|
deadline = Time.now + timeout
|
|
305
329
|
details_shown = false
|
|
306
330
|
|
|
@@ -338,7 +362,7 @@ module Kamal::Cli
|
|
|
338
362
|
say "Releasing the deploy lock...", :magenta
|
|
339
363
|
execute_lock_release
|
|
340
364
|
|
|
341
|
-
|
|
365
|
+
DASH.holding_lock = false
|
|
342
366
|
end
|
|
343
367
|
|
|
344
368
|
def raise_if_locked
|
|
@@ -349,21 +373,21 @@ module Kamal::Cli
|
|
|
349
373
|
raise LockError, "Deploy lock found. Run 'dash lock help' for more information"
|
|
350
374
|
end
|
|
351
375
|
|
|
352
|
-
def execute_lock_acquire(message, lock:
|
|
353
|
-
on(hosts) { execute *lock.acquire(message,
|
|
376
|
+
def execute_lock_acquire(message, lock: DASH.lock, hosts: DASH.primary_host)
|
|
377
|
+
on(hosts) { execute *lock.acquire(message, DASH.config.version), verbosity: :debug }
|
|
354
378
|
rescue SSHKit::Runner::ExecuteError => e
|
|
355
379
|
raise LockHeldError if e.message =~ /cannot create directory/
|
|
356
380
|
raise
|
|
357
381
|
end
|
|
358
382
|
|
|
359
|
-
def execute_lock_release(lock:
|
|
383
|
+
def execute_lock_release(lock: DASH.lock, hosts: DASH.primary_host)
|
|
360
384
|
on(hosts) { execute *lock.release, verbosity: :debug }
|
|
361
385
|
rescue SSHKit::Runner::ExecuteError => e
|
|
362
386
|
raise LockMissingError if e.message =~ /No such file or directory/
|
|
363
387
|
raise
|
|
364
388
|
end
|
|
365
389
|
|
|
366
|
-
def capture_lock_status(lock:
|
|
390
|
+
def capture_lock_status(lock: DASH.lock, hosts: DASH.primary_host)
|
|
367
391
|
status = nil
|
|
368
392
|
on(hosts) { status = capture_with_debug(*lock.status) }
|
|
369
393
|
status
|
|
@@ -379,19 +403,19 @@ module Kamal::Cli
|
|
|
379
403
|
end
|
|
380
404
|
|
|
381
405
|
def pre_connect_if_required
|
|
382
|
-
if !
|
|
406
|
+
if !DASH.connected?
|
|
383
407
|
run_hook "pre-connect", secrets: true unless options[:skip_hooks]
|
|
384
|
-
|
|
408
|
+
DASH.connected = true
|
|
385
409
|
end
|
|
386
410
|
end
|
|
387
411
|
|
|
388
412
|
def command
|
|
389
413
|
@kamal_command ||= begin
|
|
390
414
|
invocation_class, invocation_commands = *first_invocation
|
|
391
|
-
if invocation_class ==
|
|
415
|
+
if invocation_class == Dash::Cli::Main
|
|
392
416
|
invocation_commands[0]
|
|
393
417
|
else
|
|
394
|
-
|
|
418
|
+
Dash::Cli::Main.subcommand_classes.find { |command, clazz| clazz == invocation_class }[0]
|
|
395
419
|
end
|
|
396
420
|
end
|
|
397
421
|
end
|
|
@@ -399,7 +423,7 @@ module Kamal::Cli
|
|
|
399
423
|
def subcommand
|
|
400
424
|
@kamal_subcommand ||= begin
|
|
401
425
|
invocation_class, invocation_commands = *first_invocation
|
|
402
|
-
invocation_commands[0] if invocation_class !=
|
|
426
|
+
invocation_commands[0] if invocation_class != Dash::Cli::Main
|
|
403
427
|
end
|
|
404
428
|
end
|
|
405
429
|
|
|
@@ -412,8 +436,8 @@ module Kamal::Cli
|
|
|
412
436
|
end
|
|
413
437
|
|
|
414
438
|
def ensure_run_directory
|
|
415
|
-
on(
|
|
416
|
-
execute(*
|
|
439
|
+
on(DASH.hosts) do
|
|
440
|
+
execute(*DASH.server.ensure_run_directory)
|
|
417
441
|
end
|
|
418
442
|
end
|
|
419
443
|
|
|
@@ -429,7 +453,7 @@ module Kamal::Cli
|
|
|
429
453
|
def ensure_docker_installed
|
|
430
454
|
run_locally do
|
|
431
455
|
begin
|
|
432
|
-
execute *
|
|
456
|
+
execute *DASH.builder.ensure_docker_installed
|
|
433
457
|
rescue SSHKit::Command::Failed => e
|
|
434
458
|
error = e.message =~ /command not found/ ?
|
|
435
459
|
"Docker is not installed locally" :
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
class Dash::Cli::Build::Clone
|
|
2
|
+
attr_reader :sshkit
|
|
3
|
+
delegate :info, :error, :execute, :capture_with_info, to: :sshkit
|
|
4
|
+
|
|
5
|
+
def initialize(sshkit)
|
|
6
|
+
@sshkit = sshkit
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def prepare
|
|
10
|
+
begin
|
|
11
|
+
clone_repo
|
|
12
|
+
rescue SSHKit::Command::Failed => e
|
|
13
|
+
if e.message =~ /already exists and is not an empty directory/
|
|
14
|
+
reset
|
|
15
|
+
else
|
|
16
|
+
raise Dash::Cli::Build::BuildError, "Failed to clone repo: #{e.message}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
validate!
|
|
21
|
+
rescue Dash::Cli::Build::BuildError => e
|
|
22
|
+
error "Error preparing clone: #{e.message}, deleting and retrying..."
|
|
23
|
+
|
|
24
|
+
FileUtils.rm_rf DASH.config.builder.clone_directory
|
|
25
|
+
clone_repo
|
|
26
|
+
validate!
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
def clone_repo
|
|
31
|
+
info "Cloning repo into build directory `#{DASH.config.builder.build_directory}`..."
|
|
32
|
+
|
|
33
|
+
FileUtils.mkdir_p DASH.config.builder.clone_directory
|
|
34
|
+
execute *DASH.builder.clone
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def reset
|
|
38
|
+
info "Resetting local clone as `#{DASH.config.builder.build_directory}` already exists..."
|
|
39
|
+
|
|
40
|
+
DASH.builder.clone_reset_steps.each { |step| execute *step }
|
|
41
|
+
rescue SSHKit::Command::Failed => e
|
|
42
|
+
raise Dash::Cli::Build::BuildError, "Failed to clone repo: #{e.message}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def validate!
|
|
46
|
+
status = capture_with_info(*DASH.builder.clone_status).strip
|
|
47
|
+
|
|
48
|
+
unless status.empty?
|
|
49
|
+
raise Dash::Cli::Build::BuildError, "Clone in #{DASH.config.builder.build_directory} is dirty, #{status}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
revision = capture_with_info(*DASH.builder.clone_revision).strip
|
|
53
|
+
if revision != Dash::Git.revision
|
|
54
|
+
raise Dash::Cli::Build::BuildError, "Clone in #{DASH.config.builder.build_directory} is not on the correct revision, expected `#{Dash::Git.revision}` but got `#{revision}`"
|
|
55
|
+
end
|
|
56
|
+
rescue SSHKit::Command::Failed => e
|
|
57
|
+
raise Dash::Cli::Build::BuildError, "Failed to validate clone: #{e.message}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class Dash::Cli::Build < Dash::Cli::Base
|
|
2
2
|
class BuildError < StandardError; end
|
|
3
3
|
|
|
4
4
|
desc "deliver", "Build app and push app image to registry then pull image on servers"
|
|
@@ -18,14 +18,14 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
18
18
|
pre_connect_if_required
|
|
19
19
|
|
|
20
20
|
ensure_docker_installed
|
|
21
|
-
setup_local_registry if
|
|
22
|
-
login_to_registry_locally unless
|
|
21
|
+
setup_local_registry if DASH.registry.local?
|
|
22
|
+
login_to_registry_locally unless DASH.registry.local?
|
|
23
23
|
|
|
24
24
|
run_hook "pre-build"
|
|
25
25
|
|
|
26
|
-
uncommitted_changes =
|
|
26
|
+
uncommitted_changes = Dash::Git.uncommitted_changes
|
|
27
27
|
|
|
28
|
-
if
|
|
28
|
+
if DASH.config.builder.git_clone?
|
|
29
29
|
if uncommitted_changes.present?
|
|
30
30
|
say "Building from a local git clone, so ignoring these uncommitted changes:\n #{uncommitted_changes}", :yellow
|
|
31
31
|
end
|
|
@@ -38,10 +38,10 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
forward_local_registry_port_for_remote_builder do
|
|
41
|
-
with_env(
|
|
41
|
+
with_env(DASH.config.builder.secrets) do
|
|
42
42
|
run_locally do
|
|
43
43
|
begin
|
|
44
|
-
execute *
|
|
44
|
+
execute *DASH.builder.inspect_builder
|
|
45
45
|
rescue SSHKit::Command::Failed => e
|
|
46
46
|
if e.message =~ /(context not found|no builder|no compatible builder|does not exist)/
|
|
47
47
|
warn "Missing compatible builder, so creating a new one first"
|
|
@@ -57,10 +57,10 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
# Get the command here to ensure the Dir.chdir doesn't interfere with it
|
|
60
|
-
push =
|
|
60
|
+
push = DASH.builder.push(cli.options[:output], no_cache: cli.options[:no_cache])
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
Dir.chdir(
|
|
62
|
+
DASH.with_verbosity(:debug) do
|
|
63
|
+
Dir.chdir(DASH.config.builder.build_directory) { execute *push, env: DASH.builder.push_env }
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
end
|
|
@@ -69,31 +69,31 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
69
69
|
|
|
70
70
|
desc "pull", "Pull app image from registry onto servers"
|
|
71
71
|
def pull
|
|
72
|
-
login_to_registry_remotely unless
|
|
72
|
+
login_to_registry_remotely unless DASH.registry.local?
|
|
73
73
|
|
|
74
|
-
forward_local_registry_port(
|
|
74
|
+
forward_local_registry_port(DASH.hosts, **DASH.config.ssh.options) do
|
|
75
75
|
if (first_hosts = mirror_hosts).any?
|
|
76
76
|
# Pull on a single host per mirror first to seed them
|
|
77
77
|
say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
|
|
78
78
|
pull_on_hosts(first_hosts)
|
|
79
79
|
say "Pulling image on remaining hosts...", :magenta
|
|
80
|
-
pull_on_hosts(
|
|
80
|
+
pull_on_hosts(DASH.app_hosts - first_hosts)
|
|
81
81
|
else
|
|
82
|
-
pull_on_hosts(
|
|
82
|
+
pull_on_hosts(DASH.app_hosts)
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
desc "create", "Create a build setup"
|
|
88
88
|
def create
|
|
89
|
-
if (remote_host =
|
|
89
|
+
if (remote_host = DASH.config.builder.remote)
|
|
90
90
|
connect_to_remote_host(remote_host)
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
run_locally do
|
|
94
94
|
begin
|
|
95
|
-
debug "Using builder: #{
|
|
96
|
-
execute *
|
|
95
|
+
debug "Using builder: #{DASH.builder.name}"
|
|
96
|
+
execute *DASH.builder.create
|
|
97
97
|
rescue SSHKit::Command::Failed => e
|
|
98
98
|
if e.message =~ /stderr=(.*)/
|
|
99
99
|
error "Couldn't create remote builder: #{$1}"
|
|
@@ -108,16 +108,16 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
108
108
|
desc "remove", "Remove build setup"
|
|
109
109
|
def remove
|
|
110
110
|
run_locally do
|
|
111
|
-
debug "Using builder: #{
|
|
112
|
-
execute *
|
|
111
|
+
debug "Using builder: #{DASH.builder.name}"
|
|
112
|
+
execute *DASH.builder.remove
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
|
|
116
116
|
desc "details", "Show build setup"
|
|
117
117
|
def details
|
|
118
118
|
run_locally do
|
|
119
|
-
puts "Builder: #{
|
|
120
|
-
puts capture(*
|
|
119
|
+
puts "Builder: #{DASH.builder.name}"
|
|
120
|
+
puts capture(*DASH.builder.info)
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
123
|
|
|
@@ -129,9 +129,9 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
129
129
|
|
|
130
130
|
ensure_docker_installed
|
|
131
131
|
|
|
132
|
-
docker_included_files = Set.new(
|
|
133
|
-
git_uncommitted_files = Set.new(
|
|
134
|
-
git_untracked_files = Set.new(
|
|
132
|
+
docker_included_files = Set.new(Dash::Docker.included_files)
|
|
133
|
+
git_uncommitted_files = Set.new(Dash::Git.uncommitted_files)
|
|
134
|
+
git_untracked_files = Set.new(Dash::Git.untracked_files)
|
|
135
135
|
|
|
136
136
|
docker_uncommitted_files = docker_included_files & git_uncommitted_files
|
|
137
137
|
if docker_uncommitted_files.any?
|
|
@@ -147,10 +147,10 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
147
147
|
say
|
|
148
148
|
end
|
|
149
149
|
|
|
150
|
-
with_env(
|
|
150
|
+
with_env(DASH.config.builder.secrets) do
|
|
151
151
|
run_locally do
|
|
152
|
-
build =
|
|
153
|
-
|
|
152
|
+
build = DASH.builder.push(cli.options[:output], tag_as_dirty: true, no_cache: cli.options[:no_cache])
|
|
153
|
+
DASH.with_verbosity(:debug) do
|
|
154
154
|
execute(*build)
|
|
155
155
|
end
|
|
156
156
|
end
|
|
@@ -172,10 +172,10 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
def mirror_hosts
|
|
175
|
-
if
|
|
175
|
+
if DASH.app_hosts.many?
|
|
176
176
|
mirror_hosts = Concurrent::Hash.new
|
|
177
|
-
on(
|
|
178
|
-
first_mirror = capture_with_info(*
|
|
177
|
+
on(DASH.app_hosts) do |host|
|
|
178
|
+
first_mirror = capture_with_info(*DASH.builder.first_mirror).strip.presence
|
|
179
179
|
mirror_hosts[first_mirror] ||= host.to_s if first_mirror
|
|
180
180
|
rescue SSHKit::Command::Failed => e
|
|
181
181
|
raise unless e.message =~ /error calling index: reflect: slice index out of range/
|
|
@@ -188,34 +188,34 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
188
188
|
|
|
189
189
|
def pull_on_hosts(hosts)
|
|
190
190
|
on(hosts) do
|
|
191
|
-
execute *
|
|
192
|
-
execute *
|
|
193
|
-
execute *
|
|
194
|
-
execute *
|
|
191
|
+
execute *DASH.auditor.record("Pulled image with version #{DASH.config.version}"), verbosity: :debug
|
|
192
|
+
execute *DASH.builder.clean, raise_on_non_zero_exit: false
|
|
193
|
+
execute *DASH.builder.pull
|
|
194
|
+
execute *DASH.builder.validate_image
|
|
195
195
|
end
|
|
196
196
|
end
|
|
197
197
|
|
|
198
198
|
def setup_local_registry
|
|
199
199
|
run_locally do
|
|
200
|
-
execute *
|
|
200
|
+
execute *DASH.registry.setup
|
|
201
201
|
end
|
|
202
202
|
end
|
|
203
203
|
|
|
204
204
|
def login_to_registry_locally
|
|
205
205
|
run_locally do
|
|
206
|
-
execute *
|
|
206
|
+
execute *DASH.registry.login
|
|
207
207
|
end
|
|
208
208
|
end
|
|
209
209
|
|
|
210
210
|
def login_to_registry_remotely
|
|
211
|
-
on(
|
|
212
|
-
execute *
|
|
211
|
+
on(DASH.app_hosts) do
|
|
212
|
+
execute *DASH.registry.login
|
|
213
213
|
end
|
|
214
214
|
end
|
|
215
215
|
|
|
216
216
|
def forward_local_registry_port_for_remote_builder(&block)
|
|
217
|
-
if
|
|
218
|
-
remote_uri = URI(
|
|
217
|
+
if DASH.builder.remote?
|
|
218
|
+
remote_uri = URI(DASH.config.builder.remote)
|
|
219
219
|
forward_local_registry_port([ remote_uri.host ], **remote_builder_ssh_options(remote_uri), &block)
|
|
220
220
|
else
|
|
221
221
|
yield
|
|
@@ -223,9 +223,9 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
223
223
|
end
|
|
224
224
|
|
|
225
225
|
def forward_local_registry_port(hosts, **ssh_options, &block)
|
|
226
|
-
if
|
|
226
|
+
if DASH.config.registry.local?
|
|
227
227
|
say "Setting up local registry port forwarding to #{hosts.join(', ')}..."
|
|
228
|
-
PortForwarding.new(hosts,
|
|
228
|
+
PortForwarding.new(hosts, DASH.config.registry.local_port, **ssh_options).forward(&block)
|
|
229
229
|
else
|
|
230
230
|
yield
|
|
231
231
|
end
|
|
@@ -234,9 +234,9 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
234
234
|
def remote_builder_ssh_options(remote_uri)
|
|
235
235
|
{ user: remote_uri.user,
|
|
236
236
|
port: remote_uri.port,
|
|
237
|
-
keepalive:
|
|
238
|
-
keepalive_interval:
|
|
239
|
-
logger:
|
|
237
|
+
keepalive: DASH.config.ssh.options[:keepalive],
|
|
238
|
+
keepalive_interval: DASH.config.ssh.options[:keepalive_interval],
|
|
239
|
+
logger: DASH.config.ssh.options[:logger]
|
|
240
240
|
}.compact
|
|
241
241
|
end
|
|
242
242
|
end
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# Checks that only read the configuration — no SSH, no network. They report problems
|
|
2
2
|
# that are visible in deploy.yml alone, so they still run when every host is unreachable.
|
|
3
|
-
class
|
|
3
|
+
class Dash::Cli::Doctor::ConfigChecks
|
|
4
4
|
def run
|
|
5
5
|
readiness_results
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
private
|
|
9
9
|
def result(check, target, status, detail)
|
|
10
|
-
|
|
10
|
+
Dash::Cli::Doctor::Result.new(check, target, status, detail)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def readiness_results
|
|
14
|
-
|
|
14
|
+
DASH.roles.map { |role| readiness_check(role) }
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def readiness_check(role)
|