Repository: mrsked/mrsk
File: lib/kamal/cli/build.rb (Ruby)

Code before:
          if e.message =~ /(context not found|no builder|no compatible builder|does not exist)/
            warn "Missing compatible builder, so creating a new one first"
            begin
              cli.remove
            rescue SSHKit::Command::Failed
              raise unless e.message =~ /(context not found|no builder|does not exist)/
            end
            cli.create
          else
            raise
          end
        end

        # Get the command here to ensure the Dir.chdir doesn't interfere with it
        push = KAMAL.builder.push

        KAMAL.with_verbosity(:debug) do
          Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
        end
      end
    end
  end

  desc "pull", "Pull app image from registry onto servers"
  def pull
    Kamal::Cli::PortForwarding.new(KAMAL.hosts, KAMAL.config.registry.local_port).forward do
      if (first_hosts = mirror_hosts).any?
        #  Pull on a single host per mirror first to seed them
        say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
        pull_on_hosts(first_hosts)
        say "Pulling image on remaining hosts...", :magenta
        pull_on_hosts(KAMAL.hosts - first_hosts)
      else
        pull_on_hosts(KAMAL.hosts)
      end
    end
  end

  desc "create", "Create a build setup"
  def create
    if (remote_host = KAMAL.config.builder.remote)
      connect_to_remote_host(remote_host)
    end

    run_locally do
      begin
        debug "Using builder: #{KAMAL.builder.name}"
        execute *KAMAL.builder.create
      rescue SSHKit::Command::Failed => e
        if e.message =~ /stderr=(.*)/
          error "Couldn't create remote builder: #{$1}"

Review comment (refactor):
Thank you guys for working on this feature! 🙏🏼  I was thinking to try it myself 😄 

Minor 2 cents suggestion:
- This line read like forward ports always. While there is condition inside of class implementation. Even with local registry by default it kind of confusing in case of remote registry usage.
- And the condition in the class `if KAMAL.config.registry.local?` is the only part that coupled to registry port. Extracting that condition will make class more general purpose `PortForwarding` to use for any other service port forwarding.

So how about, instead of this line here (and condition in the class), to do something like following with direct condition?

```ruby
    def pull_on_hosts(hosts, forward_registry_port: KAMAL.config.registry.local?)
      if forward_registry_port
        return Kamal::Cli::PortForwarding.new(hosts, KAMAL.config.registry.local_port).forward do
          pull_on_hosts(hosts, forward_registry_port: false)
        end
      end

      ...
    end
```


Code after:
      say "Building with uncommitted changes:\n #{uncommitted_changes}", :yellow
    end

    with_env(KAMAL.config.builder.secrets) do
      run_locally do
        begin
          execute *KAMAL.builder.inspect_builder
        rescue SSHKit::Command::Failed => e
          if e.message =~ /(context not found|no builder|no compatible builder|does not exist)/
            warn "Missing compatible builder, so creating a new one first"
            begin
              cli.remove
            rescue SSHKit::Command::Failed
              raise unless e.message =~ /(context not found|no builder|does not exist)/
            end
            cli.create
          else
            raise
          end
        end

        # Get the command here to ensure the Dir.chdir doesn't interfere with it
        push = KAMAL.builder.push(cli.options[:output], no_cache: cli.options[:no_cache])

        KAMAL.with_verbosity(:debug) do
          Dir.chdir(KAMAL.config.builder.build_directory) { execute *push, env: KAMAL.builder.push_env }
        end
      end
    end
  end

  desc "pull", "Pull app image from registry onto servers"
  def pull
    login_to_registry_remotely unless KAMAL.registry.local?

    forward_local_registry_port do
      if (first_hosts = mirror_hosts).any?
        #  Pull on a single host per mirror first to seed them
        say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
        pull_on_hosts(first_hosts)
        say "Pulling image on remaining hosts...", :magenta
        pull_on_hosts(KAMAL.app_hosts - first_hosts)
      else
        pull_on_hosts(KAMAL.app_hosts)
      end
    end
  end

  desc "create", "Create a build setup"
  def create
    if (remote_host = KAMAL.config.builder.remote)