kamal 0.16.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 +1021 -0
 - data/bin/kamal +18 -0
 - data/lib/kamal/cli/accessory.rb +239 -0
 - data/lib/kamal/cli/app.rb +296 -0
 - data/lib/kamal/cli/base.rb +171 -0
 - data/lib/kamal/cli/build.rb +106 -0
 - data/lib/kamal/cli/healthcheck.rb +20 -0
 - data/lib/kamal/cli/lock.rb +37 -0
 - data/lib/kamal/cli/main.rb +249 -0
 - data/lib/kamal/cli/prune.rb +30 -0
 - data/lib/kamal/cli/registry.rb +18 -0
 - data/lib/kamal/cli/server.rb +21 -0
 - data/lib/kamal/cli/templates/deploy.yml +74 -0
 - data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +14 -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 +109 -0
 - data/lib/kamal/cli/templates/template.env +2 -0
 - data/lib/kamal/cli/traefik.rb +111 -0
 - data/lib/kamal/cli.rb +7 -0
 - data/lib/kamal/commander.rb +154 -0
 - data/lib/kamal/commands/accessory.rb +113 -0
 - data/lib/kamal/commands/app.rb +175 -0
 - data/lib/kamal/commands/auditor.rb +28 -0
 - data/lib/kamal/commands/base.rb +65 -0
 - data/lib/kamal/commands/builder/base.rb +60 -0
 - data/lib/kamal/commands/builder/multiarch/remote.rb +51 -0
 - data/lib/kamal/commands/builder/multiarch.rb +29 -0
 - data/lib/kamal/commands/builder/native/cached.rb +16 -0
 - data/lib/kamal/commands/builder/native/remote.rb +59 -0
 - data/lib/kamal/commands/builder/native.rb +20 -0
 - data/lib/kamal/commands/builder.rb +62 -0
 - data/lib/kamal/commands/docker.rb +21 -0
 - data/lib/kamal/commands/healthcheck.rb +57 -0
 - data/lib/kamal/commands/hook.rb +14 -0
 - data/lib/kamal/commands/lock.rb +63 -0
 - data/lib/kamal/commands/prune.rb +38 -0
 - data/lib/kamal/commands/registry.rb +20 -0
 - data/lib/kamal/commands/traefik.rb +104 -0
 - data/lib/kamal/commands.rb +2 -0
 - data/lib/kamal/configuration/accessory.rb +169 -0
 - data/lib/kamal/configuration/boot.rb +20 -0
 - data/lib/kamal/configuration/builder.rb +114 -0
 - data/lib/kamal/configuration/role.rb +155 -0
 - data/lib/kamal/configuration/ssh.rb +38 -0
 - data/lib/kamal/configuration/sshkit.rb +20 -0
 - data/lib/kamal/configuration.rb +251 -0
 - data/lib/kamal/sshkit_with_ext.rb +104 -0
 - data/lib/kamal/tags.rb +39 -0
 - data/lib/kamal/utils/healthcheck_poller.rb +39 -0
 - data/lib/kamal/utils/sensitive.rb +19 -0
 - data/lib/kamal/utils.rb +100 -0
 - data/lib/kamal/version.rb +3 -0
 - data/lib/kamal.rb +10 -0
 - metadata +266 -0
 
| 
         @@ -0,0 +1,175 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Kamal::Commands::App < Kamal::Commands::Base
         
     | 
| 
      
 2 
     | 
    
         
            +
              ACTIVE_DOCKER_STATUSES = [ :running, :restarting ]
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              attr_reader :role
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def initialize(config, role: nil)
         
     | 
| 
      
 7 
     | 
    
         
            +
                super(config)
         
     | 
| 
      
 8 
     | 
    
         
            +
                @role = role
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              def start_or_run(hostname: nil)
         
     | 
| 
      
 12 
     | 
    
         
            +
                combine start, run(hostname: hostname), by: "||"
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              def run(hostname: nil)
         
     | 
| 
      
 16 
     | 
    
         
            +
                role = config.role(self.role)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                docker :run,
         
     | 
| 
      
 19 
     | 
    
         
            +
                  "--detach",
         
     | 
| 
      
 20 
     | 
    
         
            +
                  "--restart unless-stopped",
         
     | 
| 
      
 21 
     | 
    
         
            +
                  "--name", container_name,
         
     | 
