From 897b3b4e4628337e06f3e8a797a7df62ba3e0ee1 Mon Sep 17 00:00:00 2001 From: Nick Hammond Date: Tue, 27 Aug 2024 22:25:56 -0700 Subject: [PATCH 1/8] Add a pack option to the builder options --- lib/kamal/commands/base.rb | 4 ++++ lib/kamal/commands/builder.rb | 5 +++++ lib/kamal/commands/builder/base.rb | 2 +- lib/kamal/commands/builder/native/pack.rb | 26 +++++++++++++++++++++++ lib/kamal/configuration/builder.rb | 16 ++++++++++++++ lib/kamal/configuration/docs/builder.yml | 10 +++++++++ test/commands/builder_test.rb | 8 +++++++ test/configuration/builder_test.rb | 18 ++++++++++++++++ 8 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 lib/kamal/commands/builder/native/pack.rb diff --git a/lib/kamal/commands/base.rb b/lib/kamal/commands/base.rb index 2173d0644..731e543f8 100644 --- a/lib/kamal/commands/base.rb +++ b/lib/kamal/commands/base.rb @@ -77,6 +77,10 @@ def docker(*args) args.compact.unshift :docker end + def pack(*args) + args.compact.unshift :pack + end + def git(*args, path: nil) [ :git, *([ "-C", path ] if path), *args.compact ] end diff --git a/lib/kamal/commands/builder.rb b/lib/kamal/commands/builder.rb index 13c3d8296..41f07dc24 100644 --- a/lib/kamal/commands/builder.rb +++ b/lib/kamal/commands/builder.rb @@ -18,6 +18,8 @@ def target else native_remote end + elsif config.builder.pack? + pack else multiarch end @@ -50,6 +52,9 @@ def multiarch_remote @multiarch_remote ||= Kamal::Commands::Builder::Multiarch::Remote.new(config) end + def pack + @pack ||= Kamal::Commands::Builder::Native::Pack.new(config) + end def ensure_local_dependencies_installed if name.native? diff --git a/lib/kamal/commands/builder/base.rb b/lib/kamal/commands/builder/base.rb index cedfeadf0..fa095f609 100644 --- a/lib/kamal/commands/builder/base.rb +++ b/lib/kamal/commands/builder/base.rb @@ -5,7 +5,7 @@ class BuilderError < StandardError; end ENDPOINT_DOCKER_HOST_INSPECT = "'{{.Endpoints.docker.Host}}'" delegate :argumentize, to: Kamal::Utils - delegate :args, :secrets, :dockerfile, :target, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, :ssh, to: :builder_config + delegate :args, :secrets, :dockerfile, :target, :local_arch, :local_host, :pack_arch, :pack_builder, :pack_buildpacks, :remote_arch, :remote_host, :cache_from, :cache_to, :ssh, to: :builder_config def clean docker :image, :rm, "--force", config.absolute_image diff --git a/lib/kamal/commands/builder/native/pack.rb b/lib/kamal/commands/builder/native/pack.rb new file mode 100644 index 000000000..d4ca19314 --- /dev/null +++ b/lib/kamal/commands/builder/native/pack.rb @@ -0,0 +1,26 @@ +class Kamal::Commands::Builder::Native::Pack < Kamal::Commands::Builder::Native + def push + combine \ + pack(:build, + config.absolute_image, + "--platform", platform, + "--builder", pack_builder, + buildpacks, + "-t", config.absolute_image, + "-t", config.latest_image, + "--env", "BP_IMAGE_LABELS=service=#{config.service}", + secrets.map { |secret| ["--env", Kamal::Utils.sensitive(ENV[secret])] }, + "--path", build_context), + docker(:push, config.absolute_image), + docker(:push, config.latest_image) + end + + private + def platform + "linux/#{pack_arch}" + end + + def buildpacks + (pack_buildpacks << "paketo-buildpacks/image-labels").map { |buildpack| ["--buildpack", buildpack] } + end +end diff --git a/lib/kamal/configuration/builder.rb b/lib/kamal/configuration/builder.rb index 74c8d1c66..634b18464 100644 --- a/lib/kamal/configuration/builder.rb +++ b/lib/kamal/configuration/builder.rb @@ -35,6 +35,10 @@ def cached? !!builder_config["cache"] end + def pack? + !!builder_config["pack"] + end + def args builder_config["args"] || {} end @@ -63,6 +67,18 @@ def local_host builder_config["local"]["host"] if local? end + def pack_arch + builder_config["pack"]["arch"] if pack? + end + + def pack_builder + builder_config["pack"]["builder"] if pack? + end + + def pack_buildpacks + builder_config["pack"]["buildpacks"] if pack? + end + def remote_arch builder_config["remote"]["arch"] if remote? end diff --git a/lib/kamal/configuration/docs/builder.yml b/lib/kamal/configuration/docs/builder.yml index b10711058..706fe83cc 100644 --- a/lib/kamal/configuration/docs/builder.yml +++ b/lib/kamal/configuration/docs/builder.yml @@ -37,6 +37,16 @@ builder: arch: arm64 host: ssh://docker@docker-builder + # Buildpack configuration + # + # The build configuration for using pack to build a Cloud Native Buildpack image. + pack: + builder: heroku/builder:24 + arch: amd64 + buildpacks: + - heroku/ruby + - heroku/procfile + # Builder cache # # The type must be either 'gha' or 'registry' diff --git a/test/commands/builder_test.rb b/test/commands/builder_test.rb index f3faa5f60..29c657037 100644 --- a/test/commands/builder_test.rb +++ b/test/commands/builder_test.rb @@ -53,6 +53,14 @@ class CommandsBuilderTest < ActiveSupport::TestCase builder.push.join(" ") end + test "target pack when pack is set" do + builder = new_builder_command(builder: { "pack" => { "arch" => "amd64" , "builder" => "heroku/builder:24", "buildpacks" => [ "heroku/ruby", "heroku/procfile" ] }}) + assert_equal "native/pack", builder.name + assert_equal \ + "pack build dhh/app:123 --platform linux/amd64 --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 "build args" do builder = new_builder_command(builder: { "args" => { "a" => 1, "b" => 2 } }) assert_equal \ diff --git a/test/configuration/builder_test.rb b/test/configuration/builder_test.rb index 4b37b5e8a..8576e06aa 100644 --- a/test/configuration/builder_test.rb +++ b/test/configuration/builder_test.rb @@ -32,6 +32,24 @@ class ConfigurationBuilderTest < ActiveSupport::TestCase assert_equal false, config.builder.remote? end + test "pack?" do + refute config.builder.pack? + end + + test "pack? with pack builder" do + @deploy[:builder] = { "pack" => {"builder" => "heroku/builder:24"} } + + assert config.builder.pack? + end + + test "pack details" do + @deploy[:builder] = { "pack" => {"arch" => "amd64", "builder" => "heroku/builder:24", "buildpacks" => ["heroku/ruby", "heroku/procfile"]} } + + assert_equal "amd64", config.builder.pack_arch + assert_equal "heroku/builder:24", config.builder.pack_builder + assert_equal ["heroku/ruby", "heroku/procfile"], config.builder.pack_buildpacks + end + test "remote_arch" do assert_nil config.builder.remote_arch end From 826308aabd52971711bd26fc2a48304d7da0af4f Mon Sep 17 00:00:00 2001 From: Nick Hammond Date: Tue, 27 Aug 2024 22:52:06 -0700 Subject: [PATCH 2/8] Clean things up via Rubocop --- lib/kamal/commands/builder/native/pack.rb | 4 ++-- test/commands/builder_test.rb | 2 +- test/configuration/builder_test.rb | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/kamal/commands/builder/native/pack.rb b/lib/kamal/commands/builder/native/pack.rb index d4ca19314..d64b091a4 100644 --- a/lib/kamal/commands/builder/native/pack.rb +++ b/lib/kamal/commands/builder/native/pack.rb @@ -9,7 +9,7 @@ def push "-t", config.absolute_image, "-t", config.latest_image, "--env", "BP_IMAGE_LABELS=service=#{config.service}", - secrets.map { |secret| ["--env", Kamal::Utils.sensitive(ENV[secret])] }, + secrets.map { |secret| [ "--env", Kamal::Utils.sensitive(ENV[secret]) ] }, "--path", build_context), docker(:push, config.absolute_image), docker(:push, config.latest_image) @@ -21,6 +21,6 @@ def platform end def buildpacks - (pack_buildpacks << "paketo-buildpacks/image-labels").map { |buildpack| ["--buildpack", buildpack] } + (pack_buildpacks << "paketo-buildpacks/image-labels").map { |buildpack| [ "--buildpack", buildpack ] } end end diff --git a/test/commands/builder_test.rb b/test/commands/builder_test.rb index 29c657037..aae889198 100644 --- a/test/commands/builder_test.rb +++ b/test/commands/builder_test.rb @@ -54,7 +54,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase end test "target pack when pack is set" do - builder = new_builder_command(builder: { "pack" => { "arch" => "amd64" , "builder" => "heroku/builder:24", "buildpacks" => [ "heroku/ruby", "heroku/procfile" ] }}) + builder = new_builder_command(builder: { "pack" => { "arch" => "amd64", "builder" => "heroku/builder:24", "buildpacks" => [ "heroku/ruby", "heroku/procfile" ] } }) assert_equal "native/pack", builder.name assert_equal \ "pack build dhh/app:123 --platform linux/amd64 --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", diff --git a/test/configuration/builder_test.rb b/test/configuration/builder_test.rb index 8576e06aa..092edd4f3 100644 --- a/test/configuration/builder_test.rb +++ b/test/configuration/builder_test.rb @@ -33,21 +33,21 @@ class ConfigurationBuilderTest < ActiveSupport::TestCase end test "pack?" do - refute config.builder.pack? + assert_not config.builder.pack? end test "pack? with pack builder" do - @deploy[:builder] = { "pack" => {"builder" => "heroku/builder:24"} } + @deploy[:builder] = { "pack" => { "builder" => "heroku/builder:24" } } assert config.builder.pack? end test "pack details" do - @deploy[:builder] = { "pack" => {"arch" => "amd64", "builder" => "heroku/builder:24", "buildpacks" => ["heroku/ruby", "heroku/procfile"]} } + @deploy[:builder] = { "pack" => { "arch" => "amd64", "builder" => "heroku/builder:24", "buildpacks" => [ "heroku/ruby", "heroku/procfile" ] } } assert_equal "amd64", config.builder.pack_arch assert_equal "heroku/builder:24", config.builder.pack_builder - assert_equal ["heroku/ruby", "heroku/procfile"], config.builder.pack_buildpacks + assert_equal [ "heroku/ruby", "heroku/procfile" ], config.builder.pack_buildpacks end test "remote_arch" do From d0ffb850da92c8970e1e8e1c007112eb8113e82e Mon Sep 17 00:00:00 2001 From: Nick Hammond Date: Wed, 4 Sep 2024 09:42:40 -0700 Subject: [PATCH 3/8] Utilize repository name for pack name --- lib/kamal/commands/builder/native/pack.rb | 2 +- test/commands/builder_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/kamal/commands/builder/native/pack.rb b/lib/kamal/commands/builder/native/pack.rb index d64b091a4..f3e7d27ac 100644 --- a/lib/kamal/commands/builder/native/pack.rb +++ b/lib/kamal/commands/builder/native/pack.rb @@ -2,7 +2,7 @@ class Kamal::Commands::Builder::Native::Pack < Kamal::Commands::Builder::Native def push combine \ pack(:build, - config.absolute_image, + config.repository, "--platform", platform, "--builder", pack_builder, buildpacks, diff --git a/test/commands/builder_test.rb b/test/commands/builder_test.rb index aae889198..2f6f86b18 100644 --- a/test/commands/builder_test.rb +++ b/test/commands/builder_test.rb @@ -54,10 +54,10 @@ class CommandsBuilderTest < ActiveSupport::TestCase end test "target pack when pack is set" do - builder = new_builder_command(builder: { "pack" => { "arch" => "amd64", "builder" => "heroku/builder:24", "buildpacks" => [ "heroku/ruby", "heroku/procfile" ] } }) + builder = new_builder_command(image: "dhh/app", builder: { "pack" => { "arch" => "amd64", "builder" => "heroku/builder:24", "buildpacks" => [ "heroku/ruby", "heroku/procfile" ] } }) assert_equal "native/pack", builder.name assert_equal \ - "pack build dhh/app:123 --platform linux/amd64 --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", + "pack build dhh/app --platform linux/amd64 --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 From ae68193f999bd5976d1585dddf0e8a682a61a995 Mon Sep 17 00:00:00 2001 From: Nick Hammond Date: Thu, 5 Sep 2024 22:17:28 -0700 Subject: [PATCH 4/8] pack arch no longer needed, update builder name in tests --- lib/kamal/configuration/builder.rb | 4 ---- test/commands/builder_test.rb | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/kamal/configuration/builder.rb b/lib/kamal/configuration/builder.rb index f863e7f21..3efd62f5a 100644 --- a/lib/kamal/configuration/builder.rb +++ b/lib/kamal/configuration/builder.rb @@ -85,10 +85,6 @@ def driver builder_config.fetch("driver", "docker-container") end - def pack_arch - builder_config["pack"]["arch"] if pack? - end - def pack_builder builder_config["pack"]["builder"] if pack? end diff --git a/test/commands/builder_test.rb b/test/commands/builder_test.rb index be6686c5f..aff8c0c6c 100644 --- a/test/commands/builder_test.rb +++ b/test/commands/builder_test.rb @@ -62,8 +62,8 @@ class CommandsBuilderTest < ActiveSupport::TestCase end test "target pack when pack is set" do - builder = new_builder_command(image: "dhh/app", builder: { "pack" => { "arch" => "amd64", "builder" => "heroku/builder:24", "buildpacks" => [ "heroku/ruby", "heroku/procfile" ] } }) - assert_equal "native/pack", builder.name + 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 --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(" ") From 2c5f2a7ce0d0c999c934e1c5a9be5bfc479f28d5 Mon Sep 17 00:00:00 2001 From: Nick Hammond Date: Thu, 5 Sep 2024 22:25:50 -0700 Subject: [PATCH 5/8] Don't need to inspect the builder if pack --- lib/kamal/commands/builder/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/kamal/commands/builder/base.rb b/lib/kamal/commands/builder/base.rb index 4dfaf46cd..a04548180 100644 --- a/lib/kamal/commands/builder/base.rb +++ b/lib/kamal/commands/builder/base.rb @@ -6,7 +6,7 @@ class BuilderError < StandardError; end delegate :argumentize, to: Kamal::Utils delegate \ :args, :secrets, :dockerfile, :target, :arches, :local_arches, :remote_arches, :remote, - :pack_builder, :pack_buildpacks, + :pack?, :pack_builder, :pack_buildpacks, :cache_from, :cache_to, :ssh, :driver, :docker_driver?, to: :builder_config @@ -34,7 +34,7 @@ def info end def inspect_builder - docker :buildx, :inspect, builder_name unless docker_driver? + docker :buildx, :inspect, builder_name unless docker_driver? || pack? end def build_options From e252004eef8697fecdaecf661fe47e659c2e7c0f Mon Sep 17 00:00:00 2001 From: Nick Hammond Date: Mon, 23 Sep 2024 20:16:06 -0700 Subject: [PATCH 6/8] Use argumentize for secrets with pack --- lib/kamal/commands/builder/pack.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/kamal/commands/builder/pack.rb b/lib/kamal/commands/builder/pack.rb index 3b8fbb949..cdd0e1a71 100644 --- a/lib/kamal/commands/builder/pack.rb +++ b/lib/kamal/commands/builder/pack.rb @@ -9,7 +9,7 @@ def push "-t", config.absolute_image, "-t", config.latest_image, "--env", "BP_IMAGE_LABELS=service=#{config.service}", - secrets.map { |secret| [ "--env", Kamal::Utils.sensitive(ENV[secret]) ] }, + *argumentize("--env", secrets, sensitive: true), "--path", build_context), docker(:push, config.absolute_image), docker(:push, config.latest_image) From dda8efe39a7378eeb4d73db6b480392b2de2d79a Mon Sep 17 00:00:00 2001 From: Nick Hammond Date: Tue, 1 Oct 2024 14:08:26 -0700 Subject: [PATCH 7/8] Point to project.toml in docs --- lib/kamal/configuration/docs/builder.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/kamal/configuration/docs/builder.yml b/lib/kamal/configuration/docs/builder.yml index d517e7055..9b1a3f893 100644 --- a/lib/kamal/configuration/docs/builder.yml +++ b/lib/kamal/configuration/docs/builder.yml @@ -8,7 +8,6 @@ # # Options go under the builder key in the root configuration. builder: - # Arch # # The architectures to build for — you can set an array or just a single value. @@ -34,11 +33,15 @@ builder: # 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 + - heroku/ruby + - heroku/procfile # Builder cache # From 89b44153bbb90b76a913c113012aa0da395bd401 Mon Sep 17 00:00:00 2001 From: Nick Hammond Date: Wed, 2 Oct 2024 09:55:57 -0700 Subject: [PATCH 8/8] Ensure build args and secrets are used with pack --- lib/kamal/commands/builder/pack.rb | 1 + test/commands/builder_test.rb | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/lib/kamal/commands/builder/pack.rb b/lib/kamal/commands/builder/pack.rb index cdd0e1a71..df9e6d195 100644 --- a/lib/kamal/commands/builder/pack.rb +++ b/lib/kamal/commands/builder/pack.rb @@ -9,6 +9,7 @@ def push "-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), docker(:push, config.absolute_image), diff --git a/test/commands/builder_test.rb b/test/commands/builder_test.rb index 7102c672c..96e3e75e0 100644 --- a/test/commands/builder_test.rb +++ b/test/commands/builder_test.rb @@ -69,6 +69,24 @@ class CommandsBuilderTest < ActiveSupport::TestCase 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 --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 --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 "build args" do builder = new_builder_command(builder: { "args" => { "a" => 1, "b" => 2 } }) assert_equal \