kuby-core 0.11.16 → 0.12.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 +4 -4
- data/CHANGELOG.md +18 -0
- data/Gemfile +1 -2
- data/kuby-core.gemspec +1 -1
- data/lib/kuby.rb +2 -20
- data/lib/kuby/commands.rb +13 -67
- data/lib/kuby/docker.rb +27 -25
- data/lib/kuby/docker/alpine.rb +2 -1
- data/lib/kuby/docker/app_image.rb +19 -0
- data/lib/kuby/docker/cli.rb +4 -12
- data/lib/kuby/docker/docker_uri.rb +18 -7
- data/lib/kuby/docker/errors.rb +1 -19
- data/lib/kuby/docker/image.rb +115 -0
- data/lib/kuby/docker/layer.rb +0 -7
- data/lib/kuby/docker/local_tags.rb +9 -10
- data/lib/kuby/docker/package_phase.rb +0 -5
- data/lib/kuby/docker/packages.rb +1 -0
- data/lib/kuby/docker/remote_tags.rb +10 -5
- data/lib/kuby/docker/setup_phase.rb +17 -9
- data/lib/kuby/docker/spec.rb +29 -62
- data/lib/kuby/docker/timestamp_tag.rb +8 -1
- data/lib/kuby/docker/timestamped_image.rb +113 -0
- data/lib/kuby/environment.rb +1 -10
- data/lib/kuby/kubernetes/bare_metal_provider.rb +16 -4
- data/lib/kuby/kubernetes/deployer.rb +1 -1
- data/lib/kuby/kubernetes/docker_desktop_provider.rb +0 -15
- data/lib/kuby/kubernetes/spec.rb +21 -17
- data/lib/kuby/plugin.rb +2 -2
- data/lib/kuby/plugins/rails_app.rb +1 -0
- data/lib/kuby/plugins/rails_app/assets.rb +60 -70
- data/lib/kuby/plugins/rails_app/assets_image.rb +55 -0
- data/lib/kuby/plugins/rails_app/plugin.rb +53 -236
- data/lib/kuby/tasks.rb +30 -69
- data/lib/kuby/version.rb +1 -1
- data/spec/docker/spec_spec.rb +9 -118
- data/spec/docker/timestamped_image_spec.rb +123 -0
- data/spec/spec_helper.rb +10 -11
- metadata +9 -10
- data/lib/kuby/dev_setup.rb +0 -346
- data/lib/kuby/docker/dev_spec.rb +0 -202
- data/lib/kuby/docker/metadata.rb +0 -90
- data/lib/kuby/docker/tags.rb +0 -92
- data/lib/kuby/rails_commands.rb +0 -84
- data/spec/docker/metadata_spec.rb +0 -73
| @@ -0,0 +1,115 @@ | |
| 1 | 
            +
            # typed: strict
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Kuby
         | 
| 4 | 
            +
              module Docker
         | 
| 5 | 
            +
                class Image
         | 
| 6 | 
            +
                  extend T::Sig
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  sig { returns(String) }
         | 
| 9 | 
            +
                  attr_reader :image_url
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  sig { returns(Credentials) }
         | 
| 12 | 
            +
                  attr_reader :credentials
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  sig { returns(T.nilable(String)) }
         | 
| 15 | 
            +
                  attr_reader :main_tag
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  sig { returns T::Array[String] }
         | 
| 18 | 
            +
                  attr_reader :alias_tags
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  sig {
         | 
| 21 | 
            +
                    params(
         | 
| 22 | 
            +
                      dockerfile: T.any(Dockerfile, T.proc.returns(Dockerfile)),
         | 
| 23 | 
            +
                      image_url: String,
         | 
| 24 | 
            +
                      credentials: Credentials,
         | 
| 25 | 
            +
                      main_tag: T.nilable(String),
         | 
| 26 | 
            +
                      alias_tags: T::Array[String]
         | 
| 27 | 
            +
                    ).void
         | 
| 28 | 
            +
                  }
         | 
| 29 | 
            +
                  def initialize(dockerfile, image_url, credentials, main_tag = nil, alias_tags = [])
         | 
| 30 | 
            +
                    @dockerfile = T.let(dockerfile, T.any(Dockerfile, T.proc.returns(Dockerfile)))
         | 
| 31 | 
            +
                    @image_url = T.let(image_url, String)
         | 
| 32 | 
            +
                    @credentials = T.let(credentials, Credentials)
         | 
| 33 | 
            +
                    @main_tag = T.let(main_tag, T.nilable(String))
         | 