| 
      
 22 
     | 
    
         
            +
                  *(["--hostname", hostname] if hostname),
         
     | 
| 
      
 23 
     | 
    
         
            +
                  "-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
         
     | 
| 
      
 24 
     | 
    
         
            +
                  *role.env_args,
         
     | 
| 
      
 25 
     | 
    
         
            +
                  *role.health_check_args,
         
     | 
| 
      
 26 
     | 
    
         
            +
                  *config.logging_args,
         
     | 
| 
      
 27 
     | 
    
         
            +
                  *config.volume_args,
         
     | 
| 
      
 28 
     | 
    
         
            +
                  *role.label_args,
         
     | 
| 
      
 29 
     | 
    
         
            +
                  *role.option_args,
         
     | 
| 
      
 30 
     | 
    
         
            +
                  config.absolute_image,
         
     | 
| 
      
 31 
     | 
    
         
            +
                  role.cmd
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              def start
         
     | 
| 
      
 35 
     | 
    
         
            +
                docker :start, container_name
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              def status(version:)
         
     | 
| 
      
 39 
     | 
    
         
            +
                pipe container_id_for_version(version), xargs(docker(:inspect, "--format", DOCKER_HEALTH_STATUS_FORMAT))
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              def stop(version: nil)
         
     | 
| 
      
 43 
     | 
    
         
            +
                pipe \
         
     | 
| 
      
 44 
     | 
    
         
            +
                  version ? container_id_for_version(version) : current_running_container_id,
         
     | 
| 
      
 45 
     | 
    
         
            +
                  xargs(config.stop_wait_time ? docker(:stop, "-t", config.stop_wait_time) : docker(:stop))
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
              def info
         
     | 
| 
      
 49 
     | 
    
         
            +
                docker :ps, *filter_args
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              def logs(since: nil, lines: nil, grep: nil)
         
     | 
| 
      
 54 
     | 
    
         
            +
                pipe \
         
     | 
| 
      
 55 
     | 
    
         
            +
                  current_running_container_id,
         
     | 
| 
      
 56 
     | 
    
         
            +
                  "xargs docker logs#{" --since #{since}" if since}#{" --tail #{lines}" if lines} 2>&1",
         
     | 
| 
      
 57 
     | 
    
         
            +
                  ("grep '#{grep}'" if grep)
         
     | 
| 
      
 58 
     | 
    
         
            +
              end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
              def follow_logs(host:, grep: nil)
         
     | 
| 
      
 61 
     | 
    
         
            +
                run_over_ssh \
         
     | 
| 
      
 62 
     | 
    
         
            +
                  pipe(
         
     | 
| 
      
 63 
     | 
    
         
            +
                    current_running_container_id,
         
     | 
| 
      
 64 
     | 
    
         
            +
                    "xargs docker logs --timestamps --tail 10 --follow 2>&1",
         
     | 
| 
      
 65 
     | 
    
         
            +
                    (%(grep "#{grep}") if grep)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  ),
         
     | 
| 
      
 67 
     | 
    
         
            +
                  host: host
         
     | 
| 
      
 68 
     | 
    
         
            +
              end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
              def execute_in_existing_container(*command, interactive: false)
         
     | 
| 
      
 72 
     | 
    
         
            +
                docker :exec,
         
     | 
| 
      
 73 
     | 
    
         
            +
                  ("-it" if interactive),
         
     | 
| 
      
 74 
     | 
    
         
            +
                  container_name,
         
     | 
| 
      
 75 
     | 
    
         
            +
                  *command
         
     | 
| 
      
 76 
     | 
    
         
            +
              end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
              def execute_in_new_container(*command, interactive: false)
         
     | 
| 
      
 79 
     | 
    
         
            +
                role = config.role(self.role)
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                docker :run,
         
     | 
| 
      
 82 
     | 
    
         
            +
                  ("-it" if interactive),
         
     | 
| 
      
 83 
     | 
    
         
            +
                  "--rm",
         
     | 
| 
      
 84 
     | 
    
         
            +
                  *config.env_args,
         
     | 
| 
      
 85 
     | 
    
         
            +
                  *config.volume_args,
         
     | 
| 
      
 86 
     | 
    
         
            +
                  *role&.option_args,
         
     | 
| 
      
 87 
     | 
    
         
            +
                  config.absolute_image,
         
     | 
| 
      
 88 
     | 
    
         
            +
                  *command
         
     | 
