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,115 @@
1
+ class Kamal::Commands::App < Kamal::Commands::Base
2
+ include Assets, Containers, 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
+ "-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
24
+ "-e", "KAMAL_VERSION=\"#{config.version}\"",
25
+ *role.env_args(host),
26
+ *role.logging_args,
27
+ *config.volume_args,
28
+ *role.asset_volume_args,
29
+ *role.label_args,
30
+ *role.option_args,
31
+ config.absolute_image,
32
+ role.cmd
33
+ end
34
+
35
+ def start
36
+ docker :start, container_name
37
+ end
38
+
39
+ def status(version:)
40
+ pipe container_id_for_version(version), xargs(docker(:inspect, "--format", DOCKER_HEALTH_STATUS_FORMAT))
41
+ end
42
+
43
+ def stop(version: nil)
44
+ pipe \
45
+ version ? container_id_for_version(version) : current_running_container_id,
46
+ xargs(docker(:stop, *role.stop_args))
47
+ end
48
+
49
+ def info
50
+ docker :ps, *filter_args
51
+ end
52
+
53
+
54
+ def current_running_container_id
55
+ current_running_container(format: "--quiet")
56
+ end
57
+
58
+ def container_id_for_version(version, only_running: false)
59
+ container_id_for(container_name: container_name(version), only_running: only_running)
60
+ end
61
+
62
+ def current_running_version
63
+ pipe \
64
+ current_running_container(format: "--format '{{.Names}}'"),
65
+ extract_version_from_name
66
+ end
67
+
68
+ def list_versions(*docker_args, statuses: nil)
69
+ pipe \
70
+ docker(:ps, *filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
71
+ extract_version_from_name
72
+ end
73
+
74
+ def ensure_env_directory
75
+ make_directory role.env_directory
76
+ end
77
+
78
+ private
79
+ def latest_image_id
80
+ docker :image, :ls, *argumentize("--filter", "reference=#{config.latest_image}"), "--format", "'{{.ID}}'"
81
+ end
82
+
83
+ def current_running_container(format:)
84
+ pipe \
85
+ shell(chain(latest_image_container(format: format), latest_container(format: format))),
86
+ [ :head, "-1" ]
87
+ end
88
+
89
+ def latest_image_container(format:)
90
+ latest_container format: format, filters: [ "ancestor=$(#{latest_image_id.join(" ")})" ]
91
+ end
92
+
93
+ def latest_container(format:, filters: nil)
94
+ docker :ps, "--latest", *format, *filter_args(statuses: ACTIVE_DOCKER_STATUSES), argumentize("--filter", filters)
95
+ end
96
+
97
+ def filter_args(statuses: nil)
98
+ argumentize "--filter", filters(statuses: statuses)
99
+ end
100
+
101
+ def extract_version_from_name
102
+ # Extract SHA from "service-role-dest-SHA"
103
+ %(while read line; do echo ${line##{role.container_prefix}-}; done)
104
+ end
105
+
106
+ def filters(statuses: nil)
107
+ [ "label=service=#{config.service}" ].tap do |filters|
108
+ filters << "label=destination=#{config.destination}" if config.destination
109
+ filters << "label=role=#{role}" if role
110
+ statuses&.each do |status|
111
+ filters << "status=#{status}"
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,33 @@
1
+ class Kamal::Commands::Auditor < Kamal::Commands::Base
2
+ attr_reader :details
3
+
4
+ def initialize(config, **details)
5
+ super(config)
6
+ @details = details
7
+ end
8
+
9
+ # Runs remotely
10
+ def record(line, **details)
11
+ combine \
12
+ [ :mkdir, "-p", config.run_directory ],
13
+ append(
14
+ [ :echo, audit_tags(**details).except(:version, :service_version, :service).to_s, line ],
15
+ audit_log_file
16
+ )
17
+ end
18
+
19
+ def reveal
20
+ [ :tail, "-n", 50, audit_log_file ]
21
+ end
22
+
23
+ private
24
+ def audit_log_file
25
+ file = [ config.service, config.destination, "audit.log" ].compact.join("-")
26
+
27
+ File.join(config.run_directory, file)
28
+ end
29
+
30
+ def audit_tags(**details)
31
+ tags(**self.details, **details)
32
+ end
33
+ end
@@ -0,0 +1,98 @@
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} -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
+ private
38
+ def combine(*commands, by: "&&")
39
+ commands
40
+ .compact
41
+ .collect { |command| Array(command) + [ by ] }.flatten # Join commands
42
+ .tap { |commands| commands.pop } # Remove trailing combiner
43
+ end
44
+
45
+ def chain(*commands)
46
+ combine *commands, by: ";"
47
+ end
48
+
49
+ def pipe(*commands)
50
+ combine *commands, by: "|"
51
+ end
52
+
53
+ def append(*commands)
54
+ combine *commands, by: ">>"
55
+ end
56
+
57
+ def write(*commands)
58
+ combine *commands, by: ">"
59
+ end
60
+
61
+ def any(*commands)
62
+ combine *commands, by: "||"
63
+ end
64
+
65
+ def xargs(command)
66
+ [ :xargs, command ].flatten
67
+ end
68
+
69
+ def shell(command)
70
+ [ :sh, "-c", "'#{command.flatten.join(" ").gsub("'", "'\\\\''")}'" ]
71
+ end
72
+
73
+ def docker(*args)
74
+ args.compact.unshift :docker
75
+ end
76
+
77
+ def git(*args, path: nil)
78
+ [ :git, *([ "-C", path ] if path), *args.compact ]
79
+ end
80
+
81
+ def grep(*args)
82
+ args.compact.unshift :grep
83
+ end
84
+
85
+ def tags(**details)
86
+ Kamal::Tags.from_config(config, **details)
87
+ end
88
+
89
+ def ssh_proxy_args
90
+ case config.ssh.proxy
91
+ when Net::SSH::Proxy::Jump
92
+ " -J #{config.ssh.proxy.jump_proxies}"
93
+ when Net::SSH::Proxy::Command
94
+ " -o ProxyCommand='#{config.ssh.proxy.command_line_template}'"
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,111 @@
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
+ :cache_from, :cache_to, :ssh, :provenance, :driver, :docker_driver?,
10
+ to: :builder_config
11
+
12
+ def clean
13
+ docker :image, :rm, "--force", config.absolute_image
14
+ end
15
+
16
+ def push
17
+ docker :buildx, :build,
18
+ "--push",
19
+ *platform_options(arches),
20
+ *([ "--builder", builder_name ] unless docker_driver?),
21
+ *build_options,
22
+ build_context
23
+ end
24
+
25
+ def pull
26
+ docker :pull, config.absolute_image
27
+ end
28
+
29
+ def info
30
+ combine \
31
+ docker(:context, :ls),
32
+ docker(:buildx, :ls)
33
+ end
34
+
35
+ def inspect_builder
36
+ docker :buildx, :inspect, builder_name unless docker_driver?
37
+ end
38
+
39
+ def build_options
40
+ [ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_target, *build_ssh, *builder_provenance ]
41
+ end
42
+
43
+ def build_context
44
+ config.builder.context
45
+ end
46
+
47
+ def validate_image
48
+ pipe \
49
+ docker(:inspect, "-f", "'{{ .Config.Labels.service }}'", config.absolute_image),
50
+ any(
51
+ [ :grep, "-x", config.service ],
52
+ "(echo \"Image #{config.absolute_image} is missing the 'service' label\" && exit 1)"
53
+ )
54
+ end
55
+
56
+ def first_mirror
57
+ docker(:info, "--format '{{index .RegistryConfig.Mirrors 0}}'")
58
+ end
59
+
60
+ private
61
+ def build_tags
62
+ [ "-t", config.absolute_image, "-t", config.latest_image ]
63
+ end
64
+
65
+ def build_cache
66
+ if cache_to && cache_from
67
+ [ "--cache-to", cache_to,
68
+ "--cache-from", cache_from ]
69
+ end
70
+ end
71
+
72
+ def build_labels
73
+ argumentize "--label", { service: config.service }
74
+ end
75
+
76
+ def build_args
77
+ argumentize "--build-arg", args, sensitive: true
78
+ end
79
+
80
+ def build_secrets
81
+ argumentize "--secret", secrets.keys.collect { |secret| [ "id", secret ] }
82
+ end
83
+
84
+ def build_dockerfile
85
+ if Pathname.new(File.expand_path(dockerfile)).exist?
86
+ argumentize "--file", dockerfile
87
+ else
88
+ raise BuilderError, "Missing #{dockerfile}"
89
+ end
90
+ end
91
+
92
+ def build_target
93
+ argumentize "--target", target if target.present?
94
+ end
95
+
96
+ def build_ssh
97
+ argumentize "--ssh", ssh if ssh.present?
98
+ end
99
+
100
+ def builder_provenance
101
+ argumentize "--provenance", provenance unless provenance.nil?
102
+ end
103
+
104
+ def builder_config
105
+ config.builder
106
+ end
107
+
108
+ def platform_options(arches)
109
+ argumentize "--platform", arches.map { |arch| "linux/#{arch}" }.join(",") if arches.any?
110
+ end
111
+ 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,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,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,56 @@
1
+ require "active_support/core_ext/string/filters"
2
+
3
+ class Kamal::Commands::Builder < Kamal::Commands::Base
4
+ delegate :create, :remove, :push, :clean, :pull, :info, :inspect_builder, :validate_image, :first_mirror, to: :target
5
+ delegate :local?, :remote?, 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
+ else
21
+ local
22
+ end
23
+ end
24
+
25
+ def remote
26
+ @remote ||= Kamal::Commands::Builder::Remote.new(config)
27
+ end
28
+
29
+ def local
30
+ @local ||= Kamal::Commands::Builder::Local.new(config)
31
+ end
32
+
33
+ def hybrid
34
+ @hybrid ||= Kamal::Commands::Builder::Hybrid.new(config)
35
+ end
36
+
37
+
38
+ def ensure_local_dependencies_installed
39
+ if name.native?
40
+ ensure_local_docker_installed
41
+ else
42
+ combine \
43
+ ensure_local_docker_installed,
44
+ ensure_local_buildx_installed
45
+ end
46
+ end
47
+
48
+ private
49
+ def ensure_local_docker_installed
50
+ docker "--version"
51
+ end
52
+
53
+ def ensure_local_buildx_installed
54
+ docker :buildx, "version"
55
+ end
56
+ end
@@ -0,0 +1,34 @@
1
+ class Kamal::Commands::Docker < Kamal::Commands::Base
2
+ # Install Docker using the https://github.com/docker/docker-install convenience script.
3
+ def install
4
+ pipe get_docker, :sh
5
+ end
6
+
7
+ # Checks the Docker client version. Fails if Docker is not installed.
8
+ def installed?
9
+ docker "-v"
10
+ end
11
+
12
+ # Checks the Docker server version. Fails if Docker is not running.
13
+ def running?
14
+ docker :version
15
+ end
16
+
17
+ # Do we have superuser access to install Docker and start system services?
18
+ def superuser?
19
+ [ '[ "${EUID:-$(id -u)}" -eq 0 ] || command -v sudo >/dev/null || command -v su >/dev/null' ]
20
+ end
21
+
22
+ def create_network
23
+ docker :network, :create, :kamal
24
+ end
25
+
26
+ private
27
+ def get_docker
28
+ shell \
29
+ any \
30
+ [ :curl, "-fsSL", "https://get.docker.com" ],
31
+ [ :wget, "-O -", "https://get.docker.com" ],
32
+ [ :echo, "\"exit 1\"" ]
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+ class Kamal::Commands::Hook < Kamal::Commands::Base
2
+ def run(hook)
3
+ [ hook_file(hook) ]
4
+ end
5
+
6
+ def env(secrets: false, **details)
7
+ tags(**details).env.tap do |env|
8
+ env.merge!(config.secrets.to_h) if secrets
9
+ end
10
+ end
11
+
12
+ def hook_exists?(hook)
13
+ Pathname.new(hook_file(hook)).exist?
14
+ end
15
+
16
+ private
17
+ def hook_file(hook)
18
+ File.join(config.hooks_path, hook)
19
+ end
20
+ end
@@ -0,0 +1,70 @@
1
+ require "active_support/duration"
2
+ require "time"
3
+ require "base64"
4
+
5
+ class Kamal::Commands::Lock < Kamal::Commands::Base
6
+ def acquire(message, version)
7
+ combine \
8
+ [ :mkdir, lock_dir ],
9
+ write_lock_details(message, version)
10
+ end
11
+
12
+ def release
13
+ combine \
14
+ [ :rm, lock_details_file ],
15
+ [ :rm, "-r", lock_dir ]
16
+ end
17
+
18
+ def status
19
+ combine \
20
+ stat_lock_dir,
21
+ read_lock_details
22
+ end
23
+
24
+ def ensure_locks_directory
25
+ [ :mkdir, "-p", locks_dir ]
26
+ end
27
+
28
+ private
29
+ def write_lock_details(message, version)
30
+ write \
31
+ [ :echo, "\"#{Base64.encode64(lock_details(message, version))}\"" ],
32
+ lock_details_file
33
+ end
34
+
35
+ def read_lock_details
36
+ pipe \
37
+ [ :cat, lock_details_file ],
38
+ [ :base64, "-d" ]
39
+ end
40
+
41
+ def stat_lock_dir
42
+ write \
43
+ [ :stat, lock_dir ],
44
+ "/dev/null"
45
+ end
46
+
47
+ def lock_dir
48
+ dir_name = [ "lock", config.service, config.destination ].compact.join("-")
49
+
50
+ File.join(config.run_directory, dir_name)
51
+ end
52
+
53
+ def lock_details_file
54
+ File.join(lock_dir, "details")
55
+ end
56
+
57
+ def lock_details(message, version)
58
+ <<~DETAILS.strip
59
+ Locked by: #{locked_by} at #{Time.now.utc.iso8601}
60
+ Version: #{version}
61
+ Message: #{message}
62
+ DETAILS
63
+ end
64
+
65
+ def locked_by
66
+ Kamal::Git.user_name
67
+ rescue Errno::ENOENT
68
+ "Unknown"
69
+ end
70
+ end