kamal-insecure 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +13 -0
- data/bin/kamal +18 -0
- data/lib/kamal/cli/accessory.rb +313 -0
- data/lib/kamal/cli/alias/command.rb +10 -0
- data/lib/kamal/cli/app/assets.rb +24 -0
- data/lib/kamal/cli/app/boot.rb +126 -0
- data/lib/kamal/cli/app/error_pages.rb +33 -0
- data/lib/kamal/cli/app/ssl_certificates.rb +28 -0
- data/lib/kamal/cli/app.rb +400 -0
- data/lib/kamal/cli/base.rb +223 -0
- data/lib/kamal/cli/build/clone.rb +61 -0
- data/lib/kamal/cli/build.rb +204 -0
- data/lib/kamal/cli/healthcheck/barrier.rb +33 -0
- data/lib/kamal/cli/healthcheck/error.rb +2 -0
- data/lib/kamal/cli/healthcheck/poller.rb +42 -0
- data/lib/kamal/cli/lock.rb +45 -0
- data/lib/kamal/cli/main.rb +277 -0
- data/lib/kamal/cli/proxy.rb +290 -0
- data/lib/kamal/cli/prune.rb +34 -0
- data/lib/kamal/cli/registry.rb +19 -0
- data/lib/kamal/cli/secrets.rb +49 -0
- data/lib/kamal/cli/server.rb +50 -0
- data/lib/kamal/cli/templates/deploy.yml +101 -0
- data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +14 -0
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +51 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +47 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +122 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +3 -0
- data/lib/kamal/cli/templates/secrets +17 -0
- data/lib/kamal/cli.rb +9 -0
- data/lib/kamal/commander/specifics.rb +62 -0
- data/lib/kamal/commander.rb +167 -0
- data/lib/kamal/commands/accessory/proxy.rb +16 -0
- data/lib/kamal/commands/accessory.rb +113 -0
- data/lib/kamal/commands/app/assets.rb +51 -0
- data/lib/kamal/commands/app/containers.rb +31 -0
- data/lib/kamal/commands/app/error_pages.rb +9 -0
- data/lib/kamal/commands/app/execution.rb +32 -0
- data/lib/kamal/commands/app/images.rb +13 -0
- data/lib/kamal/commands/app/logging.rb +28 -0
- data/lib/kamal/commands/app/proxy.rb +32 -0
- data/lib/kamal/commands/app.rb +124 -0
- data/lib/kamal/commands/auditor.rb +39 -0
- data/lib/kamal/commands/base.rb +134 -0
- data/lib/kamal/commands/builder/base.rb +124 -0
- data/lib/kamal/commands/builder/clone.rb +31 -0
- data/lib/kamal/commands/builder/cloud.rb +22 -0
- data/lib/kamal/commands/builder/hybrid.rb +21 -0
- data/lib/kamal/commands/builder/local.rb +14 -0
- data/lib/kamal/commands/builder/pack.rb +46 -0
- data/lib/kamal/commands/builder/remote.rb +63 -0
- data/lib/kamal/commands/builder.rb +48 -0
- data/lib/kamal/commands/docker.rb +34 -0
- data/lib/kamal/commands/hook.rb +20 -0
- data/lib/kamal/commands/lock.rb +70 -0
- data/lib/kamal/commands/proxy.rb +127 -0
- data/lib/kamal/commands/prune.rb +38 -0
- data/lib/kamal/commands/registry.rb +16 -0
- data/lib/kamal/commands/server.rb +15 -0
- data/lib/kamal/commands.rb +2 -0
- data/lib/kamal/configuration/accessory.rb +241 -0
- data/lib/kamal/configuration/alias.rb +15 -0
- data/lib/kamal/configuration/boot.rb +25 -0
- data/lib/kamal/configuration/builder.rb +211 -0
- data/lib/kamal/configuration/docs/accessory.yml +128 -0
- data/lib/kamal/configuration/docs/alias.yml +26 -0
- data/lib/kamal/configuration/docs/boot.yml +19 -0
- data/lib/kamal/configuration/docs/builder.yml +132 -0
- data/lib/kamal/configuration/docs/configuration.yml +184 -0
- data/lib/kamal/configuration/docs/env.yml +116 -0
- data/lib/kamal/configuration/docs/logging.yml +21 -0
- data/lib/kamal/configuration/docs/proxy.yml +164 -0
- data/lib/kamal/configuration/docs/registry.yml +56 -0
- data/lib/kamal/configuration/docs/role.yml +53 -0
- data/lib/kamal/configuration/docs/servers.yml +27 -0
- data/lib/kamal/configuration/docs/ssh.yml +70 -0
- data/lib/kamal/configuration/docs/sshkit.yml +23 -0
- data/lib/kamal/configuration/env/tag.rb +13 -0
- data/lib/kamal/configuration/env.rb +38 -0
- data/lib/kamal/configuration/logging.rb +33 -0
- data/lib/kamal/configuration/proxy/boot.rb +129 -0
- data/lib/kamal/configuration/proxy.rb +124 -0
- data/lib/kamal/configuration/registry.rb +32 -0
- data/lib/kamal/configuration/role.rb +222 -0
- data/lib/kamal/configuration/servers.rb +25 -0
- data/lib/kamal/configuration/ssh.rb +57 -0
- data/lib/kamal/configuration/sshkit.rb +22 -0
- data/lib/kamal/configuration/validation.rb +27 -0
- data/lib/kamal/configuration/validator/accessory.rb +13 -0
- data/lib/kamal/configuration/validator/alias.rb +15 -0
- data/lib/kamal/configuration/validator/builder.rb +15 -0
- data/lib/kamal/configuration/validator/configuration.rb +6 -0
- data/lib/kamal/configuration/validator/env.rb +54 -0
- data/lib/kamal/configuration/validator/proxy.rb +25 -0
- data/lib/kamal/configuration/validator/registry.rb +25 -0
- data/lib/kamal/configuration/validator/role.rb +13 -0
- data/lib/kamal/configuration/validator/servers.rb +7 -0
- data/lib/kamal/configuration/validator.rb +191 -0
- data/lib/kamal/configuration/volume.rb +22 -0
- data/lib/kamal/configuration.rb +372 -0
- data/lib/kamal/docker.rb +30 -0
- data/lib/kamal/env_file.rb +44 -0
- data/lib/kamal/git.rb +37 -0
- data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +51 -0
- data/lib/kamal/secrets/adapters/base.rb +33 -0
- data/lib/kamal/secrets/adapters/bitwarden.rb +81 -0
- data/lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb +66 -0
- data/lib/kamal/secrets/adapters/doppler.rb +57 -0
- data/lib/kamal/secrets/adapters/enpass.rb +71 -0
- data/lib/kamal/secrets/adapters/gcp_secret_manager.rb +112 -0
- data/lib/kamal/secrets/adapters/last_pass.rb +40 -0
- data/lib/kamal/secrets/adapters/one_password.rb +104 -0
- data/lib/kamal/secrets/adapters/passbolt.rb +130 -0
- data/lib/kamal/secrets/adapters/test.rb +14 -0
- data/lib/kamal/secrets/adapters.rb +16 -0
- data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +33 -0
- data/lib/kamal/secrets.rb +42 -0
- data/lib/kamal/sshkit_with_ext.rb +142 -0
- data/lib/kamal/tags.rb +40 -0
- data/lib/kamal/utils/sensitive.rb +20 -0
- data/lib/kamal/utils.rb +110 -0
- data/lib/kamal/version.rb +3 -0
- data/lib/kamal.rb +14 -0
- metadata +365 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module Kamal::Commands::App::Proxy
|
2
|
+
delegate :container_name, to: :"config.proxy_boot", prefix: :proxy
|
3
|
+
|
4
|
+
def deploy(target:)
|
5
|
+
proxy_exec :deploy, role.container_prefix, *role.proxy.deploy_command_args(target: target)
|
6
|
+
end
|
7
|
+
|
8
|
+
def remove
|
9
|
+
proxy_exec :remove, role.container_prefix
|
10
|
+
end
|
11
|
+
|
12
|
+
def live
|
13
|
+
proxy_exec :resume, role.container_prefix
|
14
|
+
end
|
15
|
+
|
16
|
+
def maintenance(**options)
|
17
|
+
proxy_exec :stop, role.container_prefix, *role.proxy.stop_command_args(**options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove_proxy_app_directory
|
21
|
+
remove_directory config.proxy_boot.app_directory
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_ssl_directory
|
25
|
+
make_directory(File.join(config.proxy_boot.tls_directory, role.name))
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def proxy_exec(*command)
|
30
|
+
docker :exec, proxy_container_name, "kamal-proxy", *command
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
class Kamal::Commands::App < Kamal::Commands::Base
|
2
|
+
include Assets, Containers, ErrorPages, Execution, Images, Logging, Proxy
|
3
|
+
|
4
|
+
ACTIVE_DOCKER_STATUSES = [ :running, :restarting ]
|
5
|
+
|
6
|
+
attr_reader :role, :host
|
7
|
+
|
8
|
+
delegate :container_name, to: :role
|
9
|
+
|
10
|
+
def initialize(config, role: nil, host: nil)
|
11
|
+
super(config)
|
12
|
+
@role = role
|
13
|
+
@host = host
|
14
|
+
end
|
15
|
+
|
16
|
+
def run(hostname: nil)
|
17
|
+
docker :run,
|
18
|
+
"--detach",
|
19
|
+
"--restart unless-stopped",
|
20
|
+
"--name", container_name,
|
21
|
+
"--network", "kamal",
|
22
|
+
*([ "--hostname", hostname ] if hostname),
|
23
|
+
"--env", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
|
24
|
+
"--env", "KAMAL_VERSION=\"#{config.version}\"",
|
25
|
+
"--env", "KAMAL_HOST=\"#{host}\"",
|
26
|
+
*role.env_args(host),
|
27
|
+
*role.logging_args,
|
28
|
+
*config.volume_args,
|
29
|
+
*role.asset_volume_args,
|
30
|
+
*role.label_args,
|
31
|
+
*role.option_args,
|
32
|
+
config.absolute_image,
|
33
|
+
role.cmd
|
34
|
+
end
|
35
|
+
|
36
|
+
def start
|
37
|
+
docker :start, container_name
|
38
|
+
end
|
39
|
+
|
40
|
+
def status(version:)
|
41
|
+
pipe container_id_for_version(version), xargs(docker(:inspect, "--format", DOCKER_HEALTH_STATUS_FORMAT))
|
42
|
+
end
|
43
|
+
|
44
|
+
def stop(version: nil)
|
45
|
+
pipe \
|
46
|
+
version ? container_id_for_version(version) : current_running_container_id,
|
47
|
+
xargs(docker(:stop, *role.stop_args))
|
48
|
+
end
|
49
|
+
|
50
|
+
def info
|
51
|
+
docker :ps, *container_filter_args
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def current_running_container_id
|
56
|
+
current_running_container(format: "--quiet")
|
57
|
+
end
|
58
|
+
|
59
|
+
def container_id_for_version(version, only_running: false)
|
60
|
+
container_id_for(container_name: container_name(version), only_running: only_running)
|
61
|
+
end
|
62
|
+
|
63
|
+
def current_running_version
|
64
|
+
pipe \
|
65
|
+
current_running_container(format: "--format '{{.Names}}'"),
|
66
|
+
extract_version_from_name
|
67
|
+
end
|
68
|
+
|
69
|
+
def list_versions(*docker_args, statuses: nil)
|
70
|
+
pipe \
|
71
|
+
docker(:ps, *container_filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
|
72
|
+
extract_version_from_name
|
73
|
+
end
|
74
|
+
|
75
|
+
def ensure_env_directory
|
76
|
+
make_directory role.env_directory
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def latest_image_id
|
81
|
+
docker :image, :ls, *argumentize("--filter", "reference=#{config.latest_image}"), "--format", "'{{.ID}}'"
|
82
|
+
end
|
83
|
+
|
84
|
+
def current_running_container(format:)
|
85
|
+
pipe \
|
86
|
+
shell(chain(latest_image_container(format: format), latest_container(format: format))),
|
87
|
+
[ :head, "-1" ]
|
88
|
+
end
|
89
|
+
|
90
|
+
def latest_image_container(format:)
|
91
|
+
latest_container format: format, filters: [ "ancestor=$(#{latest_image_id.join(" ")})" ]
|
92
|
+
end
|
93
|
+
|
94
|
+
def latest_container(format:, filters: nil)
|
95
|
+
docker :ps, "--latest", *format, *container_filter_args(statuses: ACTIVE_DOCKER_STATUSES), argumentize("--filter", filters)
|
96
|
+
end
|
97
|
+
|
98
|
+
def container_filter_args(statuses: nil)
|
99
|
+
argumentize "--filter", container_filters(statuses: statuses)
|
100
|
+
end
|
101
|
+
|
102
|
+
def image_filter_args
|
103
|
+
argumentize "--filter", image_filters
|
104
|
+
end
|
105
|
+
|
106
|
+
def extract_version_from_name
|
107
|
+
# Extract SHA from "service-role-dest-SHA"
|
108
|
+
%(while read line; do echo ${line##{role.container_prefix}-}; done)
|
109
|
+
end
|
110
|
+
|
111
|
+
def container_filters(statuses: nil)
|
112
|
+
[ "label=service=#{config.service}" ].tap do |filters|
|
113
|
+
filters << "label=destination=#{config.destination}"
|
114
|
+
filters << "label=role=#{role}" if role
|
115
|
+
statuses&.each do |status|
|
116
|
+
filters << "status=#{status}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def image_filters
|
122
|
+
[ "label=service=#{config.service}" ]
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Kamal::Commands::Auditor < Kamal::Commands::Base
|
2
|
+
attr_reader :details
|
3
|
+
delegate :escape_shell_value, to: Kamal::Utils
|
4
|
+
|
5
|
+
def initialize(config, **details)
|
6
|
+
super(config)
|
7
|
+
@details = details
|
8
|
+
end
|
9
|
+
|
10
|
+
# Runs remotely
|
11
|
+
def record(line, **details)
|
12
|
+
combine \
|
13
|
+
make_run_directory,
|
14
|
+
append([ :echo, escape_shell_value(audit_line(line, **details)) ], audit_log_file)
|
15
|
+
end
|
16
|
+
|
17
|
+
def reveal
|
18
|
+
[ :tail, "-n", 50, audit_log_file ]
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def audit_log_file
|
23
|
+
file = [ config.service, config.destination, "audit.log" ].compact.join("-")
|
24
|
+
|
25
|
+
File.join(config.run_directory, file)
|
26
|
+
end
|
27
|
+
|
28
|
+
def audit_tags(**details)
|
29
|
+
tags(**self.details, **details)
|
30
|
+
end
|
31
|
+
|
32
|
+
def make_run_directory
|
33
|
+
[ :mkdir, "-p", config.run_directory ]
|
34
|
+
end
|
35
|
+
|
36
|
+
def audit_line(line, **details)
|
37
|
+
"#{audit_tags(**details).except(:version, :service_version, :service)} #{line}"
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Kamal::Commands
|
2
|
+
class Base
|
3
|
+
delegate :sensitive, :argumentize, to: Kamal::Utils
|
4
|
+
|
5
|
+
DOCKER_HEALTH_STATUS_FORMAT = "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'"
|
6
|
+
|
7
|
+
attr_accessor :config
|
8
|
+
|
9
|
+
def initialize(config)
|
10
|
+
@config = config
|
11
|
+
end
|
12
|
+
|
13
|
+
def run_over_ssh(*command, host:)
|
14
|
+
"ssh#{ssh_proxy_args}#{ssh_keys_args} -t #{config.ssh.user}@#{host} -p #{config.ssh.port} '#{command.join(" ").gsub("'", "'\\\\''")}'"
|
15
|
+
end
|
16
|
+
|
17
|
+
def container_id_for(container_name:, only_running: false)
|
18
|
+
docker :container, :ls, *("--all" unless only_running), "--filter", "name=^#{container_name}$", "--quiet"
|
19
|
+
end
|
20
|
+
|
21
|
+
def make_directory_for(remote_file)
|
22
|
+
make_directory Pathname.new(remote_file).dirname.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def make_directory(path)
|
26
|
+
[ :mkdir, "-p", path ]
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_directory(path)
|
30
|
+
[ :rm, "-r", path ]
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_file(path)
|
34
|
+
[ :rm, path ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def ensure_docker_installed
|
38
|
+
combine \
|
39
|
+
ensure_local_docker_installed,
|
40
|
+
ensure_local_buildx_installed
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def combine(*commands, by: "&&")
|
45
|
+
commands
|
46
|
+
.compact
|
47
|
+
.collect { |command| Array(command) + [ by ] }.flatten # Join commands
|
48
|
+
.tap { |commands| commands.pop } # Remove trailing combiner
|
49
|
+
end
|
50
|
+
|
51
|
+
def chain(*commands)
|
52
|
+
combine *commands, by: ";"
|
53
|
+
end
|
54
|
+
|
55
|
+
def pipe(*commands)
|
56
|
+
combine *commands, by: "|"
|
57
|
+
end
|
58
|
+
|
59
|
+
def append(*commands)
|
60
|
+
combine *commands, by: ">>"
|
61
|
+
end
|
62
|
+
|
63
|
+
def write(*commands)
|
64
|
+
combine *commands, by: ">"
|
65
|
+
end
|
66
|
+
|
67
|
+
def any(*commands)
|
68
|
+
combine *commands, by: "||"
|
69
|
+
end
|
70
|
+
|
71
|
+
def substitute(*commands)
|
72
|
+
"\$\(#{commands.join(" ")}\)"
|
73
|
+
end
|
74
|
+
|
75
|
+
def xargs(command)
|
76
|
+
[ :xargs, command ].flatten
|
77
|
+
end
|
78
|
+
|
79
|
+
def shell(command)
|
80
|
+
[ :sh, "-c", "'#{command.flatten.join(" ").gsub("'", "'\\\\''")}'" ]
|
81
|
+
end
|
82
|
+
|
83
|
+
def docker(*args)
|
84
|
+
args.compact.unshift :docker
|
85
|
+
end
|
86
|
+
|
87
|
+
def pack(*args)
|
88
|
+
args.compact.unshift :pack
|
89
|
+
end
|
90
|
+
|
91
|
+
def git(*args, path: nil)
|
92
|
+
[ :git, *([ "-C", path ] if path), *args.compact ]
|
93
|
+
end
|
94
|
+
|
95
|
+
def grep(*args)
|
96
|
+
args.compact.unshift :grep
|
97
|
+
end
|
98
|
+
|
99
|
+
def tags(**details)
|
100
|
+
Kamal::Tags.from_config(config, **details)
|
101
|
+
end
|
102
|
+
|
103
|
+
def ssh_proxy_args
|
104
|
+
case config.ssh.proxy
|
105
|
+
when Net::SSH::Proxy::Jump
|
106
|
+
" -J #{config.ssh.proxy.jump_proxies}"
|
107
|
+
when Net::SSH::Proxy::Command
|
108
|
+
" -o ProxyCommand='#{config.ssh.proxy.command_line_template}'"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def ssh_keys_args
|
113
|
+
"#{ ssh_keys.join("") if ssh_keys}" + "#{" -o IdentitiesOnly=yes" if config.ssh&.keys_only}"
|
114
|
+
end
|
115
|
+
|
116
|
+
def ssh_keys
|
117
|
+
config.ssh.keys&.map do |key|
|
118
|
+
" -i #{key}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def ensure_local_docker_installed
|
123
|
+
docker "--version"
|
124
|
+
end
|
125
|
+
|
126
|
+
def ensure_local_buildx_installed
|
127
|
+
docker :buildx, "version"
|
128
|
+
end
|
129
|
+
|
130
|
+
def docker_interactive_args
|
131
|
+
STDIN.isatty ? "-it" : "-i"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
2
|
+
class BuilderError < StandardError; end
|
3
|
+
|
4
|
+
ENDPOINT_DOCKER_HOST_INSPECT = "'{{.Endpoints.docker.Host}}'"
|
5
|
+
|
6
|
+
delegate :argumentize, to: Kamal::Utils
|
7
|
+
delegate \
|
8
|
+
:args, :secrets, :dockerfile, :target, :arches, :local_arches, :remote_arches, :remote,
|
9
|
+
:pack?, :pack_builder, :pack_buildpacks,
|
10
|
+
:cache_from, :cache_to, :ssh, :provenance, :sbom, :driver, :docker_driver?,
|
11
|
+
to: :builder_config
|
12
|
+
|
13
|
+
def clean
|
14
|
+
docker :image, :rm, "--force", config.absolute_image
|
15
|
+
end
|
16
|
+
|
17
|
+
def push(export_action = "registry", tag_as_dirty: false)
|
18
|
+
docker :buildx, :build,
|
19
|
+
"--output=type=#{export_action},registry.insecure=true",
|
20
|
+
*platform_options(arches),
|
21
|
+
*([ "--builder", builder_name ] unless docker_driver?),
|
22
|
+
*build_tag_options(tag_as_dirty: tag_as_dirty),
|
23
|
+
*build_options,
|
24
|
+
build_context,
|
25
|
+
"2>&1"
|
26
|
+
end
|
27
|
+
|
28
|
+
def pull
|
29
|
+
docker :pull, config.absolute_image
|
30
|
+
end
|
31
|
+
|
32
|
+
def info
|
33
|
+
combine \
|
34
|
+
docker(:context, :ls),
|
35
|
+
docker(:buildx, :ls)
|
36
|
+
end
|
37
|
+
|
38
|
+
def inspect_builder
|
39
|
+
docker :buildx, :inspect, builder_name unless docker_driver?
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_options
|
43
|
+
[ *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_target, *build_ssh, *builder_provenance, *builder_sbom ]
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_context
|
47
|
+
config.builder.context
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate_image
|
51
|
+
pipe \
|
52
|
+
docker(:inspect, "-f", "'{{ .Config.Labels.service }}'", config.absolute_image),
|
53
|
+
any(
|
54
|
+
[ :grep, "-x", config.service ],
|
55
|
+
"(echo \"Image #{config.absolute_image} is missing the 'service' label\" && exit 1)"
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def first_mirror
|
60
|
+
docker(:info, "--format '{{index .RegistryConfig.Mirrors 0}}'")
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def build_tag_names(tag_as_dirty: false)
|
65
|
+
tag_names = [ config.absolute_image, config.latest_image ]
|
66
|
+
tag_names.map! { |t| "#{t}-dirty" } if tag_as_dirty
|
67
|
+
tag_names
|
68
|
+
end
|
69
|
+
|
70
|
+
def build_tag_options(tag_as_dirty: false)
|
71
|
+
build_tag_names(tag_as_dirty: tag_as_dirty).flat_map { |name| [ "-t", name ] }
|
72
|
+
end
|
73
|
+
|
74
|
+
def build_cache
|
75
|
+
if cache_to && cache_from
|
76
|
+
[ "--cache-to", cache_to,
|
77
|
+
"--cache-from", cache_from ]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_labels
|
82
|
+
argumentize "--label", { service: config.service }
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_args
|
86
|
+
argumentize "--build-arg", args, sensitive: true
|
87
|
+
end
|
88
|
+
|
89
|
+
def build_secrets
|
90
|
+
argumentize "--secret", secrets.keys.collect { |secret| [ "id", secret ] }
|
91
|
+
end
|
92
|
+
|
93
|
+
def build_dockerfile
|
94
|
+
if Pathname.new(File.expand_path(dockerfile)).exist?
|
95
|
+
argumentize "--file", dockerfile
|
96
|
+
else
|
97
|
+
raise BuilderError, "Missing #{dockerfile}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def build_target
|
102
|
+
argumentize "--target", target if target.present?
|
103
|
+
end
|
104
|
+
|
105
|
+
def build_ssh
|
106
|
+
argumentize "--ssh", ssh if ssh.present?
|
107
|
+
end
|
108
|
+
|
109
|
+
def builder_provenance
|
110
|
+
argumentize "--provenance", provenance unless provenance.nil?
|
111
|
+
end
|
112
|
+
|
113
|
+
def builder_sbom
|
114
|
+
argumentize "--sbom", sbom unless sbom.nil?
|
115
|
+
end
|
116
|
+
|
117
|
+
def builder_config
|
118
|
+
config.builder
|
119
|
+
end
|
120
|
+
|
121
|
+
def platform_options(arches)
|
122
|
+
argumentize "--platform", arches.map { |arch| "linux/#{arch}" }.join(",") if arches.any?
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Kamal::Commands::Builder::Clone
|
2
|
+
def clone
|
3
|
+
git :clone, escaped_root, "--recurse-submodules", path: config.builder.clone_directory.shellescape
|
4
|
+
end
|
5
|
+
|
6
|
+
def clone_reset_steps
|
7
|
+
[
|
8
|
+
git(:remote, "set-url", :origin, escaped_root, path: escaped_build_directory),
|
9
|
+
git(:fetch, :origin, path: escaped_build_directory),
|
10
|
+
git(:reset, "--hard", Kamal::Git.revision, path: escaped_build_directory),
|
11
|
+
git(:clean, "-fdx", path: escaped_build_directory),
|
12
|
+
git(:submodule, :update, "--init", path: escaped_build_directory)
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
def clone_status
|
17
|
+
git :status, "--porcelain", path: escaped_build_directory
|
18
|
+
end
|
19
|
+
|
20
|
+
def clone_revision
|
21
|
+
git :"rev-parse", :HEAD, path: escaped_build_directory
|
22
|
+
end
|
23
|
+
|
24
|
+
def escaped_root
|
25
|
+
Kamal::Git.root.shellescape
|
26
|
+
end
|
27
|
+
|
28
|
+
def escaped_build_directory
|
29
|
+
config.builder.build_directory.shellescape
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Kamal::Commands::Builder::Cloud < Kamal::Commands::Builder::Base
|
2
|
+
# Expects `driver` to be of format "cloud docker-org-name/builder-name"
|
3
|
+
|
4
|
+
def create
|
5
|
+
docker :buildx, :create, "--driver", driver
|
6
|
+
end
|
7
|
+
|
8
|
+
def remove
|
9
|
+
docker :buildx, :rm, builder_name
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def builder_name
|
14
|
+
driver.gsub(/[ \/]/, "-")
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect_buildx
|
18
|
+
pipe \
|
19
|
+
docker(:buildx, :inspect, builder_name),
|
20
|
+
grep("-q", "Endpoint:.*cloud://.*")
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Kamal::Commands::Builder::Hybrid < Kamal::Commands::Builder::Remote
|
2
|
+
def create
|
3
|
+
combine \
|
4
|
+
create_local_buildx,
|
5
|
+
create_remote_context,
|
6
|
+
append_remote_buildx
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
def builder_name
|
11
|
+
"kamal-hybrid-#{driver}-#{remote.gsub(/[^a-z0-9_-]/, "-")}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_local_buildx
|
15
|
+
docker :buildx, :create, *platform_options(local_arches), "--name", builder_name, "--driver=#{driver}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def append_remote_buildx
|
19
|
+
docker :buildx, :create, *platform_options(remote_arches), "--append", "--name", builder_name, remote_context_name
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base
|
2
|
+
def create
|
3
|
+
docker :buildx, :create, "--name", builder_name, "--driver=#{driver}" unless docker_driver?
|
4
|
+
end
|
5
|
+
|
6
|
+
def remove
|
7
|
+
docker :buildx, :rm, builder_name unless docker_driver?
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def builder_name
|
12
|
+
"kamal-local-#{driver}"
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class Kamal::Commands::Builder::Pack < Kamal::Commands::Builder::Base
|
2
|
+
def push(export_action = "registry")
|
3
|
+
combine \
|
4
|
+
build,
|
5
|
+
export(export_action)
|
6
|
+
end
|
7
|
+
|
8
|
+
def remove;end
|
9
|
+
|
10
|
+
def info
|
11
|
+
pack :builder, :inspect, pack_builder
|
12
|
+
end
|
13
|
+
alias_method :inspect_builder, :info
|
14
|
+
|
15
|
+
private
|
16
|
+
def build
|
17
|
+
pack(:build,
|
18
|
+
config.repository,
|
19
|
+
"--platform", platform,
|
20
|
+
"--creation-time", "now",
|
21
|
+
"--builder", pack_builder,
|
22
|
+
buildpacks,
|
23
|
+
"-t", config.absolute_image,
|
24
|
+
"-t", config.latest_image,
|
25
|
+
"--env", "BP_IMAGE_LABELS=service=#{config.service}",
|
26
|
+
*argumentize("--env", args),
|
27
|
+
*argumentize("--env", secrets, sensitive: true),
|
28
|
+
"--path", build_context)
|
29
|
+
end
|
30
|
+
|
31
|
+
def export(export_action)
|
32
|
+
return unless export_action == "registry"
|
33
|
+
|
34
|
+
combine \
|
35
|
+
docker(:push, config.absolute_image),
|
36
|
+
docker(:push, config.latest_image)
|
37
|
+
end
|
38
|
+
|
39
|
+
def platform
|
40
|
+
"linux/#{local_arches.first}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def buildpacks
|
44
|
+
(pack_buildpacks << "paketo-buildpacks/image-labels").map { |buildpack| [ "--buildpack", buildpack ] }
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class Kamal::Commands::Builder::Remote < Kamal::Commands::Builder::Base
|
2
|
+
def create
|
3
|
+
chain \
|
4
|
+
create_remote_context,
|
5
|
+
create_buildx
|
6
|
+
end
|
7
|
+
|
8
|
+
def remove
|
9
|
+
chain \
|
10
|
+
remove_remote_context,
|
11
|
+
remove_buildx
|
12
|
+
end
|
13
|
+
|
14
|
+
def info
|
15
|
+
chain \
|
16
|
+
docker(:context, :ls),
|
17
|
+
docker(:buildx, :ls)
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect_builder
|
21
|
+
combine \
|
22
|
+
combine inspect_buildx, inspect_remote_context,
|
23
|
+
[ "(echo no compatible builder && exit 1)" ],
|
24
|
+
by: "||"
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def builder_name
|
29
|
+
"kamal-remote-#{remote.gsub(/[^a-z0-9_-]/, "-")}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def remote_context_name
|
33
|
+
"#{builder_name}-context"
|
34
|
+
end
|
35
|
+
|
36
|
+
def inspect_buildx
|
37
|
+
pipe \
|
38
|
+
docker(:buildx, :inspect, builder_name),
|
39
|
+
grep("-q", "Endpoint:.*#{remote_context_name}")
|
40
|
+
end
|
41
|
+
|
42
|
+
def inspect_remote_context
|
43
|
+
pipe \
|
44
|
+
docker(:context, :inspect, remote_context_name, "--format", ENDPOINT_DOCKER_HOST_INSPECT),
|
45
|
+
grep("-xq", remote)
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_remote_context
|
49
|
+
docker :context, :create, remote_context_name, "--description", "'#{builder_name} host'", "--docker", "'host=#{remote}'"
|
50
|
+
end
|
51
|
+
|
52
|
+
def remove_remote_context
|
53
|
+
docker :context, :rm, remote_context_name
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_buildx
|
57
|
+
docker :buildx, :create, "--name", builder_name, remote_context_name
|
58
|
+
end
|
59
|
+
|
60
|
+
def remove_buildx
|
61
|
+
docker :buildx, :rm, builder_name
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "active_support/core_ext/string/filters"
|
2
|
+
|
3
|
+
class Kamal::Commands::Builder < Kamal::Commands::Base
|
4
|
+
delegate :create, :remove, :dev, :push, :clean, :pull, :info, :inspect_builder, :validate_image, :first_mirror, to: :target
|
5
|
+
delegate :local?, :remote?, :pack?, :cloud?, to: "config.builder"
|
6
|
+
|
7
|
+
include Clone
|
8
|
+
|
9
|
+
def name
|
10
|
+
target.class.to_s.remove("Kamal::Commands::Builder::").underscore.inquiry
|
11
|
+
end
|
12
|
+
|
13
|
+
def target
|
14
|
+
if remote?
|
15
|
+
if local?
|
16
|
+
hybrid
|
17
|
+
else
|
18
|
+
remote
|
19
|
+
end
|
20
|
+
elsif pack?
|
21
|
+
pack
|
22
|
+
elsif cloud?
|
23
|
+
cloud
|
24
|
+
else
|
25
|
+
local
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def remote
|
30
|
+
@remote ||= Kamal::Commands::Builder::Remote.new(config)
|
31
|
+
end
|
32
|
+
|
33
|
+
def local
|
34
|
+
@local ||= Kamal::Commands::Builder::Local.new(config)
|
35
|
+
end
|
36
|
+
|
37
|
+
def hybrid
|
38
|
+
@hybrid ||= Kamal::Commands::Builder::Hybrid.new(config)
|
39
|
+
end
|
40
|
+
|
41
|
+
def pack
|
42
|
+
@pack ||= Kamal::Commands::Builder::Pack.new(config)
|
43
|
+
end
|
44
|
+
|
45
|
+
def cloud
|
46
|
+
@cloud ||= Kamal::Commands::Builder::Cloud.new(config)
|
47
|
+
end
|
48
|
+
end
|