| 
      
 89 
     | 
    
         
            +
              end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
              def execute_in_existing_container_over_ssh(*command, host:)
         
     | 
| 
      
 92 
     | 
    
         
            +
                run_over_ssh execute_in_existing_container(*command, interactive: true), host: host
         
     | 
| 
      
 93 
     | 
    
         
            +
              end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
              def execute_in_new_container_over_ssh(*command, host:)
         
     | 
| 
      
 96 
     | 
    
         
            +
                run_over_ssh execute_in_new_container(*command, interactive: true), host: host
         
     | 
| 
      
 97 
     | 
    
         
            +
              end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              def current_running_container_id
         
     | 
| 
      
 101 
     | 
    
         
            +
                docker :ps, "--quiet", *filter_args(statuses: ACTIVE_DOCKER_STATUSES), "--latest"
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
              def container_id_for_version(version, only_running: false)
         
     | 
| 
      
 105 
     | 
    
         
            +
                container_id_for(container_name: container_name(version), only_running: only_running)
         
     | 
| 
      
 106 
     | 
    
         
            +
              end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
              def current_running_version
         
     | 
| 
      
 109 
     | 
    
         
            +
                list_versions("--latest", statuses: ACTIVE_DOCKER_STATUSES)
         
     | 
| 
      
 110 
     | 
    
         
            +
              end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
              def list_versions(*docker_args, statuses: nil)
         
     | 
| 
      
 113 
     | 
    
         
            +
                pipe \
         
     | 
| 
      
 114 
     | 
    
         
            +
                  docker(:ps, *filter_args(statuses: statuses), *docker_args, "--format", '"{{.Names}}"'),
         
     | 
