kamal 2.7.0 → 2.8.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.
- checksums.yaml +4 -4
 - data/lib/kamal/cli/accessory.rb +13 -0
 - data/lib/kamal/cli/build.rb +33 -15
 - data/lib/kamal/cli/main.rb +7 -2
 - data/lib/kamal/cli/port_forwarding.rb +55 -0
 - data/lib/kamal/cli/registry.rb +16 -8
 - data/lib/kamal/cli/templates/deploy.yml +4 -3
 - data/lib/kamal/cli/templates/secrets +1 -1
 - data/lib/kamal/commander.rb +1 -1
 - data/lib/kamal/commands/accessory.rb +4 -0
 - data/lib/kamal/commands/app.rb +1 -0
 - data/lib/kamal/commands/builder/base.rb +10 -1
 - data/lib/kamal/commands/builder/local.rb +15 -2
 - data/lib/kamal/commands/builder/pack.rb +5 -5
 - data/lib/kamal/commands/builder/remote.rb +9 -1
 - data/lib/kamal/commands/builder.rb +8 -2
 - data/lib/kamal/commands/registry.rb +22 -0
 - data/lib/kamal/configuration/docs/proxy.yml +24 -16
 - data/lib/kamal/configuration/proxy.rb +5 -1
 - data/lib/kamal/configuration/registry.rb +8 -0
 - data/lib/kamal/configuration/validator/registry.rb +5 -3
 - data/lib/kamal/configuration/validator.rb +3 -3
 - data/lib/kamal/configuration.rb +11 -2
 - data/lib/kamal/secrets/adapters/one_password.rb +1 -1
 - data/lib/kamal/sshkit_with_ext.rb +8 -0
 - data/lib/kamal/version.rb +1 -1
 - metadata +3 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: fd8b2329effde7405d2a1b8d60f394d1da81faaf5e8bcd6980c83f8bb590f988
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 5543d21fbe88acf86a24fcf1f44dff30e0aaf410b3ead0293b0f005b15fa2cbb
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: bdae637114b1ad0630d5b1bbc0e76be5ebb3817d370d1f3f5a4222024f13fa3546a24806ad812e9d6d14735c2be23ed6956702723e75cc27c5ec34ebe87d72f9
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 8db206acbeb707f3a0d3267720b9d248cb2566517d340a61072777e0fdbbf237701d42e203d2d96b21fb7c04596896d6039c4ba9d59eb1515d47de0f85148248
         
     | 
    
        data/lib/kamal/cli/accessory.rb
    CHANGED
    
    | 
         @@ -77,6 +77,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base 
     | 
|
| 
       77 
77 
     | 
    
         
             
                    KAMAL.accessory_names.each { |accessory_name| reboot(accessory_name) }
         
     | 
| 
       78 
78 
     | 
    
         
             
                  else
         
     | 
| 
       79 
79 
     | 
    
         
             
                    prepare(name)
         
     | 
| 
      
 80 
     | 
    
         
            +
                    pull_image(name)
         
     | 
| 
       80 
81 
     | 
    
         
             
                    stop(name)
         
     | 
| 
       81 
82 
     | 
    
         
             
                    remove_container(name)
         
     | 
| 
       82 
83 
     | 
    
         
             
                    boot(name, prepare: false)
         
     | 
| 
         @@ -203,6 +204,18 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base 
     | 
|
| 
       203 
204 
     | 
    
         
             
                end
         
     | 
| 
       204 
205 
     | 
    
         
             
              end
         
     | 
| 
       205 
206 
     | 
    
         | 
| 
      
 207 
     | 
    
         
            +
              desc "pull_image [NAME]", "Pull accessory image on host", hide: true
         
     | 
| 
      
 208 
     | 
    
         
            +
              def pull_image(name)
         
     | 
| 
      
 209 
     | 
    
         
            +
                with_lock do
         
     | 
| 
      
 210 
     | 
    
         
            +
                  with_accessory(name) do |accessory, hosts|
         
     | 
| 
      
 211 
     | 
    
         
            +
                    on(hosts) do
         
     | 
| 
      
 212 
     | 
    
         
            +
                      execute *KAMAL.auditor.record("Pull #{name} accessory image"), verbosity: :debug
         
     | 
| 
      
 213 
     | 
    
         
            +
                      execute *accessory.pull_image
         
     | 
| 
      
 214 
     | 
    
         
            +
                    end
         
     | 
| 
      
 215 
     | 
    
         
            +
                  end
         
     | 
| 
      
 216 
     | 
    
         
            +
                end
         
     | 
| 
      
 217 
     | 
    
         
            +
              end
         
     | 
| 
      
 218 
     | 
    
         
            +
             
     | 
| 
       206 
219 
     | 
    
         
             
              desc "remove [NAME]", "Remove accessory container, image and data directory from host (use NAME=all to remove all accessories)"
         
     | 
| 
       207 
220 
     | 
    
         
             
              option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
         
     | 
| 
       208 
221 
     | 
    
         
             
              def remove(name)
         
     | 
    
        data/lib/kamal/cli/build.rb
    CHANGED
    
    | 
         @@ -11,6 +11,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base 
     | 
|
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
              desc "push", "Build and push app image to registry"
         
     | 
| 
       13 
13 
     | 
    
         
             
              option :output, type: :string, default: "registry", banner: "export_type", desc: "Exported type for the build result, and may be any exported type supported by 'buildx --output'."
         
     | 
| 
      
 14 
     | 
    
         
            +
              option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
         
     | 
| 
       14 
15 
     | 
    
         
             
              def push
         
     | 
| 
       15 
16 
     | 
    
         
             
                cli = self
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
         @@ -19,7 +20,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base 
     | 
|
| 
       19 
20 
     | 
    
         
             
                pre_connect_if_required
         
     | 
| 
       20 
21 
     | 
    
         | 
| 
       21 
22 
     | 
    
         
             
                ensure_docker_installed
         
     | 
| 
       22 
     | 
    
         
            -
                login_to_registry_locally
         
     | 
| 
      
 23 
     | 
    
         
            +
                login_to_registry_locally if KAMAL.builder.login_to_registry_locally?
         
     | 
| 
       23 
24 
     | 
    
         | 
| 
       24 
25 
     | 
    
         
             
                run_hook "pre-build"
         
     | 
| 
       25 
26 
     | 
    
         | 
| 
         @@ -56,10 +57,10 @@ class Kamal::Cli::Build < Kamal::Cli::Base 
     | 
|
| 
       56 
57 
     | 
    
         
             
                    end
         
     | 
| 
       57 
58 
     | 
    
         | 
| 
       58 
59 
     | 
    
         
             
                    # Get the command here to ensure the Dir.chdir doesn't interfere with it
         
     | 
| 
       59 
     | 
    
         
            -
                    push = KAMAL.builder.push(cli.options[:output])
         
     | 
| 
      
 60 
     | 
    
         
            +
                    push = KAMAL.builder.push(cli.options[:output], no_cache: cli.options[:no_cache])
         
     | 
