boringbuilder 0.1.0.alpha.3 → 0.1.0.alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 823ab9ebc22d049c27671d07ea1be44315dbdf3f63cdd29c46cd209e6e5fc279
4
- data.tar.gz: 58c38219ef59ff3bf7515d10140be7500784bf276f5b0ea71edd965d7ae10b86
3
+ metadata.gz: 265bb7643d090142fc6a68cba78809ffabf6dd3d5436e8a605d1e5e69848984c
4
+ data.tar.gz: ac30aa3a7160d742ce7742c4c1f86720deb5061a8c8eb85a4a6c57c7e79c7b13
5
5
  SHA512:
6
- metadata.gz: 7812ac305fa0015e0007049f6272a81fcf237db37304522323bad5a7a9947ac2f6e77d9e9788cf57490c31d680c17bda5aa3cc025cc19a4b310f9711b080d907
7
- data.tar.gz: 67a48f5ce9298763f588c2e979a3bcca6b1a38a9c8f9350597f82876c69168c644b480affc62728d27ad1c8d114071267fbc7592a5703446441dc385b3459357
6
+ metadata.gz: 1774754066715572ee3d1946642e00e07baa3b5d5f97626e04eb76b45d4e5ec6a226627e8033ead3967591082b41b2cff71cb730320df1026333495e7048ec3f
7
+ data.tar.gz: 391c319a4fd5e8a8c8eb634efc31f45df7bc2ecf4b2357e8a90deae236785c34dc88cdce53d1b4f154bed96950b27b8453cf4bd4e7957ae9a76d6db523c42f86
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0.alpha.5 - 2026-08-28
4
+
5
+ - Record the exact local or BoringCache build result for deployment tools.
6
+ - Finalize Rails runtime metadata without starting the application command.
7
+ - Publish each release with the matching human changelog notes.
8
+
9
+ ## 0.1.0.alpha.4 - 2026-08-28
10
+
11
+ - Expose the Rails build task as `boring:build`.
12
+ - Run the Rails task through the same pretty, streaming CLI experience as
13
+ `boringbuilder build`.
14
+ - Keep BoringCache Artifact receipt JSON out of streamed build logs.
15
+
3
16
  ## 0.1.0.alpha.3 - 2026-08-27
4
17
 
5
18
  - Remove Bundler cache metadata before compiling assets so cold and restored Rails builds stay identical.
data/README.md CHANGED
@@ -70,7 +70,7 @@ boringbuilder build --runtime docker --platform linux/arm64
70
70
  Rails applications can also use the included task:
71
71
 
72
72
  ```sh
73
- bin/rails boringbuilder:build
73
+ bin/rails boring:build
74
74
  ```
75
75
 
76
76
  ## Caching
@@ -85,7 +85,8 @@ See [Caching](docs/caching.md) for custom cached steps and restore-only builds.
85
85
 
86
86
  The default `tar.zst` output is intended for atomic filesystem deployers such as BoringDeploy. Without shared
87
87
  credentials it is written under `dist/`. With BoringCache save credentials it is published as a shared BoringCache
88
- Artifact instead; pass `--output` when a local copy is also wanted.
88
+ Artifact instead; pass `--output` when a local copy is also wanted. Each successful export records its exact result in
89
+ `tmp/builds/boringbuilder.json` so deployment tools can consume the same artifact without guessing.
89
90
 
90
91
  The same build can produce or publish a container image:
91
92
 
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "json"
5
+ require "open3"
6
+ require "pathname"
7
+ require "tempfile"
8
+ require "time"
9
+
10
+ module BoringBuilder
11
+ class BuildReceipt
12
+ RELATIVE_PATH = "tmp/builds/boringbuilder.json"
13
+
14
+ def self.write(root:, result:, clock: Time, command_runner: Open3.method(:capture3))
15
+ return unless result.exported? || result.artifact_published? || result.published?
16
+
17
+ root = Pathname.new(root).expand_path
18
+ path = root.join(RELATIVE_PATH)
19
+ FileUtils.mkdir_p(path.dirname)
20
+ document = {
21
+ schema_version: 1,
22
+ created_at: clock.now.utc.iso8601,
23
+ source_revision: source_revision(root, command_runner),
24
+ result: result.to_h
25
+ }
26
+
27
+ Tempfile.create(["boringbuilder", ".json"], path.dirname) do |file|
28
+ file.write(JSON.pretty_generate(document))
29
+ file.write("\n")
30
+ file.flush
31
+ file.fsync
32
+ File.rename(file.path, path)
33
+ end
34
+
35
+ path
36
+ end
37
+
38
+ def self.source_revision(root, command_runner)
39
+ stdout, _stderr, status = command_runner.call("git", "-C", root.to_s, "rev-parse", "HEAD")
40
+ status.success? ? stdout.strip : nil
41
+ rescue Errno::ENOENT
42
+ nil
43
+ end
44
+
45
+ private_class_method :source_revision
46
+ end
47
+ end
@@ -21,10 +21,12 @@ module BoringBuilder
21
21
  silent: configuration.progress.nil?
22
22
  )
23
23
 
