Build from within a git clone by default

Docker does not respect the .dockerignore file when building from a tar.

Instead by default we'll make a local clone into a tmp directory and
build from there. Subsequent builds will reset the clone to match the
checkout.

Compared to building directly in the repo, we'll have reproducible
builds.

Compared to using a git archive:
1. .dockerignore is respected
2. We'll have faster builds - docker can be smarter about caching the
build context on subsequent builds from a directory

To build from the repo directly, set the build context to "." in the
config.

If there are uncommitted changes, we'll warn about them either being
included or ignored depending on whether we build from the clone.
This commit is contained in:
Donal McBreen
2024-05-10 15:23:43 +01:00
parent 88947b6a7b
commit 307750ff70
15 changed files with 253 additions and 76 deletions

View File

@@ -19,21 +19,34 @@ class Kamal::Cli::Build < Kamal::Cli::Base
verify_local_dependencies verify_local_dependencies
run_hook "pre-build" run_hook "pre-build"
if (uncommitted_changes = Kamal::Git.uncommitted_changes).present? uncommitted_changes = Kamal::Git.uncommitted_changes
say "The following paths have uncommitted changes:\n #{uncommitted_changes}", :yellow
if KAMAL.config.builder.git_clone?
if uncommitted_changes.present?
say "Building from a local git clone, so ignoring these uncommitted changes:\n #{uncommitted_changes}", :yellow
end
prepare_clone
elsif uncommitted_changes.present?
say "Building with uncommitted changes:\n #{uncommitted_changes}", :yellow
end end
# Get the command here to ensure the Dir.chdir doesn't interfere with it
push = KAMAL.builder.push
run_locally do run_locally do
begin begin
KAMAL.with_verbosity(:debug) do KAMAL.with_verbosity(:debug) do
execute *KAMAL.builder.push Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
end end
rescue SSHKit::Command::Failed => e rescue SSHKit::Command::Failed => e
if e.message =~ /(no builder)|(no such file or directory)/ if e.message =~ /(no builder)|(no such file or directory)/
warn "Missing compatible builder, so creating a new one first" warn "Missing compatible builder, so creating a new one first"
if cli.create if cli.create
KAMAL.with_verbosity(:debug) { execute *KAMAL.builder.push } KAMAL.with_verbosity(:debug) do
Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
end
end end
else else
raise raise
@@ -120,4 +133,23 @@ class Kamal::Cli::Build < Kamal::Cli::Base
end end
end end
end end
def prepare_clone
run_locally do
begin
info "Cloning repo into build directory `#{KAMAL.config.builder.build_directory}`..."
execute *KAMAL.builder.create_clone_directory
execute *KAMAL.builder.clone
rescue SSHKit::Command::Failed => e
if e.message =~ /already exists and is not an empty directory/
info "Resetting local clone as `#{KAMAL.config.builder.build_directory}` already exists..."
KAMAL.builder.clone_reset_steps.each { |step| execute *step }
else
raise
end
end
end
end
end end

View File

@@ -78,8 +78,8 @@ module Kamal::Commands
args.compact.unshift :docker args.compact.unshift :docker
end end
def git(*args) def git(*args, path: nil)
args.compact.unshift :git [ :git, *([ "-C", path ] if path), *args.compact ]
end end
def tags(**details) def tags(**details)

View File

@@ -2,6 +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, :push, :clean, :pull, :info, :validate_image, to: :target delegate :create, :remove, :push, :clean, :pull, :info, :validate_image, to: :target
delegate :clone_directory, :build_directory, to: :"config.builder"
def name def name
target.class.to_s.remove("Kamal::Commands::Builder::").underscore.inquiry target.class.to_s.remove("Kamal::Commands::Builder::").underscore.inquiry
@@ -53,6 +54,23 @@ class Kamal::Commands::Builder < Kamal::Commands::Base
end end
end end
def create_clone_directory
make_directory clone_directory
end
def clone
git :clone, Kamal::Git.root, path: clone_directory
end
def clone_reset_steps
[
git(:remote, "set-url", :origin, Kamal::Git.root, path: build_directory),
git(:fetch, :origin, path: build_directory),
git(:reset, "--hard", Kamal::Git.revision, path: build_directory),
git(:clean, "-fdx", path: build_directory)
]
end
private private
def ensure_local_docker_installed def ensure_local_docker_installed
docker "--version" docker "--version"

View File

