dash 3.1.5 → 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.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -6
  3. data/bin/dash +2 -2
  4. data/lib/{kamal → dash}/cli/accessory.rb +24 -24
  5. data/lib/dash/cli/alias/command.rb +10 -0
  6. data/lib/{kamal → dash}/cli/app/assets.rb +2 -2
  7. data/lib/{kamal → dash}/cli/app/boot.rb +15 -15
  8. data/lib/{kamal → dash}/cli/app/error_pages.rb +6 -6
  9. data/lib/{kamal → dash}/cli/app/rollout_boot.rb +6 -6
  10. data/lib/{kamal → dash}/cli/app/ssl_certificates.rb +3 -3
  11. data/lib/{kamal → dash}/cli/app.rb +87 -87
  12. data/lib/{kamal → dash}/cli/base.rb +73 -49
  13. data/lib/dash/cli/build/clone.rb +59 -0
  14. data/lib/{kamal → dash}/cli/build/port_forwarding.rb +1 -1
  15. data/lib/{kamal → dash}/cli/build.rb +45 -45
  16. data/lib/{kamal → dash}/cli/doctor/config_checks.rb +3 -3
  17. data/lib/{kamal → dash}/cli/doctor/endpoint_checks.rb +25 -7
  18. data/lib/{kamal → dash}/cli/doctor/host_checks.rb +23 -23
  19. data/lib/{kamal → dash}/cli/doctor.rb +9 -9
  20. data/lib/{kamal → dash}/cli/healthcheck/barrier.rb +2 -2
  21. data/lib/{kamal → dash}/cli/healthcheck/drift_error.rb +2 -2
  22. data/lib/dash/cli/healthcheck/error.rb +2 -0
  23. data/lib/{kamal → dash}/cli/healthcheck/poller.rb +7 -7
  24. data/lib/{kamal → dash}/cli/lock.rb +1 -1
  25. data/lib/dash/cli/main/migrate.rb +77 -0
  26. data/lib/{kamal → dash}/cli/main.rb +87 -76
  27. data/lib/{kamal → dash}/cli/proxy/drift.rb +4 -4
  28. data/lib/{kamal → dash}/cli/proxy/loadbalancer_claim.rb +6 -6
  29. data/lib/{kamal → dash}/cli/proxy/loadbalancer_reboot.rb +23 -23
  30. data/lib/{kamal → dash}/cli/proxy/reboot.rb +15 -15
  31. data/lib/{kamal → dash}/cli/proxy.rb +172 -160
  32. data/lib/{kamal → dash}/cli/prune.rb +10 -10
  33. data/lib/{kamal → dash}/cli/registry.rb +13 -13
  34. data/lib/{kamal → dash}/cli/secrets.rb +3 -3
  35. data/lib/{kamal → dash}/cli/server.rb +15 -15
  36. data/lib/{kamal → dash}/cli/templates/deploy.yml +6 -6
  37. data/lib/dash/cli/templates/sample_hooks/docker-setup.sample +3 -0
  38. data/lib/dash/cli/templates/sample_hooks/post-app-boot.sample +3 -0
  39. data/lib/dash/cli/templates/sample_hooks/post-app-stop.sample +9 -0
  40. data/lib/dash/cli/templates/sample_hooks/post-deploy.sample +14 -0
  41. data/lib/dash/cli/templates/sample_hooks/post-proxy-deploy.sample +3 -0
  42. data/lib/dash/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
  43. data/lib/dash/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
  44. data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-app-stop.sample +2 -2
  45. data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-build.sample +8 -8
  46. data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-connect.sample +8 -8
  47. data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-deploy.sample +9 -9
  48. data/lib/dash/cli/templates/sample_hooks/pre-proxy-deploy.sample +3 -0
  49. data/lib/dash/cli/templates/sample_hooks/pre-proxy-reboot.sample +3 -0
  50. data/lib/dash/cli/templates/secrets +22 -0
  51. data/lib/{kamal → dash}/cli.rb +2 -2
  52. data/lib/{kamal → dash}/commander/specifics.rb +2 -2
  53. data/lib/{kamal → dash}/commander.rb +31 -23
  54. data/lib/{kamal → dash}/commands/accessory/proxy.rb +1 -1
  55. data/lib/{kamal → dash}/commands/accessory.rb +2 -2
  56. data/lib/{kamal → dash}/commands/app/assets.rb +1 -1
  57. data/lib/{kamal → dash}/commands/app/containers.rb +1 -1
  58. data/lib/{kamal → dash}/commands/app/error_pages.rb +2 -2
  59. data/lib/{kamal → dash}/commands/app/execution.rb +1 -1
  60. data/lib/{kamal → dash}/commands/app/images.rb +1 -1
  61. data/lib/{kamal → dash}/commands/app/logging.rb +1 -1
  62. data/lib/{kamal → dash}/commands/app/proxy.rb +1 -1
  63. data/lib/{kamal → dash}/commands/app.rb +1 -1
  64. data/lib/{kamal → dash}/commands/auditor.rb +3 -7
  65. data/lib/{kamal → dash}/commands/base.rb +39 -3
  66. data/lib/{kamal → dash}/commands/builder/base.rb +2 -2
  67. data/lib/{kamal → dash}/commands/builder/clone.rb +3 -3
  68. data/lib/{kamal → dash}/commands/builder/cloud.rb +1 -1
  69. data/lib/{kamal → dash}/commands/builder/hybrid.rb +1 -1
  70. data/lib/{kamal → dash}/commands/builder/local.rb +1 -1
  71. data/lib/{kamal → dash}/commands/builder/pack.rb +1 -1
  72. data/lib/{kamal → dash}/commands/builder/remote.rb +1 -1
  73. data/lib/{kamal → dash}/commands/builder.rb +7 -7
  74. data/lib/{kamal → dash}/commands/docker.rb +1 -1
  75. data/lib/{kamal → dash}/commands/hook.rb +1 -1
  76. data/lib/{kamal → dash}/commands/loadbalancer.rb +9 -8
  77. data/lib/{kamal → dash}/commands/lock.rb +2 -2
  78. data/lib/{kamal → dash}/commands/proxy/cert_transfer.rb +3 -3
  79. data/lib/{kamal → dash}/commands/proxy.rb +20 -10
  80. data/lib/{kamal → dash}/commands/prune.rb +1 -1
  81. data/lib/dash/commands/registry.rb +44 -0
  82. data/lib/{kamal → dash}/commands/server.rb +1 -5
  83. data/lib/dash/commands.rb +2 -0
  84. data/lib/{kamal → dash}/configuration/accessory.rb +11 -11
  85. data/lib/{kamal → dash}/configuration/alias.rb +3 -3
  86. data/lib/{kamal → dash}/configuration/boot.rb +2 -2
  87. data/lib/{kamal → dash}/configuration/builder.rb +7 -7
  88. data/lib/{kamal → dash}/configuration/docs/accessory.yml +6 -6
  89. data/lib/{kamal → dash}/configuration/docs/alias.yml +2 -2
  90. data/lib/{kamal → dash}/configuration/docs/boot.yml +1 -1
  91. data/lib/{kamal → dash}/configuration/docs/builder.yml +3 -3
  92. data/lib/{kamal → dash}/configuration/docs/configuration.yml +25 -18
  93. data/lib/{kamal → dash}/configuration/docs/env.yml +17 -17
  94. data/lib/{kamal → dash}/configuration/docs/proxy.yml +53 -30
  95. data/lib/{kamal → dash}/configuration/docs/registry.yml +6 -6
  96. data/lib/{kamal → dash}/configuration/docs/role.yml +1 -1
  97. data/lib/{kamal → dash}/configuration/docs/servers.yml +2 -2
  98. data/lib/{kamal → dash}/configuration/env/tag.rb +2 -2
  99. data/lib/{kamal → dash}/configuration/env.rb +5 -5
  100. data/lib/{kamal → dash}/configuration/loadbalancer.rb +3 -3
  101. data/lib/{kamal → dash}/configuration/logging.rb +3 -3
  102. data/lib/{kamal → dash}/configuration/output.rb +4 -4
  103. data/lib/{kamal → dash}/configuration/proxy/acme.rb +4 -3
  104. data/lib/{kamal → dash}/configuration/proxy/boot.rb +10 -8
  105. data/lib/{kamal → dash}/configuration/proxy/run.rb +14 -14
  106. data/lib/{kamal → dash}/configuration/proxy.rb +16 -16
  107. data/lib/{kamal → dash}/configuration/registry.rb +3 -3
  108. data/lib/{kamal → dash}/configuration/role/healthcheck.rb +3 -3
  109. data/lib/{kamal → dash}/configuration/role.rb +13 -13
  110. data/lib/{kamal → dash}/configuration/servers.rb +4 -4
  111. data/lib/{kamal → dash}/configuration/ssh.rb +3 -3
  112. data/lib/{kamal → dash}/configuration/sshkit.rb +2 -2
  113. data/lib/{kamal → dash}/configuration/validation.rb +2 -2
  114. data/lib/{kamal → dash}/configuration/validator/accessory.rb +1 -1
  115. data/lib/{kamal → dash}/configuration/validator/alias.rb +2 -2
  116. data/lib/{kamal → dash}/configuration/validator/builder.rb +1 -1
  117. data/lib/dash/configuration/validator/configuration.rb +6 -0
  118. data/lib/{kamal → dash}/configuration/validator/env.rb +2 -2
  119. data/lib/{kamal → dash}/configuration/validator/proxy.rb +8 -8
  120. data/lib/{kamal → dash}/configuration/validator/registry.rb +1 -1
  121. data/lib/{kamal → dash}/configuration/validator/role.rb +1 -1
  122. data/lib/{kamal → dash}/configuration/validator/servers.rb +1 -1
  123. data/lib/{kamal → dash}/configuration/validator.rb +4 -4
  124. data/lib/{kamal → dash}/configuration/volume.rb +2 -2
  125. data/lib/{kamal → dash}/configuration.rb +40 -30
  126. data/lib/{kamal → dash}/docker.rb +1 -1
  127. data/lib/{kamal → dash}/env_file.rb +1 -1
  128. data/lib/{kamal → dash}/git.rb +1 -1
  129. data/lib/{kamal → dash}/otel_shipper.rb +3 -3
  130. data/lib/{kamal → dash}/output/base_logger.rb +1 -1
  131. data/lib/{kamal → dash}/output/file_logger.rb +1 -1
  132. data/lib/{kamal → dash}/output/formatter.rb +1 -1
  133. data/lib/{kamal → dash}/output/otel_logger.rb +3 -3
  134. data/lib/dash/project_directory.rb +28 -0
  135. data/lib/{kamal → dash}/secrets/adapters/aws_secrets_manager.rb +1 -1
  136. data/lib/{kamal → dash}/secrets/adapters/base.rb +2 -2
  137. data/lib/{kamal → dash}/secrets/adapters/bitwarden.rb +1 -1
  138. data/lib/{kamal → dash}/secrets/adapters/bitwarden_secrets_manager.rb +1 -1
  139. data/lib/{kamal → dash}/secrets/adapters/doppler.rb +1 -1
  140. data/lib/{kamal → dash}/secrets/adapters/enpass.rb +3 -3
  141. data/lib/{kamal → dash}/secrets/adapters/gcp_secret_manager.rb +1 -1
  142. data/lib/{kamal → dash}/secrets/adapters/last_pass.rb +1 -1
  143. data/lib/{kamal → dash}/secrets/adapters/one_password.rb +2 -2
  144. data/lib/{kamal → dash}/secrets/adapters/passbolt.rb +1 -1
  145. data/lib/{kamal → dash}/secrets/adapters/test.rb +1 -1
  146. data/lib/{kamal → dash}/secrets/adapters.rb +2 -2
  147. data/lib/{kamal → dash}/secrets/dotenv/inline_command_substitution.rb +5 -3
  148. data/lib/{kamal → dash}/secrets.rb +6 -6
  149. data/lib/{kamal → dash}/tags.rb +8 -3
  150. data/lib/{kamal → dash}/utils/sensitive.rb +1 -1
  151. data/lib/{kamal → dash}/utils.rb +15 -5
  152. data/lib/dash/version.rb +3 -0
  153. data/lib/{kamal.rb → dash.rb} +5 -5
  154. metadata +171 -216
  155. data/bin/kamal +0 -18
  156. data/lib/kamal/cli/alias/command.rb +0 -10
  157. data/lib/kamal/cli/build/clone.rb +0 -59
  158. data/lib/kamal/cli/healthcheck/error.rb +0 -2
  159. data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +0 -3
  160. data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +0 -3
  161. data/lib/kamal/cli/templates/sample_hooks/post-app-stop.sample +0 -9
  162. data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +0 -14
  163. data/lib/kamal/cli/templates/sample_hooks/post-proxy-deploy.sample +0 -3
  164. data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +0 -3
  165. data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +0 -3
  166. data/lib/kamal/cli/templates/sample_hooks/pre-proxy-deploy.sample +0 -3
  167. data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +0 -3
  168. data/lib/kamal/cli/templates/secrets +0 -22
  169. data/lib/kamal/commands/registry.rb +0 -38
  170. data/lib/kamal/commands.rb +0 -2
  171. data/lib/kamal/configuration/validator/configuration.rb +0 -6
  172. data/lib/kamal/version.rb +0 -3
  173. /data/lib/{kamal → dash}/configuration/docs/logging.yml +0 -0
  174. /data/lib/{kamal → dash}/configuration/docs/output.yml +0 -0
  175. /data/lib/{kamal → dash}/configuration/docs/ssh.yml +0 -0
  176. /data/lib/{kamal → dash}/configuration/docs/sshkit.yml +0 -0
  177. /data/lib/{kamal → dash}/sshkit_with_ext.rb +0 -0
