Skip to content

Commit

Permalink
Allow overriding compose env for local builds
Browse files Browse the repository at this point in the history
Now we can write this very simple pipeline for debugging builds locally.

```ruby
Buildkite::Builder.pipeline do
  require "buildkite_config"
  use Buildkite::Config::BuildContext
  use Buildkite::Config::DockerBuild
  use Buildkite::Config::RakeCommand
  use Buildkite::Config::RubyGroup

  plugin :docker_compose, "docker-compose#v4.16.0"
  plugin :artifacts, "artifacts#v1.9.3"

  build_context.setup_rubies %w(3.3)

  group do
    label "build"
    build_context.rubies.each do |ruby|
      builder ruby, compose: {
        "cli_version": "2",
        "image-name": "buildkite_base",
        "cache-from": ["buildkite_base"],
        "push": "",
        "image-repository": "",
      }
    end
  end

  build_context.rubies.each do |ruby|
    ruby_group ruby do
      rake "actioncable", task: "test:integration", retry_on: { exit_status: -1, limit: 3 }, compose: {
        "cli_version": "2",
        "pull": "",
      }, env: {
        "IMAGE_NAME": "buildkite_base",
      }
    end
  end
end
```

:nail_care: whitespace
  • Loading branch information
zzak committed Nov 30, 2024
1 parent 13b395c commit d963c43
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 25 deletions.
24 changes: 13 additions & 11 deletions lib/buildkite/config/docker_build.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,23 @@ def prepare
end

dsl do
def builder(ruby)
def builder(ruby, compose: nil)
build_context = context.extensions.find(BuildContext)
build_context.ruby = ruby
return unless build_context.ruby.build?

command do
compose_options = {
build: "base",
config: ".buildkite/docker-compose.yml",
env: %w[PRE_STEPS RACK],
"image-name" => build_context.ruby.image_name_for(build_context.build_id),
"cache-from" => cache_from(build_context),
push: build_push(build_context),
"image-repository" => build_context.image_base,
}
compose_options.merge!(compose) if compose

label ":docker: #{build_context.ruby.prefix}#{build_context.ruby.version}"
key "docker-image-#{build_context.ruby.image_key}"
plugin :artifacts, {
Expand All @@ -66,16 +77,7 @@ def builder(ruby)
compressed: ".buildkite.tgz"
}

plugin :docker_compose, {
build: "base",
config: ".buildkite/docker-compose.yml",
env: %w[PRE_STEPS RACK],
"image-name" => build_context.ruby.image_name_for(build_context.build_id),
"cache-from" => cache_from(build_context),
push: build_push(build_context),
"image-repository" => build_context.image_base,
}