| 
       60 
61 
     | 
    
         | 
| 
       61 
62 
     | 
    
         
             
                    KAMAL.with_verbosity(:debug) do
         
     | 
| 
       62 
     | 
    
         
            -
                      Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
         
     | 
| 
      
 63 
     | 
    
         
            +
                      Dir.chdir(KAMAL.config.builder.build_directory) { execute *push, env: KAMAL.builder.push_env }
         
     | 
| 
       63 
64 
     | 
    
         
             
                    end
         
     | 
| 
       64 
65 
     | 
    
         
             
                  end
         
     | 
| 
       65 
66 
     | 
    
         
             
                end
         
     | 
| 
         @@ -67,16 +68,18 @@ class Kamal::Cli::Build < Kamal::Cli::Base 
     | 
|
| 
       67 
68 
     | 
    
         | 
| 
       68 
69 
     | 
    
         
             
              desc "pull", "Pull app image from registry onto servers"
         
     | 
| 
       69 
70 
     | 
    
         
             
              def pull
         
     | 
| 
       70 
     | 
    
         
            -
                login_to_registry_remotely
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                 
     | 
| 
       73 
     | 
    
         
            -
                   
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                   
     | 
| 
      
 71 
     | 
    
         
            +
                login_to_registry_remotely unless KAMAL.registry.local?
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                forward_local_registry_port do
         
     | 
| 
      
 74 
     | 
    
         
            +
                  if (first_hosts = mirror_hosts).any?
         
     | 
| 
      
 75 
     | 
    
         
            +
                    #  Pull on a single host per mirror first to seed them
         
     | 
| 
      
 76 
     | 
    
         
            +
                    say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
         
     | 
| 
      
 77 
     | 
    
         
            +
                    pull_on_hosts(first_hosts)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    say "Pulling image on remaining hosts...", :magenta
         
     | 
| 
      
 79 
     | 
    
         
            +
                    pull_on_hosts(KAMAL.app_hosts - first_hosts)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  else
         
     | 
| 
      
 81 
     | 
    
         
            +
                    pull_on_hosts(KAMAL.app_hosts)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
       80 
83 
     | 
    
         
             
                end
         
     | 
| 
       81 
84 
     | 
    
         
             
              end
         
     | 
| 
       82 
85 
     | 
    
         | 
| 
         @@ -119,6 +122,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base 
     | 
|
| 
       119 
122 
     | 
    
         | 
| 
       120 
123 
     | 
    
         
             
              desc "dev", "Build using the working directory, tag it as dirty, and push to local image store."
         
     | 
| 
       121 
124 
     | 
    
         
             
              option :output, type: :string, default: "docker", banner: "export_type", desc: "Exported type for the build result, and may be any exported type supported by 'buildx --output'."
         
     | 
| 
      
 125 
     | 
    
         
            +
              option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
         
     | 
| 
       122 
126 
     | 
    
         
             
              def dev
         
     | 
| 
       123 
127 
     | 
    
         
             
                cli = self
         
     | 
| 
       124 
128 
     | 
    
         | 
| 
         @@ -144,7 +148,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base 
     | 
|
| 
       144 
148 
     | 
    
         | 
| 
       145 
149 
     | 
    
         
             
                with_env(KAMAL.config.builder.secrets) do
         
     | 
| 
       146 
150 
     | 
    
         
             
                  run_locally do
         
     | 
| 
       147 
     | 
    
         
            -
                    build = KAMAL.builder.push(cli.options[:output], tag_as_dirty: true)
         
     | 
| 
      
 151 
     | 
    
         
            +
                    build = KAMAL.builder.push(cli.options[:output], tag_as_dirty: true, no_cache: cli.options[:no_cache])
         
     | 
| 
       148 
152 
     | 
    
         
             
                    KAMAL.with_verbosity(:debug) do
         
     | 
| 
       149 
153 
     | 
    
         
             
                      execute(*build)
         
     | 
| 
       150 
154 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -192,7 +196,11 @@ class Kamal::Cli::Build < Kamal::Cli::Base 
     | 
|
| 
       192 
196 
     | 
    
         | 
| 
       193 
197 
     | 
    
         
             
                def login_to_registry_locally
         
     | 
| 
       194 
198 
     | 
    
         
             
                  run_locally do
         
     | 
| 
       195 
     | 
    
         
            -
                     
     | 
| 
      
 199 
     | 
    
         
            +
                    if KAMAL.registry.local?
         
     | 
| 
      
 200 
     | 
    
         
            +
                      execute *KAMAL.registry.setup
         
     | 
| 
      
 201 
     | 
    
         
            +
                    else
         
     | 
| 
      
 202 
     | 
    
         
            +
                      execute *KAMAL.registry.login
         
     | 
| 
      
 203 
     | 
    
         
            +
                    end
         
     | 
| 
       196 
204 
     | 
    
         
             
                  end
         
     | 
| 
       197 
205 
     | 
    
         
             
                end
         
     | 
| 
       198 
206 
     | 
    
         | 
| 
         @@ -201,4 +209,14 @@ class Kamal::Cli::Build < Kamal::Cli::Base 
     | 
|
| 
       201 
209 
     | 
    
         
             
                    execute *KAMAL.registry.login
         
     | 
| 
       202 
210 
     | 
    
         
             
                  end
         
     | 
| 
       203 
211 
     | 
    
         
             
                end
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                def forward_local_registry_port(&block)
         
     | 
| 
      
 214 
     | 
    
         
            +
                  if KAMAL.config.registry.local?
         
     | 
| 
      
 215 
     | 
    
         
            +
                    Kamal::Cli::PortForwarding.
         
     | 
| 
      
 216 
     | 
    
         
            +
                      new(KAMAL.hosts, KAMAL.config.registry.local_port).
         
     | 
| 
      
 217 
     | 
    
         
            +
                      forward(&block)
         
     | 
| 
      
 218 
     | 
    
         
            +
                  else
         
     | 
| 
      
 219 
     | 
    
         
            +
                    yield
         
     | 
| 
      
 220 
     | 
    
         
            +
                  end
         
     | 
| 
      
 221 
     | 
    
         
            +
                end
         
     | 
| 
       204 
222 
     | 
    
         
             
            end
         
     | 
    
        data/lib/kamal/cli/main.rb
    CHANGED
    
    | 
         @@ -1,6 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            class Kamal::Cli::Main < Kamal::Cli::Base
         
     | 
| 
       2 
2 
     | 
    
         
             
              desc "setup", "Setup all accessories, push the env, and deploy app to servers"
         
     | 
| 
       3 
3 
     | 
    
         
             
              option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
         
     | 
| 
      
 4 
     | 
    
         
            +
              option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
         
     | 
| 
       4 
5 
     | 
    
         
             
              def setup
         
     | 
| 
       5 
6 
     | 
    
         
             
                print_runtime do
         
     | 
| 
       6 
7 
     | 
    
         
             
                  with_lock do
         
     | 