@@ -1,4 +1,4 @@
1
- class Kamal::Configuration::Loadbalancer < Kamal::Configuration::Proxy
1
+ class Dash::Configuration::Loadbalancer < Dash::Configuration::Proxy
2
2
  CONTAINER_NAME = "load-balancer".freeze
3
3
  SHARED_CONTAINER_NAME = "kamal-proxy".freeze
4
4
 
@@ -40,7 +40,7 @@ class Kamal::Configuration::Loadbalancer < Kamal::Configuration::Proxy
40
40
  # mount and the run command) to boot from, so fall back to an empty config
41
41
  # rather than to nil.
42
42
  def run
43
- @run ||= Kamal::Configuration::Proxy::Run.new(config, run_config: {})
43
+ @run ||= Dash::Configuration::Proxy::Run.new(config, run_config: {})
44
44
  end
45
45
 
46
46
  # Docker options for the load balancer container: publish/logging/options
@@ -100,7 +100,7 @@ class Kamal::Configuration::Loadbalancer < Kamal::Configuration::Proxy
100
100
 
101
101
  private
102
102
  # The edge half of the layering contract (see
103
- # Kamal::Configuration::Proxy::DEPLOY_OPTION_DISPOSITIONS): the load
103
+ # Dash::Configuration::Proxy::DEPLOY_OPTION_DISPOSITIONS): the load
104
104
  # balancer applies the edge and shared concerns, and leaves the per-app
