nocoffee-kamal 2.3.0.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 (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"