24
- DaggerRuby.connection(dagger_configuration) do |client|
24
+ result = DaggerRuby.connection(dagger_configuration) do |client|
25
25
  container = project.container(client, progress: progress)
26
26
  Exporter.new(project, client, container, runtime: resolved_runtime, progress: progress).call
27
27
  end
28
+ BuildReceipt.write(root: configuration.root, result: result)
29
+ result
28
30
  rescue DaggerRuby::DaggerError => e
29
31
  message = e.message.sub(/\s*\[traceparent:[^\]]+\]\s*\z/, "")
30
32
  raise BuildError, "Dagger build failed: #{message}"
@@ -5,6 +5,8 @@ require "json"
5
5
  module BoringBuilder
6
6
  module Exporters
7
7
  class BoringCache
8
+ RECEIPT_PATH = "/tmp/boringbuilder-artifact.json"
9
+
8
10
  attr_reader :project, :client, :cache
9
11
 
10
12
  def initialize(project, client, environment: ENV)
@@ -31,7 +33,8 @@ module BoringBuilder
31
33
  publisher = client.container.from(cache.cli_image)
32
34
  publisher = cache.prepare_artifact_publisher(publisher)
33
35
  path, publisher = mount_asset(publisher, asset)
34
- output = publisher.with_exec(publish_command(path, asset.kind)).stdout
36
+ publisher = publisher.with_exec(quiet_publish_command(path, asset.kind))
37
+ output = publisher.file(RECEIPT_PATH).contents
35
38
  artifact = JSON.parse(output).fetch("artifact")
36
39
  validate_artifact!(artifact)
37
40
  Receipt.new(artifact_id: artifact.fetch("id"), artifact_name: artifact.fetch("name"))
@@ -73,6 +76,10 @@ module BoringBuilder
73
76
  command
74
77
  end
75
78
 
79
+ def quiet_publish_command(path, kind)
80
+ ["sh", "-c", "exec \"$@\" > #{RECEIPT_PATH}", "boringbuilder-artifact", *publish_command(path, kind)]
81
+ end
82
+
76
83
  def validate_artifact!(artifact)
77
84
  return if artifact.fetch("id", "").start_with?("art_") && artifact.fetch("status", nil) == "ready"
78
85
 
@@ -3,7 +3,7 @@
3
3
  module BoringBuilder
4
4
  class Railtie < Rails::Railtie
5
5
  rake_tasks do
6
- load File.expand_path("tasks/boringbuilder.rake", __dir__)
6
+ load File.expand_path("tasks/boring.rake", __dir__)
7
7
  end
8
8
  end
9
9
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BoringBuilder
4
+ class ReleaseNotes
5
+ def self.read(path:, version:)
6
+ sections = File.read(path).lines
7
+ heading = /^## #{Regexp.escape(version)} - \d{4}-\d{2}-\d{2}\s*$/
8
+ start = sections.index { |line| line.match?(heading) }
9
+ raise ArgumentError, "CHANGELOG.md has no release notes for #{version}" unless start
10
+
11
+ finish = sections.each_index.find { |index| index > start && sections[index].start_with?("## ") }
12
+ notes = sections[(start + 1)...finish].join.strip
13
+ raise ArgumentError, "CHANGELOG.md release notes for #{version} are empty" if notes.empty?
14
+
15
+ notes
16
+ end
17
+ end
18
+ end
@@ -173,8 +173,9 @@ module BoringBuilder
173
173
  name: "[runtime] RUN mise reshim",
174
174
  workdir: application_path
175
175
  )
176
- container = runtime_metadata(container).with_user(project.application_user)
177
- progress.step("[runtime] Configure image") { container.sync }
176
+ container = container.with_user(project.application_user)
177
+ container = progress.step("[runtime] Configure image") { container.sync }
178
+ runtime_metadata(container)
178
179
  end
179
180
 
180
181
  def install_packages(container, packages, stage:)
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :boring do
4
+ desc "Build the Rails application with Dagger"
5
+ task build: :environment do
6
+ command = ["build"]
7
+ {
8
+ "--runtime" => "BORINGBUILDER_RUNTIME",
9
+ "--platform" => "BORINGBUILDER_PLATFORM",
10
+ "--format" => "BORINGBUILDER_FORMAT",
11
+ "--output" => "BORINGBUILDER_OUTPUT",
12
+ "--push" => "BORINGBUILDER_PUSH",
13
+ "--load" => "BORINGBUILDER_LOAD",
14
+ "--progress" => "BORINGBUILDER_PROGRESS",
15
+ "--exporter" => "BORINGBUILDER_EXPORTER",
16
+ "--artifact-name" => "BORINGBUILDER_ARTIFACT_NAME"
17
+ }.each do |option, environment_name|
18
+ value = ENV.fetch(environment_name, nil)
19
+ command.push(option, value) if value && !value.empty?
20
+ end
21
+
22
+ {
23
+ "--path" => "BORINGBUILDER_PATHS",
24
+ "--file" => "BORINGBUILDER_FILES",
25
+ "--host-path" => "BORINGBUILDER_HOST_PATHS"
26
+ }.each do |option, environment_name|
27
+ values = ENV.fetch(environment_name, "").split(",").reject(&:empty?)
28
+ values.each { |value| command.push(option, value) }
29
+ end
30
+
31
+ command << Rails.root.to_s
32
+ status = BoringBuilder::CLI.start(command)
33
+ raise BoringBuilder::BuildError, "BoringBuilder exited unsuccessfully" unless status.zero?
34
+ end
35
+
36
+ desc "Check the configured container runtime"
37
+ task :doctor do
38
+ requested = ENV.fetch("BORINGBUILDER_RUNTIME", "auto")
39
+ runtime = BoringBuilder::Runtime.new(requested).resolve
40
+ puts "Dagger runtime ready: #{runtime}"
41
+ end
42
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BoringBuilder
4
- VERSION = "0.1.0.alpha.3"
4
+ VERSION = "0.1.0.alpha.5"
5
5
  end