105
105
  # ones to the per-host proxies it forwards to.
106
106
  def retained_dispositions
@@ -1,7 +1,7 @@
1
- class Kamal::Configuration::Logging
2
- delegate :optionize, :argumentize, to: Kamal::Utils
1
+ class Dash::Configuration::Logging
2
+ delegate :optionize, :argumentize, to: Dash::Utils
3
3
 
4
- include Kamal::Configuration::Validation
4
+ include Dash::Configuration::Validation
5
5
 
6
6
  attr_reader :logging_config
7
7
 
@@ -1,9 +1,9 @@
1
- class Kamal::Configuration::Output
2
- include Kamal::Configuration::Validation
1
+ class Dash::Configuration::Output
2
+ include Dash::Configuration::Validation
3
3
 
4
4
  LOGGER_TYPES = {
5
- "otel" => "Kamal::Output::OtelLogger",
6
- "file" => "Kamal::Output::FileLogger"
5
+ "otel" => "Dash::Output::OtelLogger",
6
+ "file" => "Dash::Output::FileLogger"
7
7
  }
8
8
 
9
9
  attr_reader :output_config, :loggers
@@ -8,7 +8,7 @@
8
8
  # Credentials go the other way, through an env file uploaded at mode 0600. A DNS
9
9
  # API token can rewrite your zone; `docker run --env TOKEN=...` would put it in
10
10
  # the host's process listing and in kamal's own audit log.
11
- class Kamal::Configuration::Proxy::Acme
11
+ class Dash::Configuration::Proxy::Acme
12
12
  # The canonical provider names kamal-proxy MINIMUM_VERSION advertises. Kept in
13
13
  # step with the proxy by test/proxy_flag_coverage_test.rb, which compares this
14
14
  # list against the manifest bin/sync-proxy-flags generates from the image.
@@ -53,7 +53,7 @@ class Kamal::Configuration::Proxy::Acme
53
53
  # as a stray argument. Both booleans default to true in the proxy, which makes
54
54
  # false the value an operator actually writes.
55
55
  def run_command_args
56
- Kamal::Utils.optionize(run_command_options, with: "=")
56
+ Dash::Utils.optionize(run_command_options, with: "=")
57
57
  end
58
58
 
59
59
  # compact, not compact_blank: false is a meaningful value for both booleans.
@@ -63,7 +63,8 @@ class Kamal::Configuration::Proxy::Acme
63
63
  "acme-dns-provider": dns_provider_entries,
64
64
  "acme-directory": acme_config["directory"],
65
65
  "acme-prefer-wildcard": acme_config["prefer_wildcard"],
66
- "acme-http-fallback": acme_config["http_fallback"]
66
+ "acme-http-fallback": acme_config["http_fallback"],
67
+ "acme-release-probe-interval": Dash::Utils.seconds_duration(acme_config["release_probe_interval"])
67
68
  }.compact
68
69
  end
69
70
 
@@ -1,6 +1,6 @@
1
- class Kamal::Configuration::Proxy::Boot
1
+ class Dash::Configuration::Proxy::Boot
2
2
  attr_reader :config
3
- delegate :argumentize, :optionize, to: Kamal::Utils
3
+ delegate :argumentize, :optionize, to: Dash::Utils
4
4
 
5
5
  def initialize(config:)
6
6
  @config = config
@@ -11,8 +11,8 @@ class Kamal::Configuration::Proxy::Boot
11
11
 
