prebake 0.2.2 → 0.2.4

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: 7152aa806669f0896d95a649eeeaad41a487d8e3dee400550a605f2b4595e761
4
- data.tar.gz: 5f4f0ab6eb003077b367a11112fed63a3ab2fe626062c550a4ad13fa871e9f96
3
+ metadata.gz: dd89a26b2545f4c3a080e52028f1ec5fcc4100b36021321c45c2e35f70e6190c
4
+ data.tar.gz: 78c2f94618cbf112ad075ee84f780184bb53322db744a5012f1e299b03cf8d22
5
5
  SHA512:
6
- metadata.gz: 658cbdca9d891f2d3c371b0b54adce27334357bf2fb748b71143dedac88ad9edbf8a77eaad71022d86acd1cd61d35e85d0f312fdb3d5c349c74787feb5241c86
7
- data.tar.gz: 1a16e177051bd07a0fbf3ea4ccad9da56291c771a442a4624f6dfd9835140b4fa4a6c4a08a237b53502ab2563ddc4be8628c30fa3400888c63802c58da3efb50
6
+ metadata.gz: c846dc96b8e3366cf674c55e453f5808bf6f25c20aec20c6f62d00f0f86f7f18c589fb85c0b8cb400c7fd80ce7b9037adc4250ee484b8d798bf67bb812ceffc6
7
+ data.tar.gz: b906601a833da0313c4d1cbaad020abde631f1212337e0174d8062ca9777575b19ed98d4c882c109bd172d8de339d22a8fd59ef1986f5cd3444c437625634a51
@@ -9,39 +9,17 @@ require_relative "logger"
9
9
  module Prebake
10
10
  module AsyncPublisher
11
11
  @pending_specs = []
12
- @threads = []
13
12
  @mutex = Mutex.new
14
13
 
15
14
  def self.reset!
16
- @mutex.synchronize do
17
- @pending_specs.clear
18
- @threads.clear
19
- end
15
+ @mutex.synchronize { @pending_specs.clear }
20
16
  end
21
17
 
22
- # Queue a spec for later processing (no threads, no chdir)
23
18
  def self.enqueue(spec, backend)
24
19
  @mutex.synchronize { @pending_specs << [spec, backend] }
25
20
  end
26
21
 
27
- # For testing - enqueue a raw block for async execution
28
- def self.enqueue_block(&block)
29
- thread = Thread.new do
30
- block.call
31
- rescue StandardError => e
32
- Logger.warn "Error in background task: #{e.message}"
33
- end
34
-
35
- @mutex.synchronize { @threads << thread }
36
- end
37
-
38
- # Build all queued gems (serial, safe for Dir.chdir) then push in parallel
39
22
  def self.wait_for_completion(timeout: 120)
40
- # First, wait for any raw block threads (from tests)
41
- block_threads = @mutex.synchronize { @threads.dup }
42
- block_threads.each { |t| t.join(timeout) }
43
-
44
- # Then process queued specs
45
23
  specs = @mutex.synchronize { @pending_specs.dup }
46
24
  return if specs.empty?
47
25
 
@@ -71,10 +49,7 @@ module Prebake
71
49
 
72
50
  Logger.info "All pushes complete."
73
51
 
74
- @mutex.synchronize do
75
- @pending_specs.clear
76
- @threads.clear
77
- end
52
+ @mutex.synchronize { @pending_specs.clear }
78
53
  end
79
54
 
80
55
  def self.build_gem(spec, backend)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../logger"
4
+
3
5
  module Prebake
4
6
  module Backends
5
7
  class Base
@@ -7,25 +9,47 @@ module Prebake
7
9
  raise NotImplementedError, "#{self.class}#fetch not implemented"
8
10
  end
9
11
 
10
- def fetch_checksum(cache_key)
11
- raise NotImplementedError, "#{self.class}#fetch_checksum not implemented"
12
+ def fetch_checksum(_cache_key)
13
+ nil
12
14
  end
13
15
 
14
16
  def push(gem_path, cache_key, checksum)
15
17
  raise NotImplementedError, "#{self.class}#push not implemented"
16
18
  end
17
19
 
18
- def exists?(cache_key)
19
- raise NotImplementedError, "#{self.class}#exists? not implemented"
20
+ def exists?(_cache_key)
21
+ false
20
22
  end
21
23
 
22
- def delete(cache_key)
23
- raise NotImplementedError, "#{self.class}#delete not implemented"
24
+ def delete(_cache_key)
25
+ false
24
26
  end
25
27
 
26
28
  def checksums_supported?
27
29
  true
28
30
  end
