nocoffee-kamal 2.3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +13 -0
  4. data/bin/kamal +18 -0
  5. data/lib/kamal/cli/accessory.rb +287 -0
  6. data/lib/kamal/cli/alias/command.rb +9 -0
  7. data/lib/kamal/cli/app/boot.rb +125 -0
  8. data/lib/kamal/cli/app/prepare_assets.rb +24 -0
  9. data/lib/kamal/cli/app.rb +335 -0
  10. data/lib/kamal/cli/base.rb +198 -0
  11. data/lib/kamal/cli/build/clone.rb +61 -0
  12. data/lib/kamal/cli/build.rb +162 -0
  13. data/lib/kamal/cli/healthcheck/barrier.rb +33 -0
  14. data/lib/kamal/cli/healthcheck/error.rb +2 -0
  15. data/lib/kamal/cli/healthcheck/poller.rb +42 -0
  16. data/lib/kamal/cli/lock.rb +45 -0
  17. data/lib/kamal/cli/main.rb +279 -0
  18. data/lib/kamal/cli/proxy.rb +257 -0
  19. data/lib/kamal/cli/prune.rb +34 -0
  20. data/lib/kamal/cli/registry.rb +17 -0
  21. data/lib/kamal/cli/secrets.rb +43 -0
  22. data/lib/kamal/cli/server.rb +48 -0
  23. data/lib/kamal/cli/templates/deploy.yml +98 -0
  24. data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +3 -0
  25. data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +14 -0
  26. data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
  27. data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +51 -0
  28. data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +47 -0
  29. data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +109 -0
  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 +8 -0
  33. data/lib/kamal/commander/specifics.rb +54 -0
  34. data/lib/kamal/commander.rb +176 -0
  35. data/lib/kamal/commands/accessory.rb +113 -0
  36. data/lib/kamal/commands/app/assets.rb +51 -0
  37. data/lib/kamal/commands/app/containers.rb +31 -0
  38. data/lib/kamal/commands/app/execution.rb +30 -0
  39. data/lib/kamal/commands/app/images.rb +13 -0
  40. data/lib/kamal/commands/app/logging.rb +18 -0
  41. data/lib/kamal/commands/app/proxy.rb +16 -0
  42. data/lib/kamal/commands/app.rb +115 -0
  43. data/lib/kamal/commands/auditor.rb +33 -0
  44. data/lib/kamal/commands/base.rb +98 -0
  45. data/lib/kamal/commands/builder/base.rb +111 -0
  46. data/lib/kamal/commands/builder/clone.rb +31 -0
  47. data/lib/kamal/commands/builder/hybrid.rb +21 -0
  48. data/lib/kamal/commands/builder/local.rb +14 -0
  49. data/lib/kamal/commands/builder/remote.rb +63 -0
  50. data/lib/kamal/commands/builder.rb +56 -0
  51. data/lib/kamal/commands/docker.rb +34 -0
  52. data/lib/kamal/commands/hook.rb +20 -0
  53. data/lib/kamal/commands/lock.rb +70 -0
  54. data/lib/kamal/commands/proxy.rb +87 -0
  55. data/lib/kamal/commands/prune.rb +38 -0
  56. data/lib/kamal/commands/registry.rb +14 -0
  57. data/lib/kamal/commands/server.rb +15 -0
  58. data/lib/kamal/commands.rb +2 -0
  59. data/lib/kamal/configuration/accessory.rb +186 -0
  60. data/lib/kamal/configuration/alias.rb +15 -0
  61. data/lib/kamal/configuration/boot.rb +25 -0
  62. data/lib/kamal/configuration/builder.rb +191 -0
  63. data/lib/kamal/configuration/docs/accessory.yml +100 -0
  64. data/lib/kamal/configuration/docs/alias.yml +26 -0
  65. data/lib/kamal/configuration/docs/boot.yml +19 -0
  66. data/lib/kamal/configuration/docs/builder.yml +110 -0
  67. data/lib/kamal/configuration/docs/configuration.yml +178 -0
  68. data/lib/kamal/configuration/docs/env.yml +85 -0
  69. data/lib/kamal/configuration/docs/logging.yml +21 -0
  70. data/lib/kamal/configuration/docs/proxy.yml +110 -0
  71. data/lib/kamal/configuration/docs/registry.yml +52 -0
  72. data/lib/kamal/configuration/docs/role.yml +53 -0
  73. data/lib/kamal/configuration/docs/servers.yml +27 -0
  74. data/lib/kamal/configuration/docs/ssh.yml +70 -0
  75. data/lib/kamal/configuration/docs/sshkit.yml +23 -0
  76. data/lib/kamal/configuration/env/tag.rb +13 -0
  77. data/lib/kamal/configuration/env.rb +29 -0
  78. data/lib/kamal/configuration/logging.rb +33 -0
  79. data/lib/kamal/configuration/proxy.rb +63 -0
  80. data/lib/kamal/configuration/registry.rb +32 -0
  81. data/lib/kamal/configuration/role.rb +220 -0
  82. data/lib/kamal/configuration/servers.rb +18 -0
  83. data/lib/kamal/configuration/ssh.rb +57 -0
  84. data/lib/kamal/configuration/sshkit.rb +22 -0
  85. data/lib/kamal/configuration/validation.rb +27 -0
  86. data/lib/kamal/configuration/validator/accessory.rb +9 -0
  87. data/lib/kamal/configuration/validator/alias.rb +15 -0
  88. data/lib/kamal/configuration/validator/builder.rb +13 -0
  89. data/lib/kamal/configuration/validator/configuration.rb +6 -0
  90. data/lib/kamal/configuration/validator/env.rb +54 -0
  91. data/lib/kamal/configuration/validator/proxy.rb +15 -0
  92. data/lib/kamal/configuration/validator/registry.rb +25 -0
  93. data/lib/kamal/configuration/validator/role.rb +11 -0
  94. data/lib/kamal/configuration/validator/servers.rb +7 -0
  95. data/lib/kamal/configuration/validator.rb +171 -0
  96. data/lib/kamal/configuration/volume.rb +22 -0
  97. data/lib/kamal/configuration.rb +393 -0
  98. data/lib/kamal/env_file.rb +44 -0
  99. data/lib/kamal/git.rb +27 -0
  100. data/lib/kamal/secrets/adapters/base.rb +23 -0
  101. data/lib/kamal/secrets/adapters/bitwarden.rb +81 -0
  102. data/lib/kamal/secrets/adapters/last_pass.rb +39 -0
  103. data/lib/kamal/secrets/adapters/one_password.rb +70 -0
  104. data/lib/kamal/secrets/adapters/test.rb +14 -0
  105. data/lib/kamal/secrets/adapters.rb +14 -0
  106. data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +32 -0
  107. data/lib/kamal/secrets.rb +42 -0
  108. data/lib/kamal/sshkit_with_ext.rb +142 -0
  109. data/lib/kamal/tags.rb +40 -0
  110. data/lib/kamal/utils/sensitive.rb +20 -0
  111. data/lib/kamal/utils.rb +110 -0
  112. data/lib/kamal/version.rb +3 -0
  113. data/lib/kamal.rb +14 -0
  114. metadata +349 -0