12
12
  (bind_ips || [ nil ]).map do |bind_ip|
13
13
  bind_ip = format_bind_ip(bind_ip)
14
- publish_http = [ bind_ip, http_port, Kamal::Configuration::Proxy::Run::DEFAULT_HTTP_PORT ].compact.join(":")
15
- publish_https = [ bind_ip, https_port, Kamal::Configuration::Proxy::Run::DEFAULT_HTTPS_PORT ].compact.join(":")
14
+ publish_http = [ bind_ip, http_port, Dash::Configuration::Proxy::Run::DEFAULT_HTTP_PORT ].compact.join(":")
15
+ publish_https = [ bind_ip, https_port, Dash::Configuration::Proxy::Run::DEFAULT_HTTPS_PORT ].compact.join(":")
16
16
 
17
17
  argumentize "--publish", [ publish_http, publish_https ]
18
18
  end.join(" ")
@@ -24,8 +24,8 @@ class Kamal::Configuration::Proxy::Boot
24
24
 
25
25
  def default_boot_options
26
26
  [
27
- *(publish_args(Kamal::Configuration::Proxy::Run::DEFAULT_HTTP_PORT, Kamal::Configuration::Proxy::Run::DEFAULT_HTTPS_PORT, nil)),
28
- *(logging_args(Kamal::Configuration::Proxy::Run::DEFAULT_LOG_MAX_SIZE))
27
+ *(publish_args(Dash::Configuration::Proxy::Run::DEFAULT_HTTP_PORT, Dash::Configuration::Proxy::Run::DEFAULT_HTTPS_PORT, nil)),
28
+ *(logging_args(Dash::Configuration::Proxy::Run::DEFAULT_LOG_MAX_SIZE))
29
29
  ]
30
30
  end
31
31
 
@@ -34,13 +34,15 @@ class Kamal::Configuration::Proxy::Boot
34
34
  end
35
35
 
36
36
  def image_name
37
- "kamal-proxy"
37
+ "dash-proxy"
38
38
  end
39
39
 
40
40
  def image_default
41
41
  "#{repository_name}/#{image_name}"
42
42
  end
43
43
 
44
+ # Stays kamal-proxy until the server-artifact rename ships a migration
45
+ # bridge — every deployed host has a running container by this name.
44
46
  def container_name
45
47
  "kamal-proxy"
46
48
  end
@@ -74,7 +76,7 @@ class Kamal::Configuration::Proxy::Boot
74
76
  end
75
77
 
76
78
  def apps_volume
77
- Kamal::Configuration::Volume.new \
79
+ Dash::Configuration::Volume.new \
78
80
  host_path: apps_directory,
79
81
  container_path: apps_container_directory
80
82
  end
@@ -1,12 +1,12 @@
1
- class Kamal::Configuration::Proxy::Run
2
- MINIMUM_VERSION = "v1.0.0.5"
1
+ class Dash::Configuration::Proxy::Run
2
+ MINIMUM_VERSION = "v1.0.0.7"
3
3
  DEFAULT_HTTP_PORT = 80
4
4
  DEFAULT_HTTPS_PORT = 443
5
5
  DEFAULT_LOG_MAX_SIZE = "10m"
6
6
 
7
7
  # One env file for everything the proxy must know but nothing may print:
8
8
  # ACME DNS credentials and the cache store URL. Also referenced by
9
- # Kamal::Commands::Proxy#remove_proxy_secrets_file, which cleans it up when
9
+ # Dash::Commands::Proxy#remove_proxy_secrets_file, which cleans it up when
10
10
  # the config no longer needs it.
11
11
  SECRETS_FILENAME = "secrets.env"
12
12
 
@@ -15,7 +15,7 @@ class Kamal::Configuration::Proxy::Run
15
15
  DIGEST_SCHEMA_VERSION = "v1"
16
16
 
17
17
  attr_reader :config, :run_config
18
- delegate :argumentize, :optionize, :seconds_duration, to: Kamal::Utils
18
+ delegate :argumentize, :optionize, :seconds_duration, to: Dash::Utils
19
19
 
20
20
  def initialize(config, run_config:, context: "proxy/run")
21
21
  @config = config
@@ -37,13 +37,13 @@ class Kamal::Configuration::Proxy::Run
37
37
  # running. The values deliberately stay out — the digest is published as a
38
38
  # docker label, and hashing secret material into a world-readable label buys
39
39
  # an offline guessing target for nothing. Rotating a credential's value or
40
- # the store URL still needs an explicit `kamal proxy reboot`.
40
+ # the store URL still needs an explicit `dash proxy reboot`.
41
41
  def config_digest
42
42
  self.class.digest(image, run_command, *docker_options_args, *secret_names)
43
43
  end
44
44
 
45
45
  def acme
46
- @acme ||= Kamal::Configuration::Proxy::Acme.new(acme_config: run_config["acme"], secrets: config.secrets)
46
+ @acme ||= Dash::Configuration::Proxy::Acme.new(acme_config: run_config["acme"], secrets: config.secrets)
47
47
  end
48
48
 
49
49
  def debug?
@@ -95,7 +95,7 @@ class Kamal::Configuration::Proxy::Run
95
95
  end
96
96
 
97
97
  def repository
98
- run_config.fetch("repository", "ghcr.io/zoolutions/kamal-proxy")
98
+ run_config.fetch("repository", "ghcr.io/zoolutions/dash-proxy")
99
99
  end
100
100
 
101
101
  def image
@@ -194,14 +194,14 @@ class Kamal::Configuration::Proxy::Run
194
194
  # wakes - which the operator sees as one hung request, not as a misconfiguration.
195
195
  def docker_socket_args
196
196
  if docker_socket.present?
197
- Kamal::Configuration::Volume.new(host_path: docker_socket, container_path: docker_socket).docker_args
197
+ Dash::Configuration::Volume.new(host_path: docker_socket, container_path: docker_socket).docker_args
198
198
  end
199
199
  end
200
200
 
201
201
  # Where the proxy's secrets land on the host - the ACME DNS credentials and
202
202
  # the cache store URL, which may embed one. Under the proxy's own directory
203
203
  # rather than the app's env directory, because the container is host-scoped