31
+
32
+ protected
33
+
34
+ def warn_if_insecure_http(url)
35
+ return if url.start_with?("https://")
36
+ return if ENV.fetch("PREBAKE_ALLOW_INSECURE", "false") == "true"
37
+ return unless url.start_with?("http://")
38
+ return if darwin_with_default_host?(url)
39
+
40
+ Logger.warn(
41
+ "Using insecure HTTP connection to #{url}. " \
42
+ "Set PREBAKE_ALLOW_INSECURE=true to suppress this warning."
43
+ )
44
+ end
45
+
46
+ private
47
+
48
+ def darwin_with_default_host?(url)
49
+ RUBY_PLATFORM.include?("darwin") &&
50
+ Prebake.backend_type == "http" &&
51
+ url.chomp("/") == Prebake::DEFAULT_HTTP_URL
52
+ end
29
53
  end
30
54
  end
31
55
  end
@@ -27,13 +27,7 @@ module Prebake
27
27
  @url = url.chomp("/")
28
28
  @key = key
29
29
 
30
- return if @url.start_with?("https://") || ENV.fetch("PREBAKE_ALLOW_INSECURE", "false") == "true"
31
- return unless @url.start_with?("http://")
32
-
33
- Logger.warn(
34
- "Using insecure HTTP connection to #{@url}. " \
35
- "Set PREBAKE_ALLOW_INSECURE=true to suppress this warning."
36
- )
30
+ warn_if_insecure_http(@url)
37
31
  end
38
32
 
39
33
  def fetch(cache_key)
@@ -15,13 +15,7 @@ module Prebake
15
15
  @url = url.chomp("/")
16
16
  @token = token
17
17
 
18
- return if @url.start_with?("https://") || ENV.fetch("PREBAKE_ALLOW_INSECURE", "false") == "true"
19
- return unless @url.start_with?("http://")
20
-
21
- Logger.warn(
22
- "Using insecure HTTP connection to #{@url}. " \
23
- "Set PREBAKE_ALLOW_INSECURE=true to suppress this warning."
24
- )
18
+ warn_if_insecure_http(@url)
25
19
  end
26
20
 
27
21
  def fetch(cache_key)
@@ -12,11 +12,13 @@ module Prebake
12
12
  @bucket = bucket
13
13
  @region = region
14
14
  @prefix = prefix
15
+
16
+ require "aws-sdk-s3"
17
+ rescue LoadError
18
+ raise Prebake::Error, "aws-sdk-s3 gem is required for S3 backend"
15
19
  end
16
20
 
17
21
  def fetch(cache_key)
18
- return nil unless sdk_available?
19
-
20
22
  response = client.get_object(bucket: @bucket, key: object_key(cache_key))
21
23
  path = File.join(Dir.tmpdir, "prebake-#{SecureRandom.hex(16)}.gem")
22
24
  File.binwrite(path, response.body.read)
@@ -27,8 +29,6 @@ module Prebake
27
29
  end
28
30
 
29
31
  def fetch_checksum(cache_key)
30
- return nil unless sdk_available?
31
-
32
32
  response = client.get_object(bucket: @bucket, key: checksum_key(cache_key))
33
33
  response.body.read.strip
34
34
  rescue StandardError => e
@@ -37,8 +37,6 @@ module Prebake
37
37
  end
38
38
 
39
39
  def push(gem_path, cache_key, checksum)
40
- return false unless sdk_available?
41
-
42
40
  gem_key = object_key(cache_key)
43
41
  File.open(gem_path, "rb") do |file|
44
42
  client.put_object(bucket: @bucket, key: gem_key, body: file)
@@ -60,8 +58,6 @@ module Prebake
60
58
  end
61
59
 
62
60
  def exists?(cache_key)
63
- return false unless sdk_available?
64
-
65
61
  client.head_object(bucket: @bucket, key: object_key(cache_key))
66
62
  true
67
63
  rescue StandardError
@@ -69,8 +65,6 @@ module Prebake
69
65
  end
70
66
 
71
67
  def delete(cache_key)
72
- return false unless sdk_available?
73
-
74
68
  client.delete_object(bucket: @bucket, key: object_key(cache_key))
75
69
  client.delete_object(bucket: @bucket, key: checksum_key(cache_key))
76
70
  Logger.info "Deleted #{cache_key} from S3"
@@ -90,14 +84,6 @@ module Prebake
90
84
  "#{object_key(cache_key)}.sha256"
91
85
  end
92
86
 
93
- def sdk_available?
94
- require "aws-sdk-s3"
95
- true
96
- rescue LoadError
97
- Logger.warn "aws-sdk-s3 not available. Install it to use S3 backend."
98
- false
99
- end
100
-
101
87
  def client
102
88
  @client ||= Aws::S3::Client.new(region: @region)
103
89
  end
@@ -21,11 +21,7 @@ module Prebake
21
21
  expected_checksum = Prebake.backend.fetch_checksum(cache_key)
22
22
 
23
23
  if expected_checksum.nil? && Prebake.backend.checksums_supported?
24
- Logger.warn "No checksum available for #{cache_key}, removing cached gem"
25
- Prebake.backend.delete(cache_key)
26
- # GET triggers the worker to dispatch a rebuild via GH Actions on cache miss
27
- trigger_path = Prebake.backend.fetch(cache_key)
28
- FileUtils.rm_f(trigger_path) if trigger_path
24
+ trigger_rebuild(cache_key)
29
25
  return super