@@ -3,7 +3,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
class BuilderError < StandardError; end class BuilderError < StandardError; end
delegate :argumentize, to: Kamal::Utils delegate :argumentize, to: Kamal::Utils
delegate :args, :secrets, :dockerfile, :target, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, :ssh, :git_archive?, to: :builder_config delegate :args, :secrets, :dockerfile, :target, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, :ssh, to: :builder_config
def clean def clean
docker :image, :rm, "--force", config.absolute_image docker :image, :rm, "--force", config.absolute_image
@@ -13,16 +13,6 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
docker :pull, config.absolute_image docker :pull, config.absolute_image
end end
def push
if git_archive?
pipe \
git(:archive, "--format=tar", :HEAD),
build_and_push
else
build_and_push
end
end
def build_options def build_options
[ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_target, *build_ssh ] [ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_target, *build_ssh ]
end end

View File

@@ -13,6 +13,15 @@ class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
docker(:buildx, :ls) docker(:buildx, :ls)
end end
def push
docker :buildx, :build,
"--push",
"--platform", platform_names,
"--builder", builder_name,
*build_options,
build_context
end
private private
def builder_name def builder_name
"kamal-#{config.service}-multiarch" "kamal-#{config.service}-multiarch"
@@ -25,13 +34,4 @@ class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
"linux/amd64,linux/arm64" "linux/amd64,linux/arm64"
end end
end end
def build_and_push
docker :buildx, :build,
"--push",
"--platform", platform_names,
"--builder", builder_name,
*build_options,
build_context
end
end end

View File

@@ -11,11 +11,10 @@ class Kamal::Commands::Builder::Native < Kamal::Commands::Builder::Base
# No-op on native # No-op on native
end end
private def push
def build_and_push combine \
combine \ docker(:build, *build_options, build_context),
docker(:build, *build_options, build_context), docker(:push, config.absolute_image),
docker(:push, config.absolute_image), docker(:push, config.latest_image)
docker(:push, config.latest_image) end
end
end end

View File

@@ -7,11 +7,10 @@ class Kamal::Commands::Builder::Native::Cached < Kamal::Commands::Builder::Nativ
docker :buildx, :rm, builder_name docker :buildx, :rm, builder_name
end end
private def push
def build_and_push docker :buildx, :build,
docker :buildx, :build, "--push",
"--push", *build_options,
*build_options, build_context
build_context end
end
end end

View File

@@ -17,6 +17,15 @@ class Kamal::Commands::Builder::Native::Remote < Kamal::Commands::Builder::Nativ
docker(:buildx, :ls) docker(:buildx, :ls)
end end
def push
docker :buildx, :build,
"--push",
"--platform", platform,
"--builder", builder_name,
*build_options,
build_context
end
private private
def builder_name def builder_name
@@ -47,13 +56,4 @@ class Kamal::Commands::Builder::Native::Remote < Kamal::Commands::Builder::Nativ
def remove_buildx def remove_buildx
docker :buildx, :rm, builder_name docker :buildx, :rm, builder_name
end end
def build_and_push
docker :buildx, :build,
"--push",
"--platform", platform,
"--builder", builder_name,
*build_options,
build_context
end
end end

View File

@@ -336,7 +336,7 @@ class Kamal::Configuration
def git_version def git_version
@git_version ||= @git_version ||=
if Kamal::Git.used? if Kamal::Git.used?
if Kamal::Git.uncommitted_changes.present? && !builder.git_archive? if Kamal::Git.uncommitted_changes.present? && !builder.git_clone?
uncommitted_suffix = "_uncommitted_#{SecureRandom.hex(8)}" uncommitted_suffix = "_uncommitted_#{SecureRandom.hex(8)}"
end end
[ Kamal::Git.revision, uncommitted_suffix ].compact.join [ Kamal::Git.revision, uncommitted_suffix ].compact.join

View File

