boringbuilder 0.1.0.alpha.1 → 0.1.0.alpha.3

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: e20e908005e1a14c27c241b1540be57b73b7fa20f4a79513a22ce9535149fce4
4
- data.tar.gz: e7b7c17cf76c2fcc852dd03765c1002bb8c32a993d3aac87b0b0561ac8e7a382
3
+ metadata.gz: 823ab9ebc22d049c27671d07ea1be44315dbdf3f63cdd29c46cd209e6e5fc279
4
+ data.tar.gz: 58c38219ef59ff3bf7515d10140be7500784bf276f5b0ea71edd965d7ae10b86
5
5
  SHA512:
6
- metadata.gz: 3950b539d458467b05b258bceb0d0e5a04f1c08fbb3dc1943db5f2750200c337694459090e5318334910be42f03495f260d134044e9a61fb4c6d4806bf4e5d9e
7
- data.tar.gz: a7c8ee8d4b21afa7362c5f15846f943eff9ef1009c40b3860eae2802cddac826a25c6bd12e8c182468237378630cfbb0dbb53c2d9d5d6d6a020976ca37e20537
6
+ metadata.gz: 7812ac305fa0015e0007049f6272a81fcf237db37304522323bad5a7a9947ac2f6e77d9e9788cf57490c31d680c17bda5aa3cc025cc19a4b310f9711b080d907
7
+ data.tar.gz: 67a48f5ce9298763f588c2e979a3bcca6b1a38a9c8f9350597f82876c69168c644b480affc62728d27ad1c8d114071267fbc7592a5703446441dc385b3459357
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0.alpha.3 - 2026-08-27
4
+
5
+ - Remove Bundler cache metadata before compiling assets so cold and restored Rails builds stay identical.
6
+
7
+ ## 0.1.0.alpha.2 - 2026-08-27
8
+
9
+ - Stream clean, Docker-style build steps and command output through Dagger Ruby.
10
+ - Run Bundler with the available CPUs unless `BUNDLE_JOBS` is explicitly configured.
11
+ - Keep dependency cache metadata out of Rails artifacts.
12
+
3
13
  ## 0.1.0.alpha.1 - 2026-08-27
4
14
 
5
15
  - Reintroduce BoringBuilder as a Ruby gem backed by Dagger Ruby.
@@ -6,7 +6,7 @@ module BoringBuilder
6
6
  class Builder
7
7
  attr_reader :configuration, :progress
8
8
 
9
- def initialize(configuration, progress: BuildProgress.silent)
9
+ def initialize(configuration, progress: DaggerRuby::Progress.silent)
10
10
  @configuration = configuration
11
11
  @progress = progress
12
12
  end
@@ -59,7 +59,7 @@ module BoringBuilder
59
59
  end
60
60
 
61
61
  def build
62
- options = {}
62
+ options = { progress: "pretty" }
63
63
  config_file = :auto
64
64
  dry_run = false
65
65
  json = false
@@ -71,6 +71,8 @@ module BoringBuilder
71
71
  return 0 if help_requested
72
72
  raise OptionParser::InvalidArgument, "expected at most one PROJECT directory" if @argv.length > 1
73
73
 
74
+ options[:progress] = nil if json
75
+
74
76
  configuration = BoringBuilder.configuration(root: @argv.first || Dir.pwd, config: config_file, **options)
75
77
  project = Project.new(configuration).validate!
76
78
  if dry_run
@@ -78,8 +80,9 @@ module BoringBuilder
78
80
  return 0
79
81
  end
80
82
 
81
- print_build_start(project, json: json)
82
- result = @builder_class.new(configuration, progress: build_progress(configuration, json: json)).build
83
+ progress = build_progress(configuration, json: json)
84
+ print_build_start(project, json: json, progress: progress)
85
+ result = @builder_class.new(configuration, progress: progress).build
83
86
  json ? @out.puts(JSON.generate(result.to_h)) : print_result(result)
84
87
  0
85
88
  end
@@ -109,7 +112,8 @@ module BoringBuilder
109
112
  parser.on("--platform PLATFORM", "Target platform, for example linux/amd64") do |value|
110
113
  options[:platform] = value
111
114
  end
112
- parser.on("--progress MODE", %w[auto plain tty], "Dagger progress: auto, plain, or tty") do |value|
115
+ parser.on("--progress MODE", %w[pretty auto plain tty dots logs],
116
+ "Build progress: pretty (default), auto, plain, tty, dots, or logs") do |value|
113
117
  options[:progress] = value