| 34 | 
            +
                    @alias_tags = T.let(alias_tags, T::Array[String])
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    @image_host = T.let(@image_host, T.nilable(String))
         | 
| 37 | 
            +
                    @image_hostname = T.let(@image_hostname, T.nilable(String))
         | 
| 38 | 
            +
                    @image_repo = T.let(@image_repo, T.nilable(String))
         | 
| 39 | 
            +
                    @full_image_uri = T.let(@full_image_uri, T.nilable(DockerURI))
         | 
| 40 | 
            +
                    @docker_cli = T.let(@docker_cli, T.nilable(Docker::CLI))
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  sig { returns(Image) }
         | 
| 44 | 
            +
                  def new_version
         | 
| 45 | 
            +
                    raise NotImplementedError, 'please use a Docker::Image subclass'
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  sig { returns(Image) }
         | 
| 49 | 
            +
                  def current_version
         | 
| 50 | 
            +
                    raise NotImplementedError, 'please use a Docker::Image subclass'
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  sig { params(current_tag: T.nilable(String)).returns(Image) }
         | 
| 54 | 
            +
                  def previous_version(current_tag = nil)
         | 
| 55 | 
            +
                    raise NotImplementedError, 'please use a Docker::Image subclass'
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  sig { returns(Dockerfile) }
         | 
| 59 | 
            +
                  def dockerfile
         | 
| 60 | 
            +
                    if @dockerfile.respond_to?(:call)
         | 
| 61 | 
            +
                      T.cast(@dockerfile, T.proc.returns(Dockerfile)).call
         | 
| 62 | 
            +
                    else
         | 
| 63 | 
            +
                      T.cast(@dockerfile, Dockerfile)
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  sig { returns(String) }
         | 
| 68 | 
            +
                  def image_host
         | 
| 69 | 
            +
                    @image_host ||= "#{image_uri.host}:#{image_uri.port}"
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  sig { returns(String) }
         | 
| 73 | 
            +
                  def image_hostname
         | 
| 74 | 
            +
                    @image_hostname ||= image_uri.host
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  sig { returns(String) }
         | 
| 78 | 
            +
                  def image_repo
         | 
| 79 | 
            +
                    @image_repo ||= image_uri.path
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  sig { returns(DockerURI) }
         | 
| 83 | 
            +
                  def image_uri
         | 
| 84 | 
            +
                    @full_image_uri ||= DockerURI.parse(image_url)
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  sig { returns(T::Array[String]) }
         | 
| 88 | 
            +
                  def tags
         | 
| 89 | 
            +
                    [main_tag, *alias_tags].compact
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  sig { params(build_args: T::Hash[String, String]).void }
         | 
| 93 | 
            +
                  def build(build_args = {})
         | 
| 94 | 
            +
                    raise NotImplementedError, 'please use a Docker::Image subclass'
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  sig { params(tag: String).void }
         | 
| 98 | 
            +
                  def push(tag)
         | 
| 99 | 
            +
                    raise NotImplementedError, 'please use a Docker::Image subclass'
         | 
| 100 | 
            +
                  end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  sig { returns(Docker::CLI) }
         | 
| 103 | 
            +
                  def docker_cli
         | 
| 104 | 
            +
                    @docker_cli ||= Docker::CLI.new
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  private
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  sig { params(main_tag: String, alias_tags: T::Array[String]).returns(Image) }
         | 
| 110 | 
            +
                  def duplicate_with_tags(main_tag, alias_tags)
         | 
| 111 | 
            +
                    self.class.new(dockerfile, image_url, credentials, main_tag, alias_tags)
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
              end
         | 
| 115 | 
            +
            end
         | 
    
        data/lib/kuby/docker/layer.rb
    CHANGED
    
    
| @@ -8,37 +8,36 @@ module Kuby | |
| 8 8 | 
             
                  sig { returns CLI }
         | 
| 9 9 | 
             
                  attr_reader :cli
         | 
