bootsnap 1.13.0 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="