114
118
  end
115
119
  end
@@ -209,7 +213,7 @@ module BoringBuilder
209
213
  @out.puts "Artifact: #{result.artifact_id} (#{result.artifact_name})" if result.artifact_published?
210
214
  end
211
215
 
212
- def print_build_start(project, json:)
216
+ def print_build_start(project, json:, progress:)
213
217
  return if json || !dagger_session?
214
218
 
215
219
  cache = BoringCache.new(project, nil, environment: @environment).mode
@@ -217,8 +221,7 @@ module BoringBuilder
217
221
  details = [pipeline, "#{project.configuration.runtime} runtime", "#{cache} cache",
218
222
  "#{project.configuration.format.to_s.tr('_', '.')} output"]
219
223
 
220
- @out.puts "Building #{project.app_name} with Dagger"
221
- @out.puts " #{details.join(' · ')}"
224
+ progress.message("Building #{project.app_name} with Dagger\n #{details.join(' · ')}")
222
225
  end
223
226
 
224
227
  def dagger_session?
@@ -226,9 +229,8 @@ module BoringBuilder
226
229
  end
227
230
 
228
231
  def build_progress(configuration, json:)
229
- enabled = !json && dagger_session? && configuration.progress.nil?
230
- animated = enabled && @out.respond_to?(:tty?) && @out.tty?
231
- BuildProgress.new(out: @out, enabled: enabled, animated: animated)
232
+ config = DaggerRuby::Config.new(progress: configuration.progress, verify_version: false)
233
+ config.progress_reporter(out: @out, enabled: !json && dagger_session?, environment: @environment)
232
234
  end
233
235
  end
234
236
  end
@@ -10,7 +10,7 @@ module BoringBuilder
10
10
  attr_reader :project, :client, :container, :runtime, :exporters, :progress
11
11
 
12
12
  def initialize(project, client, container, runtime:, command_runner: nil, exporters: nil,
13
- progress: BuildProgress.silent)
13
+ progress: DaggerRuby::Progress.silent)
14
14
  @project = project
15
15
  @client = client
16
16
  @container = container
@@ -56,33 +56,36 @@ module BoringBuilder
56
56
  end
57
57
 
58
58
  def export_step_name(adapter)
59
- return "Publish artifact to BoringCache" if adapter.name == :boringcache
59
+ return "[export] Publish artifact to BoringCache" if adapter.name == :boringcache
60
60
 
61
- "Export #{configuration.format.to_s.tr('_', '.')} artifact"
61
+ "[export] Write #{configuration.format.to_s.tr('_', '.')} artifact"
62
62
  end
63
63
 
64
64
  def build_asset
65
65
  filename = project.output_path.basename.to_s
66
66
  case configuration.format
67
67
  when :directory
68
- Exporters::Asset.new(kind: :directory, source: artifact_directory, filename: filename)
68
+ Exporters::Asset.new(kind: :directory, source: prepared_artifact_directory, filename: filename)
69
69
  when :tar
70
70
  Exporters::Asset.new(kind: :file, source: archive_file("artifact.tar", compression: nil), filename: filename)
71
71
  when :tar_zst
72
72
  Exporters::Asset.new(kind: :file, source: archive_file("artifact.tar.zst", compression: :zstd),
73
73
  filename: filename)
74
74
  when :oci, :docker
75
- Exporters::Asset.new(kind: :file, source: container.as_tarball(media_types: media_types), filename: filename)
75
+ source = progress.step("[export] Create #{configuration.format.to_s.upcase} image archive") do
76
+ container.as_tarball(media_types: media_types).sync
77
+ end
78
+ Exporters::Asset.new(kind: :file, source: source, filename: filename)
76
79
  end
77
80
  end
78
81
 
79
82
  def export_reference
80
83
  if configuration.publish
81
- progress.step("Publish container image") do
84
+ progress.step("[export] Publish container image") do
82
85
  container.publish(configuration.publish, media_types: media_types)
83
86
  end
84
87
  elsif configuration.load
85
- progress.step("Load container image") { load_image }
88
+ progress.step("[export] Load container image") { load_image }
86
89
  end
87
90
  end
88
91
 
@@ -123,20 +126,48 @@ module BoringBuilder
123
126
  @artifact_directory ||= project.artifact.export_directory(client, container)
124
127
  end
125
128
 
