From 6ccb3d231990fa9b361d2aa5dd629b0f62a1fd3d Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 13 Jan 2023 09:31:47 +0100 Subject: [PATCH] Allow for fully native builds too Skipping multiarch if there's a platform match between dev and prod. --- lib/mrsk/commands/builder.rb | 50 ++++++++--------- lib/mrsk/commands/builder/multiarch.rb | 19 +++++++ lib/mrsk/commands/builder/multiarch/remote.rb | 19 +++++++ lib/mrsk/commands/builder/native.rb | 19 +++++++ lib/tasks/mrsk/build.rake | 54 ++++++++----------- test/builder_command_test.rb | 24 +++++++++ 6 files changed, 129 insertions(+), 56 deletions(-) create mode 100644 lib/mrsk/commands/builder/multiarch.rb create mode 100644 lib/mrsk/commands/builder/multiarch/remote.rb create mode 100644 lib/mrsk/commands/builder/native.rb create mode 100644 test/builder_command_test.rb diff --git a/lib/mrsk/commands/builder.rb b/lib/mrsk/commands/builder.rb index 9d910d4f..d2b9cf1f 100644 --- a/lib/mrsk/commands/builder.rb +++ b/lib/mrsk/commands/builder.rb @@ -1,37 +1,39 @@ require "mrsk/commands/base" class Mrsk::Commands::Builder < Mrsk::Commands::Base - def create - docker :buildx, :create, "--use", "--name", "mrsk" + delegate :create, :remove, :push, :pull, to: :target + delegate :native?, :multiarch?, :remote?, to: :name + + def name + target.class.to_s.demodulize.downcase.inquiry end - def remove - docker :buildx, :rm, "mrsk" + def target + case + when config.builder.nil? + multiarch + when config.builder["multiarch"] == false + native + when config.builder["local"] && config.builder["local"] + multiarch_remote + else + raise ArgumentError, "Builder configuration incorrect: #{config.builder.inspect}" + end end - def push - docker :buildx, :build, "--push", "--platform linux/amd64,linux/arm64", "-t", config.absolute_image, "." + def native + @native ||= Mrsk::Commands::Builder::Native.new(config) end - def pull - docker :pull, config.absolute_image + def multiarch + @multiarch ||= Mrsk::Commands::Builder::Multiarch.new(config) end - - def create_context(arch, host) - docker :context, :create, "mrsk-#{arch}", "--description", "'MRSK #{arch} Native Host'", "--docker", "'host=#{host}'" - end - - def remove_context(arch) - docker :context, :rm, "mrsk-#{arch}" - end - - - def create_with_context(arch) - docker :buildx, :create, "--use", "--name", "mrsk", "mrsk-#{arch}", "--platform", "linux/#{arch}" - end - - def append_context(arch) - docker :buildx, :create, "--append", "--name", "mrsk", "mrsk-#{arch}", "--platform", "linux/#{arch}" + def multiarch_remote + @multiarch_remote ||= Mrsk::Commands::Builder::Multiarch::Remote.new(config) end end + +require "mrsk/commands/builder/native" +require "mrsk/commands/builder/multiarch" +require "mrsk/commands/builder/multiarch/remote" diff --git a/lib/mrsk/commands/builder/multiarch.rb b/lib/mrsk/commands/builder/multiarch.rb new file mode 100644 index 00000000..8639e1c6 --- /dev/null +++ b/lib/mrsk/commands/builder/multiarch.rb @@ -0,0 +1,19 @@ +require "mrsk/commands/base" + +class Mrsk::Commands::Builder::Multiarch < Mrsk::Commands::Base + def create + docker :buildx, :create, "--use", "--name", "mrsk" + end + + def remove + docker :buildx, :rm, "mrsk" + end + + def push + docker :buildx, :build, "--push", "--platform linux/amd64,linux/arm64", "-t", config.absolute_image, "." + end + + def pull + docker :pull, config.absolute_image + end +end diff --git a/lib/mrsk/commands/builder/multiarch/remote.rb b/lib/mrsk/commands/builder/multiarch/remote.rb new file mode 100644 index 00000000..125e11bd --- /dev/null +++ b/lib/mrsk/commands/builder/multiarch/remote.rb @@ -0,0 +1,19 @@ +require "mrsk/commands/builder/multiarch" + +class Mrsk::Commands::Builder::Multiarch::Remote < Mrsk::Commands::Builder::Multiarch + def create(arch) + super + [ "mrsk-#{arch}", "--platform", "linux/#{arch}" ] + end + + def append(arch) + docker :buildx, :create, "--append", "--name", "mrsk", "mrsk-#{arch}", "--platform", "linux/#{arch}" + end + + def create_context(arch, host) + docker :context, :create, "mrsk-#{arch}", "--description", "'MRSK #{arch} Native Host'", "--docker", "'host=#{host}'" + end + + def remove_context(arch) + docker :context, :rm, "mrsk-#{arch}" + end +end diff --git a/lib/mrsk/commands/builder/native.rb b/lib/mrsk/commands/builder/native.rb new file mode 100644 index 00000000..bb41ec78 --- /dev/null +++ b/lib/mrsk/commands/builder/native.rb @@ -0,0 +1,19 @@ +require "mrsk/commands/base" + +class Mrsk::Commands::Builder::Native < Mrsk::Commands::Base + def create + # No op on native + end + + def remove + # No op on native + end + + def push + docker :build, "--push", "-t", config.absolute_image, "." + end + + def pull + docker :pull, config.absolute_image + end +end diff --git a/lib/tasks/mrsk/build.rake b/lib/tasks/mrsk/build.rake index 8264bf89..7d49e529 100644 --- a/lib/tasks/mrsk/build.rake +++ b/lib/tasks/mrsk/build.rake @@ -13,7 +13,7 @@ namespace :mrsk do execute *MRSK.builder.push rescue SSHKit::Command::Failed => e error "Missing compatible buildx builder, so creating a new one first" - execute *MRSK.builder.create + Rake::Task["mrsk:build:create"].invoke execute *MRSK.builder.push end end unless ENV["VERSION"] @@ -24,17 +24,25 @@ namespace :mrsk do on(MRSK.config.hosts) { execute *MRSK.builder.pull } end - desc "Create a local buildx setup to produce multi-arch images" + desc "Create a local build setup" task :create do run_locally do - execute *MRSK.builder.create + if MRSK.builder.remote? + Rake::Task["mrsk:build:remote:create"].invoke + else + execute *MRSK.builder.create + end end end - desc "Remove local buildx setup" + desc "Remove local build setup" task :remove do run_locally do - execute *MRSK.builder.remove + if MRSK.builder.remote? + Rake::Task["mrsk:build:remote:create"].invoke + else + execute *MRSK.builder.remove + end end end @@ -44,28 +52,16 @@ namespace :mrsk do namespace :create do task :context do - if MRSK.config.builder && - (local = MRSK.config.builder["local"]) && - (remote = MRSK.config.builder["remote"]) - run_locally do - execute *MRSK.builder.create_context(local["arch"], local["host"]) - execute *MRSK.builder.create_context(remote["arch"], remote["host"]) - end - else - error "Missing configuration of builder:local/remote in config" + run_locally do + execute *MRSK.builder.create_context(local["arch"], local["host"]) + execute *MRSK.builder.create_context(remote["arch"], remote["host"]) end end task :buildx do - if MRSK.config.builder && - (local = MRSK.config.builder["local"]) && - (remote = MRSK.config.builder["remote"]) - run_locally do - execute *MRSK.builder.create_with_context(local["arch"]) - execute *MRSK.builder.append_context(remote["arch"]) - end - else - error "Missing configuration of builder:local/remote in config" + run_locally do + execute *MRSK.builder.create_with_context(local["arch"]) + execute *MRSK.builder.append_context(remote["arch"]) end end end @@ -76,15 +72,9 @@ namespace :mrsk do namespace :remove do task :context do - if MRSK.config.builder && - (local = MRSK.config.builder["local"]) && - (remote = MRSK.config.builder["remote"]) - run_locally do - execute *MRSK.builder.remove_context(local["arch"]) - execute *MRSK.builder.remove_context(remote["arch"]) - end - else - error "Missing configuration of builder:local/remote in config" + run_locally do + execute *MRSK.builder.remove_context(local["arch"]) + execute *MRSK.builder.remove_context(remote["arch"]) end end end diff --git a/test/builder_command_test.rb b/test/builder_command_test.rb new file mode 100644 index 00000000..2890c910 --- /dev/null +++ b/test/builder_command_test.rb @@ -0,0 +1,24 @@ +require "test_helper" +require "mrsk/configuration" +require "mrsk/commands/builder" + +class BuilderCommandTest < ActiveSupport::TestCase + setup do + @config = { service: "app", image: "dhh/app", registry: { "username" => "dhh", "password" => "secret" }, servers: [ "1.1.1.1" ] } + end + + test "target multiarch by default" do + builder = Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config)) + assert builder.multiarch? + end + + test "target native when multiarch is off" do + builder = Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config.merge({ builder: { "multiarch" => false } }))) + assert builder.native? + end + + test "target multiarch remote when local and remote is set" do + builder = Mrsk::Commands::Builder.new(Mrsk::Configuration.new(@config.merge({ builder: { "local" => { }, "remote" => { } } }))) + assert builder.remote? + end +end