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 +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +3 -2
- data/lib/boringbuilder/build_receipt.rb +47 -0
- data/lib/boringbuilder/builder.rb +3 -1
- data/lib/boringbuilder/exporters/boring_cache.rb +8 -1
- data/lib/boringbuilder/railtie.rb +1 -1
- data/lib/boringbuilder/release_notes.rb +18 -0
- data/lib/boringbuilder/ruby_build.rb +3 -2
- data/lib/boringbuilder/tasks/boring.rake +42 -0
- data/lib/boringbuilder/version.rb +1 -1
- data/lib/boringbuilder.rb +1 -0
- metadata +5 -3
- data/lib/boringbuilder/tasks/boringbuilder.rake +0 -40
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 265bb7643d090142fc6a68cba78809ffabf6dd3d5436e8a605d1e5e69848984c
|
|
4
|
+
data.tar.gz: ac30aa3a7160d742ce7742c4c1f86720deb5061a8c8eb85a4a6c57c7e79c7b13
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
-
|
|
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
|
|
|
@@ -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 =
|
|
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
|
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.
|
|
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/
|
|
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.
|
|
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
|