129
+ def prepared_artifact_directory
130
+ @prepared_artifact_directory ||= progress.step("[export] Assemble artifact filesystem") do
131
+ artifact_directory.sync
132
+ end
133
+ end
134
+
126
135
  def archive_file(filename, compression:)
127
- packer = client.container.from(PACKER_IMAGE).with_exec(%w[apk add --no-cache tar zstd])
128
- packer = packer.with_mounted_directory("/artifact", artifact_directory)
136
+ packer = progress.step("[export] FROM #{PACKER_IMAGE}") { client.container.from(PACKER_IMAGE).sync }
137
+ packer = run_archive_step(packer, %w[apk add --no-cache tar zstd], "[export] RUN apk add tar zstd")
138
+ packer = packer.with_mounted_directory("/artifact", prepared_artifact_directory)
129
139
  tar_path = compression == :zstd ? "/artifact.tar" : "/#{filename}"
130
- packer = packer.with_exec(
140
+ packer = run_archive_step(
141
+ packer,
131
142
  [
132
143
  "tar", "--sort=name", "--mtime=@0", "--owner=0", "--group=0", "--numeric-owner",
133
144
  "--pax-option=delete=atime,delete=ctime", "-C", "/artifact", "-cf", tar_path, "."
134
- ]
145
+ ],
146
+ "[export] RUN tar -cf #{tar_path} ."
135
147
  )
136
- packer = packer.with_exec(["zstd", "-q", "-T1", tar_path, "-o", "/#{filename}"]) if compression == :zstd
148
+ if compression == :zstd
149
+ packer = run_archive_step(
150
+ packer,
151
+ ["zstd", "-q", "-T0", tar_path, "-o", "/#{filename}"],
152
+ "[export] RUN zstd -T0 #{tar_path}"
153
+ )
154
+ end
137
155
  packer.file("/#{filename}")
138
156
  end
139
157
 
158
+ def run_archive_step(container, command, name)
159
+ executed = container.with_exec(command)
160
+ progress.step(name) do
161
+ output = command_output(executed)
162
+ progress.write(output) unless progress.streaming?
163
+ executed.sync
164
+ end
165
+ end
166
+
167
+ def command_output(container)
168
+ [container.stdout, container.stderr].map(&:strip).reject(&:empty?).join("\n")
169
+ end
170
+
140
171
  def media_types
141
172
  configuration.format == :docker ? :DockerMediaTypes : :OCIMediaTypes
142
173
  end
@@ -40,7 +40,7 @@ module BoringBuilder
40
40
  at: CACHE_PATH,
41
41
  entry: "mise",
42
42
  workdir: workdir,
43
- name: "Prepare Mise toolchain"
43
+ name: "[build] RUN #{command.join(' ')}"
44
44
  )
45
45
  end
46
46
 
@@ -4,7 +4,7 @@ module BoringBuilder
4
4
  class Pipeline
5
5
  attr_reader :project, :client, :progress
6
6
 
7
- def initialize(project, client, environment: ENV, progress: BuildProgress.silent)
7
+ def initialize(project, client, environment: ENV, progress: DaggerRuby::Progress.silent)
8
8
  @project = project
9
9
  @client = client
10
10
  @environment = environment
@@ -124,7 +124,10 @@ module BoringBuilder
124
124
  return container if container.equal?(@last_synced_container)
125
125
 
126
126
  @last_synced_container = progress.step(name) do
127
- progress.write { output.call } if output
127
+ if output
128
+ result = output.call
129
+ progress.write(result) unless progress.streaming?
130
+ end
128
131
  container.sync
129
132
  end
130
133
  end
@@ -5,6 +5,7 @@ module BoringBuilder
5
5
  DEFAULT_EXCLUDES = %w[
6
6
  .env
7
7
  .env.*
8
+ .bundle/cache
8
9
  .git
9
10
  coverage
10
11
  dist
@@ -29,7 +30,7 @@ module BoringBuilder
29
30
  self
30
31
  end
31
32
 
32
- def container(client, progress: BuildProgress.silent)
33
+ def container(client, progress: DaggerRuby::Progress.silent)
33
34
  return Pipeline.new(self, client, progress: progress).build(&configuration.pipeline) if custom_pipeline?
34
35
 
35
36
  builder = rails? ? RailsBuild : RubyBuild
@@ -20,23 +20,29 @@ module BoringBuilder
20
20
  "BUNDLE_CLEAN" => "true",
21
21
  "BUNDLE_DEPLOYMENT" => "1",
22
22
  "BUNDLE_IGNORE_CONFIG" => "true",
23
- "BUNDLE_JOBS" => "1",
24
23
  "BUNDLE_PATH" => "/usr/local/bundle",
25
24
  "BUNDLE_WITHOUT" => "development:test"
26
25
  }.freeze