| 
      
 115 
     | 
    
         
            +
                  %(while read line; do echo ${line##{service_role_dest}-}; done) # Extract SHA from "service-role-dest-SHA"
         
     | 
| 
      
 116 
     | 
    
         
            +
              end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
              def list_containers
         
     | 
| 
      
 119 
     | 
    
         
            +
                docker :container, :ls, "--all", *filter_args
         
     | 
| 
      
 120 
     | 
    
         
            +
              end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
              def list_container_names
         
     | 
| 
      
 123 
     | 
    
         
            +
                [ *list_containers, "--format", "'{{ .Names }}'" ]
         
     | 
| 
      
 124 
     | 
    
         
            +
              end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
              def remove_container(version:)
         
     | 
| 
      
 127 
     | 
    
         
            +
                pipe \
         
     | 
| 
      
 128 
     | 
    
         
            +
                  container_id_for(container_name: container_name(version)),
         
     | 
| 
      
 129 
     | 
    
         
            +
                  xargs(docker(:container, :rm))
         
     | 
| 
      
 130 
     | 
    
         
            +
              end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
              def rename_container(version:, new_version:)
         
     | 
| 
      
 133 
     | 
    
         
            +
                docker :rename, container_name(version), container_name(new_version)
         
     | 
| 
      
 134 
     | 
    
         
            +
              end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
              def remove_containers
         
     | 
| 
      
 137 
     | 
    
         
            +
                docker :container, :prune, "--force", *filter_args
         
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
              def list_images
         
     | 
| 
      
 141 
     | 
    
         
            +
                docker :image, :ls, config.repository
         
     | 
| 
      
 142 
     | 
    
         
            +
              end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
              def remove_images
         
     | 
| 
      
 145 
     | 
    
         
            +
                docker :image, :prune, "--all", "--force", *filter_args
         
     | 
| 
      
 146 
     | 
    
         
            +
              end
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
              def tag_current_as_latest
         
     | 
| 
      
 149 
     | 
    
         
            +
                docker :tag, config.absolute_image, config.latest_image
         
     | 
| 
      
 150 
     | 
    
         
            +
              end
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
              private
         
     | 
| 
      
 154 
     | 
    
         
            +
                def container_name(version = nil)
         
     | 
| 
      
 155 
     | 
    
         
            +
                  [ config.service, role, config.destination, version || config.version ].compact.join("-")
         
     | 
| 
      
 156 
     | 
    
         
            +
                end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                def filter_args(statuses: nil)
         
     | 
| 
      
 159 
     | 
    
         
            +
                  argumentize "--filter", filters(statuses: statuses)
         
     | 
| 
      
 160 
     | 
    
         
            +
                end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                def service_role_dest
         
     | 
| 
      
 163 
     | 
    
         
            +
                  [config.service, role, config.destination].compact.join("-")
         
     | 
| 
      
 164 
     | 
    
         
            +
                end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                def filters(statuses: nil)
         
     | 
| 
      
 167 
     | 
    
         
            +
                  [ "label=service=#{config.service}" ].tap do |filters|
         
     | 
| 
      
 168 
     | 
    
         
            +
                    filters << "label=destination=#{config.destination}" if config.destination
         
     | 
| 
      
 169 
     | 
    
         
            +
                    filters << "label=role=#{role}" if role
         
     | 
| 
      
 170 
     | 
    
         
            +
                    statuses&.each do |status|
         
     | 
| 
      
 171 
     | 
    
         
            +
                      filters << "status=#{status}"
         
     | 
| 
      
 172 
     | 
    
         
            +
                    end
         
     | 
| 
      
 173 
     | 
    
         
            +
                  end
         
     | 
| 
      
 174 
     | 
    
         
            +
                end
         
     | 
| 
      
 175 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
      
 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 
     | 
    
         
            +
                append \
         
     | 
| 
      
 12 
     | 
    
         
            +
                  [ :echo, audit_tags(**details).except(:version, :service_version).to_s, line ],
         
     | 
| 
      
 13 
     | 
    
         
            +
                  audit_log_file
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              def reveal
         
     | 
| 
      
 17 
     | 
    
         
            +
                [ :tail, "-n", 50, audit_log_file ]
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              private
         
     | 
| 
      
 21 
     | 
    
         
            +
                def audit_log_file
         
     | 
| 
      
 22 
     | 
    
         
            +
                  [ "kamal", config.service, config.destination, "audit.log" ].compact.join("-")
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def audit_tags(**details)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  tags(**self.details, **details)
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,65 @@ 
     | 
|
| 
      
 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 
     | 
    
         
            +
                DOCKER_HEALTH_LOG_FORMAT    = "'{{json .State.Health}}'"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                attr_accessor :config
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def initialize(config)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @config = config
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def run_over_ssh(*command, host:)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  "ssh".tap do |cmd|
         
     | 
| 
      
 16 
     | 
    
         
            +
                    if config.ssh.proxy && config.ssh.proxy.is_a?(Net::SSH::Proxy::Jump)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      cmd << " -J #{config.ssh.proxy.jump_proxies}"
         
     | 
| 
      
 18 
     | 
    
         
            +
                    elsif config.ssh.proxy && config.ssh.proxy.is_a?(Net::SSH::Proxy::Command)
         
     | 
| 
      
 19 
     | 
    
         
            +
                      cmd << " -o ProxyCommand='#{config.ssh.proxy.command_line_template}'"
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
                    cmd << " -t #{config.ssh.user}@#{host} '#{command.join(" ")}'"
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def container_id_for(container_name:, only_running: false)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  docker :container, :ls, *("--all" unless only_running), "--filter", "name=^#{container_name}$", "--quiet"
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                private
         
     | 
| 
      
 30 
     | 
    
         
            +
                  def combine(*commands, by: "&&")
         
     | 
| 
      
 31 
     | 
    
         
            +
                    commands
         
     | 
| 
      
 32 
     | 
    
         
            +
                      .compact
         
     | 
| 
      
 33 
     | 
    
         
            +
                      .collect { |command| Array(command) + [ by ] }.flatten # Join commands
         
     | 
| 
      
 34 
     | 
    
         
            +
                      .tap     { |commands| commands.pop } # Remove trailing combiner
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  def chain(*commands)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    combine *commands, by: ";"
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  def pipe(*commands)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    combine *commands, by: "|"
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  def append(*commands)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    combine *commands, by: ">>"
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def write(*commands)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    combine *commands, by: ">"
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  def xargs(command)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    [ :xargs, command ].flatten
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  def docker(*args)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    args.compact.unshift :docker
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  def tags(**details)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    Kamal::Tags.from_config(config, **details)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,60 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
             
     | 
| 
      
 2 
     | 
    
         
            +
            class Kamal::Commands::Builder::Base < Kamal::Commands::Base
         
     | 
| 
      
 3 
     | 
    
         
            +
              class BuilderError < StandardError; end
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              delegate :argumentize, to: Kamal::Utils
         
     | 
| 
      
 6 
     | 
    
         
            +
              delegate :args, :secrets, :dockerfile, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, to: :builder_config
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              def clean
         
     | 
| 
      
 9 
     | 
    
         
            +
                docker :image, :rm, "--force", config.absolute_image
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              def pull
         
     | 
| 
      
 13 
     | 
    
         
            +
                docker :pull, config.absolute_image
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              def build_options
         
     | 
| 
      
 17 
     | 
    
         
            +
                [ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile ]
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              def build_context
         
     | 
| 
      
 21 
     | 
    
         
            +
                config.builder.context
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              private
         
     | 
| 
      
 26 
     | 
    
         
            +
                def build_tags
         
     | 
| 
      
 27 
     | 
    
         
            +
                  [ "-t", config.absolute_image, "-t", config.latest_image ]
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                def build_cache
         
     | 
| 
      
 31 
     | 
    
         
            +
                  if cache_to && cache_from
         
     | 
| 
      
 32 
     | 
    
         
            +
                    ["--cache-to", cache_to,
         
     | 
| 
      
 33 
     | 
    
         
            +
                      "--cache-from", cache_from]
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def build_labels
         
     | 
| 
      
 38 
     | 
    
         
            +
                  argumentize "--label", { service: config.service }
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def build_args
         
     | 
| 
      
 42 
     | 
    
         
            +
                  argumentize "--build-arg", args, sensitive: true
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def build_secrets
         
     | 
| 
      
 46 
     | 
    
         
            +
                  argumentize "--secret", secrets.collect { |secret| [ "id", secret ] }
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def build_dockerfile
         
     | 
| 
      
 50 
     | 
    
         
            +
                  if Pathname.new(File.expand_path(dockerfile)).exist?
         
     | 
| 
      
 51 
     | 
    
         
            +
                    argumentize "--file", dockerfile
         
     | 
| 
      
 52 
     | 
    
         
            +
                  else
         
     | 
| 
      
 53 
     | 
    
         
            +
                    raise BuilderError, "Missing #{dockerfile}"
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def builder_config
         
     | 
| 
      
 58 
     | 
    
         
            +
                  config.builder
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,51 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Kamal::Commands::Builder::Multiarch::Remote < Kamal::Commands::Builder::Multiarch
         
     | 
| 
      
 2 
     | 
    
         
            +
              def create
         
     | 
| 
      
 3 
     | 
    
         
            +
                combine \
         
     | 
| 
      
 4 
     | 
    
         
            +
                  create_contexts,
         
     | 
| 
      
 5 
     | 
    
         
            +
                  create_local_buildx,
         
     | 
| 
      
 6 
     | 
    
         
            +
                  append_remote_buildx
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def remove
         
     | 
| 
      
 10 
     | 
    
         
            +
                combine \
         
     | 
| 
      
 11 
     | 
    
         
            +
                  remove_contexts,
         
     | 
| 
      
 12 
     | 
    
         
            +
                  super
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              private
         
     | 
| 
      
 16 
     | 
    
         
            +
                def builder_name
         
     | 
| 
      
 17 
     | 
    
         
            +
                  super + "-remote"
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def builder_name_with_arch(arch)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  "#{builder_name}-#{arch}"
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def create_local_buildx
         
     | 
| 
      
 25 
     | 
    
         
            +
                  docker :buildx, :create, "--name", builder_name, builder_name_with_arch(local_arch), "--platform", "linux/#{local_arch}"
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def append_remote_buildx
         
     | 
| 
      
 29 
     | 
    
         
            +
                  docker :buildx, :create, "--append", "--name", builder_name, builder_name_with_arch(remote_arch), "--platform", "linux/#{remote_arch}"
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def create_contexts
         
     | 
| 
      
 33 
     | 
    
         
            +
                  combine \
         
     | 
| 
      
 34 
     | 
    
         
            +
                    create_context(local_arch, local_host),
         
     | 
| 
      
 35 
     | 
    
         
            +
                    create_context(remote_arch, remote_host)
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def create_context(arch, host)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  docker :context, :create, builder_name_with_arch(arch), "--description", "'#{builder_name} #{arch} native host'", "--docker", "'host=#{host}'"
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                def remove_contexts
         
     | 
| 
      
 43 
     | 
    
         
            +
                  combine \
         
     | 
| 
      
 44 
     | 
    
         
            +
                    remove_context(local_arch),
         
     | 
| 
      
 45 
     | 
    
         
            +
                    remove_context(remote_arch)
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def remove_context(arch)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  docker :context, :rm, builder_name_with_arch(arch)
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
         
     | 
| 
      
 2 
     | 
    
         
            +
              def create
         
     | 
| 
      
 3 
     | 
    
         
            +
                docker :buildx, :create, "--use", "--name", builder_name
         
     | 
| 
      
 4 
     | 
    
         
            +
              end
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def remove
         
     | 
| 
      
 7 
     | 
    
         
            +
                docker :buildx, :rm, builder_name
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def push
         
     | 
| 
      
 11 
     | 
    
         
            +
                docker :buildx, :build,
         
     | 
| 
      
 12 
     | 
    
         
            +
                  "--push",
         
     | 
| 
      
 13 
     | 
    
         
            +
                  "--platform", "linux/amd64,linux/arm64",
         
     | 
| 
      
 14 
     | 
    
         
            +
                  "--builder", builder_name,
         
     | 
| 
      
 15 
     | 
    
         
            +
                  *build_options,
         
     | 
| 
      
 16 
     | 
    
         
            +
                  build_context
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              def info
         
     | 
| 
      
 20 
     | 
    
         
            +
                combine \
         
     | 
| 
      
 21 
     | 
    
         
            +
                  docker(:context, :ls),
         
     | 
| 
      
 22 
     | 
    
         
            +
                  docker(:buildx, :ls)
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              private
         
     | 
| 
      
 26 
     | 
    
         
            +
                def builder_name
         
     | 
| 
      
 27 
     | 
    
         
            +
                  "kamal-#{config.service}-multiarch"
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Kamal::Commands::Builder::Native::Cached < Kamal::Commands::Builder::Native
         
     | 
| 
      
 2 
     | 
    
         
            +
              def create
         
     | 
| 
      
 3 
     | 
    
         
            +
                docker :buildx, :create, "--use", "--driver=docker-container"
         
     | 
| 
      
 4 
     | 
    
         
            +
              end
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def remove
         
     | 
| 
      
 7 
     | 
    
         
            +
                docker :buildx, :rm, builder_name
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def push
         
     | 
| 
      
 11 
     | 
    
         
            +
                docker :buildx, :build,
         
     | 
| 
      
 12 
     | 
    
         
            +
                  "--push",
         
     | 
| 
      
 13 
     | 
    
         
            +
                  *build_options,
         
     | 
| 
      
 14 
     | 
    
         
            +
                  build_context
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,59 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Kamal::Commands::Builder::Native::Remote < Kamal::Commands::Builder::Native
         
     | 
| 
      
 2 
     | 
    
         
            +
              def create
         
     | 
| 
      
 3 
     | 
    
         
            +
                chain \
         
     | 
| 
      
 4 
     | 
    
         
            +
                  create_context,
         
     | 
| 
      
 5 
     | 
    
         
            +
                  create_buildx
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              def remove
         
     | 
| 
      
 9 
     | 
    
         
            +
                chain \
         
     | 
| 
      
 10 
     | 
    
         
            +
                  remove_context,
         
     | 
| 
      
 11 
     | 
    
         
            +
                  remove_buildx
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              def push
         
     | 
| 
      
 15 
     | 
    
         
            +
                docker :buildx, :build,
         
     | 
| 
      
 16 
     | 
    
         
            +
                  "--push",
         
     | 
| 
      
 17 
     | 
    
         
            +
                  "--platform", platform,
         
     | 
| 
      
 18 
     | 
    
         
            +
                  "--builder", builder_name,
         
     | 
| 
      
 19 
     | 
    
         
            +
                  *build_options,
         
     | 
| 
      
 20 
     | 
    
         
            +
                  build_context
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              def info
         
     | 
| 
      
 24 
     | 
    
         
            +
                chain \
         
     | 
| 
      
 25 
     | 
    
         
            +
                  docker(:context, :ls),
         
     | 
| 
      
 26 
     | 
    
         
            +
                  docker(:buildx, :ls)
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              private
         
     | 
| 
      
 31 
     | 
    
         
            +
                def builder_name
         
     | 
| 
      
 32 
     | 
    
         
            +
                  "kamal-#{config.service}-native-remote"
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                def builder_name_with_arch
         
     | 
| 
      
 36 
     | 
    
         
            +
                  "#{builder_name}-#{remote_arch}"
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def platform
         
     | 
| 
      
 40 
     | 
    
         
            +
                  "linux/#{remote_arch}"
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def create_context
         
     | 
| 
      
 44 
     | 
    
         
            +
                  docker :context, :create,
         
     | 
| 
      
 45 
     | 
    
         
            +
                    builder_name_with_arch, "--description", "'#{builder_name} #{remote_arch} native host'", "--docker", "'host=#{remote_host}'"
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def remove_context
         
     | 
| 
      
 49 
     | 
    
         
            +
                  docker :context, :rm, builder_name_with_arch
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def create_buildx
         
     | 
| 
      
 53 
     | 
    
         
            +
                  docker :buildx, :create, "--name", builder_name, builder_name_with_arch, "--platform", platform
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                def remove_buildx
         
     | 
| 
      
 57 
     | 
    
         
            +
                  docker :buildx, :rm, builder_name
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Kamal::Commands::Builder::Native < Kamal::Commands::Builder::Base
         
     | 
| 
      
 2 
     | 
    
         
            +
              def create
         
     | 
| 
      
 3 
     | 
    
         
            +
                # No-op on native without cache
         
     | 
| 
      
 4 
     | 
    
         
            +
              end
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def remove
         
     | 
| 
      
 7 
     | 
    
         
            +
                # No-op on native without cache
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              def push
         
     | 
| 
      
 11 
     | 
    
         
            +
                combine \
         
     | 
| 
      
 12 
     | 
    
         
            +
                  docker(:build, *build_options, build_context),
         
     | 
| 
      
 13 
     | 
    
         
            +
                  docker(:push, config.absolute_image),
         
     | 
| 
      
 14 
     | 
    
         
            +
                  docker(:push, config.latest_image)
         
     | 
| 
      
 15 
     | 
    
         
            +
              end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              def info
         
     | 
| 
      
 18 
     | 
    
         
            +
                # No-op on native
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Kamal::Commands::Builder < Kamal::Commands::Base
         
     | 
| 
      
 2 
     | 
    
         
            +
              delegate :create, :remove, :push, :clean, :pull, :info, to: :target
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              def name
         
     | 
| 
      
 5 
     | 
    
         
            +
                target.class.to_s.remove("Kamal::Commands::Builder::").underscore.inquiry
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              def target
         
     | 
| 
      
 9 
     | 
    
         
            +
                case
         
     | 
| 
      
 10 
     | 
    
         
            +
                when !config.builder.multiarch? && !config.builder.cached?
         
     | 
| 
      
 11 
     | 
    
         
            +
                  native
         
     | 
| 
      
 12 
     | 
    
         
            +
                when !config.builder.multiarch? && config.builder.cached?
         
     | 
| 
      
 13 
     | 
    
         
            +
                  native_cached
         
     | 
| 
      
 14 
     | 
    
         
            +
                when config.builder.local? && config.builder.remote?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  multiarch_remote
         
     | 
| 
      
 16 
     | 
    
         
            +
                when config.builder.remote?
         
     | 
| 
      
 17 
     | 
    
         
            +
                  native_remote
         
     | 
| 
      
 18 
     | 
    
         
            +
                else
         
     | 
| 
      
 19 
     | 
    
         
            +
                  multiarch
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              def native
         
     | 
| 
      
 24 
     | 
    
         
            +
                @native ||= Kamal::Commands::Builder::Native.new(config)
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              def native_cached
         
     | 
| 
      
 28 
     | 
    
         
            +
                @native ||= Kamal::Commands::Builder::Native::Cached.new(config)
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def native_remote
         
     | 
| 
      
 32 
     | 
    
         
            +
                @native ||= Kamal::Commands::Builder::Native::Remote.new(config)
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              def multiarch
         
     | 
| 
      
 36 
     | 
    
         
            +
                @multiarch ||= Kamal::Commands::Builder::Multiarch.new(config)
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              def multiarch_remote
         
     | 
| 
      
 40 
     | 
    
         
            +
                @multiarch_remote ||= Kamal::Commands::Builder::Multiarch::Remote.new(config)
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              def ensure_local_dependencies_installed
         
     | 
| 
      
 45 
     | 
    
         
            +
                if name.native?
         
     | 
| 
      
 46 
     | 
    
         
            +
                  ensure_local_docker_installed
         
     | 
| 
      
 47 
     | 
    
         
            +
                else
         
     | 
| 
      
 48 
     | 
    
         
            +
                  combine \
         
     | 
| 
      
 49 
     | 
    
         
            +
                    ensure_local_docker_installed,
         
     | 
| 
      
 50 
     | 
    
         
            +
                    ensure_local_buildx_installed
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              private
         
     | 
| 
      
 55 
     | 
    
         
            +
                def ensure_local_docker_installed
         
     | 
| 
      
 56 
     | 
    
         
            +
                  docker "--version"
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                def ensure_local_buildx_installed
         
     | 
| 
      
 60 
     | 
    
         
            +
                  docker :buildx, "version"
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 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 [ :curl, "-fsSL", "https://get.docker.com" ], :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 ]' ]
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,57 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Kamal::Commands::Healthcheck < Kamal::Commands::Base
         
     | 
| 
      
 2 
     | 
    
         
            +
              EXPOSED_PORT = 3999
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              def run
         
     | 
| 
      
 5 
     | 
    
         
            +
                web = config.role(:web)
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                docker :run,
         
     | 
| 
      
 8 
     | 
    
         
            +
                  "--detach",
         
     | 
| 
      
 9 
     | 
    
         
            +
                  "--name", container_name_with_version,
         
     | 
| 
      
 10 
     | 
    
         
            +
                  "--publish", "#{EXPOSED_PORT}:#{config.healthcheck["port"]}",
         
     | 
| 
      
 11 
     | 
    
         
            +
                  "--label", "service=#{container_name}",
         
     | 
| 
      
 12 
     | 
    
         
            +
                  "-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
         
     | 
| 
      
 13 
     | 
    
         
            +
                  *web.env_args,
         
     | 
| 
      
 14 
     | 
    
         
            +
                  *web.health_check_args,
         
     | 
| 
      
 15 
     | 
    
         
            +
                  *config.volume_args,
         
     | 
| 
      
 16 
     | 
    
         
            +
                  *web.option_args,
         
     | 
| 
      
 17 
     | 
    
         
            +
                  config.absolute_image,
         
     | 
| 
      
 18 
     | 
    
         
            +
                  web.cmd
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              def status
         
     | 
| 
      
 22 
     | 
    
         
            +
                pipe container_id, xargs(docker(:inspect, "--format", DOCKER_HEALTH_STATUS_FORMAT))
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              def container_health_log
         
     | 
| 
      
 26 
     | 
    
         
            +
                pipe container_id, xargs(docker(:inspect, "--format", DOCKER_HEALTH_LOG_FORMAT))
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              def logs
         
     | 
| 
      
 30 
     | 
    
         
            +
                pipe container_id, xargs(docker(:logs, "--tail", 50, "2>&1"))
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              def stop
         
     | 
| 
      
 34 
     | 
    
         
            +
                pipe container_id, xargs(docker(:stop))
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              def remove
         
     | 
| 
      
 38 
     | 
    
         
            +
                pipe container_id, xargs(docker(:container, :rm))
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              private
         
     | 
| 
      
 42 
     | 
    
         
            +
                def container_name
         
     | 
| 
      
 43 
     | 
    
         
            +
                  [ "healthcheck", config.service, config.destination ].compact.join("-")
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def container_name_with_version
         
     | 
| 
      
 47 
     | 
    
         
            +
                  "#{container_name}-#{config.version}"
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def container_id
         
     | 
| 
      
 51 
     | 
    
         
            +
                  container_id_for(container_name: container_name_with_version)
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def health_url
         
     | 
| 
      
 55 
     | 
    
         
            +
                  "http://localhost:#{EXPOSED_PORT}#{config.healthcheck["path"]}"
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Kamal::Commands::Hook < Kamal::Commands::Base
         
     | 
| 
      
 2 
     | 
    
         
            +
              def run(hook, **details)
         
     | 
| 
      
 3 
     | 
    
         
            +
                [ hook_file(hook), env: tags(**details).env ]
         
     | 
| 
      
 4 
     | 
    
         
            +
              end
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def hook_exists?(hook)
         
     | 
| 
      
 7 
     | 
    
         
            +
                Pathname.new(hook_file(hook)).exist?
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              private
         
     | 
| 
      
 11 
     | 
    
         
            +
                def hook_file(hook)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  "#{config.hooks_path}/#{hook}"
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     |