bootsnap 1.13.0 → 1.16.0

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: 40e594cf459a80b9e118cf8cdb6d9f306228fe682e3c2af3a1c2b70432f47890
4
- data.tar.gz: c120e3c24944e9c0568615a0b6ec23d422e949755339c8ffa118f0265e85755b
3
+ metadata.gz: 1bf1eca00971c561ca1e15941903dcf193ec8fc84c68736177a3aa72558c536b
4
+ data.tar.gz: bcd5596d1b4b00905af82cf25693fb72e71897685e024b27311353eef6f588bf
5
5
  SHA512:
6
- metadata.gz: 45c52711bd2fbf273f93d27ea4a8ee71d04c81b7444f0f49aa9b672e131544103b74d3326afe5228122dc52844b681c1e18bbb59fe429b2a2216e182e90ac5e7
7
- data.tar.gz: ad78b4da53ffc8d6653f74ed777bae1987704de2a76062bbebc2b1d3977baeca900b36066994957adf3cefbda446b4393dd18e41637d1a39f9adac42878b6732
6
+ metadata.gz: 0d3e37e56d994647ac88a1c9b83f087f137d32acfc09bfba1e71a85ce254697b1dcf6c9e1c90b5c71728ce0f3c0a63ee86680455a8f95003d237671435042859
7
+ data.tar.gz: 270bf8fc609981d25441c9c4bd975348130177564f2e1a0744f18720d7f59cda3c3da24bea0c07f2498a185308647b05715e1b1dd5a6045e653600ab2f5907a1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Unreleased
2
2
 
3
+ # 1.16.0
4
+
5
+ * Use `RbConfig::CONFIG["rubylibdir"]` instead of `RbConfig::CONFIG["libdir"]` to check for stdlib files. See #431.
6
+ * Fix the cached version of `YAML.load_file` being slightly more permissive than the default `Psych` one. See #434.
7
+ `Date` and `Time` values are now properly rejected, as well as aliases.
8
+ If this causes a regression in your application, it is recommended to load *trusted* YAML files with `YAML.unsafe_load_file`.
9
+
10
+ # 1.15.0
11
+
12
+ * Add a readonly mode, for environments in which the updated cache wouldn't be persisted. See #428 and #423.
13
+
14
+ # 1.14.0
15
+
16
+ * Require Ruby 2.6.
17
+ * Add a way to skip directories during load path scanning.
18
+ If you have large non-ruby directories in the middle of your load path, it can severely slow down scanning.
19
+ Typically this is a problem with `node_modules`. See #277.
20
+ * Fix `Bootsnap.unload_cache!`, it simply wouldn't work at all becaue of a merge mistake. See #421.
21
+
3
22
  # 1.13.0
4
23
 
5
24
  * Stop decorating `Kernel.load`. This used to be very useful in development because the Rails "classic" autoloader
data/README.md CHANGED
@@ -52,10 +52,12 @@ require 'bootsnap'
52
52
  env = ENV['RAILS_ENV'] || "development"
53
53
  Bootsnap.setup(
54
54
  cache_dir: 'tmp/cache', # Path to your cache
55
+ ignore_directories: ['node_modules'], # Directory names to skip.
55
56
  development_mode: env == 'development', # Current working environment, e.g. RACK_ENV, RAILS_ENV, etc
56
57
  load_path_cache: true, # Optimize the LOAD_PATH with a cache
57
58
  compile_cache_iseq: true, # Compile Ruby code into ISeq cache, breaks coverage reporting.
58
- compile_cache_yaml: true # Compile YAML into a cache
59
+ compile_cache_yaml: true, # Compile YAML into a cache
60
+ readonly: true, # Use the caches but don't update them on miss or stale entries.
59
61
  )