27
- BUNDLE_INSTALL_COMMAND = ["sh", "-c", "bundle install && bundle clean --force"].freeze
26
+ BUNDLE_INSTALL_COMMAND = [
27
+ "sh", "-c",
28
+ 'export BUNDLE_JOBS="${BUNDLE_JOBS:-$(nproc)}"; bundle install && bundle clean --force'
29
+ ].freeze
30
+ APT_INSTALL_COMMAND = [
31
+ "sh", "-c",
32
+ "apt-get update && apt-get install -y --no-install-recommends \"$@\" && rm -rf /var/lib/apt/lists/*",
33
+ "apt-get"
34
+ ].freeze
28
35
  BUNDLE_FILES = %w[Gemfile Gemfile.lock .ruby-version].freeze
29
36
  BUNDLE_CONFIG = <<~YAML
30
37
  ---
31
38
  BUNDLE_PATH: "vendor/bundle"
32
39
  BUNDLE_WITHOUT: "development:test"
33
- BUNDLE_JOBS: "1"
34
40
  BUNDLE_CLEAN: "true"
35
41
  YAML
36
42
 
37
43
  attr_reader :project, :client, :progress
38
44
 
39
- def initialize(project, client, progress: BuildProgress.silent)
45
+ def initialize(project, client, progress: DaggerRuby::Progress.silent)
40
46
  @project = project
41
47
  @client = client
42
48
  @progress = progress
@@ -44,15 +50,27 @@ module BoringBuilder
44
50
 
45
51
  def container
46
52
  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)
53
+ builder = install_packages(builder, (BUILD_PACKAGES + configuration.build_packages).uniq, stage: :build)
54
+ builder = create_application_user(builder, stage: :build)
49
55
  builder = install_toolchain(builder)
50
56
  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])
57
+ builder = pipeline.step("[build] COPY application source", builder) do |container|
58
+ container.with_directory(application_path, project.source(client), owner: user_owner)
59
+ end
60
+ builder = pipeline.exec(
61
+ builder,
62
+ %w[bundle check],
63
+ name: "[build] RUN bundle check",
64
+ workdir: application_path
65
+ )
66
+ builder = pipeline.exec(
67
+ builder,
68
+ %w[rm -rf .bundle/cache],
69
+ name: "[build] RUN rm -rf .bundle/cache",
70
+ workdir: application_path
71
+ )
53
72
  builder = precompile(builder)
54
- builder = write_bundle_config(builder)
55
- builder = progress.step("Build #{project.framework.to_s.capitalize} application") { builder.sync }
73
+ builder = pipeline.step("[build] Write Bundler configuration", builder) { write_bundle_config(_1) }
56
74
 
57
75
  runtime_container(builder)
58
76
  end
@@ -77,12 +95,12 @@ module BoringBuilder
77
95
 
78
96
  def build_image_container
79
97
  address = configuration.base_image || DEFAULT_BUILD_IMAGE
80
- client.container(image_options).from(address)
98
+ progress.step("[build] FROM #{address}") { client.container(image_options).from(address).sync }
81
99
  end
82
100
 
83
101
  def runtime_image_container
84
102
  address = configuration.base_image || DEFAULT_RUNTIME_IMAGE
85
- client.container(image_options).from(address)
103
+ progress.step("[runtime] FROM #{address}") { client.container(image_options).from(address).sync }
86
104
  end
87
105
 
88
106
  def build_environment
@@ -119,7 +137,7 @@ module BoringBuilder
119
137
  cache: "bundle",
120
138
  at: "/usr/local/bundle/cache",
121
139
  entry: "bundler",
122
- name: "Install gems"
140
+ name: "[build] RUN bundle install"
123
141
  )
124
142
  container.with_exec(["find", "/usr/local/bundle", "-path", "*/cache/*.gem", "-type", "f", "-delete"])