30
26
  end
31
27
 
@@ -52,6 +48,14 @@ module Prebake
52
48
 
53
49
  private
54
50
 
51
+ def trigger_rebuild(cache_key)
52
+ Logger.warn "No checksum available for #{cache_key}, removing cached gem"
53
+ Prebake.backend.delete(cache_key)
54
+ # GET triggers the worker to dispatch a rebuild via GH Actions on cache miss
55
+ trigger_path = Prebake.backend.fetch(cache_key)
56
+ FileUtils.rm_f(trigger_path) if trigger_path
57
+ end
58
+
55
59
  def verify_checksum(cache_key, expected, gem_path)
56
60
  return true if expected.nil?
57
61
 
@@ -28,6 +28,14 @@ module Prebake
28
28
  next unless real_binary.start_with?("#{real_tmpdir}/")
29
29
 
30
30
  relative = binary.sub("#{tmpdir}/", "")
31
+
32
+ # Normalize paths from legacy cached gems where binaries were
33
+ # packaged from gem_dir build artifacts instead of extension_dir.
34
+ # ext/<name>/<name>.so → <name>.so (build artifact path)
35
+ # lib/<name>/<name>.so → <name>/<name>.so (gem lib path)
36
+ relative = relative.sub(%r{\Aext/[^/]+/}, "") if relative.start_with?("ext/")
37
+ relative = relative.sub(%r{\Alib/}, "") if relative.start_with?("lib/")
38
+
31
39
  dest = File.join(spec.extension_dir, relative)
32
40
  FileUtils.mkdir_p(File.dirname(dest))
33
41
  FileUtils.cp(binary, dest)
@@ -5,7 +5,7 @@ module Prebake
5
5
  LEVELS = { debug: 0, info: 1, warn: 2 }.freeze
6
6
 
7
7
  def self.level
8
- LEVELS.fetch(ENV.fetch("PREBAKE_LOG_LEVEL", "warn").to_sym, 1)
8
+ @level ||= LEVELS.fetch(ENV.fetch("PREBAKE_LOG_LEVEL", "warn").to_sym, 1)
9
9
  end
10
10
 
11
11
  def self.debug(msg)
@@ -22,23 +22,12 @@ module Prebake
22
22
 
23
23
  def self.warn(msg)
24
24
  return unless level <= 2
25
- return if darwin_with_default_host?
26
25
 
27
26
  output " [prebake] WARN: #{msg}"
28
27
  end
29
28
 
30
29
  def self.reset!
31
- remove_instance_variable(:@darwin_with_default_host) if instance_variable_defined?(:@darwin_with_default_host)
32
- end
33
-
34
- private_class_method def self.darwin_with_default_host?
35
- return @darwin_with_default_host if defined?(@darwin_with_default_host)
36
-
37
- @darwin_with_default_host =
38
- RUBY_PLATFORM.include?("darwin") &&
39
- Prebake.backend_type == "http" &&
40
- (url = ENV.fetch("PREBAKE_HTTP_URL", nil)
41
- url.nil? || url.chomp("/") == Prebake::DEFAULT_HTTP_URL)
30
+ @level = nil
42
31
  end
43
32
 
44
33
  def self.output(msg)
@@ -48,6 +48,22 @@ module Prebake
48
48
  platform_spec.platform = Gem::Platform.new(Platform.generalized)
49
49
  platform_spec.extensions = []
50
50
 
51
+ # Remove build-artifact binaries copied from gem_dir (they live at
52
+ # wrong paths like ext/<name>/<name>.so). The properly-installed
53
+ # binaries are in extension_dir, placed there by `make install`.
54
+ Dir.glob(File.join(build_dir, "**/*.{so,bundle,dll}")).each { |f| File.delete(f) }
55
+
56
+ ext_dir = @spec.extension_dir
57
+ if ext_dir && File.directory?(ext_dir)
58
+ Dir.glob(File.join(ext_dir, "**/*.{so,bundle,dll}")).each do |binary|
59
+ next if File.symlink?(binary)
60
+ relative = binary.delete_prefix("#{ext_dir}/")
61
+ dest = File.join(build_dir, relative)
62
+ FileUtils.mkdir_p(File.dirname(dest))
63
+ FileUtils.cp(binary, dest)
64
+ end
65
+ end
66
+
51
67
  prefix = "#{build_dir}/"
52
68
  compiled = Dir.glob(File.join(build_dir, "**/*.{so,bundle,dll}"))
53
69
  .map { |f| f.delete_prefix(prefix) }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prebake
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thejus Paul
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-24 00:00:00.000000000 Z
11
+ date: 2026-03-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Prebake speeds up bundle install by skipping native gem compilation.
14
14
  It fetches precompiled binaries for gems like puma, nokogiri, pg, grpc, and bootsnap