Merge pull request #916 from nickhammond/buildpacks
Add pack option to the builder options for cloud native buildpacks
This commit is contained in:
@@ -84,6 +84,10 @@ module Kamal::Commands
|
|||||||
args.compact.unshift :docker
|
args.compact.unshift :docker
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pack(*args)
|
||||||
|
args.compact.unshift :pack
|
||||||
|
end
|
||||||
|
|
||||||
def git(*args, path: nil)
|
def git(*args, path: nil)
|
||||||
[ :git, *([ "-C", path ] if path), *args.compact ]
|
[ :git, *([ "-C", path ] if path), *args.compact ]
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ require "active_support/core_ext/string/filters"
|
|||||||
|
|
||||||
class Kamal::Commands::Builder < Kamal::Commands::Base
|
class Kamal::Commands::Builder < Kamal::Commands::Base
|
||||||
delegate :create, :remove, :dev, :push, :clean, :pull, :info, :inspect_builder, :validate_image, :first_mirror, to: :target
|
delegate :create, :remove, :dev, :push, :clean, :pull, :info, :inspect_builder, :validate_image, :first_mirror, to: :target
|
||||||
delegate :local?, :remote?, :cloud?, to: "config.builder"
|
delegate :local?, :remote?, :pack?, :cloud?, to: "config.builder"
|
||||||
|
|
||||||
include Clone
|
include Clone
|
||||||
|
|
||||||
@@ -17,6 +17,8 @@ class Kamal::Commands::Builder < Kamal::Commands::Base
|
|||||||
else
|
else
|
||||||
remote
|
remote
|
||||||
end
|
end
|
||||||
|
elsif pack?
|
||||||
|
pack
|
||||||
elsif cloud?
|
elsif cloud?
|
||||||
cloud
|
cloud
|
||||||
else
|
else
|
||||||
@@ -36,6 +38,10 @@ class Kamal::Commands::Builder < Kamal::Commands::Base
|
|||||||
@hybrid ||= Kamal::Commands::Builder::Hybrid.new(config)
|
@hybrid ||= Kamal::Commands::Builder::Hybrid.new(config)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pack
|
||||||
|
@pack ||= Kamal::Commands::Builder::Pack.new(config)
|
||||||
|
end
|
||||||
|
|
||||||
def cloud
|
def cloud
|
||||||
@cloud ||= Kamal::Commands::Builder::Cloud.new(config)
|
@cloud ||= Kamal::Commands::Builder::Cloud.new(config)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|||||||
delegate :argumentize, to: Kamal::Utils
|
delegate :argumentize, to: Kamal::Utils
|
||||||
delegate \
|
delegate \
|
||||||
:args, :secrets, :dockerfile, :target, :arches, :local_arches, :remote_arches, :remote,
|
:args, :secrets, :dockerfile, :target, :arches, :local_arches, :remote_arches, :remote,
|
||||||
|
:pack?, :pack_builder, :pack_buildpacks,
|
||||||
:cache_from, :cache_to, :ssh, :provenance, :sbom, :driver, :docker_driver?,
|
:cache_from, :cache_to, :ssh, :provenance, :sbom, :driver, :docker_driver?,
|
||||||
to: :builder_config
|
to: :builder_config
|
||||||
|
|
||||||
|
|||||||
46
lib/kamal/commands/builder/pack.rb
Normal file
46
lib/kamal/commands/builder/pack.rb
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
class Kamal::Commands::Builder::Pack < Kamal::Commands::Builder::Base
|
||||||
|
def push(export_action = "registry")
|
||||||
|
combine \
|
||||||
|
build,
|
||||||
|
export(export_action)
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove;end
|
||||||
|
|
||||||
|
def info
|
||||||
|
pack :builder, :inspect, pack_builder
|
||||||
|
end
|
||||||
|
alias_method :inspect_builder, :info
|
||||||
|
|
||||||
|
private
|
||||||
|
def build
|
||||||
|
pack(:build,
|
||||||
|
config.repository,
|
||||||
|
"--platform", platform,
|
||||||
|
"--creation-time", "now",
|
||||||
|
"--builder", pack_builder,
|
||||||
|
buildpacks,
|
||||||
|
"-t", config.absolute_image,
|
||||||
|
"-t", config.latest_image,
|
||||||
|
"--env", "BP_IMAGE_LABELS=service=#{config.service}",
|
||||||
|
*argumentize("--env", args),
|
||||||
|
*argumentize("--env", secrets, sensitive: true),
|
||||||
|
"--path", build_context)
|
||||||
|
end
|
||||||
|
|
||||||
|
def export(export_action)
|
||||||
|
return unless export_action == "registry"
|
||||||
|
|
||||||
|
combine \
|
||||||
|
docker(:push, config.absolute_image),
|
||||||
|
docker(:push, config.latest_image)
|
||||||
|
end
|
||||||
|
|
||||||
|
def platform
|
||||||
|
"linux/#{local_arches.first}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def buildpacks
|
||||||
|
(pack_buildpacks << "paketo-buildpacks/image-labels").map { |buildpack| [ "--buildpack", buildpack ] }
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -61,6 +61,10 @@ class Kamal::Configuration::Builder
|
|||||||
!!builder_config["cache"]
|
!!builder_config["cache"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pack?
|
||||||
|
!!builder_config["pack"]
|
||||||
|
end
|
||||||
|
|
||||||
def args
|
def args
|
||||||
builder_config["args"] || {}
|
builder_config["args"] || {}
|
||||||
end
|
end
|
||||||
@@ -85,6 +89,14 @@ class Kamal::Configuration::Builder
|
|||||||
builder_config.fetch("driver", "docker-container")
|
builder_config.fetch("driver", "docker-container")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pack_builder
|
||||||
|
builder_config["pack"]["builder"] if pack?
|
||||||
|
end
|
||||||
|
|
||||||
|
def pack_buildpacks
|
||||||
|
builder_config["pack"]["buildpacks"] if pack?
|
||||||
|
end
|
||||||
|
|
||||||
def local_disabled?
|
def local_disabled?
|
||||||
builder_config["local"] == false
|
builder_config["local"] == false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#
|
#
|
||||||
# Options go under the builder key in the root configuration.
|
# Options go under the builder key in the root configuration.
|
||||||
builder:
|
builder:
|
||||||
|
|
||||||
# Arch
|
# Arch
|
||||||
#
|
#
|
||||||
# The architectures to build for — you can set an array or just a single value.
|
# The architectures to build for — you can set an array or just a single value.
|
||||||
@@ -31,6 +30,19 @@ builder:
|
|||||||
# Defaults to true:
|
# Defaults to true:
|
||||||
local: true
|
local: true
|
||||||
|
|
||||||
|
# Buildpack configuration
|
||||||
|
#
|
||||||
|
# The build configuration for using pack to build a Cloud Native Buildpack image.
|
||||||
|
#
|
||||||
|
# For additional buildpack customization options you can create a project descriptor
|
||||||
|
# file(project.toml) that the Pack CLI will automatically use.
|
||||||
|
# See https://buildpacks.io/docs/for-app-developers/how-to/build-inputs/use-project-toml/ for more information.
|
||||||
|
pack:
|
||||||
|
builder: heroku/builder:24
|
||||||
|
buildpacks:
|
||||||
|
- heroku/ruby
|
||||||
|
- heroku/procfile
|
||||||
|
|
||||||
# Builder cache
|
# Builder cache
|
||||||
#
|
#
|
||||||
# The type must be either 'gha' or 'registry'.
|
# The type must be either 'gha' or 'registry'.
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ class Kamal::Configuration::Validator::Builder < Kamal::Configuration::Validator
|
|||||||
|
|
||||||
error "Builder arch not set" unless config["arch"].present?
|
error "Builder arch not set" unless config["arch"].present?
|
||||||
|
|
||||||
|
error "buildpacks only support building for one arch" if config["pack"] && config["arch"].is_a?(Array) && config["arch"].size > 1
|
||||||
|
|
||||||
error "Cannot disable local builds, no remote is set" if config["local"] == false && config["remote"].blank?
|
error "Cannot disable local builds, no remote is set" if config["local"] == false && config["remote"].blank?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -61,6 +61,32 @@ class CommandsBuilderTest < ActiveSupport::TestCase
|
|||||||
builder.push.join(" ")
|
builder.push.join(" ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "target pack when pack is set" do
|
||||||
|
builder = new_builder_command(image: "dhh/app", builder: { "arch" => "amd64", "pack" => { "builder" => "heroku/builder:24", "buildpacks" => [ "heroku/ruby", "heroku/procfile" ] } })
|
||||||
|
assert_equal "pack", builder.name
|
||||||
|
assert_equal \
|
||||||
|
"pack build dhh/app --platform linux/amd64 --creation-time now --builder heroku/builder:24 --buildpack heroku/ruby --buildpack heroku/procfile --buildpack paketo-buildpacks/image-labels -t dhh/app:123 -t dhh/app:latest --env BP_IMAGE_LABELS=service=app --path . && docker push dhh/app:123 && docker push dhh/app:latest",
|
||||||
|
builder.push.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "pack build args passed as env" do
|
||||||
|
builder = new_builder_command(image: "dhh/app", builder: { "args" => { "a" => 1, "b" => 2 }, "arch" => "amd64", "pack" => { "builder" => "heroku/builder:24", "buildpacks" => [ "heroku/ruby", "heroku/procfile" ] } })
|
||||||
|
|
||||||
|
assert_equal \
|
||||||
|
"pack build dhh/app --platform linux/amd64 --creation-time now --builder heroku/builder:24 --buildpack heroku/ruby --buildpack heroku/procfile --buildpack paketo-buildpacks/image-labels -t dhh/app:123 -t dhh/app:latest --env BP_IMAGE_LABELS=service=app --env a=\"1\" --env b=\"2\" --path . && docker push dhh/app:123 && docker push dhh/app:latest",
|
||||||
|
builder.push.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "pack build secrets as env" do
|
||||||
|
with_test_secrets("secrets" => "token_a=foo\ntoken_b=bar") do
|
||||||
|
builder = new_builder_command(image: "dhh/app", builder: { "secrets" => [ "token_a", "token_b" ], "arch" => "amd64", "pack" => { "builder" => "heroku/builder:24", "buildpacks" => [ "heroku/ruby", "heroku/procfile" ] } })
|
||||||
|
|
||||||
|
assert_equal \
|
||||||
|
"pack build dhh/app --platform linux/amd64 --creation-time now --builder heroku/builder:24 --buildpack heroku/ruby --buildpack heroku/procfile --buildpack paketo-buildpacks/image-labels -t dhh/app:123 -t dhh/app:latest --env BP_IMAGE_LABELS=service=app --env token_a=\"foo\" --env token_b=\"bar\" --path . && docker push dhh/app:123 && docker push dhh/app:latest",
|
||||||
|
builder.push.join(" ")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "cloud builder" do
|
test "cloud builder" do
|
||||||
builder = new_builder_command(builder: { "arch" => [ "#{local_arch}" ], "driver" => "cloud docker-org-name/builder-name" })
|
builder = new_builder_command(builder: { "arch" => [ "#{local_arch}" ], "driver" => "cloud docker-org-name/builder-name" })
|
||||||
assert_equal "cloud", builder.name
|
assert_equal "cloud", builder.name
|
||||||
|
|||||||
@@ -16,6 +16,23 @@ class ConfigurationBuilderTest < ActiveSupport::TestCase
|
|||||||
assert_equal false, config.builder.remote?
|
assert_equal false, config.builder.remote?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "pack?" do
|
||||||
|
assert_not config.builder.pack?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "pack? with pack builder" do
|
||||||
|
@deploy[:builder] = { "arch" => "arm64", "pack" => { "builder" => "heroku/builder:24" } }
|
||||||
|
|
||||||
|
assert config.builder.pack?
|
||||||
|
end
|
||||||
|
|
||||||
|
test "pack details" do
|
||||||
|
@deploy[:builder] = { "arch" => "amd64", "pack" => { "builder" => "heroku/builder:24", "buildpacks" => [ "heroku/ruby", "heroku/procfile" ] } }
|
||||||
|
|
||||||
|
assert_equal "heroku/builder:24", config.builder.pack_builder
|
||||||
|
assert_equal [ "heroku/ruby", "heroku/procfile" ], config.builder.pack_buildpacks
|
||||||
|
end
|
||||||
|
|
||||||
test "remote" do
|
test "remote" do
|
||||||
assert_nil config.builder.remote
|
assert_nil config.builder.remote
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ class ConfigurationValidationTest < ActiveSupport::TestCase
|
|||||||
assert_error "builder/arch: should be an array or a string", builder: { "arch" => {} }
|
assert_error "builder/arch: should be an array or a string", builder: { "arch" => {} }
|
||||||
assert_error "builder/args: should be a hash", builder: { "args" => [ "foo" ] }
|
assert_error "builder/args: should be a hash", builder: { "args" => [ "foo" ] }
|
||||||
assert_error "builder/cache/options: should be a string", builder: { "cache" => { "options" => [] } }
|
assert_error "builder/cache/options: should be a string", builder: { "cache" => { "options" => [] } }
|
||||||
|
assert_error "builder: buildpacks only support building for one arch", builder: { "arch" => [ "amd64", "arm64" ], "pack" => { "builder" => "heroku/builder:24" } }
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
Reference in New Issue
Block a user