125
143
  .with_exec([
@@ -130,6 +148,7 @@ module BoringBuilder
130
148
 
131
149
  def write_bundle_config(container)
132
150
  container.with_workdir(application_path)
151
+ .with_exec(%w[rm -rf .bundle/cache])
133
152
  .with_exec(%w[mkdir -p .bundle])
134
153
  .with_new_file("#{application_path}/.bundle/config", BUNDLE_CONFIG)
135
154
  end
@@ -140,41 +159,59 @@ module BoringBuilder
140
159
 
141
160
  def runtime_container(builder)
142
161
  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])
162
+ container = install_packages(container, (RUNTIME_PACKAGES + configuration.runtime_packages).uniq,
163
+ stage: :runtime)
164
+ container = create_application_user(container, stage: :runtime)
165
+ container = pipeline.step("[runtime] COPY toolchain, gems, and application", container) do |runtime|
166
+ runtime.with_directory("/mise/installs", builder.directory("/mise/installs"))
167
+ .with_directory("/usr/local/bundle", builder.directory("/usr/local/bundle"), owner: user_owner)
168
+ .with_directory(application_path, builder.directory(application_path), owner: user_owner)
169
+ end
170
+ container = pipeline.exec(
171
+ container,
172
+ %w[mise reshim],
173
+ name: "[runtime] RUN mise reshim",
174
+ workdir: application_path
175
+ )
151
176
  container = runtime_metadata(container).with_user(project.application_user)
152
- progress.step("Assemble runtime image") { container.sync }
177
+ progress.step("[runtime] Configure image") { container.sync }
153
178
  end
154
179
 
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])
180
+ def install_packages(container, packages, stage:)
181
+ pipeline.exec(
182
+ container,
183
+ [*APT_INSTALL_COMMAND, *packages],
184
+ name: "[#{stage}] RUN apt-get update && apt-get install packages"
185
+ )
160
186
  end
161
187
 
162
- def create_application_user(container)
188
+ def create_application_user(container, stage:)
163
189
  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"])
190
+ pipeline.step("[#{stage}] Create application user", container) do |step|
191
+ step.with_exec(["groupadd", "--system", "--gid", "1000", name])
192
+ .with_exec(["useradd", name, "--uid", "1000", "--gid", "1000", "--create-home", "--shell", "/bin/bash"])
193
+ end
167
194
  end
168
195
 
169
196
  def precompile(container)
170
- container = container.with_exec(%w[bin/rails assets:precompile]) if project.rails? && project.assets?
197
+ if project.rails? && project.assets?
198
+ container = pipeline.exec(
199
+ container,
200
+ %w[bin/rails assets:precompile],
201
+ name: "[build] RUN bin/rails assets:precompile"
202
+ )
203
+ end
171
204
  if project.bootsnap?
172
205
  paths = project.rails? ? %w[app/ lib/] : %w[app/ lib/ config/]
173
- container = container.with_exec(["bundle", "exec", "bootsnap", "precompile", "--gemfile", *paths])
206
+ container = pipeline.exec(
207
+ container,
208
+ ["bundle", "exec", "bootsnap", "precompile", "--gemfile", *paths],
209
+ name: "[build] RUN bundle exec bootsnap precompile"
210
+ )
174
211
  end
175
212
  return container unless project.rails?
176
213
 
177
- normalize_rails_output(container)
214
+ pipeline.step("[build] Normalize Rails output", container) { normalize_rails_output(_1) }
178
215
  end
179
216
 
180
217
  def normalize_rails_output(container)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BoringBuilder
4
- VERSION = "0.1.0.alpha.1"
4
+ VERSION = "0.1.0.alpha.3"
5
5
  end
data/lib/boringbuilder.rb CHANGED
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dagger_ruby"
4
+
3
5
  require_relative "boringbuilder/version"
4
6
  require_relative "boringbuilder/errors"
5
7
  require_relative "boringbuilder/artifact"
6
8
  require_relative "boringbuilder/configuration"
7
9
  require_relative "boringbuilder/config_file"
8
10
  require_relative "boringbuilder/initializer"
9
- require_relative "boringbuilder/build_progress"
10
11
  require_relative "boringbuilder/boring_cache"
11
12
  require_relative "boringbuilder/mise"
12
13
  require_relative "boringbuilder/pipeline"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boringbuilder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.alpha.1
4
+ version: 0.1.0.alpha.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - BoringCache
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2026-08-27 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: dagger_ruby
@@ -16,14 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '0.10'
18
+ version: '0.11'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: '0.10'
25
+ version: '0.11'
27
26
  description: BoringBuilder turns application source into filesystem artifacts and