60
62
  ```
61
63
 
@@ -66,7 +68,7 @@ will help optimize boot time further if you have an extremely large `$LOAD_PATH`
66
68
  Note: Bootsnap and [Spring](https://github.com/rails/spring) are orthogonal tools. While Bootsnap
67
69
  speeds up the loading of individual source files, Spring keeps a copy of a pre-booted Rails process
68
70
  on hand to completely skip parts of the boot process the next time it's needed. The two tools work
69
- well together, and are both included in a newly-generated Rails applications by default.
71
+ well together.
70
72
 
71
73
  ### Environment variables
72
74
 
@@ -76,7 +78,11 @@ well together, and are both included in a newly-generated Rails applications by
76
78
  - `DISABLE_BOOTSNAP` allows to entirely disable bootsnap.
77
79
  - `DISABLE_BOOTSNAP_LOAD_PATH_CACHE` allows to disable load path caching.
78
80
  - `DISABLE_BOOTSNAP_COMPILE_CACHE` allows to disable ISeq and YAML caches.
81
+ - `BOOTSNAP_READONLY` configure bootsnap to not update the cache on miss or stale entries.
79
82
  - `BOOTSNAP_LOG` configure bootsnap to log all caches misses to STDERR.
83
+ - `BOOTSNAP_IGNORE_DIRECTORIES` a comma separated list of directories that shouldn't be scanned.
84
+ Useful when you have large directories of non-ruby files inside `$LOAD_PATH`.
85
+ It defaults to ignore any directory named `node_modules`.
80
86
 
81
87
  ### Environments
82
88
 
@@ -90,9 +90,11 @@ static ID instrumentation_method;
90
90
  static VALUE sym_miss;
91
91
  static VALUE sym_stale;
92
92
  static bool instrumentation_enabled = false;
93
+ static bool readonly = false;
93
94
 
94
95
  /* Functions exposed as module functions on Bootsnap::CompileCache::Native */
95
96
  static VALUE bs_instrumentation_enabled_set(VALUE self, VALUE enabled);
97
+ static VALUE bs_readonly_set(VALUE self, VALUE enabled);
96
98
  static VALUE bs_compile_option_crc32_set(VALUE self, VALUE crc32_v);
97
99
  static VALUE bs_rb_fetch(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler, VALUE args);
98
100
  static VALUE bs_rb_precompile(VALUE self, VALUE cachedir_v, VALUE path_v, VALUE handler);
@@ -166,6 +168,7 @@ Init_bootsnap(void)
166
168
  rb_global_variable(&sym_stale);
167
169
 
168
170
  rb_define_module_function(rb_mBootsnap, "instrumentation_enabled=", bs_instrumentation_enabled_set, 1);
171
+ rb_define_module_function(rb_mBootsnap_CompileCache_Native, "readonly=", bs_readonly_set, 1);
169
172
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "coverage_running?", bs_rb_coverage_running, 0);
170
173
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "fetch", bs_rb_fetch, 4);
171
174
  rb_define_module_function(rb_mBootsnap_CompileCache_Native, "precompile", bs_rb_precompile, 3);
@@ -182,6 +185,13 @@ bs_instrumentation_enabled_set(VALUE self, VALUE enabled)
182
185
  return enabled;
183
186
  }
184
187
 
188
+ static VALUE
189
+ bs_readonly_set(VALUE self, VALUE enabled)
190
+ {
191
+ readonly = RTEST(enabled);
192
+ return enabled;
193
+ }
194
+
185
195
  /*
186
196
  * Bootsnap's ruby code registers a hook that notifies us via this function
187
197
  * when compile_option changes. These changes invalidate all existing caches.
@@ -945,12 +955,17 @@ try_input_to_storage(VALUE arg)
945
955
  static int
946
956
  bs_input_to_storage(VALUE handler, VALUE args, VALUE input_data, VALUE pathval, VALUE * storage_data)
947
957
  {
948
- int state;
949
- struct i2s_data i2s_data = {
950
- .handler = handler,
951
- .input_data = input_data,
952
- .pathval = pathval,
953
- };
954
- *storage_data = rb_protect(try_input_to_storage, (VALUE)&i2s_data, &state);
955
- return state;
958
+ if (readonly) {
959
+ *storage_data = rb_cBootsnap_CompileCache_UNCOMPILABLE;
960
+ return 0;
961
+ } else {
962
+ int state;
963
+ struct i2s_data i2s_data = {
964
+ .handler = handler,
965
+ .input_data = input_data,
966
+ .pathval = pathval,
967
+ };
968
+ *storage_data = rb_protect(try_input_to_storage, (VALUE)&i2s_data, &state);
969
+ return state;
970
+ }
956
971
  }
@@ -65,7 +65,7 @@ module Bootsnap
65
65
  end
66
66
 
67
67
  unless const_defined?(:NoTagsVisitor)
68
- visitor = Class.new(Psych::Visitors::ToRuby) do
68
+ visitor = Class.new(Psych::Visitors::NoAliasRuby) do
69
69
  def visit(target)
70
70
  if target.tag
71
71
  raise UnsupportedTags, "YAML tags are not supported: #{target.tag}"
@@ -129,7 +129,10 @@ module Bootsnap
129
129
  ast = ::YAML.parse(payload)
130
130
  return ast unless ast
131
131
 
132
- NoTagsVisitor.create.visit(ast)
132
+ loader = ::Psych::ClassLoader::Restricted.new(["Symbol"], [])
133
+ scanner = ::Psych::ScalarScanner.new(loader)
134
+
135
+ NoTagsVisitor.new(scanner, loader).visit(ast)
133
136
  end
134
137
  end
135
138
 
@@ -10,7 +10,7 @@ module Bootsnap
10
10
  Error = Class.new(StandardError)
11
11
  PermissionError = Class.new(Error)
12
12
 
13
- def self.setup(cache_dir:, iseq:, yaml:, json:)
13
+ def self.setup(cache_dir:, iseq:, yaml:, json:, readonly: false)
14
14
  if iseq
15
15
  if supported?
16
16
  require_relative("compile_cache/iseq")
@@ -37,6 +37,10 @@ module Bootsnap
37
37
  warn("[bootsnap/setup] JSON parsing caching is not supported on this implementation of Ruby")
38
38
  end
39
39
  end
40
+
41
+ if supported? && defined?(Bootsnap::CompileCache::Native)
42
+ Bootsnap::CompileCache::Native.readonly = readonly
43
+ end
40
44
  end
41
45
 
42
46
  def self.permission_error(path)
@@ -66,7 +66,7 @@ module Bootsnap
66
66
  end
67
67
 
68
68
  def self.unregister(arr)
69
- return unless arr.instance_variable_get(:@lpc_observer)
69
+ return unless arr.instance_variable_defined?(:@lpc_observer) && arr.instance_variable_get(:@lpc_observer)
70
70
 
71
71
  ArrayMixin.instance_methods.each do |method_name|
72
72
  arr.singleton_class.send(:remove_method, method_name)
@@ -40,7 +40,7 @@ module Bootsnap
40
40
 
41
41
  # /a/b/lib/my/foo.rb
42
42
  # ^^^^^^^^^
43
- short = feat[(lpe.length + 1)..-1]
43
+ short = feat[(lpe.length + 1)..]
44
44
  stripped = strip_extension_if_elidable(short)
45
45
  @lfi[short] = hash
46
46
  @lfi[stripped] = hash
@@ -76,7 +76,7 @@ module Bootsnap
76
76
  end
77
77
 
78
78
  def identify(short, cursor)
79
- $LOADED_FEATURES[cursor..-1].detect do |feat|
79
+ $LOADED_FEATURES[cursor..].detect do |feat|
80
80
  offset = 0
81
81
  while (offset = feat.index(short, offset))
82
82
  if feat.index(".", offset + 1) && !feat.index("/", offset + 2)
@@ -116,8 +116,8 @@ module Bootsnap
116
116
  VOLATILE = :volatile
117
117
 
118
118
  # Built-in ruby lib stuff doesn't change, but things can occasionally be
119
- # installed into sitedir, which generally lives under libdir.
120
- RUBY_LIBDIR = RbConfig::CONFIG["libdir"]
119
+ # installed into sitedir, which generally lives under rubylibdir.
120
+ RUBY_LIBDIR = RbConfig::CONFIG["rubylibdir"]
121
121
  RUBY_SITEDIR = RbConfig::CONFIG["sitedir"]
122
122
 
123
123
  def stability
@@ -15,7 +15,11 @@ module Bootsnap
15
15
  ""
16
16
  end
17
17
 
18
+ @ignored_directories = %w(node_modules)
19
+
18
20
  class << self
21
+ attr_accessor :ignored_directories
22
+
19
23
  def call(path)
20
24
  path = File.expand_path(path.to_s).freeze
21
25
  return [[], []] unless File.directory?(path)
@@ -50,6 +54,8 @@ module Bootsnap
50
54
 
51
55
  absolute_path = "#{absolute_dir_path}/#{name}"
52
56
  if File.directory?(absolute_path)
57
+ next if ignored_directories.include?(name)
58
+
53
59
  if yield relative_path, absolute_path, true
54
60
  walk(absolute_path, relative_path, &block)
55
61
  end
@@ -13,10 +13,11 @@ module Bootsnap
13
13
  NestedTransactionError = Class.new(StandardError)
14
14
  SetOutsideTransactionNotAllowed = Class.new(StandardError)
15
15
 
16
- def initialize(store_path)
16
+ def initialize(store_path, readonly: false)
17
17
  @store_path = store_path
18
18
  @txn_mutex = Mutex.new
19
19
  @dirty = false
20
+ @readonly = readonly
20
21
  load_data
21
22
  end
22
23
 
@@ -63,7 +64,7 @@ module Bootsnap
63
64
  end
64
65
 
65
66
  def commit_transaction
66
- if @dirty
67
+ if @dirty && !@readonly
67
68
  dump_data
68
69
  @dirty = false
69
70
  end
@@ -28,17 +28,18 @@ module Bootsnap
28
28
  alias_method :enabled?, :enabled
29
29
  remove_method(:enabled)
30
30
 
31
- def setup(cache_path:, development_mode:)
31
+ def setup(cache_path:, development_mode:, ignore_directories:, readonly: false)
32
32
  unless supported?
33
33
  warn("[bootsnap/setup] Load path caching is not supported on this implementation of Ruby") if $VERBOSE
34
34
  return
35
35
  end
36
36
 
37
- store = Store.new(cache_path)
37
+ store = Store.new(cache_path, readonly: readonly)
38
38
 
39
39
  @loaded_features_index = LoadedFeaturesIndex.new
40
40
 
41
41
  @load_path_cache = Cache.new(store, $LOAD_PATH, development_mode: development_mode)
42
+ PathScanner.ignored_directories = ignore_directories if ignore_directories
42
43
  @enabled = true
43
44
  require_relative("load_path_cache/core_ext/kernel_require")
44
45
  require_relative("load_path_cache/core_ext/loaded_features")
@@ -50,7 +51,6 @@ module Bootsnap
50
51
  @realpath_cache = nil
51
52
  @load_path_cache = nil
52
53
  ChangeObserver.unregister($LOAD_PATH)
53
- ::Kernel.alias_method(:require_relative, :require_relative_without_bootsnap)
54
54
  end
55
55
 
56
56
  def supported?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bootsnap
4
- VERSION = "1.13.0"
4
+ VERSION = "1.16.0"
5
5
  end
data/lib/bootsnap.rb CHANGED
@@ -39,32 +39,18 @@ module Bootsnap
39
39
  cache_dir:,
40
40
  development_mode: true,
41
41
  load_path_cache: true,
42
- autoload_paths_cache: nil,
43
- disable_trace: nil,
42
+ ignore_directories: nil,
43
+ readonly: false,
44
44
  compile_cache_iseq: true,
45
45
  compile_cache_yaml: true,
46
46
  compile_cache_json: true
47
47
  )
48
- unless autoload_paths_cache.nil?
49
- warn "[DEPRECATED] Bootsnap's `autoload_paths_cache:` option is deprecated and will be removed. " \
50
- "If you use Zeitwerk this option is useless, and if you are still using the classic autoloader " \
51
- "upgrading is recommended."
52
- end
53
-
54
- unless disable_trace.nil?
55
- warn "[DEPRECATED] Bootsnap's `disable_trace:` option is deprecated and will be removed. " \
56
- "If you use Ruby 2.5 or newer this option is useless, if not upgrading is recommended."
57
- end
58
-
59
- if compile_cache_iseq && !iseq_cache_supported?
60
- warn "Ruby 2.5 has a bug that break code tracing when code is loaded from cache. It is recommened " \
61
- "to turn `compile_cache_iseq` off on Ruby 2.5"
62
- end
63
-
64
48
  if load_path_cache
65
49
  Bootsnap::LoadPathCache.setup(
66
50
  cache_path: "#{cache_dir}/bootsnap/load-path-cache",
67
51
  development_mode: development_mode,
52
+ ignore_directories: ignore_directories,
53
+ readonly: readonly,
68
54
  )
69
55
  end
70
56
 
@@ -73,20 +59,14 @@ module Bootsnap
73
59
  iseq: compile_cache_iseq,
74
60
  yaml: compile_cache_yaml,
75
61
  json: compile_cache_json,
62
+ readonly: readonly,
76
63
  )
77
64
  end
78
65
 
79
- def self.unload_cache!
66
+ def unload_cache!
80
67
  LoadPathCache.unload!
81
68
  end
82
69
 
83
- def iseq_cache_supported?
84
- return @iseq_cache_supported if defined? @iseq_cache_supported
85
-
86
- ruby_version = Gem::Version.new(RUBY_VERSION)
87
- @iseq_cache_supported = ruby_version < Gem::Version.new("2.5.0") || ruby_version >= Gem::Version.new("2.6.0")
88
- end
89
-
90
70
  def default_setup
91
71
  env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || ENV["ENV"]
92
72
  development_mode = ["", nil, "development"].include?(env)
@@ -113,13 +93,19 @@ module Bootsnap
113
93
  cache_dir = File.join(app_root, "tmp", "cache")
114
94
  end
115
95
 
96
+ ignore_directories = if ENV.key?("BOOTSNAP_IGNORE_DIRECTORIES")
97
+ ENV["BOOTSNAP_IGNORE_DIRECTORIES"].split(",")
98
+ end
99
+
116
100
  setup(
117
101
  cache_dir: cache_dir,
118
102
  development_mode: development_mode,
119
103
  load_path_cache: !ENV["DISABLE_BOOTSNAP_LOAD_PATH_CACHE"],
120
- compile_cache_iseq: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"] && iseq_cache_supported?,
104
+ compile_cache_iseq: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"],
121
105
  compile_cache_yaml: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"],
122
106
  compile_cache_json: !ENV["DISABLE_BOOTSNAP_COMPILE_CACHE"],
107
+ readonly: !!ENV["BOOTSNAP_READONLY"],
108
+ ignore_directories: ignore_directories,
123
109
  )
124
110
 
125
111
  if ENV["BOOTSNAP_LOG"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bootsnap
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.0
4
+ version: 1.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-28 00:00:00.000000000 Z
11
+ date: 2023-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -76,7 +76,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
76
76
  requirements:
77
77
  - - ">="
78
78
  - !ruby/object:Gem::Version
79
- version: 2.5.0
79
+ version: 2.6.0
80
80
  required_rubygems_version: !ruby/object:Gem::Requirement
81
81
  requirements:
82
82
  - - ">="