scint 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/FEATURES.md +4 -0
- data/README.md +161 -41
- data/VERSION +1 -1
- data/bin/scint +9 -0
- data/lib/bundler.rb +106 -0
- data/lib/scint/cache/layout.rb +72 -14
- data/lib/scint/cache/manifest.rb +120 -0
- data/lib/scint/cache/metadata_store.rb +4 -11
- data/lib/scint/cache/prewarm.rb +445 -33
- data/lib/scint/cache/validity.rb +134 -0
- data/lib/scint/cli/cache.rb +36 -7
- data/lib/scint/cli/exec.rb +13 -25
- data/lib/scint/cli/install.rb +1452 -164
- data/lib/scint/credentials.rb +78 -15
- data/lib/scint/debug/io_trace.rb +26 -7
- data/lib/scint/downloader/fetcher.rb +25 -1
- data/lib/scint/downloader/pool.rb +67 -15
- data/lib/scint/errors.rb +10 -0
- data/lib/scint/fs.rb +215 -26
- data/lib/scint/gem/package.rb +6 -2
- data/lib/scint/gemfile/parser.rb +44 -10
- data/lib/scint/installer/extension_builder.rb +80 -55
- data/lib/scint/installer/linker.rb +51 -26
- data/lib/scint/installer/planner.rb +53 -34
- data/lib/scint/installer/preparer.rb +170 -47
- data/lib/scint/installer/promoter.rb +97 -0
- data/lib/scint/linker.sh +137 -0
- data/lib/scint/lockfile/parser.rb +2 -1
- data/lib/scint/lockfile/writer.rb +85 -36
- data/lib/scint/platform.rb +8 -0
- data/lib/scint/resolver/provider.rb +15 -2
- data/lib/scint/runtime/exec.rb +52 -26
- data/lib/scint/runtime/setup.rb +29 -1
- data/lib/scint/scheduler.rb +6 -1
- data/lib/scint/spec_utils.rb +133 -0
- data/lib/scint.rb +1 -0
- metadata +6 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "../platform"
|
|
5
|
+
require_relative "../installer/extension_builder"
|
|
6
|
+
|
|
7
|
+
module Scint
|
|
8
|
+
module Cache
|
|
9
|
+
class Telemetry
|
|
10
|
+
def initialize
|
|
11
|
+
@counts = Hash.new(0)
|
|
12
|
+
@mutex = Thread::Mutex.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def increment(key, by = 1)
|
|
16
|
+
@mutex.synchronize do
|
|
17
|
+
@counts[key] += by
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def counts
|
|
22
|
+
@mutex.synchronize { @counts.dup }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def warn_if_needed(cache_root:, io: $stderr)
|
|
26
|
+
snapshot = counts
|
|
27
|
+
return if snapshot.empty?
|
|
28
|
+
|
|
29
|
+
header = "Warning: legacy cache fallback used in #{cache_root}"
|
|
30
|
+
io.puts "#{YELLOW}#{header}#{RESET}"
|
|
31
|
+
snapshot.sort.each do |key, value|
|
|
32
|
+
io.puts " #{key}=#{value}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module Validity
|
|
38
|
+
module_function
|
|
39
|
+
|
|
40
|
+
SUPPORTED_MANIFEST_VERSION = 1
|
|
41
|
+
|
|
42
|
+
def cached_valid?(spec, layout, abi_key: Platform.abi_key, telemetry: nil)
|
|
43
|
+
cached_dir = layout.cached_path(spec, abi_key)
|
|
44
|
+
spec_path = layout.cached_spec_path(spec, abi_key)
|
|
45
|
+
manifest_path = layout.cached_manifest_path(spec, abi_key)
|
|
46
|
+
|
|
47
|
+
return false unless Dir.exist?(cached_dir)
|
|
48
|
+
return false unless File.exist?(spec_path)
|
|
49
|
+
|
|
50
|
+
manifest = read_manifest(manifest_path, telemetry: telemetry)
|
|
51
|
+
if manifest
|
|
52
|
+
return false unless manifest_matches?(manifest, spec, abi_key, layout)
|
|
53
|
+
else
|
|
54
|
+
return false unless legacy_spec_loadable?(spec_path)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def source_path_for(spec, layout, abi_key: Platform.abi_key, telemetry: nil, allow_legacy: false)
|
|
61
|
+
cached_dir = layout.cached_path(spec, abi_key)
|
|
62
|
+
return cached_dir if cached_valid?(spec, layout, abi_key: abi_key, telemetry: telemetry)
|
|
63
|
+
|
|
64
|
+
return nil unless allow_legacy
|
|
65
|
+
|
|
66
|
+
legacy = layout.extracted_path(spec)
|
|
67
|
+
return nil unless legacy_extracted_valid?(spec, legacy, layout)
|
|
68
|
+
|
|
69
|
+
telemetry&.increment("cache.legacy.extracted")
|
|
70
|
+
legacy
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def legacy_extracted_valid?(spec, extracted_dir, layout)
|
|
74
|
+
return false unless Dir.exist?(extracted_dir)
|
|
75
|
+
|
|
76
|
+
legacy_spec_path = layout.spec_cache_path(spec)
|
|
77
|
+
return true if File.exist?(legacy_spec_path)
|
|
78
|
+
|
|
79
|
+
return true if Dir.glob(File.join(extracted_dir, "*.gemspec")).any?
|
|
80
|
+
|
|
81
|
+
true
|
|
82
|
+
rescue StandardError
|
|
83
|
+
false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def read_manifest(path, telemetry: nil)
|
|
87
|
+
unless File.exist?(path)
|
|
88
|
+
telemetry&.increment("cache.manifest.missing")
|
|
89
|
+
return nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
raw = File.read(path)
|
|
93
|
+
raw.force_encoding("UTF-8") unless raw.encoding == Encoding::UTF_8
|
|
94
|
+
data = JSON.parse(raw)
|
|
95
|
+
return nil unless data.is_a?(Hash)
|
|
96
|
+
|
|
97
|
+
version = data["version"]
|
|
98
|
+
return data if version == SUPPORTED_MANIFEST_VERSION
|
|
99
|
+
|
|
100
|
+
telemetry&.increment("cache.manifest.unsupported")
|
|
101
|
+
nil
|
|
102
|
+
rescue StandardError
|
|
103
|
+
telemetry&.increment("cache.manifest.unsupported")
|
|
104
|
+
nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def manifest_matches?(manifest, spec, abi_key, layout)
|
|
108
|
+
manifest["full_name"] == layout.full_name(spec) && manifest["abi"] == abi_key
|
|
109
|
+
rescue StandardError
|
|
110
|
+
false
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def legacy_spec_loadable?(path)
|
|
114
|
+
Marshal.load(File.binread(path))
|
|
115
|
+
true
|
|
116
|
+
rescue StandardError
|
|
117
|
+
false
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def extensions_required?(spec, cached_dir)
|
|
121
|
+
Installer::ExtensionBuilder.needs_build?(spec, cached_dir)
|
|
122
|
+
rescue StandardError
|
|
123
|
+
false
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def extension_build_complete?(spec, layout, abi_key)
|
|
127
|
+
marker = File.join(layout.cached_path(spec, abi_key), Installer::ExtensionBuilder::BUILD_MARKER)
|
|
128
|
+
File.exist?(marker)
|
|
129
|
+
rescue StandardError
|
|
130
|
+
false
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
data/lib/scint/cli/cache.rb
CHANGED
|
@@ -9,6 +9,7 @@ require_relative "../gemfile/parser"
|
|
|
9
9
|
require_relative "../lockfile/parser"
|
|
10
10
|
require_relative "install"
|
|
11
11
|
require_relative "../fs"
|
|
12
|
+
require_relative "../spec_utils"
|
|
12
13
|
|
|
13
14
|
module Scint
|
|
14
15
|
module CLI
|
|
@@ -175,17 +176,45 @@ module Scint
|
|
|
175
176
|
|
|
176
177
|
def clear_packages(packages)
|
|
177
178
|
removed = 0
|
|
178
|
-
%w[inbound extracted ext].each do |subdir|
|
|
179
|
-
subdir_path = File.join(cache_root, subdir)
|
|
180
|
-
next unless Dir.exist?(subdir_path)
|
|
181
179
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
packages.each do |pkg|
|
|
181
|
+
# inbound gems
|
|
182
|
+
Dir.glob(File.join(cache_root, "inbound", "gems", "#{pkg}-*.gem")).each do |path|
|
|
183
|
+
FileUtils.rm_rf(path)
|
|
184
|
+
removed += 1
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# assembling + cached (per-ABI)
|
|
188
|
+
%w[assembling cached].each do |subdir|
|
|
189
|
+
Dir.glob(File.join(cache_root, subdir, "*", "#{pkg}-*")) do |path|
|
|
190
|
+
FileUtils.rm_rf(path)
|
|
191
|
+
removed += 1
|
|
192
|
+
end
|
|
193
|
+
Dir.glob(File.join(cache_root, subdir, "*", "#{pkg}-*.spec.marshal")) do |path|
|
|
194
|
+
FileUtils.rm_rf(path)
|
|
195
|
+
removed += 1
|
|
196
|
+
end
|
|
197
|
+
Dir.glob(File.join(cache_root, subdir, "*", "#{pkg}-*.manifest")) do |path|
|
|
198
|
+
FileUtils.rm_rf(path)
|
|
185
199
|
removed += 1
|
|
186
200
|
end
|
|
187
201
|
end
|
|
202
|
+
|
|
203
|
+
# legacy directories (extracted/ext)
|
|
204
|
+
Dir.glob(File.join(cache_root, "extracted", "#{pkg}-*")) do |path|
|
|
205
|
+
FileUtils.rm_rf(path)
|
|
206
|
+
removed += 1
|
|
207
|
+
end
|
|
208
|
+
Dir.glob(File.join(cache_root, "extracted", "#{pkg}-*.spec.marshal")) do |path|
|
|
209
|
+
FileUtils.rm_rf(path)
|
|
210
|
+
removed += 1
|
|
211
|
+
end
|
|
212
|
+
Dir.glob(File.join(cache_root, "ext", "*", "#{pkg}-*")) do |path|
|
|
213
|
+
FileUtils.rm_rf(path)
|
|
214
|
+
removed += 1
|
|
215
|
+
end
|
|
188
216
|
end
|
|
217
|
+
|
|
189
218
|
removed
|
|
190
219
|
end
|
|
191
220
|
|
|
@@ -306,7 +335,7 @@ module Scint
|
|
|
306
335
|
def dedupe_specs(specs)
|
|
307
336
|
seen = {}
|
|
308
337
|
specs.each do |spec|
|
|
309
|
-
key =
|
|
338
|
+
key = SpecUtils.full_key(spec)
|
|
310
339
|
seen[key] ||= spec
|
|
311
340
|
end
|
|
312
341
|
seen.values
|
data/lib/scint/cli/exec.rb
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
require_relative "../runtime/exec"
|
|
4
4
|
require_relative "../fs"
|
|
5
5
|
require_relative "../platform"
|
|
6
|
+
require_relative "../spec_utils"
|
|
6
7
|
require_relative "../lockfile/parser"
|
|
8
|
+
require "pathname"
|
|
7
9
|
|
|
8
10
|
module Scint
|
|
9
11
|
module CLI
|
|
@@ -68,19 +70,18 @@ module Scint
|
|
|
68
70
|
data = {}
|
|
69
71
|
|
|
70
72
|
lockfile.specs.each do |spec|
|
|
71
|
-
full =
|
|
73
|
+
full = SpecUtils.full_name(spec)
|
|
72
74
|
gem_dir = File.join(ruby_dir, "gems", full)
|
|
73
75
|
next unless Dir.exist?(gem_dir)
|
|
74
76
|
|
|
75
77
|
spec_file = File.join(ruby_dir, "specifications", "#{full}.gemspec")
|
|
76
78
|
require_paths = read_require_paths(spec_file)
|
|
77
79
|
load_paths = require_paths
|
|
78
|
-
.map { |rp|
|
|
80
|
+
.map { |rp| expand_require_path(gem_dir, rp) }
|
|
79
81
|
.select { |path| Dir.exist?(path) }
|
|
80
82
|
|
|
81
83
|
lib_path = File.join(gem_dir, "lib")
|
|
82
84
|
load_paths << lib_path if load_paths.empty? && Dir.exist?(lib_path)
|
|
83
|
-
load_paths.concat(detect_nested_lib_paths(gem_dir))
|
|
84
85
|
load_paths.uniq!
|
|
85
86
|
|
|
86
87
|
ext_path = File.join(ruby_dir, "extensions",
|
|
@@ -102,7 +103,7 @@ module Scint
|
|
|
102
103
|
end
|
|
103
104
|
|
|
104
105
|
def detect_ruby_dir(bundle_dir)
|
|
105
|
-
target =
|
|
106
|
+
target = Platform.ruby_minor_version_dir
|
|
106
107
|
preferred = File.join(bundle_dir, "ruby", target)
|
|
107
108
|
return preferred if Dir.exist?(preferred)
|
|
108
109
|
|
|
@@ -111,39 +112,26 @@ module Scint
|
|
|
111
112
|
end
|
|
112
113
|
|
|
113
114
|
def spec_full_name(spec)
|
|
114
|
-
|
|
115
|
-
version = spec[:version]
|
|
116
|
-
platform = spec[:platform]
|
|
117
|
-
base = "#{name}-#{version}"
|
|
118
|
-
return base if platform.nil? || platform.to_s == "ruby" || platform.to_s.empty?
|
|
119
|
-
|
|
120
|
-
"#{base}-#{platform}"
|
|
115
|
+
SpecUtils.full_name(spec)
|
|
121
116
|
end
|
|
122
117
|
|
|
123
118
|
def read_require_paths(spec_file)
|
|
124
119
|
return ["lib"] unless File.exist?(spec_file)
|
|
125
120
|
|
|
126
|
-
gemspec =
|
|
121
|
+
gemspec = SpecUtils.load_gemspec(spec_file)
|
|
127
122
|
paths = Array(gemspec&.require_paths).reject(&:empty?)
|
|
128
123
|
paths.empty? ? ["lib"] : paths
|
|
129
124
|
rescue StandardError
|
|
130
125
|
["lib"]
|
|
131
126
|
end
|
|
132
127
|
|
|
133
|
-
def
|
|
134
|
-
|
|
135
|
-
return
|
|
136
|
-
|
|
137
|
-
children = Dir.children(lib_dir)
|
|
138
|
-
top_level_rb = children.any? do |entry|
|
|
139
|
-
path = File.join(lib_dir, entry)
|
|
140
|
-
File.file?(path) && entry.end_with?(".rb")
|
|
141
|
-
end
|
|
142
|
-
return [] if top_level_rb
|
|
128
|
+
def expand_require_path(gem_dir, require_path)
|
|
129
|
+
value = require_path.to_s
|
|
130
|
+
return value if Pathname.new(value).absolute?
|
|
143
131
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
132
|
+
File.join(gem_dir, value)
|
|
133
|
+
rescue StandardError
|
|
134
|
+
File.join(gem_dir, require_path.to_s)
|
|
147
135
|
end
|
|
148
136
|
end
|
|
149
137
|
end
|