boringbuilder 0.1.0.alpha.1

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/AGENTS.md +38 -0
  3. data/CHANGELOG.md +13 -0
  4. data/LICENSE +21 -0
  5. data/README.md +131 -0
  6. data/SECURITY.md +25 -0
  7. data/SOUL.md +40 -0
  8. data/STYLE.md +33 -0
  9. data/docs/artifacts.md +106 -0
  10. data/docs/building.md +175 -0
  11. data/docs/caching.md +53 -0
  12. data/exe/boringbuilder +6 -0
  13. data/lib/boringbuilder/artifact.rb +87 -0
  14. data/lib/boringbuilder/boring_cache.rb +126 -0
  15. data/lib/boringbuilder/build_progress.rb +90 -0
  16. data/lib/boringbuilder/builder.rb +33 -0
  17. data/lib/boringbuilder/cli.rb +234 -0
  18. data/lib/boringbuilder/config_file.rb +40 -0
  19. data/lib/boringbuilder/configuration.rb +165 -0
  20. data/lib/boringbuilder/errors.rb +9 -0
  21. data/lib/boringbuilder/exporter.rb +144 -0
  22. data/lib/boringbuilder/exporters/asset.rb +7 -0
  23. data/lib/boringbuilder/exporters/boring_cache.rb +83 -0
  24. data/lib/boringbuilder/exporters/local.rb +27 -0
  25. data/lib/boringbuilder/exporters/receipt.rb +11 -0
  26. data/lib/boringbuilder/exporters/resolver.rb +57 -0
  27. data/lib/boringbuilder/exporters.rb +7 -0
  28. data/lib/boringbuilder/initializer.rb +180 -0
  29. data/lib/boringbuilder/mise.rb +88 -0
  30. data/lib/boringbuilder/pipeline.rb +156 -0
  31. data/lib/boringbuilder/project.rb +264 -0
  32. data/lib/boringbuilder/project_plan.rb +78 -0
  33. data/lib/boringbuilder/rails_build.rb +6 -0
  34. data/lib/boringbuilder/railtie.rb +9 -0
  35. data/lib/boringbuilder/result.rb +33 -0
  36. data/lib/boringbuilder/ruby_application.rb +142 -0
  37. data/lib/boringbuilder/ruby_build.rb +212 -0
  38. data/lib/boringbuilder/runtime.rb +89 -0
  39. data/lib/boringbuilder/tasks/boringbuilder.rake +40 -0
  40. data/lib/boringbuilder/version.rb +5 -0
  41. data/lib/boringbuilder.rb +45 -0
  42. metadata +108 -0
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BoringBuilder
4
+ class RubyApplication
5
+ attr_reader :root, :configuration
6
+
7
+ def initialize(root, configuration)
8
+ @root = root
9
+ @configuration = configuration
10
+ end
11
+
12
+ def ruby?
13
+ root.join("Gemfile").file?
14
+ end
15
+
16
+ def rails?
17
+ ruby? && root.join("config/application.rb").file?
18
+ end
19
+
20
+ def hanami?
21
+ ruby? && root.join("config/app.rb").file?
22
+ end
23
+
24
+ def framework
25
+ return unless ruby?
26
+ return :rails if rails?
27
+ return :hanami if hanami?
28
+ return :rack if rack?
29
+
30
+ :ruby
31
+ end
32
+
33
+ def path
34
+ rails? ? "/rails" : "/app"
35
+ end
36
+
37
+ def user
38
+ rails? ? "rails" : "app"
39
+ end
40
+
41
+ def runtime_environment
42
+ environment = {
43
+ "PORT" => port.to_s,
44
+ "RACK_ENV" => "production"
45
+ }
46
+ environment.merge!(rails_environment) if rails?
47
+ environment.merge!("HANAMI_ENV" => "production", "HANAMI_PORT" => port.to_s) if hanami?
48
+ environment
49
+ end
50
+
51
+ def runtime_command
52
+ configuration.command ? explicit_command : detected_command
53
+ end
54
+
55
+ def runtime_entrypoint
56
+ return [] if configuration.command || procfile_command
57
+ return %w[/rails/bin/docker-entrypoint] if rails? && docker_entrypoint?
58
+ return %w[/rails/bin/rails] if rails?
59
+
60
+ []
61
+ end
62
+
63
+ def runtime_port
64
+ port
65
+ end
66
+
67
+ def web?
68
+ rails? || hanami? || rack? || !procfile_command.nil?
69
+ end
70
+
71
+ private
72
+
73
+ def detected_command
74
+ return procfile_command if procfile_command
75
+ return rails_command if rails?
76
+ return hanami_command if hanami?
77
+ return rack_command if rack?
78
+ return %w[bundle exec rake] if root.join("Rakefile").file?
79
+
80
+ []
81
+ end
82
+
83
+ def explicit_command
84
+ command = configuration.command
85
+ return command.map(&:to_s) if command.is_a?(Array)
86
+
87
+ ["/bin/sh", "-lc", command.to_s]
88
+ end
89
+
90
+ def rails_command
91
+ return %w[./bin/thrust ./bin/rails server] if thrust?
92
+
93
+ docker_entrypoint? ? %w[./bin/rails server] : %w[server]
94
+ end
95
+
96
+ def hanami_command
97
+ return %w[bundle exec puma -C config/puma.rb] if root.join("config/puma.rb").file?
98
+ return rack_command if rack?
99
+
100
+ []
101
+ end
102
+
103
+ def rack_command
104
+ ["bundle", "exec", "rackup", "config.ru", "--host", "0.0.0.0", "--port", port.to_s]
105
+ end
106
+
107
+ def procfile_command
108
+ return @procfile_command if defined?(@procfile_command)
109
+
110
+ path = root.join("Procfile")
111
+ @procfile_command = if path.file?
112
+ command = path.each_line.filter_map { |line| line[/\Aweb:\s*(.+)\s*\z/, 1] }.first
113
+ ["/bin/sh", "-lc", command] if command
114
+ end
115
+ end
116
+
117
+ def rack?
118
+ root.join("config.ru").file?
119
+ end
120
+
121
+ def docker_entrypoint?
122
+ root.join("bin/docker-entrypoint").file?
123
+ end
124
+
125
+ def thrust?
126
+ root.join("bin/thrust").file?
127
+ end
128
+
129
+ def port
130
+ configuration.port || (rails? && thrust? ? 80 : 3000)
131
+ end
132
+
133
+ def rails_environment
134
+ {
135
+ "NODE_ENV" => "production",
136
+ "RAILS_ENV" => "production",
137
+ "RAILS_LOG_TO_STDOUT" => "true",
138
+ "RAILS_SERVE_STATIC_FILES" => "true"
139
+ }
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BoringBuilder
4
+ class RubyBuild
5
+ DEFAULT_BUILD_IMAGE = Mise::DEFAULT_IMAGE
6
+ DEFAULT_RUNTIME_IMAGE = BoringCache::CLI_IMAGE
7
+ BUILD_PACKAGES = %w[
8
+ build-essential
9
+ curl
10
+ git
11
+ libpq-dev
12
+ libyaml-dev
13
+ pkg-config
14
+ ].freeze
15
+ RUNTIME_PACKAGES = %w[
16
+ curl
17
+ libpq5
18
+ ].freeze
19
+ BUNDLE_ENVIRONMENT = {
20
+ "BUNDLE_CLEAN" => "true",
21
+ "BUNDLE_DEPLOYMENT" => "1",
22
+ "BUNDLE_IGNORE_CONFIG" => "true",
23
+ "BUNDLE_JOBS" => "1",
24
+ "BUNDLE_PATH" => "/usr/local/bundle",
25
+ "BUNDLE_WITHOUT" => "development:test"
26
+ }.freeze
27
+ BUNDLE_INSTALL_COMMAND = ["sh", "-c", "bundle install && bundle clean --force"].freeze
28
+ BUNDLE_FILES = %w[Gemfile Gemfile.lock .ruby-version].freeze
29
+ BUNDLE_CONFIG = <<~YAML
30
+ ---
31
+ BUNDLE_PATH: "vendor/bundle"
32
+ BUNDLE_WITHOUT: "development:test"
33
+ BUNDLE_JOBS: "1"
34
+ BUNDLE_CLEAN: "true"
35
+ YAML
36
+
37
+ attr_reader :project, :client, :progress
38
+
39
+ def initialize(project, client, progress: BuildProgress.silent)
40
+ @project = project
41
+ @client = client
42
+ @progress = progress
43
+ end
44
+
45
+ def container
46
+ builder = with_environment(build_image_container, build_environment)
47
+ builder = install_packages(builder, (BUILD_PACKAGES + configuration.build_packages).uniq)
48
+ builder = create_application_user(builder)
49
+ builder = install_toolchain(builder)
50
+ builder = install_gems(builder)
51
+ builder = builder.with_directory(application_path, project.source(client), owner: user_owner)
52
+ builder = builder.with_workdir(application_path).with_exec(%w[bundle check])
53
+ builder = precompile(builder)
54
+ builder = write_bundle_config(builder)
55
+ builder = progress.step("Build #{project.framework.to_s.capitalize} application") { builder.sync }
56
+
57
+ runtime_container(builder)
58
+ end
59
+
60
+ private
61
+
62
+ def configuration
63
+ project.configuration
64
+ end
65
+
66
+ def application_path
67
+ project.application_path
68
+ end
69
+
70
+ def user_owner
71
+ "#{project.application_user}:#{project.application_user}"
72
+ end
73
+
74
+ def image_options
75
+ configuration.platform ? { platform: configuration.platform } : {}
76
+ end
77
+
78
+ def build_image_container
79
+ address = configuration.base_image || DEFAULT_BUILD_IMAGE
80
+ client.container(image_options).from(address)
81
+ end
82
+
83
+ def runtime_image_container
84
+ address = configuration.base_image || DEFAULT_RUNTIME_IMAGE
85
+ client.container(image_options).from(address)
86
+ end
87
+
88
+ def build_environment
89
+ BUNDLE_ENVIRONMENT
90
+ .merge(project.runtime_environment)
91
+ .merge("SECRET_KEY_BASE_DUMMY" => "1")
92
+ .merge(configuration.build_environment)
93
+ end
94
+
95
+ def runtime_environment
96
+ BUNDLE_ENVIRONMENT.merge(project.runtime_environment)
97
+ end
98
+
99
+ def with_environment(container, environment)
100
+ environment.reduce(container) do |result, (name, value)|
101
+ result.with_env_variable(name, value)
102
+ end
103
+ end
104
+
105
+ def install_toolchain(container)
106
+ toolchain = Mise.new(project, pipeline)
107
+ container = toolchain.with_project_metadata(container, at: application_path)
108
+ toolchain.install(container, tools: { ruby: project.ruby_version }, workdir: application_path, only: :ruby)
109
+ end
110
+
111
+ def install_gems(container)
112
+ container = BUNDLE_FILES.reduce(container.with_workdir(application_path)) do |result, name|
113
+ path = project.root.join(name)
114
+ path.file? ? result.with_file("#{application_path}/#{name}", project.source_file(client, name)) : result
115
+ end
116
+ container = pipeline.run(
117
+ container,
118
+ BUNDLE_INSTALL_COMMAND,
119
+ cache: "bundle",
120
+ at: "/usr/local/bundle/cache",
121
+ entry: "bundler",
122
+ name: "Install gems"
123
+ )
124
+ container.with_exec(["find", "/usr/local/bundle", "-path", "*/cache/*.gem", "-type", "f", "-delete"])
125
+ .with_exec([
126
+ "sh", "-c",
127
+ "find /usr/local/bundle -path '*/bundler/gems/*/.git' -type d -prune -exec rm -rf '{}' +"
128
+ ])
129
+ end
130
+
131
+ def write_bundle_config(container)
132
+ container.with_workdir(application_path)
133
+ .with_exec(%w[mkdir -p .bundle])
134
+ .with_new_file("#{application_path}/.bundle/config", BUNDLE_CONFIG)
135
+ end
136
+
137
+ def pipeline
138
+ @pipeline ||= Pipeline.new(project, client, progress: progress)
139
+ end
140
+
141
+ def runtime_container(builder)
142
+ container = with_environment(runtime_image_container, Mise::ENVIRONMENT.merge(runtime_environment))
143
+ container = install_packages(container, (RUNTIME_PACKAGES + configuration.runtime_packages).uniq)
144
+ container = create_application_user(container)
145
+ container = container.with_directory("/mise/installs", builder.directory("/mise/installs"))
146
+ .with_directory("/usr/local/bundle", builder.directory("/usr/local/bundle"),
147
+ owner: user_owner)
148
+ .with_directory(application_path, builder.directory(application_path), owner: user_owner)
149
+ .with_workdir(application_path)
150
+ .with_exec(%w[mise reshim])
151
+ container = runtime_metadata(container).with_user(project.application_user)
152
+ progress.step("Assemble runtime image") { container.sync }
153
+ end
154
+
155
+ def install_packages(container, packages)
156
+ container
157
+ .with_exec(%w[apt-get update])
158
+ .with_exec(["apt-get", "install", "-y", "--no-install-recommends", *packages])
159
+ .with_exec(%w[rm -rf /var/lib/apt/lists])
160
+ end
161
+
162
+ def create_application_user(container)
163
+ name = project.application_user
164
+ container
165
+ .with_exec(["groupadd", "--system", "--gid", "1000", name])
166
+ .with_exec(["useradd", name, "--uid", "1000", "--gid", "1000", "--create-home", "--shell", "/bin/bash"])
167
+ end
168
+
169
+ def precompile(container)
170
+ container = container.with_exec(%w[bin/rails assets:precompile]) if project.rails? && project.assets?
171
+ if project.bootsnap?
172
+ paths = project.rails? ? %w[app/ lib/] : %w[app/ lib/ config/]
173
+ container = container.with_exec(["bundle", "exec", "bootsnap", "precompile", "--gemfile", *paths])
174
+ end
175
+ return container unless project.rails?
176
+
177
+ normalize_rails_output(container)
178
+ end
179
+
180
+ def normalize_rails_output(container)
181
+ ruby = <<~RUBY.strip
182
+ path = "public/assets/.manifest.json"
183
+ if File.file?(path)
184
+ manifest = JSON.parse(File.binread(path))
185
+ File.binwrite(path, JSON.generate(manifest.sort.to_h))
186
+ end
187
+ RUBY
188
+ container
189
+ .with_exec(["ruby", "-rjson", "-e", ruby])
190
+ .with_exec(%w[rm -f tmp/local_secret.txt tmp/cache/bootsnap/load-path-cache])
191
+ end
192
+
193
+ def runtime_metadata(container)
194
+ entrypoint = project.runtime_entrypoint
195
+ command = project.runtime_command
196
+ container = container.with_entrypoint(entrypoint) unless entrypoint.empty?
197
+ container = container.with_default_args(command) unless command.empty?
198
+ return container unless project.web?
199
+
200
+ port = project.runtime_port
201
+ container = container.with_exposed_port(port, description: "Ruby HTTP server")
202
+ return container unless project.rails?
203
+
204
+ container.with_docker_healthcheck(
205
+ ["curl", "--fail", "--silent", "http://localhost:#{port}/up"],
206
+ interval: "30s",
207
+ timeout: "5s",
208
+ retries: 3
209
+ )
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+ require "timeout"
5
+
6
+ module BoringBuilder
7
+ class Runtime
8
+ Probe = Data.define(:success, :message) do
9
+ alias_method :success?, :success
10
+ end
11
+ RUNTIME_COMMANDS = {
12
+ apple: %w[container system status],
13
+ docker: %w[docker version]
14
+ }.freeze
15
+
16
+ attr_reader :requested
17
+
18
+ def initialize(requested, probe: nil, host_os: RUBY_PLATFORM)
19
+ @requested = requested.to_sym
20
+ @probe = probe || method(:probe_command)
21
+ @host_os = host_os
22
+ end
23
+
24
+ def resolve
25
+ return session_runtime if dagger_session?
26
+ return :custom if custom_runner?
27
+ return ensure_available!(requested) unless requested == :auto
28
+
29
+ failures = []
30
+ runtime_order.each do |runtime|
31
+ result = @probe.call(runtime, RUNTIME_COMMANDS.fetch(runtime))
32
+ return runtime if result.success?
33
+
34
+ failures << "#{runtime}: #{result.message}"
35
+ end
36
+
37
+ raise BoringBuilder::RuntimeError, <<~MESSAGE.strip
38
+ No supported container runtime is ready. Start Apple Container with `container system start`
39
+ or start Docker, then try again. Probes: #{failures.join('; ')}
40
+ MESSAGE
41
+ end
42
+
43
+ def dagger_runtime(resolved = resolve)
44
+ %i[session custom].include?(resolved) ? :auto : resolved
45
+ end
46
+
47
+ private
48
+
49
+ def ensure_available!(runtime)
50
+ result = @probe.call(runtime, RUNTIME_COMMANDS.fetch(runtime))
51
+ return runtime if result.success?
52
+
53
+ hint = runtime == :apple ? "container system start" : "start the Docker daemon"
54
+ raise BoringBuilder::RuntimeError,
55
+ "#{runtime.to_s.capitalize} runtime is not ready: #{result.message}. Try `#{hint}`."
56
+ end
57
+
58
+ def runtime_order
59
+ @host_os.include?("darwin") ? %i[apple docker] : %i[docker apple]
60
+ end
61
+
62
+ def dagger_session?
63
+ ENV.fetch("DAGGER_SESSION_PORT", nil) && ENV.fetch("DAGGER_SESSION_TOKEN", nil)
64
+ end
65
+
66
+ def custom_runner?
67
+ ENV.fetch("_EXPERIMENTAL_DAGGER_RUNNER_HOST", nil) && requested == :auto
68
+ end
69
+
70
+ def session_runtime
71
+ runner_host = ENV.fetch("_EXPERIMENTAL_DAGGER_RUNNER_HOST", nil)
72
+ match = runner_host&.match(%r{\Aimage\+(apple|docker)://})
73
+ return match[1].to_sym if match
74
+ return :custom if runner_host
75
+
76
+ :session
77
+ end
78
+
79
+ def probe_command(_runtime, command)
80
+ stdout, stderr, status = Timeout.timeout(5) { Open3.capture3(*command) }
81
+ message = [stderr, stdout].map(&:strip).reject(&:empty?).first || "command exited #{status.exitstatus}"
82
+ Probe.new(success: status.success?, message: message)
83
+ rescue Errno::ENOENT
84
+ Probe.new(success: false, message: "#{command.first} is not installed")
85
+ rescue Timeout::Error
86
+ Probe.new(success: false, message: "probe timed out")
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,40 @@
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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BoringBuilder
4
+ VERSION = "0.1.0.alpha.1"
5
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "boringbuilder/version"
4
+ require_relative "boringbuilder/errors"
5
+ require_relative "boringbuilder/artifact"
6
+ require_relative "boringbuilder/configuration"
7
+ require_relative "boringbuilder/config_file"
8
+ require_relative "boringbuilder/initializer"
9
+ require_relative "boringbuilder/build_progress"
10
+ require_relative "boringbuilder/boring_cache"
11
+ require_relative "boringbuilder/mise"
12
+ require_relative "boringbuilder/pipeline"
13
+ require_relative "boringbuilder/runtime"
14
+ require_relative "boringbuilder/ruby_application"
15
+ require_relative "boringbuilder/project"
16
+ require_relative "boringbuilder/exporters"
17
+ require_relative "boringbuilder/project_plan"
18
+ require_relative "boringbuilder/ruby_build"
19
+ require_relative "boringbuilder/rails_build"
20
+ require_relative "boringbuilder/exporter"
21
+ require_relative "boringbuilder/result"
22
+ require_relative "boringbuilder/builder"
23
+ require_relative "boringbuilder/cli"
24
+ require_relative "boringbuilder/railtie" if defined?(Rails::Railtie)
25
+
26
+ module BoringBuilder
27
+ class << self
28
+ def configure(&)
29
+ ConfigFile.configure(&)
30
+ end
31
+
32
+ def configuration(root: Dir.pwd, config: :auto, **options)
33
+ configuration = Configuration.new(root: root)
34
+ config_path = ConfigFile.resolve(configuration.root, config)
35
+ ConfigFile.load(config_path, configuration) if config_path
36
+ configuration.apply(options)
37
+ end
38
+
39
+ def build(root: Dir.pwd, config: :auto, **)
40
+ configuration = configuration(root: root, config: config, **)
41
+ yield configuration if block_given?
42
+ Builder.new(configuration).build
43
+ end
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: boringbuilder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.alpha.1
5
+ platform: ruby
6
+ authors:
7
+ - BoringCache
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-08-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dagger_ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.10'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.10'
27
+ description: BoringBuilder turns application source into filesystem artifacts and
28
+ container images. Rails is first class, while Mise-powered Dagger Ruby recipes support
29
+ any workload.
30
+ email:
31
+ - oss@boringcache.com
32
+ executables:
33
+ - boringbuilder
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - AGENTS.md
38
+ - CHANGELOG.md
39
+ - LICENSE
40
+ - README.md
41
+ - SECURITY.md
42
+ - SOUL.md
43
+ - STYLE.md
44
+ - docs/artifacts.md
45
+ - docs/building.md
46
+ - docs/caching.md
47
+ - exe/boringbuilder
48
+ - lib/boringbuilder.rb
49
+ - lib/boringbuilder/artifact.rb
50
+ - lib/boringbuilder/boring_cache.rb
51
+ - lib/boringbuilder/build_progress.rb
52
+ - lib/boringbuilder/builder.rb
53
+ - lib/boringbuilder/cli.rb
54
+ - lib/boringbuilder/config_file.rb
55
+ - lib/boringbuilder/configuration.rb
56
+ - lib/boringbuilder/errors.rb
57
+ - lib/boringbuilder/exporter.rb
58
+ - lib/boringbuilder/exporters.rb
59
+ - lib/boringbuilder/exporters/asset.rb
60
+ - lib/boringbuilder/exporters/boring_cache.rb
61
+ - lib/boringbuilder/exporters/local.rb
62
+ - lib/boringbuilder/exporters/receipt.rb
63
+ - lib/boringbuilder/exporters/resolver.rb
64
+ - lib/boringbuilder/initializer.rb
65
+ - lib/boringbuilder/mise.rb
66
+ - lib/boringbuilder/pipeline.rb
67
+ - lib/boringbuilder/project.rb
68
+ - lib/boringbuilder/project_plan.rb
69
+ - lib/boringbuilder/rails_build.rb
70
+ - lib/boringbuilder/railtie.rb
71
+ - lib/boringbuilder/result.rb
72
+ - lib/boringbuilder/ruby_application.rb
73
+ - lib/boringbuilder/ruby_build.rb
74
+ - lib/boringbuilder/runtime.rb
75
+ - lib/boringbuilder/tasks/boringbuilder.rake
76
+ - lib/boringbuilder/version.rb
77
+ homepage: https://github.com/boringcache/builder
78
+ licenses:
79
+ - MIT
80
+ metadata:
81
+ allowed_push_host: https://rubygems.org
82
+ homepage_uri: https://github.com/boringcache/builder
83
+ source_code_uri: https://github.com/boringcache/builder/tree/v0.1.0.alpha.1
84
+ documentation_uri: https://github.com/boringcache/builder#readme
85
+ changelog_uri: https://github.com/boringcache/builder/blob/main/CHANGELOG.md
86
+ bug_tracker_uri: https://github.com/boringcache/builder/issues
87
+ security_policy_uri: https://github.com/boringcache/builder/security/policy
88
+ rubygems_mfa_required: 'true'
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: 3.2.0
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">"
101
+ - !ruby/object:Gem::Version
102
+ version: 1.3.1
103
+ requirements: []
104
+ rubygems_version: 3.4.19
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Build deployment artifacts and images with Dagger
108
+ test_files: []