204
- # and shared by every app on the host - and so `kamal proxy remove` takes
204
+ # and shared by every app on the host - and so `dash proxy remove` takes
205
205
  # the secrets with it.
206
206
  def secrets_path
207
207
  File.join host_directory, SECRETS_FILENAME
@@ -212,7 +212,7 @@ class Kamal::Configuration::Proxy::Run
212
212
  end
213
213
 
214
214
  def secrets_io
215
- Kamal::EnvFile.new(acme.credentials_env.merge(cache_store_env)).to_io
215
+ Dash::EnvFile.new(acme.credentials_env.merge(cache_store_env)).to_io
216
216
  end
217
217
 
218
218
  def host_directory
@@ -228,7 +228,7 @@ class Kamal::Configuration::Proxy::Run
228
228
  end
229
229
 
230
230
  def apps_volume
231
- Kamal::Configuration::Volume.new \
231
+ Dash::Configuration::Volume.new \
232
232
  host_path: apps_directory,
233
233
  container_path: apps_container_directory
234
234
  end
@@ -255,7 +255,7 @@ class Kamal::Configuration::Proxy::Run
255
255
  end
256
256
 
257
257
  # Whether an operator turned on PROXY protocol without saying who may speak
258
- # it. Kamal::Configuration warns about this: the header rewrites the
258
+ # it. Dash::Configuration warns about this: the header rewrites the
259
259
  # connecting address that allow_ips and rate_limit key on, so honouring it
260
260
  # from anywhere is a client-IP spoofing hole.
261
261
  def proxy_protocol_unrestricted?
@@ -264,7 +264,7 @@ class Kamal::Configuration::Proxy::Run
264
264
 
265
265
  private
266
266
  # The proxy's own listeners and process-level behaviour, as opposed to the
267
- # per-service deadlines in Kamal::Configuration::Proxy#deploy_options. Zero
267
+ # per-service deadlines in Dash::Configuration::Proxy#deploy_options. Zero
268
268
  # disables each of the timeouts, so it is a value rather than an absence.
269
269
  def server_options