data/lib/boringbuilder.rb CHANGED
@@ -20,6 +20,7 @@ require_relative "boringbuilder/ruby_build"
20
20
  require_relative "boringbuilder/rails_build"
21
21
  require_relative "boringbuilder/exporter"
22
22
  require_relative "boringbuilder/result"
23
+ require_relative "boringbuilder/build_receipt"
23
24
  require_relative "boringbuilder/builder"
24
25
  require_relative "boringbuilder/cli"
25
26
  require_relative "boringbuilder/railtie" if defined?(Rails::Railtie)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boringbuilder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.alpha.3
4
+ version: 0.1.0.alpha.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - BoringCache
@@ -47,6 +47,7 @@ files:
47
47
  - lib/boringbuilder.rb
48
48
  - lib/boringbuilder/artifact.rb
49
49
  - lib/boringbuilder/boring_cache.rb
50
+ - lib/boringbuilder/build_receipt.rb
50
51
  - lib/boringbuilder/builder.rb
51
52
  - lib/boringbuilder/cli.rb
52
53
  - lib/boringbuilder/config_file.rb
@@ -66,11 +67,12 @@ files:
66
67
  - lib/boringbuilder/project_plan.rb
67
68
  - lib/boringbuilder/rails_build.rb
68
69
  - lib/boringbuilder/railtie.rb
70
+ - lib/boringbuilder/release_notes.rb
69
71
  - lib/boringbuilder/result.rb
70
72
  - lib/boringbuilder/ruby_application.rb
71
73
  - lib/boringbuilder/ruby_build.rb
72
74
  - lib/boringbuilder/runtime.rb
73
- - lib/boringbuilder/tasks/boringbuilder.rake
75
+ - lib/boringbuilder/tasks/boring.rake
74
76
  - lib/boringbuilder/version.rb
75
77
  homepage: https://github.com/boringcache/builder
76
78
  licenses:
@@ -78,7 +80,7 @@ licenses:
78
80
  metadata:
79
81
  allowed_push_host: https://rubygems.org
80
82
  homepage_uri: https://github.com/boringcache/builder
81
- source_code_uri: https://github.com/boringcache/builder/tree/v0.1.0.alpha.3
83
+ source_code_uri: https://github.com/boringcache/builder/tree/v0.1.0.alpha.5
82
84
  documentation_uri: https://github.com/boringcache/builder#readme
83
85
  changelog_uri: https://github.com/boringcache/builder/blob/main/CHANGELOG.md
84
86
  bug_tracker_uri: https://github.com/boringcache/builder/issues
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- namespace :boringbuilder do
4
- desc "Build the Rails application with Dagger"
5
- task build: :environment do
6
- options = {}
7
- {
8
- runtime: "BORINGBUILDER_RUNTIME",
9
- platform: "BORINGBUILDER_PLATFORM",
10
- format: "BORINGBUILDER_FORMAT",
11
- output: "BORINGBUILDER_OUTPUT",
12
- publish: "BORINGBUILDER_PUSH",
13
- load: "BORINGBUILDER_LOAD"
14
- }.each do |option, environment_name|
15
- value = ENV.fetch(environment_name, nil)
16
- options[option] = value if value && !value.empty?
17
- end
18
-
19
- {
20
- paths: "BORINGBUILDER_PATHS",
21
- files: "BORINGBUILDER_FILES",
22
- host_paths: "BORINGBUILDER_HOST_PATHS"
23
- }.each do |option, environment_name|
24
- values = ENV.fetch(environment_name, "").split(",").reject(&:empty?)
25
- options[option] = values unless values.empty?
26
- end
27
-
28
- result = BoringBuilder.build(root: Rails.root, **options)
29
- puts "Built with Dagger on #{result.runtime}"
30
- puts "Exported #{result.format}: #{result.path}" if result.exported?
31
- puts "Image: #{result.reference}" if result.published?
32
- end
33
-
34
- desc "Check the configured container runtime"
35
- task :doctor do
36
- requested = ENV.fetch("BORINGBUILDER_RUNTIME", "auto")
37
- runtime = BoringBuilder::Runtime.new(requested).resolve
38
- puts "Dagger runtime ready: #{runtime}"
39
- end
40
- end