| 
         @@ -16,6 +17,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base 
     | 
|
| 
       16 
17 
     | 
    
         | 
| 
       17 
18 
     | 
    
         
             
              desc "deploy", "Deploy app to servers"
         
     | 
| 
       18 
19 
     | 
    
         
             
              option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
         
     | 
| 
      
 20 
     | 
    
         
            +
              option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
         
     | 
| 
       19 
21 
     | 
    
         
             
              def deploy(boot_accessories: false)
         
     | 
| 
       20 
22 
     | 
    
         
             
                runtime = print_runtime do
         
     | 
| 
       21 
23 
     | 
    
         
             
                  invoke_options = deploy_options
         
     | 
| 
         @@ -51,6 +53,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base 
     | 
|
| 
       51 
53 
     | 
    
         | 
| 
       52 
54 
     | 
    
         
             
              desc "redeploy", "Deploy app to servers without bootstrapping servers, starting kamal-proxy and pruning"
         
     | 
| 
       53 
55 
     | 
    
         
             
              option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
         
     | 
| 
      
 56 
     | 
    
         
            +
              option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
         
     | 
| 
       54 
57 
     | 
    
         
             
              def redeploy
         
     | 
| 
       55 
58 
     | 
    
         
             
                runtime = print_runtime do
         
     | 
| 
       56 
59 
     | 
    
         
             
                  invoke_options = deploy_options
         
     | 
| 
         @@ -182,7 +185,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base 
     | 
|
| 
       182 
185 
     | 
    
         
             
                    invoke "kamal:cli:app:remove", [], options.without(:confirmed)
         
     | 
| 
       183 
186 
     | 
    
         
             
                    invoke "kamal:cli:proxy:remove", [], options.without(:confirmed)
         
     | 
| 
       184 
187 
     | 
    
         
             
                    invoke "kamal:cli:accessory:remove", [ "all" ], options
         
     | 
| 
       185 
     | 
    
         
            -
                    invoke "kamal:cli:registry: 
     | 
| 
      
 188 
     | 
    
         
            +
                    invoke "kamal:cli:registry:remove", [], options.without(:confirmed).merge(skip_local: true)
         
     | 
| 
       186 
189 
     | 
    
         
             
                  end
         
     | 
| 
       187 
190 
     | 
    
         
             
                end
         
     | 
| 
       188 
191 
     | 
    
         
             
              end
         
     | 
| 
         @@ -272,6 +275,8 @@ class Kamal::Cli::Main < Kamal::Cli::Base 
     | 
|
| 
       272 
275 
     | 
    
         
             
                end
         
     | 
| 
       273 
276 
     | 
    
         | 
| 
       274 
277 
     | 
    
         
             
                def deploy_options
         
     | 
| 
       275 
     | 
    
         
            -
                   
     | 
| 
      
 278 
     | 
    
         
            +
                  base_options = options.without("skip_push")
         
     | 
| 
      
 279 
     | 
    
         
            +
                  base_options = base_options.except("no_cache") unless base_options["no_cache"]
         
     | 
| 
      
 280 
     | 
    
         
            +
                  { "version" => KAMAL.config.version }.merge(base_options)
         
     | 
| 
       276 
281 
     | 
    
         
             
                end
         
     | 
| 
       277 
282 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,55 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "concurrent/atomic/count_down_latch"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Kamal::Cli::PortForwarding
         
     | 
| 
      
 4 
     | 
    
         
            +
              attr_reader :hosts, :port
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              def initialize(hosts, port)
         
     | 
| 
      
 7 
     | 
    
         
            +
                @hosts = hosts
         
     | 
| 
      
 8 
     | 
    
         
            +
                @port = port
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              def forward
         
     | 
| 
      
 12 
     | 
    
         
            +
                @done = false
         
     | 
| 
      
 13 
     | 
    
         
            +
                forward_ports
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                yield
         
     | 
| 
      
 16 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 17 
     | 
    
         
            +
                stop
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              private
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              def stop
         
     | 
| 
      
 23 
     | 
    
         
            +
                @done = true
         
     | 
| 
      
 24 
     | 
    
         
            +
                @threads.to_a.each(&:join)
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              def forward_ports
         
     | 
| 
      
 28 
     | 
    
         
            +
                ready = Concurrent::CountDownLatch.new(hosts.size)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                @threads = hosts.map do |host|
         
     | 
| 
      
 31 
     | 
    
         
            +
                  Thread.new do
         
     | 
| 
      
 32 
     | 
    
         
            +
                    Net::SSH.start(host, KAMAL.config.ssh.user, **{ proxy: KAMAL.config.ssh.proxy }.compact) do |ssh|
         
     | 
| 
      
 33 
     | 
    
         
            +
                      ssh.forward.remote(port, "localhost", port, "127.0.0.1") do |remote_port, bind_address|
         
     | 
| 
      
 34 
     | 
    
         
            +
                        if remote_port == :error
         
     | 
| 
      
 35 
     | 
    
         
            +
                          raise "Failed to establish port forward on #{host}"
         
     | 
| 
      
 36 
     | 
    
         
            +
                        else
         
     | 
| 
      
 37 
     | 
    
         
            +
                          ready.count_down
         
     | 
| 
      
 38 
     | 
    
         
            +
                        end
         
     | 
| 
      
 39 
     | 
    
         
            +
                      end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                      ssh.loop(0.1) do
         
     | 
| 
      
 42 
     | 
    
         
            +
                        if @done
         
     | 
| 
      
 43 
     | 
    
         
            +
                          ssh.forward.cancel_remote(port, "127.0.0.1")
         
     | 
| 
      
 44 
     | 
    
         
            +
                          break
         
     | 
| 
      
 45 
     | 
    
         
            +
                        else
         
     | 
| 
      
 46 
     | 
    
         
            +
                          true
         
     | 
| 
      
 47 
     | 
    
         
            +
                        end
         
     | 
| 
      
 48 
     | 
    
         
            +
                      end
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                raise "Timed out waiting for port forwarding to be established" unless ready.wait(10)
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/kamal/cli/registry.rb
    CHANGED
    
    | 
         @@ -1,19 +1,27 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            class Kamal::Cli::Registry < Kamal::Cli::Base
         
     | 
| 
       2 
     | 
    
         
            -
              desc " 
     | 
| 
      
 2 
     | 
    
         
            +
              desc "setup", "Setup local registry or log in to remote registry locally and remotely"
         
     | 
| 
       3 
3 
     | 
    
         
             
              option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
         
     | 
| 
       4 
4 
     | 
    
         
             
              option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
         
     | 
| 
       5 
     | 
    
         
            -
              def  
     | 
| 
      
 5 
     | 
    
         
            +
              def setup
         
     | 
| 
       6 
6 
     | 
    
         
             
                ensure_docker_installed unless options[:skip_local]
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
                 
     | 
| 
       9 
     | 
    
         
            -
                 
     | 
| 
      
 8 
     | 
    
         
            +
                if KAMAL.registry.local?
         
     | 