| 10 10 |  | 
| 11 | 
            -
                  sig { returns( | 
| 12 | 
            -
                  attr_reader : | 
| 11 | 
            +
                  sig { returns(String) }
         | 
| 12 | 
            +
                  attr_reader :image_url
         | 
| 13 13 |  | 
| 14 14 | 
             
                  sig {
         | 
| 15 15 | 
             
                    params(
         | 
| 16 16 | 
             
                      cli: CLI,
         | 
| 17 | 
            -
                       | 
| 17 | 
            +
                      image_url: String
         | 
| 18 18 | 
             
                    )
         | 
| 19 19 | 
             
                    .void
         | 
| 20 20 | 
             
                  }
         | 
| 21 | 
            -
                  def initialize(cli,  | 
| 21 | 
            +
                  def initialize(cli, image_url)
         | 
| 22 22 | 
             
                    @cli = cli
         | 
| 23 | 
            -
                    @ | 
| 24 | 
            -
             | 
| 23 | 
            +
                    @image_url = image_url
         | 
| 25 24 | 
             
                    @latest_timestamp_tag = T.let(@latest_timestamp_tag, T.nilable(TimestampTag))
         | 
| 26 25 | 
             
                  end
         | 
| 27 26 |  | 
| 28 27 | 
             
                  sig { returns(T::Array[String]) }
         | 
| 29 28 | 
             
                  def tags
         | 
| 30 | 
            -
                    images = cli.images( | 
| 29 | 
            +
                    images = cli.images(image_url)
         | 
| 31 30 | 
             
                    images.map { |image| T.must(image[:tag]) }
         | 
| 32 31 | 
             
                  end
         | 
| 33 32 |  | 
| 34 33 | 
             
                  sig { returns(T::Array[String]) }
         | 
| 35 34 | 
             
                  def latest_tags
         | 
| 36 35 | 
             
                    # find "latest" tag
         | 
| 37 | 
            -
                    images = cli.images( | 
| 38 | 
            -
                    latest = images.find { |image| image[:tag] ==  | 
| 36 | 
            +
                    images = cli.images(image_url)
         | 
| 37 | 
            +
                    latest = images.find { |image| image[:tag] == Kuby::Docker::LATEST_TAG }
         | 
| 39 38 |  | 
| 40 39 | 
             
                    unless latest
         | 
| 41 | 
            -
                      raise MissingTagError | 
| 40 | 
            +
                      raise MissingTagError, "could not find tag #{Kuby::Docker::LATEST_TAG}"
         | 
| 42 41 | 
             
                    end
         | 
| 43 42 |  | 
| 44 43 | 
             
                    # find all tags that point to the same image as 'latest'
         | 
    
        data/lib/kuby/docker/packages.rb
    CHANGED
    
    
| @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # typed: strict
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'docker/remote'
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module Kuby
         | 
| 4 6 | 
             
              module Docker
         | 
| 5 7 | 
             
                class RemoteTags
         | 
| @@ -8,24 +10,27 @@ module Kuby | |
| 8 10 | 
             
                  sig { returns(::Docker::Remote::Client) }
         | 
| 9 11 | 
             
                  attr_reader :remote_client
         | 
| 10 12 |  | 
| 11 | 
            -
                  sig { returns( | 
| 12 | 
            -
                  attr_reader : | 
| 13 | 
            +
                  sig { returns(String) }
         | 
| 14 | 
            +
                  attr_reader :image_url
         | 
| 13 15 |  | 
| 14 16 | 
             
                  sig {
         | 
| 15 17 | 
             
                    params(
         | 
| 16 18 | 
             
                      remote_client: ::Docker::Remote::Client,
         | 
| 17 | 
            -
                       | 
| 19 | 
            +
                      image_url: String
         | 
| 18 20 | 
             
                    )
         | 
| 19 21 | 
             
                    .void
         | 
| 20 22 | 
             
                  }
         | 
| 21 | 
            -
                  def initialize(remote_client,  | 
| 23 | 
            +
                  def initialize(remote_client, image_url)
         | 
| 22 24 | 
             
                    @remote_client = remote_client
         | 
| 23 | 
            -
                    @ | 
| 25 | 
            +
                    @image_url = image_url
         | 
| 24 26 | 
             
                  end
         | 
| 25 27 |  | 
| 26 28 | 
             
                  sig { returns(T::Array[String]) }
         | 
| 27 29 | 
             
                  def tags
         | 
| 28 30 | 
             
                    remote_client.tags
         | 
| 31 | 
            +
                  rescue ::Docker::Remote::UnknownRepoError
         | 
| 32 | 
            +
                    # this can happen if we've never pushed to the repo before
         | 
| 33 | 
            +
                    []
         | 
| 29 34 | 
             
                  end
         | 
| 30 35 |  | 
| 31 36 | 
             
                  sig { returns(T::Array[String]) }
         | 
| @@ -7,9 +7,6 @@ module Kuby | |
| 7 7 |  | 
| 8 8 | 
             
                  DEFAULT_WORKING_DIR = T.let('/usr/src/app'.freeze, String)
         | 
| 9 9 |  | 
| 10 | 
            -
                  sig { returns(T.nilable(String)) }
         | 
| 11 | 
            -
                  attr_reader :base_image
         | 
| 12 | 
            -
             | 
| 13 10 | 
             
                  sig { params(base_image: String).void }
         | 
| 14 11 | 
             
                  attr_writer :base_image
         | 
| 15 12 |  | 
| @@ -25,35 +22,46 @@ module Kuby | |
| 25 22 | 
             
                  sig { params(rails_env: String).void }
         | 
| 26 23 | 
             
                  attr_writer :rails_env
         | 
| 27 24 |  | 
| 28 | 
            -
                  sig {  | 
| 29 | 
            -
                   | 
| 30 | 
            -
             | 
| 25 | 
            +
                  sig { returns(T.nilable(Docker::Spec)) }
         | 
| 26 | 
            +
                  attr_reader :docker_spec
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  sig { params(environment: Environment, docker_spec: Docker::Spec).void }
         | 
| 29 | 
            +
                  def initialize(environment, docker_spec)
         | 
| 30 | 
            +
                    super(environment)
         | 
| 31 31 |  | 
| 32 32 | 
             
                    @base_image = T.let(@base_image, T.nilable(String))
         | 
| 33 33 | 
             
                    @working_dir = T.let(@working_dir, T.nilable(String))
         | 
| 34 34 | 
             
                    @rails_env = T.let(@rails_env, T.nilable(String))
         | 
| 35 | 
            +
                    @docker_spec = T.let(docker_spec, T.nilable(Docker::Spec))
         | 
| 35 36 | 
             
                  end
         | 
| 36 37 |  | 
| 37 38 | 
             
                  sig { override.params(dockerfile: Dockerfile).void }
         | 
| 38 39 | 
             
                  def apply_to(dockerfile)
         | 
| 39 | 
            -
                    dockerfile.from(base_image | 
| 40 | 
            +
                    dockerfile.from(base_image)
         | 
| 40 41 | 
             
                    dockerfile.workdir(working_dir || DEFAULT_WORKING_DIR)
         | 
| 41 42 | 
             
                    dockerfile.env("RAILS_ENV=#{rails_env || Kuby.env}")
         | 
| 42 43 | 
             
                    dockerfile.env("KUBY_ENV=#{Kuby.env}")
         | 
| 43 44 | 
             
                    dockerfile.arg('RAILS_MASTER_KEY')
         | 
| 44 45 | 
             
                  end
         | 
| 45 46 |  | 
| 47 | 
            +
                  sig { returns(String) }
         | 
| 48 | 
            +
                  def base_image
         | 
| 49 | 
            +
                    @base_image || default_base_image
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 46 52 | 
             
                  private
         | 
| 47 53 |  | 
| 48 54 | 
             
                  sig { returns(String) }
         | 
| 49 55 | 
             
                  def default_base_image
         | 
| 50 | 
            -
                     | 
| 56 | 
            +
                    spec = T.must(docker_spec)
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    case spec.distro_name
         | 
| 51 59 | 
             
                      when :debian
         | 
| 52 60 | 
             
                        "ruby:#{RUBY_VERSION}"
         | 
| 53 61 | 
             
                      when :alpine
         | 
| 54 62 | 
             
                        "ruby:#{RUBY_VERSION}-alpine"
         | 
| 55 63 | 
             
                      else
         | 
| 56 | 
            -
                        raise MissingDistroError, "distro '#{ | 
| 64 | 
            +
                        raise MissingDistroError, "distro '#{spec.distro_name}' hasn't been registered"
         | 
| 57 65 | 
             
                    end
         | 
| 58 66 | 
             
                  end
         | 
| 59 67 | 
             
                end
         | 
    
        data/lib/kuby/docker/spec.rb
    CHANGED
    
    | @@ -1,15 +1,18 @@ | |
| 1 1 | 
             
            # typed: strict
         | 
| 2 2 |  | 
| 3 | 
            -
            require 'docker/remote'
         | 
| 4 | 
            -
             | 
| 5 3 | 
             
            module Kuby
         | 
| 6 4 | 
             
              module Docker
         | 
| 7 5 | 
             
                class Spec
         | 
| 8 6 | 
             
                  extend T::Sig
         | 
| 9 7 |  | 
| 8 | 
            +
                  DEFAULT_DISTRO = :debian
         | 
| 9 | 
            +
             | 
| 10 10 | 
             
                  sig { returns(Environment) }
         | 
| 11 11 | 
             
                  attr_reader :environment
         | 
| 12 12 |  | 
| 13 | 
            +
                  sig { returns(T.nilable(String)) }
         | 
| 14 | 
            +
                  attr_reader :image_url_str
         | 
| 15 | 
            +
             | 
| 13 16 | 
             
                  sig { params(environment: Environment).void }
         | 
| 14 17 | 
             
                  def initialize(environment)
         | 
| 15 18 | 
             
                    @environment = environment
         | 
| @@ -22,13 +25,18 @@ module Kuby | |
| 22 25 | 
             
                    @copy_phase = T.let(@copy_phase, T.nilable(CopyPhase))
         | 
| 23 26 | 
             
                    @assets_phase = T.let(@assets_phase, T.nilable(AssetsPhase))
         | 
| 24 27 | 
             
                    @webserver_phase = T.let(@webserver_phase, T.nilable(WebserverPhase))
         | 
| 25 | 
            -
                    @metadata = T.let(@metadata, T.nilable(Metadata))
         | 
| 26 28 |  | 
| 29 | 
            +
                    @distro_name = T.let(@distro_name, T.nilable(Symbol))
         | 
| 27 30 | 
             
                    @distro_spec = T.let(@distro_spec, T.nilable(Distro))
         | 
| 28 | 
            -
                    @cli = T.let(@cli, T.nilable(CLI))
         | 
| 29 | 
            -
                    @remote_client = T.let(@remote_client, T.nilable(::Docker::Remote::Client))
         | 
| 30 | 
            -
                    @tags = T.let(@tags, T.nilable(Tags))
         | 
| 31 31 | 
             
                    @layer_stack = T.let(@layer_stack, T.nilable(Kuby::Docker::LayerStack))
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    @image_url_str = T.let(@image_url_str, T.nilable(String))
         | 
| 34 | 
            +
                    @image = T.let(@image, T.nilable(Docker::AppImage))
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  sig { returns(Symbol) }
         | 
| 38 | 
            +
                  def distro_name
         | 
| 39 | 
            +
                    @distro_name || DEFAULT_DISTRO
         | 
| 32 40 | 
             
                  end
         | 
| 33 41 |  | 
| 34 42 | 
             
                  sig { params(image_url: String).void }
         | 
| @@ -69,7 +77,7 @@ module Kuby | |
| 69 77 |  | 
| 70 78 | 
             
                  sig { params(distro_name: Symbol).void }
         | 
| 71 79 | 
             
                  def distro(distro_name)
         | 
| 72 | 
            -
                     | 
| 80 | 
            +
                    @distro_name = distro_name
         | 
| 73 81 | 
             
                    @distro_spec = nil
         | 
| 74 82 | 
             
                  end
         | 
| 75 83 |  | 
| @@ -85,7 +93,7 @@ module Kuby | |
| 85 93 |  | 
| 86 94 | 
             
                  sig { params(url: String).void }
         | 
| 87 95 | 
             
                  def image_url(url)
         | 
| 88 | 
            -
                     | 
| 96 | 
            +
                    @image_url_str = url
         | 
| 89 97 | 
             
                  end
         | 
| 90 98 |  | 
| 91 99 | 
             
                  sig {
         | 
| @@ -132,16 +140,22 @@ module Kuby | |
| 132 140 | 
             
                    @credentials
         | 
| 133 141 | 
             
                  end
         | 
| 134 142 |  | 
| 135 | 
            -
                  sig { returns( | 
| 136 | 
            -
                  def  | 
| 137 | 
            -
                     | 
| 138 | 
            -
                       | 
| 143 | 
            +
                  sig { returns(Docker::AppImage) }
         | 
| 144 | 
            +
                  def image
         | 
| 145 | 
            +
                    @image ||= begin
         | 
| 146 | 
            +
                      dockerfile = Dockerfile.new.tap do |df|
         | 
| 147 | 
            +
                        layer_stack.each { |layer| layer.apply_to(df) }
         | 
| 148 | 
            +
                      end
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                      Docker::AppImage.new(
         | 
| 151 | 
            +
                        dockerfile, T.must(image_url_str), credentials
         | 
| 152 | 
            +
                      )
         | 
| 139 153 | 
             
                    end
         | 
| 140 154 | 
             
                  end
         | 
| 141 155 |  | 
| 142 156 | 
             
                  sig { returns(SetupPhase) }
         | 
| 143 157 | 
             
                  def setup_phase
         | 
| 144 | 
            -
                    @setup_phase ||= SetupPhase.new(environment)
         | 
| 158 | 
            +
                    @setup_phase ||= SetupPhase.new(environment, self)
         | 
| 145 159 | 
             
                  end
         | 
| 146 160 |  | 
| 147 161 | 
             
                  sig { returns(PackagePhase) }
         | 
| @@ -174,62 +188,15 @@ module Kuby | |
| 174 188 | 
             
                    @webserver_phase ||= WebserverPhase.new(environment)
         | 
| 175 189 | 
             
                  end
         | 
| 176 190 |  | 
| 177 | 
            -
                  sig { returns(Metadata) }
         | 
| 178 | 
            -
                  def metadata
         | 
| 179 | 
            -
                    @metadata ||= Metadata.new(environment)
         | 
| 180 | 
            -
                  end
         | 
| 181 | 
            -
             | 
| 182 | 
            -
                  sig { returns(String) }
         | 
| 183 | 
            -
                  def tag
         | 
| 184 | 
            -
                    t = ENV.fetch('KUBY_DOCKER_TAG') do
         | 
| 185 | 
            -
                      tags.latest_timestamp_tag
         | 
| 186 | 
            -
                    end
         | 
| 187 | 
            -
             | 
| 188 | 
            -
                    unless t
         | 
| 189 | 
            -
                      raise MissingTagError, 'could not find latest timestamped tag'
         | 
| 190 | 
            -
                    end
         | 
| 191 | 
            -
             | 
| 192 | 
            -
                    t.to_s
         | 
| 193 | 
            -
                  end
         | 
| 194 | 
            -
             | 
| 195 | 
            -
                  sig { params(current_tag: String).returns(String) }
         | 
| 196 | 
            -
                  def previous_tag(current_tag)
         | 
| 197 | 
            -
                    t = tags.previous_timestamp_tag(current_tag)
         | 
| 198 | 
            -
             | 
| 199 | 
            -
                    unless t
         | 
| 200 | 
            -
                      raise MissingTagError, 'could not find previous timestamped tag'
         | 
| 201 | 
            -
                    end
         | 
| 202 | 
            -
             | 
| 203 | 
            -
                    t.to_s
         | 
| 204 | 
            -
                  end
         | 
| 205 | 
            -
             | 
| 206 | 
            -
                  sig { returns(CLI) }
         | 
| 207 | 
            -
                  def cli
         | 
| 208 | 
            -
                    @cli ||= Docker::CLI.new
         | 
| 209 | 
            -
                  end
         | 
| 210 | 
            -
             | 
| 211 | 
            -
                  sig { returns(::Docker::Remote::Client) }
         | 
| 212 | 
            -
                  def remote_client
         | 
| 213 | 
            -
                    @remote_client ||= ::Docker::Remote::Client.new(
         | 
| 214 | 
            -
                      metadata.image_host, metadata.image_repo,
         | 
| 215 | 
            -
                      credentials.username, credentials.password,
         | 
| 216 | 
            -
                    )
         | 
| 217 | 
            -
                  end
         | 
| 218 | 
            -
             | 
| 219 191 | 
             
                  sig { returns(Distro) }
         | 
| 220 192 | 
             
                  def distro_spec
         | 
| 221 | 
            -
                    @distro_spec ||= if distro_klass = Kuby.distros[ | 
| 193 | 
            +
                    @distro_spec ||= if distro_klass = Kuby.distros[distro_name]
         | 
| 222 194 | 
             
                      distro_klass.new(self)
         | 
| 223 195 | 
             
                    else
         | 
| 224 | 
            -
                      raise MissingDistroError, "distro '#{ | 
| 196 | 
            +
                      raise MissingDistroError, "distro '#{distro_name}' hasn't been registered"
         | 
| 225 197 | 
             
                    end
         | 
| 226 198 | 
             
                  end
         | 
| 227 199 |  | 
| 228 | 
            -
                  sig { returns(Tags) }
         | 
| 229 | 
            -
                  def tags
         | 
| 230 | 
            -
                    @tags ||= Tags.new(cli, remote_client, metadata)
         | 
| 231 | 
            -
                  end
         | 
| 232 | 
            -
             | 
| 233 200 | 
             
                  private
         | 
| 234 201 |  | 
| 235 202 | 
             
                  sig { returns(Kuby::Docker::LayerStack) }
         |