270
270
  {
@@ -295,7 +295,7 @@ class Kamal::Configuration::Proxy::Run
295
295
  conflicts = flags.keys.map(&:to_s) & named_run_command_options.keys.map(&:to_s)
296
296
  return true if conflicts.empty?
297
297
 
298
- raise Kamal::ConfigurationError,
298
+ raise Dash::ConfigurationError,
299
299
  "#{@context}/flags: #{conflicts.sort.join(", ")} is already set by a named key - remove one of them"
300
300
  end
301
301
 
@@ -1,5 +1,5 @@
1
- class Kamal::Configuration::Proxy
2
- include Kamal::Configuration::Validation
1
+ class Dash::Configuration::Proxy
2
+ include Dash::Configuration::Validation
3
3
 
4
4
  DEFAULT_LOG_REQUEST_HEADERS = [ "Cache-Control", "Last-Modified", "User-Agent" ]
5
5
  CONTAINER_NAME = "kamal-proxy"
@@ -163,12 +163,12 @@ class Kamal::Configuration::Proxy
163
163
  # where it matters.
164
164
  def self.disposition(key)
165
165
  DEPLOY_OPTION_DISPOSITIONS.fetch(key) do
166
- raise Kamal::ConfigurationError,
167
- "proxy deploy option --#{key} has no layering disposition - add it to Kamal::Configuration::Proxy::DEPLOY_OPTION_DISPOSITIONS"
166
+ raise Dash::ConfigurationError,
167
+ "proxy deploy option --#{key} has no layering disposition - add it to Dash::Configuration::Proxy::DEPLOY_OPTION_DISPOSITIONS"
168
168
  end
169
169
  end
170
170
 
171
- delegate :argumentize, :optionize, :seconds_duration, to: Kamal::Utils
171
+ delegate :argumentize, :optionize, :seconds_duration, to: Dash::Utils
172
172
 
173
173
  attr_reader :config, :proxy_config, :role_name, :run, :secrets
174
174
 
@@ -182,8 +182,8 @@ class Kamal::Configuration::Proxy
182
182
  @role_name = role_name
183
183
  @load_balanced = load_balanced
184
184
  @secrets = secrets
185
- validate! @proxy_config, with: Kamal::Configuration::Validator::Proxy, context: context
186
- @run = Kamal::Configuration::Proxy::Run.new(config, run_config: @proxy_config["run"], context: "#{context}/run") if @proxy_config && @proxy_config["run"].present?
185
+ validate! @proxy_config, with: Dash::Configuration::Validator::Proxy, context: context
186
+ @run = Dash::Configuration::Proxy::Run.new(config, run_config: @proxy_config["run"], context: "#{context}/run") if @proxy_config && @proxy_config["run"].present?
187
187
  end
188
188
 
189
189
  def app_port
@@ -274,7 +274,7 @@ class Kamal::Configuration::Proxy
274
274
  ssl_config["on_demand_url"]
275
275
  end
276
276
 
277
- # The name of a secret in .kamal/secrets holding the CA bundle client
277
+ # The name of a secret in .dash/secrets holding the CA bundle client
278
278
  # certificates must chain to - mirroring ssl.certificate_pem, not a local
279
279
  # file path. Kamal uploads the content into the app's TLS directory, which
280
280
  # the proxy container already mounts, and hands the proxy the path it sees
@@ -287,14 +287,14 @@ class Kamal::Configuration::Proxy
287
287
  client_ca_pem.present?
288
288
  end
289
289
 
290
- # Resolved at upload time, not config time, so `kamal app logs` and friends
290
+ # Resolved at upload time, not config time, so `dash app logs` and friends
291
291
  # work on machines without the secret. A blank secret raises like
292
292
  # basic_auth.password_secret - silently deploying without the client CA
293
293
  # would turn mTLS off.
294
294
  def client_ca_pem_content
295
295
  secrets[client_ca_pem].tap do |content|
296
296
  if content.blank?
297
- raise Kamal::ConfigurationError, "proxy/ssl: client_ca_pem secret '#{client_ca_pem}' is empty"
297
+ raise Dash::ConfigurationError, "proxy/ssl: client_ca_pem secret '#{client_ca_pem}' is empty"
298
298
  end
299
299
  end
300
300
  end
@@ -405,7 +405,7 @@ class Kamal::Configuration::Proxy
405
405
  private
406
406
  # Which dispositions this layer keeps. The per-app proxy behind a load
407
407
  # balancer sheds the edge concerns; without load balancing there is no
408
- # other layer to defer to. Kamal::Configuration::Loadbalancer overrides
408
+ # other layer to defer to. Dash::Configuration::Loadbalancer overrides
409
409
  # this to keep the edge and shed the per-app concerns.
410
410
  def retained_dispositions
411
411
  load_balancing? ? %i[ per_app both ] : %i[ edge per_app both ]
@@ -417,7 +417,7 @@ class Kamal::Configuration::Proxy
417
417
 
418
418
  # Auto-activation needs a role the load balancer can actually front. The
419
419
  # target list is built from roles where `running_proxy?` (see
420
- # Kamal::Cli::Proxy#loadbalancer), so a proxy-less primary role would boot a
420
+ # Dash::Cli::Proxy#loadbalancer), so a proxy-less primary role would boot a
421
421
  # load balancer with an empty --target. An explicit `loadbalancer:` setting
422
422
  # skips this check — the operator asked for it.
423
423
  def auto_load_balanced_primary_role?
@@ -473,7 +473,7 @@ class Kamal::Configuration::Proxy
473
473
  # app hosts have the docker socket and the containers).
474
474
  #
475
475
  # Sleep needs the container runtime socket, which is a run-level setting -
476
- # Kamal::Configuration#ensure_sleep_has_a_docker_socket refuses the pairing
476
+ # Dash::Configuration#ensure_sleep_has_a_docker_socket refuses the pairing
477
477
  # rather than letting the first request after an idle period hang.
478
478
  def lifecycle_options
479
479
  affinity = proxy_config["session_affinity"] || {}
@@ -602,7 +602,7 @@ class Kamal::Configuration::Proxy
602
602
 
603
603
  # kamal-proxy takes the credential as <username>:<password> and cuts at the
604
604
  # first colon, so a password may contain colons but a username may not (the
605
- # validator enforces that). The password is read from .kamal/secrets when
605
+ # validator enforces that). The password is read from .dash/secrets when
606
606
  # password_secret names it, so it need not live in deploy.yml.
607
607
  def basic_auth_credential
608
608
  basic_auth = proxy_config["basic_auth"]
@@ -619,13 +619,13 @@ class Kamal::Configuration::Proxy
619
619
  # means the named secret resolved empty. Fail rather than drop the flag —
620
620
  # silently deploying an unprotected service is the worse outcome.
621
621
  if password.blank?
622
- raise Kamal::ConfigurationError, "proxy/basic_auth: password_secret '#{secret_name}' is empty"
622
+ raise Dash::ConfigurationError, "proxy/basic_auth: password_secret '#{secret_name}' is empty"
623
623
  end
624
624
 
625
625
  # Sensitive, so SSHKit redacts the credential wherever kamal prints the
626
626
  # deploy command - at :info verbosity it used to land in plain text
627
627
  # (registry-login precedent; Utils.optionize passes the marking through).
628
- Kamal::Utils.sensitive("#{basic_auth["username"]}:#{password}")
628
+ Dash::Utils.sensitive("#{basic_auth["username"]}:#{password}")
629
629
  end
630
630
 
631
631
  # Serves both --path-timeout and --path-request-timeout, which kamal-proxy
@@ -1,10 +1,10 @@
1
- class Kamal::Configuration::Registry
2
- include Kamal::Configuration::Validation
1
+ class Dash::Configuration::Registry
2
+ include Dash::Configuration::Validation
3
3
 
4
4
  def initialize(config:, secrets:, context: "registry")
5
5
  @registry_config = config["registry"] || {}
6
6
  @secrets = secrets
7
- validate! registry_config, context: context, with: Kamal::Configuration::Validator::Registry
7
+ validate! registry_config, context: context, with: Dash::Configuration::Validator::Registry
8
8
  end
9
9
 
10
10
  def server
@@ -1,4 +1,4 @@
1
- class Kamal::Configuration::Role::Healthcheck
1
+ class Dash::Configuration::Role::Healthcheck
2
2
  DEFAULT_PATH = "/up"
3
3
  DEFAULT_INTERVAL = 1
4
4
 
@@ -7,7 +7,7 @@ class Kamal::Configuration::Role::Healthcheck
7
7
  DOCKER_ONLY_KEYS = %w[ cmd port path interval timeout retries start_period start_interval ]
8
8
 
9
9
  attr_reader :healthcheck_config, :context
10
- delegate :optionize, to: Kamal::Utils
10
+ delegate :optionize, to: Dash::Utils
11
11
 
12
12
  def initialize(healthcheck_config:, context: "healthcheck")
13
13
  @healthcheck_config = healthcheck_config
@@ -90,6 +90,6 @@ class Kamal::Configuration::Role::Healthcheck
90
90
  end
91
91
 
92
92
  def error(message, context: nil)
93
- raise Kamal::ConfigurationError, "#{[ @context, context ].compact.join("/")}: #{message}"
93
+ raise Dash::ConfigurationError, "#{[ @context, context ].compact.join("/")}: #{message}"
94
94
  end
95
95
  end
@@ -1,7 +1,7 @@
1
- class Kamal::Configuration::Role
2
- include Kamal::Configuration::Validation
1
+ class Dash::Configuration::Role
2
+ include Dash::Configuration::Validation
3
3
 
4
- delegate :argumentize, :optionize, to: Kamal::Utils
4
+ delegate :argumentize, :optionize, to: Dash::Utils
5
5
 
6
6
  attr_reader :name, :config, :specialized_env, :specialized_logging, :specialized_proxy, :healthcheck
7
7
 
@@ -13,20 +13,20 @@ class Kamal::Configuration::Role
13
13
  role_config,
14
14
  example: validation_yml["servers"]["workers"],
15
15
  context: "servers/#{name}",
16
- with: Kamal::Configuration::Validator::Role
16
+ with: Dash::Configuration::Validator::Role
17
17
 
18
- @specialized_env = Kamal::Configuration::Env.new \
18
+ @specialized_env = Dash::Configuration::Env.new \
19
19
  config: specializations.fetch("env", {}),
20
20
  secrets: config.secrets,
21
21
  context: "servers/#{name}/env"
22
22
 
23
- @specialized_logging = Kamal::Configuration::Logging.new \
23
+ @specialized_logging = Dash::Configuration::Logging.new \
24
24
  logging_config: specializations.fetch("logging", {}),
25
25
  context: "servers/#{name}/logging"
26
26
 
27
27
  # `healthcheck: false` is an opt-out, not a healthcheck — it leaves @healthcheck nil.
28
28
  if healthcheck_config = specializations["healthcheck"]
29
- @healthcheck = Kamal::Configuration::Role::Healthcheck.new \
29
+ @healthcheck = Dash::Configuration::Role::Healthcheck.new \
30
30
  healthcheck_config: healthcheck_config,
31
31
  context: "servers/#{name}/healthcheck"
32
32
  end
@@ -79,7 +79,7 @@ class Kamal::Configuration::Role
79
79
  end
80
80
 
81
81
  # Kept out of option_args on purpose: Commands::App::Execution splats those into
82
- # one-shot `kamal app exec` containers, which must not inherit a service healthcheck.
82
+ # one-shot `dash app exec` containers, which must not inherit a service healthcheck.
83
83
  def healthcheck_args
84
84
  healthcheck&.args || []
85
85
  end
@@ -94,13 +94,13 @@ class Kamal::Configuration::Role
94
94
  # `boot.wait` a second time inside every group.
95
95
  #
96
96
  # Built on first read rather than in the initializer — Servers.new constructs every Role
97
- # before Kamal::Configuration#initialize has finished assigning its own collaborators.
97
+ # before Dash::Configuration#initialize has finished assigning its own collaborators.
98
98
  def boot
99
99
  return @boot if defined?(@boot)
100
100
 
101
101
  @boot =
102
102
  if (boot_config = specializations["boot"])
103
- Kamal::Configuration::Boot.new \
103
+ Dash::Configuration::Boot.new \
104
104
  config: config, boot_config: boot_config, context: "servers/#{name}/boot"
105
105
  end
106
106
  end
@@ -235,7 +235,7 @@ class Kamal::Configuration::Role
235
235
 
236
236
  def asset_volume(version = config.version)
237
237
  if assets?
238
- Kamal::Configuration::Volume.new \
238
+ Dash::Configuration::Volume.new \
239
239
  host_path: asset_volume_directory(version), container_path: asset_path, options: asset_path_options
240
240
  end
241
241
  end
@@ -255,7 +255,7 @@ class Kamal::Configuration::Role
255
255
  def ensure_one_host_for_ssl
256
256
  # Skip SSL validation when a loadbalancer is present or custom certificates are provided
257
257
  if running_proxy? && proxy.ssl? && hosts.size > 1 && !proxy.loadbalancer.present? && !proxy.custom_ssl_certificate?
258
- raise Kamal::ConfigurationError, "SSL is only supported on a single server unless you provide custom certificates or configure a loadbalancer, found #{hosts.size} servers for role #{name}"
258
+ raise Dash::ConfigurationError, "SSL is only supported on a single server unless you provide custom certificates or configure a loadbalancer, found #{hosts.size} servers for role #{name}"
259
259
  end
260
260
  end
261
261
 
@@ -274,7 +274,7 @@ class Kamal::Configuration::Role
274
274
  if running_proxy?
275
275
  proxy_config = proxy_specializations == true || proxy_specializations.nil? ? {} : proxy_specializations
276
276
 
277
- @specialized_proxy = Kamal::Configuration::Proxy.new \
277
+ @specialized_proxy = Dash::Configuration::Proxy.new \
278
278
  config: config,
279
279
  proxy_config: proxy_config,
280
280
  secrets: config.secrets,
@@ -1,14 +1,14 @@
1
- class Kamal::Configuration::Servers
2
- include Kamal::Configuration::Validation
1
+ class Dash::Configuration::Servers
2
+ include Dash::Configuration::Validation
3
3
 
4
4
  attr_reader :config, :servers_config, :roles
5
5
 
6
6
  def initialize(config:)
7
7
  @config = config
8
8
  @servers_config = config.raw_config.servers
9
- validate! servers_config, with: Kamal::Configuration::Validator::Servers
9
+ validate! servers_config, with: Dash::Configuration::Validator::Servers
10
10
 
11
- @roles = role_names.map { |role_name| Kamal::Configuration::Role.new role_name, config: config }
11
+ @roles = role_names.map { |role_name| Dash::Configuration::Role.new role_name, config: config }
12
12
  end
13
13
 
14
14
  private
@@ -1,7 +1,7 @@
1
- class Kamal::Configuration::Ssh
1
+ class Dash::Configuration::Ssh
2
2
  LOGGER = ::Logger.new(STDERR)
3
3
 
4
- include Kamal::Configuration::Validation
4
+ include Dash::Configuration::Validation
5
5
 
6
6
  attr_reader :ssh_config, :secrets
7
7
 
@@ -43,7 +43,7 @@ class Kamal::Configuration::Ssh
43
43
  if secrets.key?(k)
44
44
  secrets[k]
45
45
  else
46
- warn "Inline key_data usage is deprecated and will be removed in Kamal 3. Please store your key_data in a secret."
46
+ warn "Inline key_data usage is deprecated and will be removed in a future dash release. Please store your key_data in a secret."
47
47
  k
48
48
  end
49
49
  end
@@ -1,5 +1,5 @@
1
- class Kamal::Configuration::Sshkit
2
- include Kamal::Configuration::Validation
1
+ class Dash::Configuration::Sshkit
2
+ include Dash::Configuration::Validation
3
3
 
4
4
  attr_reader :sshkit_config
5
5
 
@@ -1,7 +1,7 @@
1
1
  require "yaml"
2
2
  require "active_support/inflector"
3
3
 
4
- module Kamal::Configuration::Validation
4
+ module Dash::Configuration::Validation
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  class_methods do
@@ -14,7 +14,7 @@ module Kamal::Configuration::Validation
14
14
  end
15
15
  end
16
16
 
17
- def validate!(config, example: nil, context: nil, with: Kamal::Configuration::Validator)
17
+ def validate!(config, example: nil, context: nil, with: Dash::Configuration::Validator)
18
18
  context ||= self.class.validation_config_key
19
19
  example ||= validation_yml[self.class.validation_config_key]
20
20
 
@@ -1,4 +1,4 @@
1
- class Kamal::Configuration::Validator::Accessory < Kamal::Configuration::Validator
1
+ class Dash::Configuration::Validator::Accessory < Dash::Configuration::Validator
2
2
  def validate!
3
3
  super
4
4
 
@@ -1,4 +1,4 @@
1
- class Kamal::Configuration::Validator::Alias < Kamal::Configuration::Validator
1
+ class Dash::Configuration::Validator::Alias < Dash::Configuration::Validator
2
2
  def validate!
3
3
  super
4
4
 
@@ -8,7 +8,7 @@ class Kamal::Configuration::Validator::Alias < Kamal::Configuration::Validator
8
8
  error "Invalid alias name: '#{name}'. Must only contain lowercase letters, alphanumeric, hyphens and underscores."
9
9
  end
10
10
 
11
- if Kamal::Cli::Main.commands.include?(name)
11
+ if Dash::Cli::Main.commands.include?(name)
12
12
  error "Alias '#{name}' conflicts with a built-in command."
13
13
  end
14
14
  end
@@ -1,4 +1,4 @@
1
- class Kamal::Configuration::Validator::Builder < Kamal::Configuration::Validator
1
+ class Dash::Configuration::Validator::Builder < Dash::Configuration::Validator
2
2
  def validate!
3
3
  super
4
4
 
@@ -0,0 +1,6 @@
1
+ class Dash::Configuration::Validator::Configuration < Dash::Configuration::Validator
2
+ private
3
+ def allow_extensions?
4
+ true
5
+ end
6
+ end
@@ -1,4 +1,4 @@
1
- class Kamal::Configuration::Validator::Env < Kamal::Configuration::Validator
1
+ class Dash::Configuration::Validator::Env < Dash::Configuration::Validator
2
2
  SPECIAL_KEYS = [ "clear", "secret", "tags" ]
3
3
 
4
4
  def validate!
@@ -39,7 +39,7 @@ class Kamal::Configuration::Validator::Env < Kamal::Configuration::Validator
39
39
  with_context(tag) do
40
40
  validate_type! value, Hash
41
41
 
42
- Kamal::Configuration::Validator::Env.new(
42
+ Dash::Configuration::Validator::Env.new(
43
43
  value,
44
44
  example: example["tags"].values[1],
45
45
  context: context
@@ -1,4 +1,4 @@
1
- class Kamal::Configuration::Validator::Proxy < Kamal::Configuration::Validator
1
+ class Dash::Configuration::Validator::Proxy < Dash::Configuration::Validator
2
2
  def validate!
3
3
  unless config.nil?
4
4
  super
@@ -149,7 +149,7 @@ class Kamal::Configuration::Validator::Proxy < Kamal::Configuration::Validator
149
149
  validate_dns_provider_zones! provider
150
150
  elsif provider.present? && !supported_dns_provider?(provider)
151
151
  error "unsupported dns_provider '#{provider}'. " \
152
- "Supported providers: #{Kamal::Configuration::Proxy::Acme::DNS_PROVIDERS.join(", ")}"
152
+ "Supported providers: #{Dash::Configuration::Proxy::Acme::DNS_PROVIDERS.join(", ")}"
153
153
  end
154
154
  end
155
155
  end
@@ -175,13 +175,13 @@ class Kamal::Configuration::Validator::Proxy < Kamal::Configuration::Validator
175
175
 
176
176
  unless zone_provider.is_a?(String) && supported_dns_provider?(zone_provider)
177
177
  error "unsupported dns_provider '#{zone_provider}' for '#{zone}'. " \
178
- "Supported providers: #{Kamal::Configuration::Proxy::Acme::DNS_PROVIDERS.join(", ")}"
178
+ "Supported providers: #{Dash::Configuration::Proxy::Acme::DNS_PROVIDERS.join(", ")}"
179
179
  end
180
180
  end
181
181
  end
182
182
 
183
183
  def supported_dns_provider?(provider)
184
- Kamal::Configuration::Proxy::Acme::SUPPORTED_DNS_PROVIDERS.include?(provider.to_s.downcase)
184
+ Dash::Configuration::Proxy::Acme::SUPPORTED_DNS_PROVIDERS.include?(provider.to_s.downcase)
185
185
  end
186
186
 
187
187
  # The whole TLS surface lives in the one `ssl` hash: certificate material,
@@ -278,7 +278,7 @@ class Kamal::Configuration::Validator::Proxy < Kamal::Configuration::Validator
278
278
  # Session affinity and scale-to-zero. The one rule NOT here is that sleep
279
279
  # needs proxy/run/docker_socket: a role can carry `sleep` while the root
280
280
  # carries the socket, and this validator runs against the role's own proxy
281
- # block before it is merged. Kamal::Configuration checks that pairing on the
281
+ # block before it is merged. Dash::Configuration checks that pairing on the
282
282
  # merged config instead.
283
283
  def validate_lifecycle!
284
284
  affinity = config["session_affinity"] || {}
@@ -604,11 +604,11 @@ class Kamal::Configuration::Validator::Proxy < Kamal::Configuration::Validator
604
604
  end
605
605
 
606
606
  encodings.each do |encoding|
607
- canonical = Kamal::Configuration::Proxy::COMPRESSION_ENCODING_ALIASES.fetch(encoding.to_s, encoding.to_s)
607
+ canonical = Dash::Configuration::Proxy::COMPRESSION_ENCODING_ALIASES.fetch(encoding.to_s, encoding.to_s)
608
608
 
609
- unless Kamal::Configuration::Proxy::SUPPORTED_COMPRESSION_ENCODINGS.include?(canonical)
609
+ unless Dash::Configuration::Proxy::SUPPORTED_COMPRESSION_ENCODINGS.include?(canonical)
610
610
  error "unsupported encoding '#{encoding}'. " \
611
- "Supported encodings: #{Kamal::Configuration::Proxy::SUPPORTED_COMPRESSION_ENCODINGS.join(", ")}"
611
+ "Supported encodings: #{Dash::Configuration::Proxy::SUPPORTED_COMPRESSION_ENCODINGS.join(", ")}"
612
612
  end
613
613
  end
614
614