| 
      
 9 
     | 
    
         
            +
                  run_locally    { execute *KAMAL.registry.setup } unless options[:skip_local]
         
     | 
| 
      
 10 
     | 
    
         
            +
                else
         
     | 
| 
      
 11 
     | 
    
         
            +
                  run_locally    { execute *KAMAL.registry.login } unless options[:skip_local]
         
     | 
| 
      
 12 
     | 
    
         
            +
                  on(KAMAL.hosts) { execute *KAMAL.registry.login } unless options[:skip_remote]
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
       10 
14 
     | 
    
         
             
              end
         
     | 
| 
       11 
15 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
              desc " 
     | 
| 
      
 16 
     | 
    
         
            +
              desc "remove", "Remove local registry or log out of remote registry locally and remotely"
         
     | 
| 
       13 
17 
     | 
    
         
             
              option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
         
     | 
| 
       14 
18 
     | 
    
         
             
              option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
         
     | 
| 
       15 
     | 
    
         
            -
              def  
     | 
| 
       16 
     | 
    
         
            -
                 
     | 
| 
       17 
     | 
    
         
            -
                 
     | 
| 
      
 19 
     | 
    
         
            +
              def remove
         
     | 
| 
      
 20 
     | 
    
         
            +
                if KAMAL.registry.local?
         
     | 
| 
      
 21 
     | 
    
         
            +
                  run_locally    { execute *KAMAL.registry.remove, raise_on_non_zero_exit: false } unless options[:skip_local]
         
     | 
| 
      
 22 
     | 
    
         
            +
                else
         
     | 
| 
      
 23 
     | 
    
         
            +
                  run_locally    { execute *KAMAL.registry.logout } unless options[:skip_local]
         
     | 
| 
      
 24 
     | 
    
         
            +
                  on(KAMAL.hosts) { execute *KAMAL.registry.logout } unless options[:skip_remote]
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
       18 
26 
     | 
    
         
             
              end
         
     | 
| 
       19 
27 
     | 
    
         
             
            end
         
     | 
| 
         @@ -25,13 +25,14 @@ proxy: 
     | 
|
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
            # Credentials for your image host.
         
     | 
| 
       27 
27 
     | 
    
         
             
            registry:
         
     | 
| 
      
 28 
     | 
    
         
            +
              server: localhost:5555
         
     | 
| 
       28 
29 
     | 
    
         
             
              # Specify the registry server, if you're not using Docker Hub
         
     | 
| 
       29 
30 
     | 
    
         
             
              # server: registry.digitalocean.com / ghcr.io / ...
         
     | 
| 
       30 
     | 
    
         
            -
              username: my-user
         
     | 
| 
      
 31 
     | 
    
         
            +
              # username: my-user
         
     | 
| 
       31 
32 
     | 
    
         | 
| 
       32 
33 
     | 
    
         
             
              # Always use an access token rather than real password (pulled from .kamal/secrets).
         
     | 
| 
       33 
     | 
    
         
            -
              password:
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
      
 34 
     | 
    
         
            +
              # password:
         
     | 
| 
      
 35 
     | 
    
         
            +
              #   - KAMAL_REGISTRY_PASSWORD
         
     | 
| 
       35 
36 
     | 
    
         | 
| 
       36 
37 
     | 
    
         
             
            # Configure builder setup.
         
     | 
| 
       37 
38 
     | 
    
         
             
            builder:
         
     | 
| 
         @@ -3,7 +3,7 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            # password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            # Option 1: Read secrets from the environment
         
     | 
| 
       6 
     | 
    
         
            -
            KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
         
     | 
| 
      
 6 
     | 
    
         
            +
            # KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            # Option 2: Read secrets via a command
         
     | 
| 
       9 
9 
     | 
    
         
             
            # RAILS_MASTER_KEY=$(cat config/master.key)
         
     | 
    
        data/lib/kamal/commander.rb
    CHANGED
    
    | 
         @@ -21,7 +21,7 @@ class Kamal::Commander 
     | 
|
| 
       21 
21 
     | 
    
         
             
              end
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         
             
              def config
         
     | 
| 
       24 
     | 
    
         
            -
                @config ||= Kamal::Configuration.create_from(**@config_kwargs).tap do |config|
         
     | 
| 
      
 24 
     | 
    
         
            +
                @config ||= Kamal::Configuration.create_from(**@config_kwargs.to_h).tap do |config|
         
     | 
| 
       25 
25 
     | 
    
         
             
                  @config_kwargs = nil
         
     | 
| 
       26 
26 
     | 
    
         
             
                  configure_sshkit_with(config)
         
     | 
| 
       27 
27 
     | 
    
         
             
                end
         
     | 
    
        data/lib/kamal/commands/app.rb
    CHANGED
    
    | 
         @@ -23,6 +23,7 @@ class Kamal::Commands::App < Kamal::Commands::Base 
     | 
|
| 
       23 
23 
     | 
    
         
             
                  "--env", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
         
     | 
| 
       24 
24 
     | 
    
         
             
                  "--env", "KAMAL_VERSION=\"#{config.version}\"",
         
     | 
| 
       25 
25 
     | 
    
         
             
                  "--env", "KAMAL_HOST=\"#{host}\"",
         
     | 
| 
      
 26 
     | 
    
         
            +
                  "--env", "KAMAL_DESTINATION=\"#{config.destination}\"",
         
     | 
| 
       26 
27 
     | 
    
         
             
                  *role.env_args(host),
         
     | 
| 
       27 
28 
     | 
    
         
             
                  *role.logging_args,
         
     | 
| 
       28 
29 
     | 
    
         
             
                  *config.volume_args,
         
     | 
| 
         @@ -14,13 +14,14 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base 
     | 
|
| 
       14 
14 
     | 
    
         
             
                docker :image, :rm, "--force", config.absolute_image
         
     | 
| 
       15 
15 
     | 
    
         
             
              end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
              def push(export_action = "registry", tag_as_dirty: false)
         
     | 
| 
      
 17 
     | 
    
         
            +
              def push(export_action = "registry", tag_as_dirty: false, no_cache: false)
         
     | 
| 
       18 
18 
     | 
    
         
             
                docker :buildx, :build,
         
     | 
| 
       19 
19 
     | 
    
         
             
                  "--output=type=#{export_action}",
         
     | 
| 
       20 
20 
     | 
    
         
             
                  *platform_options(arches),
         
     | 
| 
       21 
21 
     | 
    
         
             
                  *([ "--builder", builder_name ] unless docker_driver?),
         
     | 
| 
       22 
22 
     | 
    
         
             
                  *build_tag_options(tag_as_dirty: tag_as_dirty),
         
     | 
| 
       23 
23 
     | 
    
         
             
                  *build_options,
         
     | 
| 
      
 24 
     | 
    
         
            +
                  *([ "--no-cache" ] if no_cache),
         
     | 
| 
       24 
25 
     | 
    
         
             
                  build_context,
         
     | 
| 
       25 