@@ -3,6 +3,8 @@ class Kamal::Configuration::Builder
@options = config.raw_config.builder || {} @options = config.raw_config.builder || {}
@image = config.image @image = config.image
@server = config.registry["server"] @server = config.registry["server"]
@service = config.service
@destination = config.destination
valid? valid?
end end
@@ -44,7 +46,7 @@ class Kamal::Configuration::Builder
end end
def context def context
@options["context"] || (git_archive? ? "-" : ".") @options["context"] || "."
end end
def local_arch def local_arch
@@ -89,10 +91,23 @@ class Kamal::Configuration::Builder
@options["ssh"] @options["ssh"]
end end
def git_archive? def git_clone?
Kamal::Git.used? && @options["context"].nil? Kamal::Git.used? && @options["context"].nil?
end end
def clone_directory
@clone_directory ||= File.join Dir.tmpdir, "kamal-clones", [ @service, @destination, pwd_sha ].compact.join("-")
end
def build_directory
@build_directory ||=
if git_clone?
File.join clone_directory, repo_basename, repo_relative_pwd
else
"."
end
end
private private
def valid? def valid?
if @options["cache"] && @options["cache"]["type"] if @options["cache"] && @options["cache"]["type"]
@@ -123,4 +138,16 @@ class Kamal::Configuration::Builder
def cache_to_config_for_registry def cache_to_config_for_registry
[ "type=registry", @options["cache"]&.fetch("options", nil), "ref=#{cache_image_ref}" ].compact.join(",") [ "type=registry", @options["cache"]&.fetch("options", nil), "ref=#{cache_image_ref}" ].compact.join(",")
end end
def repo_basename
File.basename(Kamal::Git.root)
end
def repo_relative_pwd
Dir.pwd.delete_prefix(Kamal::Git.root)
end
def pwd_sha
Digest::SHA256.hexdigest(Dir.pwd)[0..12]
end
end end

View File

@@ -16,4 +16,8 @@ module Kamal::Git
def uncommitted_changes def uncommitted_changes
`git status --porcelain`.strip `git status --porcelain`.strip
end end
def root
`git rev-parse --show-toplevel`.strip
end
end end

View File

@@ -9,32 +9,84 @@ class CliBuildTest < CliTestCase
end end
test "push" do test "push" do
with_build_directory do
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "build", subcommand: "push" }
run_command("push", "--verbose").tap do |output|
assert_hook_ran "pre-build", output, **hook_variables
assert_match /Cloning repo into build directory/, output
assert_match /git -C #{Dir.tmpdir}\/kamal-clones\/app-#{pwd_sha} clone #{Dir.pwd}/, output
assert_match /docker --version && docker buildx version/, output
assert_match /docker buildx build --push --platform linux\/amd64,linux\/arm64 --builder kamal-app-multiarch -t dhh\/app:999 -t dhh\/app:latest --label service="app" --file Dockerfile \. as .*@localhost/, output
end
end
end
test "push reseting clone" do
with_build_directory do
stub_setup
build_dir = "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}/kamal/"
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:docker, "--version", "&&", :docker, :buildx, "version")
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:docker, :buildx, :create, "--use", "--name", "kamal-app-multiarch")
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:mkdir, "-p", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}")
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with(:git, "-C", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}", :clone, Dir.pwd)
.raises(SSHKit::Command::Failed.new("fatal: destination path 'kamal' already exists and is not an empty directory"))
.then
.returns(true)
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:git, "-C", build_dir, :remote, "set-url", :origin, Dir.pwd)
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:git, "-C", build_dir, :fetch, :origin)
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:git, "-C", build_dir, :reset, "--hard", Kamal::Git.revision)
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:git, "-C", build_dir, :clean, "-fdx")
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with(:docker, :buildx, :build, "--push", "--platform", "linux/amd64,linux/arm64", "--builder", "kamal-app-multiarch", "-t", "dhh/app:999", "-t", "dhh/app:latest", "--label", "service=\"app\"", "--file", "Dockerfile", ".")
run_command("push", "--verbose").tap do |output|
assert_match /Cloning repo into build directory/, output
assert_match /Resetting local clone/, output
end
end
end
test "push without clone" do
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true) Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "build", subcommand: "push" } hook_variables = { version: 999, service_version: "app@999", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "build", subcommand: "push" }
run_command("push", "--verbose").tap do |output| run_command("push", "--verbose", fixture: :without_clone).tap do |output|
assert_no_match /Cloning repo into build directory/, output
assert_hook_ran "pre-build", output, **hook_variables assert_hook_ran "pre-build", output, **hook_variables
assert_match /docker --version && docker buildx version/, output assert_match /docker --version && docker buildx version/, output
assert_match /git archive -tar HEAD | docker buildx build --push --platform linux\/amd64,linux\/arm64 --builder kamal-app-multiarch -t dhh\/app:999 -t dhh\/app:latest --label service="app" --file Dockerfile - as .*@localhost/, output assert_match /docker buildx build --push --platform linux\/amd64,linux\/arm64 --builder kamal-app-multiarch -t dhh\/app:999 -t dhh\/app:latest --label service="app" --file Dockerfile . as .*@localhost/, output
end end
end end
test "push without builder" do test "push without builder" do
stub_setup with_build_directory do
SSHKit::Backend::Abstract.any_instance.stubs(:execute) stub_setup
.with(:docker, "--version", "&&", :docker, :buildx, "version")
SSHKit::Backend::Abstract.any_instance.stubs(:execute) SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with(:docker, :buildx, :create, "--use", "--name", "kamal-app-multiarch") .with(:docker, "--version", "&&", :docker, :buildx, "version")
SSHKit::Backend::Abstract.any_instance.stubs(:execute) SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*args| p args[0..6]; args[0..6] == [ :git, :archive, "--format=tar", :HEAD, "|", :docker, :buildx ] } .with(:docker, :buildx, :create, "--use", "--name", "kamal-app-multiarch")
.raises(SSHKit::Command::Failed.new("no builder"))
.then
.returns(true)
run_command("push").tap do |output| SSHKit::Backend::Abstract.any_instance.stubs(:execute)
assert_match /WARN Missing compatible builder, so creating a new one first/, output .with { |*args| args[0..1] == [ :docker, :buildx ] }
.raises(SSHKit::Command::Failed.new("no builder"))
.then
.returns(true)
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with { |*args| args.first.start_with?("git") }
SSHKit::Backend::Abstract.any_instance.stubs(:execute).with(:mkdir, "-p", "#{Dir.tmpdir}/kamal-clones/app-#{pwd_sha}")
run_command("push").tap do |output|
assert_match /WARN Missing compatible builder, so creating a new one first/, output
end
end end
end end
@@ -118,4 +170,17 @@ class CliBuildTest < CliTestCase
SSHKit::Backend::Abstract.any_instance.stubs(:execute) SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*args| args[0..1] == [ :docker, :buildx ] } .with { |*args| args[0..1] == [ :docker, :buildx ] }
end end
def with_build_directory
build_directory = File.join Dir.tmpdir, "kamal-clones", "app-#{pwd_sha}", "kamal"
FileUtils.mkdir_p build_directory
FileUtils.touch File.join build_directory, "Dockerfile"
yield
ensure
FileUtils.rm_rf build_directory
end
def pwd_sha
Digest::SHA256.hexdigest(Dir.pwd)[0..12]
end
end end