28
27
  container images. Rails is first class, while Mise-powered Dagger Ruby recipes support
29
28
  any workload.
@@ -48,7 +47,6 @@ files:
48
47
  - lib/boringbuilder.rb
49
48
  - lib/boringbuilder/artifact.rb
50
49
  - lib/boringbuilder/boring_cache.rb
51
- - lib/boringbuilder/build_progress.rb
52
50
  - lib/boringbuilder/builder.rb
53
51
  - lib/boringbuilder/cli.rb
54
52
  - lib/boringbuilder/config_file.rb
@@ -80,13 +78,12 @@ licenses:
80
78
  metadata:
81
79
  allowed_push_host: https://rubygems.org
82
80
  homepage_uri: https://github.com/boringcache/builder
83
- source_code_uri: https://github.com/boringcache/builder/tree/v0.1.0.alpha.1
81
+ source_code_uri: https://github.com/boringcache/builder/tree/v0.1.0.alpha.3
84
82
  documentation_uri: https://github.com/boringcache/builder#readme
85
83
  changelog_uri: https://github.com/boringcache/builder/blob/main/CHANGELOG.md
86
84
  bug_tracker_uri: https://github.com/boringcache/builder/issues
87
85
  security_policy_uri: https://github.com/boringcache/builder/security/policy
88
86
  rubygems_mfa_required: 'true'
89
- post_install_message:
90
87
  rdoc_options: []
91
88
  require_paths:
92
89
  - lib
@@ -97,12 +94,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
97
94
  version: 3.2.0
98
95
  required_rubygems_version: !ruby/object:Gem::Requirement
99
96
  requirements:
100
- - - ">"
97
+ - - ">="
101
98
  - !ruby/object:Gem::Version
102
- version: 1.3.1
99
+ version: '0'
103
100
  requirements: []
104
- rubygems_version: 3.4.19
105
- signing_key:
101
+ rubygems_version: 4.0.16
106
102
  specification_version: 4
107
103
  summary: Build deployment artifacts and images with Dagger
108
104
  test_files: []
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module BoringBuilder
4
- class BuildProgress
5
- SPINNER_FRAMES = %w[⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏].freeze
6
-
7
- def self.silent
8
- @silent ||= new(enabled: false)
9
- end
10
-
11
- def initialize(out: $stdout, enabled: true, animated: false)
12
- @out = out
13
- @enabled = enabled
14
- @animated = animated
15
- @step = 0
16
- end
17
-
18
- def step(name)
19
- return yield unless @enabled
20
-
21
- number = next_step
22
- started_at = monotonic_time
23
- @out.puts "##{number} #{name}"
24
- @active_spinner = start_spinner(number, started_at)
25
- result = yield
26
- stop_active_spinner
27
- @out.puts "##{number} DONE #{elapsed_since(started_at)}"
28
- result
29
- rescue StandardError
30
- stop_active_spinner
31
- @out.puts "##{number} ERROR #{elapsed_since(started_at)}" if number
32
- raise
33
- ensure
34
- stop_active_spinner
35
- end
36
-
37
- def write(output = nil)
38
- return unless @enabled
39
-
40
- output = yield if block_given?
41
- stop_active_spinner
42
- text = output.to_s
43
- text.each_line { |line| @out.print " #{line}" }
44
- @out.puts unless text.empty? || text.end_with?("\n")
45
- end
46
-
47
- private
48
-
49
- def next_step
50
- @step += 1
51
- end
52
-
53
- def start_spinner(number, started_at)
54
- return unless @animated
55
-
56
- Thread.new do
57
- frame = 0
58
- loop do
59
- @out.print "\r##{number} #{SPINNER_FRAMES.fetch(frame % SPINNER_FRAMES.length)} #{elapsed_since(started_at)}"
60
- @out.flush
61
- frame += 1
62
- sleep 0.1
63
- end
64
- rescue IOError
65
- nil
66
- end
67
- end
68
-
69
- def stop_spinner(thread)
70
- return unless thread&.alive?
71
-
72
- thread.kill
73
- thread.join
74
- @out.print "\r\e[2K"
75
- end
76
-
77
- def stop_active_spinner
78
- stop_spinner(@active_spinner)
79
- @active_spinner = nil
80
- end
81
-
82
- def monotonic_time
83
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
84
- end
85
-
86
- def elapsed_since(started_at)
87
- format("%.1fs", monotonic_time - started_at)
88
- end
89
- end
90
- end