26 
     | 
    
         
             
                  "2>&1"
         
     | 
| 
       26 
27 
     | 
    
         
             
              end
         
     | 
| 
         @@ -60,6 +61,14 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base 
     | 
|
| 
       60 
61 
     | 
    
         
             
                docker(:info, "--format '{{index .RegistryConfig.Mirrors 0}}'")
         
     | 
| 
       61 
62 
     | 
    
         
             
              end
         
     | 
| 
       62 
63 
     | 
    
         | 
| 
      
 64 
     | 
    
         
            +
              def login_to_registry_locally?
         
     | 
| 
      
 65 
     | 
    
         
            +
                true
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              def push_env
         
     | 
| 
      
 69 
     | 
    
         
            +
                {}
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
       63 
72 
     | 
    
         
             
              private
         
     | 
| 
       64 
73 
     | 
    
         
             
                def build_tag_names(tag_as_dirty: false)
         
     | 
| 
       65 
74 
     | 
    
         
             
                  tag_names = [ config.absolute_image, config.latest_image ]
         
     | 
| 
         @@ -1,6 +1,15 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base
         
     | 
| 
       2 
2 
     | 
    
         
             
              def create
         
     | 
| 
       3 
     | 
    
         
            -
                 
     | 
| 
      
 3 
     | 
    
         
            +
                return if docker_driver?
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                options =
         
     | 
| 
      
 6 
     | 
    
         
            +
                  if KAMAL.registry.local?
         
     | 
| 
      
 7 
     | 
    
         
            +
                    "--driver=#{driver} --driver-opt network=host"
         
     | 
| 
      
 8 
     | 
    
         
            +
                  else
         
     | 
| 
      
 9 
     | 
    
         
            +
                    "--driver=#{driver}"
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                docker :buildx, :create, "--name", builder_name, options
         
     | 
| 
       4 
13 
     | 
    
         
             
              end
         
     | 
| 
       5 
14 
     | 
    
         | 
| 
       6 
15 
     | 
    
         
             
              def remove
         
     | 
| 
         @@ -9,6 +18,10 @@ class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base 
     | 
|
| 
       9 
18 
     | 
    
         | 
| 
       10 
19 
     | 
    
         
             
              private
         
     | 
| 
       11 
20 
     | 
    
         
             
                def builder_name
         
     | 
| 
       12 
     | 
    
         
            -
                   
     | 
| 
      
 21 
     | 
    
         
            +
                  if KAMAL.registry.local?
         
     | 
| 
      
 22 
     | 
    
         
            +
                    "kamal-local-registry-#{driver}"
         
     | 
| 
      
 23 
     | 
    
         
            +
                  else
         
     | 
| 
      
 24 
     | 
    
         
            +
                    "kamal-local-#{driver}"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
       13 
26 
     | 
    
         
             
                end
         
     | 
| 
       14 
27 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            class Kamal::Commands::Builder::Pack < Kamal::Commands::Builder::Base
         
     | 
| 
       2 
     | 
    
         
            -
              def push(export_action = "registry")
         
     | 
| 
      
 2 
     | 
    
         
            +
              def push(export_action = "registry", tag_as_dirty: false, no_cache: false)
         
     | 
| 
       3 
3 
     | 
    
         
             
                combine \
         
     | 
| 
       4 
     | 
    
         
            -
                  build,
         
     | 
| 
      
 4 
     | 
    
         
            +
                  build(tag_as_dirty: tag_as_dirty, no_cache: no_cache),
         
     | 
| 
       5 
5 
     | 
    
         
             
                  export(export_action)
         
     | 
| 
       6 
6 
     | 
    
         
             
              end
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
         @@ -13,15 +13,15 @@ class Kamal::Commands::Builder::Pack < Kamal::Commands::Builder::Base 
     | 
|
| 
       13 
13 
     | 
    
         
             
              alias_method :inspect_builder, :info
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
              private
         
     | 
| 
       16 
     | 
    
         
            -
                def build
         
     | 
| 
      
 16 
     | 
    
         
            +
                def build(tag_as_dirty: false, no_cache: false)
         
     | 
| 
       17 