View File

@@ -9,7 +9,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
builder = new_builder_command(builder: { "cache" => { "type" => "gha" } }) builder = new_builder_command(builder: { "cache" => { "type" => "gha" } })
assert_equal "multiarch", builder.name assert_equal "multiarch", builder.name
assert_equal \ assert_equal \
"git archive --format=tar HEAD | docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile -", "docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -17,7 +17,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
builder = new_builder_command(builder: { "multiarch" => false }) builder = new_builder_command(builder: { "multiarch" => false })
assert_equal "native", builder.name assert_equal "native", builder.name
assert_equal \ assert_equal \
"git archive --format=tar HEAD | docker build -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile - && docker push dhh/app:123 && docker push dhh/app:latest", "docker build -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile . && docker push dhh/app:123 && docker push dhh/app:latest",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -25,7 +25,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
builder = new_builder_command(builder: { "multiarch" => false, "cache" => { "type" => "gha" } }) builder = new_builder_command(builder: { "multiarch" => false, "cache" => { "type" => "gha" } })
assert_equal "native/cached", builder.name assert_equal "native/cached", builder.name
assert_equal \ assert_equal \
"git archive --format=tar HEAD | docker buildx build --push -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile -", "docker buildx build --push -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -33,7 +33,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
builder = new_builder_command(builder: { "local" => {}, "remote" => {}, "cache" => { "type" => "gha" } }) builder = new_builder_command(builder: { "local" => {}, "remote" => {}, "cache" => { "type" => "gha" } })
assert_equal "multiarch/remote", builder.name assert_equal "multiarch/remote", builder.name
assert_equal \ assert_equal \
"git archive --format=tar HEAD | docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch-remote -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile -", "docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch-remote -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -41,7 +41,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
builder = new_builder_command(builder: { "local" => { "arch" => "amd64" } }) builder = new_builder_command(builder: { "local" => { "arch" => "amd64" } })
assert_equal "multiarch", builder.name assert_equal "multiarch", builder.name
assert_equal \ assert_equal \
"git archive --format=tar HEAD | docker buildx build --push --platform linux/amd64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile -", "docker buildx build --push --platform linux/amd64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --file Dockerfile .",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -49,7 +49,7 @@ class CommandsBuilderTest < ActiveSupport::TestCase
builder = new_builder_command(builder: { "remote" => { "arch" => "amd64" }, "cache" => { "type" => "gha" } }) builder = new_builder_command(builder: { "remote" => { "arch" => "amd64" }, "cache" => { "type" => "gha" } })
assert_equal "native/remote", builder.name assert_equal "native/remote", builder.name
assert_equal \ assert_equal \
"git archive --format=tar HEAD | docker buildx build --push --platform linux/amd64 --builder kamal-app-native-remote -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile -", "docker buildx build --push --platform linux/amd64 --builder kamal-app-native-remote -t dhh/app:123 -t dhh/app:latest --cache-to type=gha --cache-from type=gha --label service=\"app\" --file Dockerfile .",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -100,21 +100,21 @@ class CommandsBuilderTest < ActiveSupport::TestCase
test "native push with build args" do test "native push with build args" do
builder = new_builder_command(builder: { "multiarch" => false, "args" => { "a" => 1, "b" => 2 } }) builder = new_builder_command(builder: { "multiarch" => false, "args" => { "a" => 1, "b" => 2 } })
assert_equal \ assert_equal \
"git archive --format=tar HEAD | docker build -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --build-arg a=\"1\" --build-arg b=\"2\" --file Dockerfile - && docker push dhh/app:123 && docker push dhh/app:latest", "docker build -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --build-arg a=\"1\" --build-arg b=\"2\" --file Dockerfile . && docker push dhh/app:123 && docker push dhh/app:latest",
builder.push.join(" ") builder.push.join(" ")
end end
test "multiarch push with build args" do test "multiarch push with build args" do
builder = new_builder_command(builder: { "args" => { "a" => 1, "b" => 2 } }) builder = new_builder_command(builder: { "args" => { "a" => 1, "b" => 2 } })
assert_equal \ assert_equal \
"git archive --format=tar HEAD | docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --build-arg a=\"1\" --build-arg b=\"2\" --file Dockerfile -", "docker buildx build --push --platform linux/amd64,linux/arm64 --builder kamal-app-multiarch -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --build-arg a=\"1\" --build-arg b=\"2\" --file Dockerfile .",
builder.push.join(" ") builder.push.join(" ")
end end
test "native push with build secrets" do test "native push with build secrets" do
builder = new_builder_command(builder: { "multiarch" => false, "secrets" => [ "a", "b" ] }) builder = new_builder_command(builder: { "multiarch" => false, "secrets" => [ "a", "b" ] })
assert_equal \ assert_equal \
"git archive --format=tar HEAD | docker build -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --secret id=\"a\" --secret id=\"b\" --file Dockerfile - && docker push dhh/app:123 && docker push dhh/app:latest", "docker build -t dhh/app:123 -t dhh/app:latest --label service=\"app\" --secret id=\"a\" --secret id=\"b\" --file Dockerfile . && docker push dhh/app:123 && docker push dhh/app:latest",
builder.push.join(" ") builder.push.join(" ")
end end
@@ -162,4 +162,8 @@ class CommandsBuilderTest < ActiveSupport::TestCase
def new_builder_command(additional_config = {}) def new_builder_command(additional_config = {})
Kamal::Commands::Builder.new(Kamal::Configuration.new(@config.merge(additional_config), version: "123")) Kamal::Commands::Builder.new(Kamal::Configuration.new(@config.merge(additional_config), version: "123"))
end end
def build_directory
"#{Dir.tmpdir}/kamal-clones/app/kamal/"
end
end end

View File

@@ -140,7 +140,7 @@ class ConfigurationBuilderTest < ActiveSupport::TestCase
end end
test "context" do test "context" do
assert_equal "-", @config.builder.context assert_equal ".", @config.builder.context
end end
test "setting context" do test "setting context" do

39
test/fixtures/deploy_without_clone.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
service: app
image: dhh/app
servers:
web:
- "1.1.1.1"
- "1.1.1.2"
workers:
- "1.1.1.3"
- "1.1.1.4"
registry:
username: user
password: pw
accessories:
mysql:
image: mysql:5.7
host: 1.1.1.3
port: 3306
env:
clear:
MYSQL_ROOT_HOST: '%'
secret:
- MYSQL_ROOT_PASSWORD
files:
- test/fixtures/files/my.cnf:/etc/mysql/my.cnf
directories:
- data:/var/lib/mysql
redis:
image: redis:latest
roles:
- web
port: 6379
directories:
- data:/data
readiness_delay: 0
builder:
context: "."