plugin :docker_compose, compose_options
env({
BUNDLER: build_context.bundler,
RUBYGEMS: build_context.rubygems,
Expand Down
31 changes: 17 additions & 14 deletions lib/buildkite/config/rake_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def build_env(build_context, pre_steps, env)
env ||= {}
pre_steps ||= []

env[:IMAGE_NAME] = build_context.image_name_for(build_context.build_id, prefix: nil)
env[:IMAGE_NAME] ||= build_context.image_name_for(build_context.build_id, prefix: nil)

if build_context.ruby.yjit_enabled?
env[:RUBY_YJIT_ENABLE] = "1"
Expand All @@ -33,7 +33,17 @@ def build_env(build_context, pre_steps, env)
env
end

def install_plugins(service = "default", env = nil, dir = ".")
def install_plugins(service = "default", env = nil, dir = ".", compose: nil)
compose_options = {
"env" => env,
"run" => service,
"pull" => service,
"pull-retries" => 3,
"config" => ".buildkite/docker-compose.yml",
"shell" => ["runner", *dir],
}
compose_options.merge!(compose) if compose

plugin :artifacts, {
download: ".dockerignore"
}
Expand All @@ -49,14 +59,7 @@ def install_plugins(service = "default", env = nil, dir = ".")
compressed: ".buildkite.tgz"
}

plugin :docker_compose, {
"env" => env,
"run" => service,
"pull" => service,
"pull-retries" => 3,
"config" => ".buildkite/docker-compose.yml",
"shell" => ["runner", *dir],
}.compact
plugin :docker_compose, compose_options.compact
end
end

Expand All @@ -65,15 +68,15 @@ def prepare
end

dsl do
def bundle(command, label:, env: nil)
def bundle(command, label:, env: nil, compose: nil)
build_context = context.extensions.find(BuildContext)

command do
label label
depends_on "docker-image-#{build_context.ruby.image_key}"
command command

install_plugins
install_plugins(compose: compose)

env build_env(build_context, nil, env)

Expand All @@ -85,7 +88,7 @@ def bundle(command, label:, env: nil)
end
end

def rake(dir, task: "test", label: nil, service: "default", pre_steps: nil, env: nil, retry_on: nil, soft_fail: nil, parallelism: nil)
def rake(dir, task: "test", label: nil, service: "default", pre_steps: nil, env: nil, retry_on: nil, soft_fail: nil, parallelism: nil, compose: nil)
build_context = context.extensions.find(BuildContext)

if task.start_with?("mysql2:") || (build_context.rails_version >= Gem::Version.new("7.1.0.alpha") && task.start_with?("trilogy:"))
Expand All @@ -99,7 +102,7 @@ def rake(dir, task: "test", label: nil, service: "default", pre_steps: nil, env:
depends_on "docker-image-#{build_context.ruby.image_key}"
command "rake #{task}"

install_plugins(service, %w[PRE_STEPS RACK], dir)
install_plugins(service, %w[PRE_STEPS RACK], dir, compose: compose)

env build_env(build_context, pre_steps, env)

Expand Down
28 changes: 28 additions & 0 deletions test/buildkite_config/test_docker_build.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,32 @@ def test_builder_gem_version
assert_equal ["base:buildkite-config-base:ruby-1-9-3-br-main"], compose["cache-from"]
assert_equal ["base:buildkite-config-base:ruby-1-9-3-br-"], compose["push"]
end

def test_builder_compose_options
pipeline = PipelineFixture.new do
use Buildkite::Config::DockerBuild

build_context.stub(:rails_version, Gem::Version.new("7.1")) do
builder Buildkite::Config::RubyConfig.new(version: "3.2"), compose: {
"cli_version": "2",
"image-name": "buildkite_base",
"cache-from": ["buildkite_base"],
"push": "",
"image-repository": "",
}
end
end

plugins = pipeline.to_h["steps"][0]["plugins"]

compose = plugins.find { |plugin|
plugin.key?("docker-compose#v1.0")
}.fetch("docker-compose#v1.0")

assert_equal "2", compose["cli_version"]
assert_equal "buildkite_base", compose["image-name"]
assert_equal ["buildkite_base"], compose["cache-from"]
assert_equal "", compose["push"]
assert_equal "", compose["image-repository"]
end
end
85 changes: 85 additions & 0 deletions test/buildkite_config/test_rake_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,21 @@ def test_env
assert_equal "buildkite-config-base:ruby-3-2-local", pipeline.to_h["steps"][0]["env"]["IMAGE_NAME"]
end

def test_env_image_name
pipeline = PipelineFixture.new do
build_context.ruby = Buildkite::Config::RubyConfig.new(prefix: "ruby:", version: Gem::Version.new("3.2"))
use Buildkite::Config::RakeCommand

build_context.stub(:rails_version, Gem::Version.new("7.1")) do
rake "test", task: "test:all", env: { "IMAGE_NAME": "override-at-command-definition" }
end
end

assert_includes pipeline.to_h["steps"][0], "env"
assert_includes pipeline.to_h["steps"][0]["env"], "IMAGE_NAME"
assert_equal "override-at-command-definition", pipeline.to_h["steps"][0]["env"]["IMAGE_NAME"]
end

def test_timeout_in_minutes
pipeline = PipelineFixture.new do
build_context.ruby = Buildkite::Config::RubyConfig.new(prefix: "ruby:", version: Gem::Version.new("3.2"))
Expand Down Expand Up @@ -220,6 +235,39 @@ def test_compose
assert_equal ["runner", "test"], compose["shell"]
end

def test_compose_options
pipeline = PipelineFixture.new do
build_context.ruby = Buildkite::Config::RubyConfig.new(prefix: "ruby:", version: Gem::Version.new("3.2"))
use Buildkite::Config::RakeCommand

build_context.stub(:rails_version, Gem::Version.new("7.1")) do
rake "test", task: "test:all", compose: {
"cli_version": "2",
"pull": "",
}
end
end

plugins = pipeline.to_h["steps"][0]["plugins"]

compose = plugins.find { |plugin|
plugin.key?("docker-compose#v1.0")
}.fetch("docker-compose#v1.0")

%w[env run pull config shell].each do |key|
assert_includes compose, key
end

assert_includes compose["env"], "PRE_STEPS"
assert_includes compose["env"], "RACK"

assert_equal "2", compose["cli_version"]
assert_equal "default", compose["run"]
assert_equal "", compose["pull"]
assert_equal ".buildkite/docker-compose.yml", compose["config"]
assert_equal ["runner", "test"], compose["shell"]
end

def test_multiple
pipeline = PipelineFixture.new do
build_context.ruby = Buildkite::Config::RubyConfig.new(version: Gem::Version.new("3.2"))
Expand Down Expand Up @@ -462,4 +510,41 @@ def test_bundle_command
assert_equal ["test-reports/*/*.xml"], step["artifact_paths"]
assert_equal 30, step["timeout_in_minutes"]
end

def test_bundle_command_options
pipeline = PipelineFixture.new do
build_context.ruby = Buildkite::Config::RubyConfig.new(prefix: "ruby:", version: Gem::Version.new("3.2"))
use Buildkite::Config::RakeCommand

build_context.stub(:rails_version, Gem::Version.new("7.1")) do
bundle "rubocop", label: "rubocop", compose: {
"cli_version": "2",
"pull": "",
}, env: {
"IMAGE_NAME": "override-at-command-definition",
}
end
end

step = pipeline.to_h["steps"][0]
assert_equal "override-at-command-definition", step["env"]["IMAGE_NAME"]

plugins = step["plugins"]

compose = plugins.find { |plugin|
plugin.key?("docker-compose#v1.0")
}.fetch("docker-compose#v1.0")

%w[run pull config shell].each do |key|
assert_includes compose, key
end

assert_predicate compose["env"], :nil?

assert_equal "2", compose["cli_version"]
assert_equal "default", compose["run"]
assert_equal "", compose["pull"]
assert_equal ".buildkite/docker-compose.yml", compose["config"]
assert_equal ["runner", "."], compose["shell"]
end
end

0 comments on commit d963c43

Please sign in to comment.