17 
     | 
    
         
             
                  pack(:build,
         
     | 
| 
       18 
18 
     | 
    
         
             
                    config.repository,
         
     | 
| 
       19 
19 
     | 
    
         
             
                    "--platform", platform,
         
     | 
| 
       20 
20 
     | 
    
         
             
                    "--creation-time", "now",
         
     | 
| 
       21 
21 
     | 
    
         
             
                    "--builder", pack_builder,
         
     | 
| 
       22 
22 
     | 
    
         
             
                    buildpacks,
         
     | 
| 
       23 
     | 
    
         
            -
                     
     | 
| 
       24 
     | 
    
         
            -
                    "- 
     | 
| 
      
 23 
     | 
    
         
            +
                    *build_tag_options(tag_as_dirty: tag_as_dirty),
         
     | 
| 
      
 24 
     | 
    
         
            +
                    *([ "--clear-cache" ] if no_cache),
         
     | 
| 
       25 
25 
     | 
    
         
             
                    "--env", "BP_IMAGE_LABELS=service=#{config.service}",
         
     | 
| 
       26 
26 
     | 
    
         
             
                    *argumentize("--env", args),
         
     | 
| 
       27 
27 
     | 
    
         
             
                    *argumentize("--env", secrets, sensitive: true),
         
     | 
| 
         @@ -19,11 +19,19 @@ class Kamal::Commands::Builder::Remote < Kamal::Commands::Builder::Base 
     | 
|
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
              def inspect_builder
         
     | 
| 
       21 
21 
     | 
    
         
             
                combine \
         
     | 
| 
       22 
     | 
    
         
            -
                  combine 
     | 
| 
      
 22 
     | 
    
         
            +
                  combine(inspect_buildx, inspect_remote_context),
         
     | 
| 
       23 
23 
     | 
    
         
             
                  [ "(echo no compatible builder && exit 1)" ],
         
     | 
| 
       24 
24 
     | 
    
         
             
                  by: "||"
         
     | 
| 
       25 
25 
     | 
    
         
             
              end
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
      
 27 
     | 
    
         
            +
              def login_to_registry_locally?
         
     | 
| 
      
 28 
     | 
    
         
            +
                false
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def push_env
         
     | 
| 
      
 32 
     | 
    
         
            +
                { "BUILDKIT_NO_CLIENT_TOKEN" => "1" }
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
       27 
35 
     | 
    
         
             
              private
         
     | 
| 
       28 
36 
     | 
    
         
             
                def builder_name
         
     | 
| 
       29 
37 
     | 
    
         
             
                  "kamal-remote-#{remote.gsub(/[^a-z0-9_-]/, "-")}"
         
     | 
| 
         @@ -1,8 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require "active_support/core_ext/string/filters"
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            class Kamal::Commands::Builder < Kamal::Commands::Base
         
     | 
| 
       4 
     | 
    
         
            -
              delegate  
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
      
 4 
     | 
    
         
            +
              delegate \
         
     | 
| 
      
 5 
     | 
    
         
            +
                :create, :remove, :dev, :push, :clean, :pull, :info, :inspect_builder,
         
     | 
| 
      
 6 
     | 
    
         
            +
                :validate_image, :first_mirror, :login_to_registry_locally?, :push_env,
         
     | 
| 
      
 7 
     | 
    
         
            +
                to: :target
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              delegate \
         
     | 
| 
      
 10 
     | 
    
         
            +
                :local?, :remote?, :pack?, :cloud?,
         
     | 
| 
      
 11 
     | 
    
         
            +
                to: "config.builder"
         
     | 
| 
       6 
12 
     | 
    
         | 
| 
       7 
13 
     | 
    
         
             
              include Clone
         
     | 
| 
       8 
14 
     | 
    
         | 
| 
         @@ -2,6 +2,8 @@ class Kamal::Commands::Registry < Kamal::Commands::Base 
     | 
|
| 
       2 
2 
     | 
    
         
             
              def login(registry_config: nil)
         
     | 
| 
       3 
3 
     | 
    
         
             
                registry_config ||= config.registry
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
                return if registry_config.local?
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
       5 
7 
     | 
    
         
             
                docker :login,
         
     | 
| 
       6 
8 
     | 
    
         
             
                  registry_config.server,
         
     | 
| 
       7 
9 
     | 
    
         
             
                  "-u", sensitive(Kamal::Utils.escape_shell_value(registry_config.username)),
         
     | 
| 
         @@ -13,4 +15,24 @@ class Kamal::Commands::Registry < Kamal::Commands::Base 
     | 
|
| 
       13 
15 
     | 
    
         | 
| 
       14 
16 
     | 
    
         
             
                docker :logout, registry_config.server
         
     | 
| 
       15 
17 
     | 
    
         
             
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              def setup(registry_config: nil)
         
     | 
| 
      
 20 
     | 
    
         
            +
                registry_config ||= config.registry
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                combine \
         
     | 
| 
      
 23 
     | 
    
         
            +
                  docker(:start, "kamal-docker-registry"),
         
     | 
| 
      
 24 
     | 
    
         
            +
                  docker(:run, "--detach", "-p", "127.0.0.1:#{registry_config.local_port}:5000", "--name", "kamal-docker-registry", "registry:3"),
         
     | 
| 
      
 25 
     | 
    
         
            +
                  by: "||"
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              def remove
         
     | 
| 
      
 29 
     | 
    
         
            +
                combine \
         
     | 
| 
      
 30 
     | 
    
         
            +
                  docker(:stop, "kamal-docker-registry"),
         
     | 
| 
      
 31 
     | 
    
         
            +
                  docker(:rm, "kamal-docker-registry"),
         
     | 
| 
      
 32 
     | 
    
         
            +
                  by: "&&"
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              def local?
         
     | 
| 
      
 36 
     | 
    
         
            +
                config.registry.local?
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
       16 
38 
     | 
    
         
             
            end
         
     | 
| 
         @@ -45,27 +45,23 @@ proxy: 
     | 
|
| 
       45 
45 
     | 
    
         
             
              # unless you explicitly set `forward_headers: true`
         
     | 
| 
       46 
46 
     | 
    
         
             
              #
         
     | 
| 
       47 
47 
     | 
    
         
             
              # Defaults to `false`:
         
     | 
| 
       48 
     | 
    
         
            -
              ssl:  
     | 
| 
      
 48 
     | 
    
         
            +
              ssl: true
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
50 
     | 
    
         
             
              # Custom SSL certificate
         
     | 
| 
       51 
51 
     | 
    
         
             
              #
         
     | 
| 
       52 
52 
     | 
    
         
             
              # In some cases, using Let's Encrypt for automatic certificate management is not an
         
     | 
| 
       53 
     | 
    
         
            -
              # option, for example if you are running from  
     | 
| 
       54 
     | 
    
         
            -
              # have SSL certificates issued by a different Certificate Authority (CA).
         
     | 
| 
       55 
     | 
    
         
            -
              # Kamal supports loading custom SSL certificates
         
     | 
| 
       56 
     | 
    
         
            -
              # directly from secrets.
         
     | 
| 
       57 
     | 
    
         
            -
              #
         
     | 
| 
       58 
     | 
    
         
            -
              # Examples:
         
     | 
| 
       59 
     | 
    
         
            -
              #   ssl: true              # Enable SSL with Let's Encrypt
         
     | 
| 
       60 
     | 
    
         
            -
              #   ssl: false             # Disable SSL
         
     | 
| 
       61 
     | 
    
         
            -
              #   ssl:                   # Enable custom SSL
         
     | 
| 
       62 
     | 
    
         
            -
              #     certificate_pem: CERTIFICATE_PEM
         
     | 
| 
       63 
     | 
    
         
            -
              #     private_key_pem: PRIVATE_KEY_PEM
         
     | 
| 
      
 53 
     | 
    
         
            +
              # option, for example if you are running from more than one host.
         
     | 
| 
       64 
54 
     | 
    
         
             
              #
         
     | 
| 
      
 55 
     | 
    
         
            +
              # Or you may already have SSL certificates issued by a different Certificate Authority (CA).
         
     | 
| 
      
 56 
     | 
    
         
            +
              #
         
     | 
| 
      
 57 
     | 
    
         
            +
              # Kamal supports loading custom SSL certificates directly from secrets. You should
         
     | 
| 
      
 58 
     | 
    
         
            +
              # pass a hash mapping the `certificate_pem` and `private_key_pem` to the secret names.
         
     | 
| 
      
 59 
     | 
    
         
            +
              ssl:
         
     | 
| 
      
 60 
     | 
    
         
            +
                certificate_pem: CERTIFICATE_PEM
         
     | 
| 
      
 61 
     | 
    
         
            +
                private_key_pem: PRIVATE_KEY_PEM
         
     | 
| 
       65 
62 
     | 
    
         
             
              # ### Notes
         
     | 
| 
       66 
     | 
    
         
            -
              # - If the certificate or key is missing or invalid,  
     | 
| 
       67 
     | 
    
         
            -
              # - Always handle SSL certificates and private keys securely. Avoid hard-coding them in  
     | 
| 
       68 
     | 
    
         
            -
              # - For automated certificate management, consider using the built-in Let's Encrypt integration instead.
         
     | 
| 
      
 63 
     | 
    
         
            +
              # - If the certificate or key is missing or invalid, deployments will fail.
         
     | 
| 
      
 64 
     | 
    
         
            +
              # - Always handle SSL certificates and private keys securely. Avoid hard-coding them in source control.
         
     | 
| 
       69 
65 
     | 
    
         | 
| 
       70 
66 
     | 
    
         
             
              # SSL redirect
         
     | 
| 
       71 
67 
     | 
    
         
             
              #
         
     | 
| 
         @@ -93,9 +89,21 @@ proxy: 
     | 
|
| 
       93 
89 
     | 
    
         
             
              #
         
     | 
| 
       94 
90 
     | 
    
         
             
              # For applications that split their traffic to different services based on the request path,
         
     | 
| 
       95 
91 
     | 
    
         
             
              # you can use path-based routing to mount services under different path prefixes.
         
     | 
| 
       96 
     | 
    
         
            -
              path_prefix: '/api'
         
     | 
| 
      
 92 
     | 
    
         
            +
              # Usage sample: path_prefix: '/api'
         
     | 
| 
      
 93 
     | 
    
         
            +
              #
         
     | 
| 
      
 94 
     | 
    
         
            +
              # You can also specify multiple paths in two ways.
         
     | 
| 
      
 95 
     | 
    
         
            +
              #
         
     | 
| 
      
 96 
     | 
    
         
            +
              # When using path_prefix you can supply multiple routes separated by commas.
         
     | 
| 
      
 97 
     | 
    
         
            +
              path_prefix: "/api,/oauth_callback"
         
     | 
| 
      
 98 
     | 
    
         
            +
              # You can also specify paths as a list of paths, the configuration will be
         
     | 
| 
      
 99 
     | 
    
         
            +
              # rolled together into a comma separated string.
         
     | 
| 
      
 100 
     | 
    
         
            +
              path_prefixes:
         
     | 
| 
      
 101 
     | 
    
         
            +
                - "/api"
         
     | 
| 
      
 102 
     | 
    
         
            +
                - "/oauth_callback"
         
     | 
| 
       97 
103 
     | 
    
         
             
              # By default, the path prefix will be stripped from the request before it is forwarded upstream.
         
     | 
| 
      
 104 
     | 
    
         
            +
              #
         
     | 
| 
       98 
105 
     | 
    
         
             
              # So in the example above, a request to /api/users/123 will be forwarded to web-1 as /users/123.
         
     | 
| 
      
 106 
     | 
    
         
            +
              #
         
     | 
| 
       99 
107 
     | 
    
         
             
              # To instead forward the request with the original path (including the prefix),
         
     | 
| 
       100 
108 
     | 
    
         
             
              # specify --strip-path-prefix=false
         
     | 
| 
       101 
109 
     | 
    
         
             
              strip_path_prefix: false
         
     | 
| 
         @@ -63,6 +63,10 @@ class Kamal::Configuration::Proxy 
     | 
|
| 
       63 
63 
     | 
    
         
             
                tls_path(config.proxy_boot.tls_container_directory, "key.pem") if custom_ssl_certificate?
         
     | 
| 
       64 
64 
     | 
    
         
             
              end
         
     | 
| 
       65 
65 
     | 
    
         | 
| 
      
 66 
     | 
    
         
            +
              def path_prefixes
         
     | 
| 
      
 67 
     | 
    
         
            +
                proxy_config["path_prefixes"] || proxy_config["path_prefix"]&.split(",") || []
         
     | 
| 
      
 68 
     | 
    
         
            +
              end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
       66 
70 
     | 
    
         
             
              def deploy_options
         
     | 
| 
       67 
71 
     | 
    
         
             
                {
         
     | 
| 
       68 
72 
     | 
    
         
             
                  host: hosts,
         
     | 
| 
         @@ -80,7 +84,7 @@ class Kamal::Configuration::Proxy 
     | 
|
| 
       80 
84 
     | 
    
         
             
                  "buffer-memory": proxy_config.dig("buffering", "memory"),
         
     | 
| 
       81 
85 
     | 
    
         
             
                  "max-request-body": proxy_config.dig("buffering", "max_request_body"),
         
     | 
| 
       82 
86 
     | 
    
         
             
                  "max-response-body": proxy_config.dig("buffering", "max_response_body"),
         
     | 
| 
       83 
     | 
    
         
            -
                  "path-prefix":  
     | 
| 
      
 87 
     | 
    
         
            +
                  "path-prefix": path_prefixes,
         
     | 
| 
       84 
88 
     | 
    
         
             
                  "strip-path-prefix": proxy_config.dig("strip_path_prefix"),
         
     | 
| 
       85 
89 
     | 
    
         
             
                  "forward-headers": proxy_config.dig("forward_headers"),
         
     | 
| 
       86 
90 
     | 
    
         
             
                  "tls-redirect": proxy_config.dig("ssl_redirect"),
         
     | 
| 
         @@ -19,6 +19,14 @@ class Kamal::Configuration::Registry 
     | 
|
| 
       19 
19 
     | 
    
         
             
                lookup("password")
         
     | 
| 
       20 
20 
     | 
    
         
             
              end
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
      
 22 
     | 
    
         
            +
              def local?
         
     | 
| 
      
 23 
     | 
    
         
            +
                server.to_s.match?("^localhost[:$]")
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              def local_port
         
     | 
| 
      
 27 
     | 
    
         
            +
                local? ? (server.split(":").last.to_i || 80) : nil
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
       22 
30 
     | 
    
         
             
              private
         
     | 
| 
       23 
31 
     | 
    
         
             
                attr_reader :registry_config, :secrets
         
     | 
| 
       24 
32 
     | 
    
         | 
| 
         @@ -15,10 +15,12 @@ class Kamal::Configuration::Validator::Registry < Kamal::Configuration::Validato 
     | 
|
| 
       15 
15 
     | 
    
         
             
                  with_context(key) do
         
     | 
| 
       16 
16 
     | 
    
         
             
                    value = config[key]
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
                     
     | 
| 
      
 18 
     | 
    
         
            +
                    unless config["server"]&.match?("^localhost[:$]")
         
     | 
| 
      
 19 
     | 
    
         
            +
                      error "is required" unless value.present?
         
     | 
| 
       19 
20 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 21 
     | 
    
         
            +
                      unless value.is_a?(String) || (value.is_a?(Array) && value.size == 1 && value.first.is_a?(String))
         
     | 
| 
      
 22 
     | 
    
         
            +
                        error "should be a string or an array with one string (for secret lookup)"
         
     | 
| 
      
 23 
     | 
    
         
            +
                      end
         
     | 
| 
       22 
24 
     | 
    
         
             
                    end
         
     | 
| 
       23 
25 
     | 
    
         
             
                  end
         
     | 
| 
       24 
26 
     | 
    
         
             
                end
         
     | 
| 
         @@ -24,11 +24,11 @@ class Kamal::Configuration::Validator 
     | 
|
| 
       24 
24 
     | 
    
         
             
                        example_value = example[key]
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                        if example_value == "..."
         
     | 
| 
       27 
     | 
    
         
            -
                           
     | 
| 
       28 
     | 
    
         
            -
                            validate_type! value, TrueClass, FalseClass, Hash
         
     | 
| 
       29 
     | 
    
         
            -
                          elsif key.to_s != "proxy" || !boolean?(value.class)
         
     | 
| 
      
 27 
     | 
    
         
            +
                          unless key.to_s == "proxy" && boolean?(value.class)
         
     | 
| 
       30 
28 
     | 
    
         
             
                            validate_type! value, *(Array if key == :servers), Hash
         
     | 
| 
       31 
29 
     | 
    
         
             
                          end
         
     | 
| 
      
 30 
     | 
    
         
            +
                        elsif key.to_s == "ssl"
         
     | 
| 
      
 31 
     | 
    
         
            +
                            validate_type! value, TrueClass, FalseClass, Hash
         
     | 
| 
       32 
32 
     | 
    
         
             
                        elsif key == "hosts"
         
     | 
| 
       33 
33 
     | 
    
         
             
                          validate_servers! value
         
     | 
| 
       34 
34 
     | 
    
         
             
                        elsif example_value.is_a?(Array)
         
     | 
    
        data/lib/kamal/configuration.rb
    CHANGED
    
    | 
         @@ -6,7 +6,7 @@ require "erb" 
     | 
|
| 
       6 
6 
     | 
    
         
             
            require "net/ssh/proxy/jump"
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            class Kamal::Configuration
         
     | 
| 
       9 
     | 
    
         
            -
              delegate :service, : 
     | 
| 
      
 9 
     | 
    
         
            +
              delegate :service, :labels, :hooks_path, to: :raw_config, allow_nil: true
         
     | 
| 
       10 
10 
     | 
    
         
             
              delegate :argumentize, :optionize, to: Kamal::Utils
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
              attr_reader :destination, :raw_config, :secrets
         
     | 
| 
         @@ -157,6 +157,13 @@ class Kamal::Configuration 
     | 
|
| 
       157 
157 
     | 
    
         
             
                (proxy_roles.flat_map(&:hosts) + proxy_accessories.flat_map(&:hosts)).uniq
         
     | 
| 
       158 
158 
     | 
    
         
             
              end
         
     | 
| 
       159 
159 
     | 
    
         | 
| 
      
 160 
     | 
    
         
            +
              def image
         
     | 
| 
      
 161 
     | 
    
         
            +
                name = raw_config&.image.presence
         
     | 
| 
      
 162 
     | 
    
         
            +
                name ||= raw_config&.service if registry.local?
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                name
         
     | 
| 
      
 165 
     | 
    
         
            +
              end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
       160 
167 
     | 
    
         
             
              def repository
         
     | 
| 
       161 
168 
     | 
    
         
             
                [ registry.server, image ].compact.join("/")
         
     | 
| 
       162 
169 
     | 
    
         
             
              end
         
     | 
| 
         @@ -282,10 +289,12 @@ class Kamal::Configuration 
     | 
|
| 
       282 
289 
     | 
    
         
             
                end
         
     | 
| 
       283 
290 
     | 
    
         | 
| 
       284 
291 
     | 
    
         
             
                def ensure_required_keys_present
         
     | 
| 
       285 
     | 
    
         
            -
                  %i[ service  
     | 
| 
      
 292 
     | 
    
         
            +
                  %i[ service registry ].each do |key|
         
     | 
| 
       286 
293 
     | 
    
         
             
                    raise Kamal::ConfigurationError, "Missing required configuration for #{key}" unless raw_config[key].present?
         
     | 
| 
       287 
294 
     | 
    
         
             
                  end
         
     | 
| 
       288 
295 
     | 
    
         | 
| 
      
 296 
     | 
    
         
            +
                  raise Kamal::ConfigurationError, "Missing required configuration for image" if image.blank?
         
     | 
| 
      
 297 
     | 
    
         
            +
             
     | 
| 
       289 
298 
     | 
    
         
             
                  if raw_config.servers.nil?
         
     | 
| 
       290 
299 
     | 
    
         
             
                    raise Kamal::ConfigurationError, "No servers or accessories specified" unless raw_config.accessories.present?
         
     | 
| 
       291 
300 
     | 
    
         
             
                  else
         
     | 
| 
         @@ -17,7 +17,7 @@ class Kamal::Secrets::Adapters::OnePassword < Kamal::Secrets::Adapters::Base 
     | 
|
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
                def fetch_secrets(secrets, from:, account:, session:)
         
     | 
| 
       19 
19 
     | 
    
         
             
                  if secrets.blank?
         
     | 
| 
       20 
     | 
    
         
            -
                    fetch_all_secrets(from: from, account: account, session: session) 
     | 
| 
      
 20 
     | 
    
         
            +
                    fetch_all_secrets(from: from, account: account, session: session)
         
     | 
| 
       21 
21 
     | 
    
         
             
                  else
         
     | 
| 
       22 
22 
     | 
    
         
             
                    fetch_specified_secrets(secrets, from: from, account: account, session: session)
         
     | 
| 
       23 
23 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -140,3 +140,11 @@ class SSHKit::Runner::Parallel 
     | 
|
| 
       140 
140 
     | 
    
         | 
| 
       141 
141 
     | 
    
         
             
              prepend CompleteAll
         
     | 
| 
       142 
142 
     | 
    
         
             
            end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
            # Avoid net-ssh debug, until https://github.com/net-ssh/net-ssh/pull/953 is merged
         
     | 
| 
      
 145 
     | 
    
         
            +
            module NetSshForwardingNoPuts
         
     | 
| 
      
 146 
     | 
    
         
            +
              def puts(*)
         
     | 
| 
      
 147 
     | 
    
         
            +
              end
         
     | 
| 
      
 148 
     | 
    
         
            +
            end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
            Net::SSH::Service::Forward.prepend NetSshForwardingNoPuts
         
     | 
    
        data/lib/kamal/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: kamal
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 2. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 2.8.1
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - David Heinemeier Hansson
         
     | 
| 
         @@ -229,6 +229,7 @@ files: 
     | 
|
| 
       229 
229 
     | 
    
         
             
            - lib/kamal/cli/healthcheck/poller.rb
         
     | 
| 
       230 
230 
     | 
    
         
             
            - lib/kamal/cli/lock.rb
         
     | 
| 
       231 
231 
     | 
    
         
             
            - lib/kamal/cli/main.rb
         
     | 
| 
      
 232 
     | 
    
         
            +
            - lib/kamal/cli/port_forwarding.rb
         
     | 
| 
       232 
233 
     | 
    
         
             
            - lib/kamal/cli/proxy.rb
         
     | 
| 
       233 
234 
     | 
    
         
             
            - lib/kamal/cli/prune.rb
         
     | 
| 
       234 
235 
     | 
    
         
             
            - lib/kamal/cli/registry.rb
         
     | 
| 
         @@ -355,7 +356,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       355 
356 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       356 
357 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       357 
358 
     | 
    
         
             
            requirements: []
         
     | 
| 
       358 
     | 
    
         
            -
            rubygems_version: 3.6. 
     | 
| 
      
 359 
     | 
    
         
            +
            rubygems_version: 3.6.9
         
     | 
| 
       359 
360 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       360 
361 
     | 
    
         
             
            summary: Deploy web apps in containers to servers running Docker with zero downtime.
         
     | 
| 
       361 
362 
     | 
    
         
             
            test_files: []
         
     |