@@ -0,0 +1,87 @@
1
+ class Kamal::Commands::Proxy < Kamal::Commands::Base
2
+ delegate :argumentize, :optionize, to: Kamal::Utils
3
+
4
+ def run
5
+ docker :run,
6
+ "--name", container_name,
7
+ "--network", "kamal",
8
+ "--detach",
9
+ "--restart", "unless-stopped",
10
+ "--volume", "kamal-proxy-config:/home/kamal-proxy/.config/kamal-proxy",
11
+ "\$\(#{get_boot_options.join(" ")}\)",
12
+ config.proxy_image
13
+ end
14
+
15
+ def start
16
+ docker :container, :start, container_name
17
+ end
18
+
19
+ def stop(name: container_name)
20
+ docker :container, :stop, name
21
+ end
22
+
23
+ def start_or_run
24
+ combine start, run, by: "||"
25
+ end
26
+
27
+ def info
28
+ docker :ps, "--filter", "name=^#{container_name}$"
29
+ end
30
+
31
+ def version
32
+ pipe \
33
+ docker(:inspect, container_name, "--format '{{.Config.Image}}'"),
34
+ [ :cut, "-d:", "-f2" ]
35
+ end
36
+
37
+ def logs(timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil)
38
+ pipe \
39
+ docker(:logs, container_name, ("--since #{since}" if since), ("--tail #{lines}" if lines), ("--timestamps" if timestamps), "2>&1"),
40
+ ("grep '#{grep}'#{" #{grep_options}" if grep_options}" if grep)
41
+ end
42
+
43
+ def follow_logs(host:, timestamps: true, grep: nil, grep_options: nil)
44
+ run_over_ssh pipe(
45
+ docker(:logs, container_name, ("--timestamps" if timestamps), "--tail", "10", "--follow", "2>&1"),
46
+ (%(grep "#{grep}"#{" #{grep_options}" if grep_options}) if grep)
47
+ ).join(" "), host: host
48
+ end
49
+
50
+ def remove_container
51
+ docker :container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=kamal-proxy"
52
+ end
53
+
54
+ def remove_image
55
+ docker :image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=kamal-proxy"
56
+ end
57
+
58
+ def cleanup_traefik
59
+ chain \
60
+ docker(:container, :stop, "traefik"),
61
+ combine(
62
+ docker(:container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=Traefik"),
63
+ docker(:image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=Traefik")
64
+ )
65
+ end
66
+
67
+ def ensure_proxy_directory
68
+ make_directory config.proxy_directory
69
+ end
70
+
71
+ def remove_proxy_directory
72
+ remove_directory config.proxy_directory
73
+ end
74
+
75
+ def get_boot_options
76
+ combine [ :cat, config.proxy_options_file ], [ :echo, "\"#{config.proxy_options_default.join(" ")}\"" ], by: "||"
77
+ end
78
+
79
+ def reset_boot_options
80
+ remove_file config.proxy_options_file
81
+ end
82
+
83
+ private
84
+ def container_name
85
+ config.proxy_container_name
86
+ end
87
+ end
@@ -0,0 +1,38 @@
1
+ require "active_support/duration"
2
+ require "active_support/core_ext/numeric/time"
3
+
4
+ class Kamal::Commands::Prune < Kamal::Commands::Base
5
+ def dangling_images
6
+ docker :image, :prune, "--force", "--filter", "label=service=#{config.service}"
7
+ end
8
+
9
+ def tagged_images
10
+ pipe \
11
+ docker(:image, :ls, *service_filter, "--format", "'{{.ID}} {{.Repository}}:{{.Tag}}'"),
12
+ grep("-v -w \"#{active_image_list}\""),
13
+ "while read image tag; do docker rmi $tag; done"
14
+ end
15
+
16
+ def app_containers(retain:)
17
+ pipe \
18
+ docker(:ps, "-q", "-a", *service_filter, *stopped_containers_filters),
19
+ "tail -n +#{retain + 1}",
20
+ "while read container_id; do docker rm $container_id; done"
21
+ end
22
+
23
+ private
24
+ def stopped_containers_filters
25
+ [ "created", "exited", "dead" ].flat_map { |status| [ "--filter", "status=#{status}" ] }
26
+ end
27
+
28
+ def active_image_list
29
+ # Pull the images that are used by any containers
30
+ # Append repo:latest - to avoid deleting the latest tag
31
+ # Append repo:<none> - to avoid deleting dangling images that are in use. Unused dangling images are deleted separately
32
+ "$(docker container ls -a --format '{{.Image}}\\|' --filter label=service=#{config.service} | tr -d '\\n')#{config.latest_image}\\|#{config.repository}:<none>"
33
+ end
34
+
35
+ def service_filter
36
+ [ "--filter", "label=service=#{config.service}" ]
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ class Kamal::Commands::Registry < Kamal::Commands::Base
2
+ delegate :registry, to: :config
3
+
4
+ def login
5
+ docker :login,
6
+ registry.server,
7
+ "-u", sensitive(Kamal::Utils.escape_shell_value(registry.username)),
8
+ "-p", sensitive(Kamal::Utils.escape_shell_value(registry.password))
9
+ end
10
+
11
+ def logout
12
+ docker :logout, registry.server
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ class Kamal::Commands::Server < Kamal::Commands::Base
2
+ def ensure_run_directory
3
+ make_directory config.run_directory
4
+ end
5
+
6
+ def remove_app_directory
7
+ remove_directory config.app_directory
8
+ end
9
+
10
+ def app_directory_count
11
+ pipe \
12
+ [ :ls, config.apps_directory ],
13
+ [ :wc, "-l" ]
14
+ end
15
+ end
@@ -0,0 +1,2 @@
1
+ module Kamal::Commands
2
+ end
@@ -0,0 +1,186 @@
1
+ class Kamal::Configuration::Accessory
2
+ include Kamal::Configuration::Validation
3
+
4
+ DEFAULT_NETWORK = "kamal"
5
+
6
+ delegate :argumentize, :optionize, to: Kamal::Utils
7
+
8
+ attr_reader :name, :accessory_config, :env
9
+
10
+ def initialize(name, config:)
11
+ @name, @config, @accessory_config = name.inquiry, config, config.raw_config["accessories"][name]
12
+
13
+ validate! \
14
+ accessory_config,
15
+ example: validation_yml["accessories"]["mysql"],
16
+ context: "accessories/#{name}",
17
+ with: Kamal::Configuration::Validator::Accessory
18
+
19
+ @env = Kamal::Configuration::Env.new \
20
+ config: accessory_config.fetch("env", {}),
21
+ secrets: config.secrets,
22
+ context: "accessories/#{name}/env"
23
+ end
24
+
25
+ def service_name
26
+ accessory_config["service"] || "#{config.service}-#{name}"
27
+ end
28
+
29
+ def image
30
+ accessory_config["image"]
31
+ end
32
+
33
+ def hosts
34
+ hosts_from_host || hosts_from_hosts || hosts_from_roles
35
+ end
36
+
37
+ def port
38
+ if port = accessory_config["port"]&.to_s
39
+ port.include?(":") ? port : "#{port}:#{port}"
40
+ end
41
+ end
42
+
43
+ def network_args
44
+ argumentize "--network", network
45
+ end
46
+
47
+ def publish_args
48
+ argumentize "--publish", port if port
49
+ end
50
+
51
+ def labels
52
+ default_labels.merge(accessory_config["labels"] || {})
53
+ end
54
+
55
+ def label_args
56
+ argumentize "--label", labels
57
+ end
58
+
59
+ def env_args
60
+ [ *env.clear_args, *argumentize("--env-file", secrets_path) ]
61
+ end
62
+
63
+ def env_directory
64
+ File.join(config.env_directory, "accessories")
65
+ end
66
+
67
+ def secrets_io
68
+ env.secrets_io
69
+ end
70
+
71
+ def secrets_path
72
+ File.join(config.env_directory, "accessories", "#{name}.env")
73
+ end
74
+
75
+ def files
76
+ accessory_config["files"]&.to_h do |local_to_remote_mapping|
77
+ local_file, remote_file = local_to_remote_mapping.split(":")
78
+ [ expand_local_file(local_file), expand_remote_file(remote_file) ]
79
+ end || {}
80
+ end
81
+
82
+ def directories
83
+ accessory_config["directories"]&.to_h do |host_to_container_mapping|
84
+ host_path, container_path = host_to_container_mapping.split(":")
85
+ [ expand_host_path(host_path), container_path ]
86
+ end || {}
87
+ end
88
+
89
+ def volumes
90
+ specific_volumes + remote_files_as_volumes + remote_directories_as_volumes
91
+ end
92
+
93
+ def volume_args
94
+ argumentize "--volume", volumes
95
+ end
96
+
97
+ def option_args
98
+ if args = accessory_config["options"]
99
+ optionize args
100
+ else
101
+ []
102
+ end
103
+ end
104
+
105
+ def cmd
106
+ accessory_config["cmd"]
107
+ end
108
+
109
+ private
110
+ attr_accessor :config
111
+
112
+ def default_labels
113
+ { "service" => service_name }
114
+ end
115
+
116
+ def expand_local_file(local_file)
117
+ if local_file.end_with?("erb")
118
+ with_clear_env_loaded { read_dynamic_file(local_file) }
119
+ else
120
+ Pathname.new(File.expand_path(local_file)).to_s
121
+ end
122
+ end
123
+
124
+ def with_clear_env_loaded
125
+ env.clear.each { |k, v| ENV[k] = v }
126
+ yield
127
+ ensure
128
+ env.clear.each { |k, v| ENV.delete(k) }
129
+ end
130
+
131
+ def read_dynamic_file(local_file)
132
+ StringIO.new(ERB.new(IO.read(local_file)).result)
133
+ end
134
+
135
+ def expand_remote_file(remote_file)
136
+ service_name + remote_file
137
+ end
138
+
139
+ def specific_volumes
140
+ accessory_config["volumes"] || []
141
+ end
142
+
143
+ def remote_files_as_volumes
144
+ accessory_config["files"]&.collect do |local_to_remote_mapping|
145
+ _, remote_file = local_to_remote_mapping.split(":")
146
+ "#{service_data_directory + remote_file}:#{remote_file}"
147
+ end || []
148
+ end
149
+
150
+ def remote_directories_as_volumes
151
+ accessory_config["directories"]&.collect do |host_to_container_mapping|
152
+ host_path, container_path = host_to_container_mapping.split(":")
153
+ [ expand_host_path(host_path), container_path ].join(":")
154
+ end || []
155
+ end
156
+
157
+ def expand_host_path(host_path)
158
+ absolute_path?(host_path) ? host_path : File.join(service_data_directory, host_path)
159
+ end
160
+
161
+ def absolute_path?(path)
162
+ Pathname.new(path).absolute?
163
+ end
164
+
165
+ def service_data_directory
166
+ "$PWD/#{service_name}"
167
+ end
168
+
169
+ def hosts_from_host
170
+ [ accessory_config["host"] ] if accessory_config.key?("host")
171
+ end
172
+
173
+ def hosts_from_hosts
174
+ accessory_config["hosts"] if accessory_config.key?("hosts")
175
+ end
176
+
177
+ def hosts_from_roles
178
+ if accessory_config.key?("roles")
179
+ accessory_config["roles"].flat_map { |role| config.role(role).hosts }
180
+ end
181
+ end
182
+
183
+ def network
184
+ accessory_config["network"] || DEFAULT_NETWORK
185
+ end
186
+ end
@@ -0,0 +1,15 @@
1
+ class Kamal::Configuration::Alias
2
+ include Kamal::Configuration::Validation
3
+
4
+ attr_reader :name, :command
5
+
6
+ def initialize(name, config:)
7
+ @name, @command = name.inquiry, config.raw_config["aliases"][name]
8
+
9
+ validate! \
10
+ command,
11
+ example: validation_yml["aliases"]["uname"],
12
+ context: "aliases/#{name}",
13
+ with: Kamal::Configuration::Validator::Alias
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ class Kamal::Configuration::Boot
2
+ include Kamal::Configuration::Validation
3
+
4
+ attr_reader :boot_config, :host_count
5
+
6
+ def initialize(config:)
7
+ @boot_config = config.raw_config.boot || {}
8
+ @host_count = config.all_hosts.count
9
+ validate! boot_config
10
+ end
11
+
12
+ def limit
13
+ limit = boot_config["limit"]
14
+
15
+ if limit.to_s.end_with?("%")
16
+ [ host_count * limit.to_i / 100, 1 ].max
17
+ else
18
+ limit
19
+ end
20
+ end
21
+
22
+ def wait
23
+ boot_config["wait"]
24
+ end
25
+ end
@@ -0,0 +1,191 @@
1
+ class Kamal::Configuration::Builder
2
+ include Kamal::Configuration::Validation
3
+
4
+ attr_reader :config, :builder_config
5
+ delegate :image, :service, to: :config
6
+ delegate :server, to: :"config.registry"
7
+
8
+ def initialize(config:)
9
+ @config = config
10
+ @builder_config = config.raw_config.builder || {}
11
+ @image = config.image
12
+ @server = config.registry.server
13
+ @service = config.service
14
+
15
+ validate! builder_config, with: Kamal::Configuration::Validator::Builder
16
+ end
17
+
18
+ def to_h
19
+ builder_config
20
+ end
21
+
22
+ def remote
23
+ builder_config["remote"]
24
+ end
25
+
26
+ def arches
27
+ Array(builder_config.fetch("arch", default_arch))
28
+ end
29
+
30
+ def local_arches
31
+ @local_arches ||= if local_disabled?
32
+ []
33
+ elsif remote
34
+ arches & [ Kamal::Utils.docker_arch ]
35
+ else
36
+ arches
37
+ end
38
+ end
39
+
40
+ def remote_arches
41
+ @remote_arches ||= if remote
42
+ arches - local_arches
43
+ else
44
+ []
45
+ end
46
+ end
47
+
48
+ def remote?
49
+ remote_arches.any?
50
+ end
51
+
52
+ def local?
53
+ !local_disabled? && (arches.empty? || local_arches.any?)
54
+ end
55
+
56
+ def cached?
57
+ !!builder_config["cache"]
58
+ end
59
+
60
+ def args
61
+ builder_config["args"] || {}
62
+ end
63
+
64
+ def secrets
65
+ (builder_config["secrets"] || []).to_h { |key| [ key, config.secrets[key] ] }
66
+ end
67
+
68
+ def dockerfile
69
+ builder_config["dockerfile"] || "Dockerfile"
70
+ end
71
+
72
+ def target
73
+ builder_config["target"]
74
+ end
75
+
76
+ def context
77
+ builder_config["context"] || "."
78
+ end
79
+
80
+ def driver
81
+ builder_config.fetch("driver", "docker-container")
82
+ end
83
+
84
+ def local_disabled?
85
+ builder_config["local"] == false
86
+ end
87
+
88
+ def cache_from
89
+ if cached?
90
+ case builder_config["cache"]["type"]
91
+ when "gha"
92
+ cache_from_config_for_gha
93
+ when "registry"
94
+ cache_from_config_for_registry
95
+ end
96
+ end
97
+ end
98
+
99
+ def cache_to
100
+ if cached?
101
+ case builder_config["cache"]["type"]
102
+ when "gha"
103
+ cache_to_config_for_gha
104
+ when "registry"
105
+ cache_to_config_for_registry
106
+ end
107
+ end
108
+ end
109
+
110
+ def ssh
111
+ builder_config["ssh"]
112
+ end
113
+
114
+ def provenance
115
+ builder_config["provenance"]
116
+ end
117
+
118
+ def git_clone?
119
+ Kamal::Git.used? && builder_config["context"].nil?
120
+ end
121
+
122
+ def clone_directory
123
+ @clone_directory ||= File.join Dir.tmpdir, "kamal-clones", [ service, pwd_sha ].compact.join("-")
124
+ end
125
+
126
+ def build_directory
127
+ @build_directory ||=
128
+ if git_clone?
129
+ File.join clone_directory, repo_basename, repo_relative_pwd
130
+ else
131
+ "."
132
+ end
133
+ end
134
+
135
+ def docker_driver?
136
+ driver == "docker"
137
+ end
138
+
139
+ private
140
+ def valid?
141
+ if docker_driver?
142
+ raise ArgumentError, "Invalid builder configuration: the `docker` driver does not not support remote builders" if remote
143
+ raise ArgumentError, "Invalid builder configuration: the `docker` driver does not not support caching" if cached?
144
+ raise ArgumentError, "Invalid builder configuration: the `docker` driver does not not support multiple arches" if arches.many?
145
+ end
146
+
147
+ if @options["cache"] && @options["cache"]["type"]
148
+ raise ArgumentError, "Invalid cache type: #{@options["cache"]["type"]}" unless [ "gha", "registry" ].include?(@options["cache"]["type"])
149
+ end
150
+ end
151
+
152
+ def cache_image
153
+ builder_config["cache"]&.fetch("image", nil) || "#{image}-build-cache"
154
+ end
155
+
156
+ def cache_image_ref
157
+ [ server, cache_image ].compact.join("/")
158
+ end
159
+
160
+ def cache_from_config_for_gha
161
+ "type=gha"
162
+ end
163
+
164
+ def cache_from_config_for_registry
165
+ [ "type=registry", "ref=#{cache_image_ref}" ].compact.join(",")
166
+ end
167
+
168
+ def cache_to_config_for_gha
169
+ [ "type=gha", builder_config["cache"]&.fetch("options", nil) ].compact.join(",")
170
+ end
171
+
172
+ def cache_to_config_for_registry
173
+ [ "type=registry", "ref=#{cache_image_ref}", builder_config["cache"]&.fetch("options", nil) ].compact.join(",")
174
+ end
175
+
176
+ def repo_basename
177
+ File.basename(Kamal::Git.root)
178
+ end
179
+
180
+ def repo_relative_pwd
181
+ Dir.pwd.delete_prefix(Kamal::Git.root)
182
+ end
183
+
184
+ def pwd_sha
185
+ Digest::SHA256.hexdigest(Dir.pwd)[0..12]
186
+ end
187
+
188
+ def default_arch
189
+ docker_driver? ? [] : [ "amd64", "arm64" ]
190
+ end
191
+ end
@@ -0,0 +1,100 @@
1
+ # Accessories
2
+ #
3
+ # Accessories can be booted on a single host, a list of hosts, or on specific roles.
4
+ # The hosts do not need to be defined in the Kamal servers configuration.
5
+ #
6
+ # Accessories are managed separately from the main service — they are not updated
7
+ # when you deploy, and they do not have zero-downtime deployments.
8
+ #
9
+ # Run `kamal accessory boot <accessory>` to boot an accessory.
10
+ # See `kamal accessory --help` for more information.
11
+
12
+ # Configuring accessories
13
+ #
14
+ # First, define the accessory in the `accessories`:
15
+ accessories:
16
+ mysql:
17
+
18
+ # Service name
19
+ #
20
+ # This is used in the service label and defaults to `<service>-<accessory>`,
21
+ # where `<service>` is the main service name from the root configuration:
22
+ service: mysql
23
+
24
+ # Image
25
+ #
26
+ # The Docker image to use, prefix it with a registry if not using Docker Hub:
27
+ image: mysql:8.0
28
+
29
+ # Accessory hosts
30
+ #
31
+ # Specify one of `host`, `hosts`, or `roles`:
32
+ host: mysql-db1
33
+ hosts:
34
+ - mysql-db1
35
+ - mysql-db2
36
+ roles:
37
+ - mysql
38
+
39
+ # Custom command
40
+ #
41
+ # You can set a custom command to run in the container if you do not want to use the default:
42
+ cmd: "bin/mysqld"
43
+
44
+ # Port mappings
45
+ #
46
+ # See https://docs.docker.com/network/, and especially note the warning about the security
47
+ # implications of exposing ports publicly.
48
+ port: "127.0.0.1:3306:3306"
49
+
50
+ # Labels
51
+ labels:
52
+ app: myapp
53
+
54
+ # Options
55
+ #
56
+ # These are passed to the Docker run command in the form `--<name> <value>`:
57
+ options:
58
+ restart: always
59
+ cpus: 2
60
+
61
+ # Environment variables
62
+ #
63
+ # See kamal docs env for more information:
64
+ env:
65
+ ...
66
+
67
+ # Copying files
68
+ #
69
+ # You can specify files to mount into the container.
70
+ # The format is `local:remote`, where `local` is the path to the file on the local machine
71
+ # and `remote` is the path to the file in the container.
72
+ #
73
+ # They will be uploaded from the local repo to the host and then mounted.
74
+ #
75
+ # ERB files will be evaluated before being copied.
76
+ files:
77
+ - config/my.cnf.erb:/etc/mysql/my.cnf
78
+ - config/myoptions.cnf:/etc/mysql/myoptions.cnf
79
+
80
+ # Directories
81
+ #
82
+ # You can specify directories to mount into the container. They will be created on the host
83
+ # before being mounted:
84
+ directories:
85
+ - mysql-logs:/var/log/mysql
86
+
87
+ # Volumes
88
+ #
89
+ # Any other volumes to mount, in addition to the files and directories.
90
+ # They are not created or copied before mounting:
91
+ volumes:
92
+ - /path/to/mysql-logs:/var/log/mysql
93
+
94
+ # Network
95
+ #
96
+ # The network the accessory will be attached to.
97
+ #
98
+ # Defaults to kamal:
99
+ network: custom
100
+
@@ -0,0 +1,26 @@
1
+ # Aliases
2
+ #
3
+ # Aliases are shortcuts for Kamal commands.
4
+ #
5
+ # For example, for a Rails app, you might open a console with:
6
+ #
7
+ # ```shell
8
+ # kamal app exec -i -r console "rails console"
9
+ # ```
10
+ #
11
+ # By defining an alias, like this:
12
+ aliases:
13
+ console: app exec -r console -i "rails console"
14
+ # You can now open the console with:
15
+ #
16
+ # ```shell
17
+ # kamal console
18
+ # ```
19
+
20
+ # Configuring aliases
21
+ #
22
+ # Aliases are defined in the root config under the alias key.
23
+ #
24
+ # Each alias is named and can only contain lowercase letters, numbers, dashes, and underscores:
25
+ aliases:
26
+ uname: app exec -p -q -r web "uname -a"