scint 0.6.0 → 0.7.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 +4 -4
- data/README.md +90 -41
- data/VERSION +1 -1
- data/bin/scint +9 -0
- data/lib/bundler.rb +106 -0
- data/lib/scint/cache/layout.rb +16 -14
- data/lib/scint/cache/metadata_store.rb +4 -11
- data/lib/scint/cli/cache.rb +2 -1
- data/lib/scint/cli/exec.rb +12 -24
- data/lib/scint/cli/install.rb +1034 -124
- 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 +44 -2
- data/lib/scint/gemfile/parser.rb +31 -4
- data/lib/scint/installer/extension_builder.rb +60 -30
- data/lib/scint/installer/linker.rb +8 -24
- data/lib/scint/installer/planner.rb +30 -7
- data/lib/scint/installer/preparer.rb +2 -9
- 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 +58 -0
- data/lib/scint.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 54c3f7baaea3992bcba60136b76f3b08caa580717d82906ebed3a0a69fb23314
|
|
4
|
+
data.tar.gz: 246966d0ba167f43b8c0aee1205e95377afec245dca33060fe3fcce202d0c3ab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 84c58296b1c391e68503090ea3889a3a469c8f63ca6c289710dc6a5f986cd33059aec6a61038ab14c2f653aefaa1675815d2e13a5ba3fb49dad6fe825cf8d3a5
|
|
7
|
+
data.tar.gz: 1cde1ae7ff37856bf44a4f909e024cb094ba1436fe062536aa4c73860ce0aa7c2494c2f5dd09ca14af5be8b226d8ada36ec212278b08a55902da708d734bea3d
|
data/README.md
CHANGED
|
@@ -37,6 +37,29 @@ scint cache clear
|
|
|
37
37
|
scint cache dir
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
Benchmark helpers:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
bin/scint-vs-bundler [--force] [--test-root /tmp/scint-tests] /path/to/project
|
|
44
|
+
bin/scint-bench-matrix [--force] --root /path/to/projects
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`bin/scint-bench-matrix` is a generic runner for a root directory where each
|
|
48
|
+
immediate subdirectory is a Ruby project under git with both `Gemfile` and
|
|
49
|
+
`Gemfile.lock`. It runs bundler cold/warm and scint cold/warm via
|
|
50
|
+
`bin/scint-vs-bundler` and writes:
|
|
51
|
+
|
|
52
|
+
1. `logs/bench-<timestamp>/summary.tsv`
|
|
53
|
+
2. `logs/bench-<timestamp>/table.md`
|
|
54
|
+
|
|
55
|
+
Optional project smoke test convention:
|
|
56
|
+
|
|
57
|
+
1. If `<root>/<project>-test.sh` exists, matrix runs it after the benchmark.
|
|
58
|
+
2. Execution is:
|
|
59
|
+
`cd <root>/<project> && scint exec ../<project>-test.sh`
|
|
60
|
+
3. The script runs against the warm scint install and is included in
|
|
61
|
+
`summary.tsv`/`table.md` status.
|
|
62
|
+
|
|
40
63
|
Performance and IO diagnostics:
|
|
41
64
|
|
|
42
65
|
```bash
|
|
@@ -64,45 +87,45 @@ Defaults:
|
|
|
64
87
|
1. Local install/runtime directory: `.bundle/`
|
|
65
88
|
2. Global cache root: `~/.cache/scint` (or `XDG_CACHE_HOME`)
|
|
66
89
|
|
|
67
|
-
## Install Architecture
|
|
90
|
+
## Install Architecture (Target)
|
|
91
|
+
|
|
92
|
+
Scint should have one clear cache lifecycle:
|
|
68
93
|
|
|
69
|
-
|
|
94
|
+
1. `inbound`
|
|
95
|
+
2. `assembling`
|
|
96
|
+
3. `cached`
|
|
97
|
+
4. `materialize`
|
|
70
98
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
99
|
+
Resolution/planning still decides *what* to install; this pipeline defines *how* each artifact becomes globally reusable.
|
|
100
|
+
|
|
101
|
+
### Phase Contract
|
|
102
|
+
|
|
103
|
+
1. Fetch into `inbound`
|
|
104
|
+
- Gem payloads go to `inbound/gems/`.
|
|
105
|
+
- Git repositories go to `inbound/gits/` using deterministic names (for example `https_github_com__tobi__try`).
|
|
106
|
+
2. Assemble into `assembling`
|
|
107
|
+
- For `.gem` sources: unpack into `assembling/<abi>/<full_name>/`.
|
|
108
|
+
- For git sources: fetch/checkout/submodules in `inbound/gits`, then export/copy the selected tree into `assembling/<abi>/<full_name>/`.
|
|
109
|
+
3. Compile in `assembling`
|
|
110
|
+
- Native extension build happens inside the assembling directory so successful outputs are part of the final cached tree.
|
|
111
|
+
4. Promote atomically to `cached`
|
|
112
|
+
- On success, move `assembling/<abi>/<full_name>/` to `cached/<abi>/<full_name>/`.
|
|
113
|
+
- Write `cached/<abi>/<full_name>.spec.marshal`.
|
|
114
|
+
- Write optional manifest metadata for fast materialization.
|
|
115
|
+
5. Materialize to project path (`.bundle` or `BUNDLE_PATH`)
|
|
116
|
+
- Use clonefile/reflink/hardlink/copy fallback from `cached/<abi>/...`.
|
|
117
|
+
- Do not rebuild if cached artifact is already complete.
|
|
118
|
+
|
|
119
|
+
This gives one primary truth source for warm installs: `cached/<abi>`.
|
|
79
120
|
|
|
80
121
|
```mermaid
|
|
81
122
|
flowchart LR
|
|
82
|
-
A[
|
|
83
|
-
B --> C[
|
|
84
|
-
C --> D[
|
|
85
|
-
D --> E[
|
|
86
|
-
E
|
|
87
|
-
|
|
88
|
-
E -->|download| H[Download .gem]
|
|
89
|
-
H --> I[Extract + Cache Metadata]
|
|
90
|
-
I --> G
|
|
91
|
-
G --> J[All Links Complete]
|
|
92
|
-
J --> K[Native Extension Build]
|
|
93
|
-
K --> L[Runtime + Lockfile Write]
|
|
94
|
-
L --> M[Done]
|
|
95
|
-
|
|
96
|
-
subgraph GlobalCache["Global Cache (~/.cache/scint)"]
|
|
97
|
-
H
|
|
98
|
-
I
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
subgraph ProjectRuntime["Project Runtime (.bundle)"]
|
|
102
|
-
G
|
|
103
|
-
K
|
|
104
|
-
L
|
|
105
|
-
end
|
|
123
|
+
A[Resolve + Plan] --> B[Fetch to inbound]
|
|
124
|
+
B --> C[Assemble in assembling]
|
|
125
|
+
C --> D[Compile in assembling]
|
|
126
|
+
D --> E[Promote to cached]
|
|
127
|
+
E --> F[Materialize to .bundle]
|
|
128
|
+
F --> G[Write Runtime + Lockfile]
|
|
106
129
|
```
|
|
107
130
|
|
|
108
131
|
## Scheduler as Session Object
|
|
@@ -154,24 +177,50 @@ stateDiagram-v2
|
|
|
154
177
|
failed --> [*]
|
|
155
178
|
```
|
|
156
179
|
|
|
157
|
-
## Data Layout
|
|
180
|
+
## Data Layout (Target)
|
|
158
181
|
|
|
159
182
|
Global cache (`~/.cache/scint`):
|
|
160
183
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
184
|
+
```text
|
|
185
|
+
~/.cache/scint/
|
|
186
|
+
inbound/
|
|
187
|
+
gems/
|
|
188
|
+
<full_name>.gem
|
|
189
|
+
gits/
|
|
190
|
+
<deterministic_repo_slug>/
|
|
191
|
+
assembling/
|
|
192
|
+
<ruby-abi>/
|
|
193
|
+
<full_name>/
|
|
194
|
+
cached/
|
|
195
|
+
<ruby-abi>/
|
|
196
|
+
<full_name>/
|
|
197
|
+
<full_name>.spec.marshal
|
|
198
|
+
<full_name>.manifest
|
|
199
|
+
index/
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Example ABI key and gem directory:
|
|
203
|
+
|
|
204
|
+
1. `cached/ruby-3.4.5-arm64-darwin24/zlib-3.2.1/`
|
|
205
|
+
2. `cached/ruby-3.4.5-arm64-darwin24/zlib-3.2.1.spec.marshal`
|
|
166
206
|
|
|
167
207
|
Project-local runtime (`.bundle/`):
|
|
168
208
|
|
|
169
|
-
1. `ruby/<major.minor.0>/gems/`
|
|
209
|
+
1. `ruby/<major.minor.0>/gems/` materialized gem trees
|
|
170
210
|
2. `ruby/<major.minor.0>/specifications/` gemspecs
|
|
171
211
|
3. `ruby/<major.minor.0>/bin/` gem binstubs
|
|
172
212
|
4. `bin/` project-level wrappers
|
|
173
213
|
5. `scint.lock.marshal` runtime lock for `scint exec`
|
|
174
214
|
|
|
215
|
+
## Warm Path Guarantees
|
|
216
|
+
|
|
217
|
+
Required behavior:
|
|
218
|
+
|
|
219
|
+
1. If `cached/<abi>/<full_name>/` exists and is valid, no fetch/extract/compile occurs for that gem.
|
|
220
|
+
2. Deleting only `.bundle/` should trigger only materialization work.
|
|
221
|
+
3. Materialization should be IO-bound and close to instantaneous on warm cache.
|
|
222
|
+
4. Incomplete assemblies must never be promoted; promotion is atomic.
|
|
223
|
+
|
|
175
224
|
## Concurrency Model
|
|
176
225
|
|
|
177
226
|
Scint parallelizes all non-conflicting work aggressively:
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.7.0
|
data/bin/scint
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
# Enable YJIT as early as possible when available.
|
|
5
|
+
if defined?(RubyVM::YJIT) && RubyVM::YJIT.respond_to?(:enable)
|
|
6
|
+
begin
|
|
7
|
+
RubyVM::YJIT.enable unless RubyVM::YJIT.enabled?
|
|
8
|
+
rescue StandardError
|
|
9
|
+
# Keep startup resilient when YJIT is unavailable or disabled at runtime.
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
4
13
|
# Ensure lib is on the load path
|
|
5
14
|
$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
|
6
15
|
|
data/lib/bundler.rb
CHANGED
|
@@ -100,10 +100,14 @@ module Bundler
|
|
|
100
100
|
Kernel.require(candidate)
|
|
101
101
|
return true
|
|
102
102
|
rescue LoadError => e
|
|
103
|
+
raise e unless retryable_load_error?(e, candidate)
|
|
104
|
+
|
|
103
105
|
last_error = e
|
|
104
106
|
end
|
|
105
107
|
end
|
|
106
108
|
|
|
109
|
+
return true if require_matching_basename(name)
|
|
110
|
+
|
|
107
111
|
raise last_error if last_error
|
|
108
112
|
end
|
|
109
113
|
|
|
@@ -164,5 +168,107 @@ module Bundler
|
|
|
164
168
|
end
|
|
165
169
|
filtered.join(" ")
|
|
166
170
|
end
|
|
171
|
+
|
|
172
|
+
def require_matching_basename(name)
|
|
173
|
+
target = normalize_basename(name)
|
|
174
|
+
return false if target.empty?
|
|
175
|
+
|
|
176
|
+
exact_candidates = []
|
|
177
|
+
fuzzy_candidates = []
|
|
178
|
+
|
|
179
|
+
$LOAD_PATH.each do |load_dir|
|
|
180
|
+
next unless File.directory?(load_dir)
|
|
181
|
+
|
|
182
|
+
Dir.children(load_dir).sort.each do |entry|
|
|
183
|
+
next unless entry.end_with?(".rb")
|
|
184
|
+
|
|
185
|
+
basename = entry.delete_suffix(".rb")
|
|
186
|
+
normalized = normalize_basename(basename)
|
|
187
|
+
if normalized == target
|
|
188
|
+
exact_candidates << basename
|
|
189
|
+
elsif compatible_basename?(target, normalized)
|
|
190
|
+
fuzzy_candidates << [basename, normalized]
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
rescue StandardError
|
|
194
|
+
next
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
exact_candidates.uniq.each do |candidate|
|
|
198
|
+
begin
|
|
199
|
+
Kernel.require(candidate)
|
|
200
|
+
return true
|
|
201
|
+
rescue LoadError => e
|
|
202
|
+
raise e unless retryable_load_error?(e, candidate)
|
|
203
|
+
|
|
204
|
+
next
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
fuzzy_candidates
|
|
209
|
+
.uniq
|
|
210
|
+
.sort_by { |_basename, normalized| [((target.length - normalized.length).abs), -normalized.length] }
|
|
211
|
+
.map(&:first)
|
|
212
|
+
.each do |candidate|
|
|
213
|
+
begin
|
|
214
|
+
Kernel.require(candidate)
|
|
215
|
+
return true
|
|
216
|
+
rescue LoadError => e
|
|
217
|
+
raise e unless retryable_load_error?(e, candidate)
|
|
218
|
+
|
|
219
|
+
next
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
false
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def normalize_basename(name)
|
|
227
|
+
name.to_s.downcase.gsub(/[^a-z0-9]/, "")
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def retryable_load_error?(error, candidate)
|
|
231
|
+
requested = if error.respond_to?(:path)
|
|
232
|
+
error.path
|
|
233
|
+
else
|
|
234
|
+
nil
|
|
235
|
+
end
|
|
236
|
+
return true if requested.nil? || requested.empty?
|
|
237
|
+
|
|
238
|
+
variants = [
|
|
239
|
+
candidate.to_s,
|
|
240
|
+
"#{candidate}.rb",
|
|
241
|
+
"#{candidate}.so",
|
|
242
|
+
]
|
|
243
|
+
variants.include?(requested)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def compatible_basename?(target, normalized)
|
|
247
|
+
return false if target.length < 3 || normalized.length < 3
|
|
248
|
+
|
|
249
|
+
target_variants = basename_variants(target)
|
|
250
|
+
normalized_variants = basename_variants(normalized)
|
|
251
|
+
return true if (target_variants & normalized_variants).any?
|
|
252
|
+
|
|
253
|
+
target_variants.any? do |t|
|
|
254
|
+
normalized_variants.any? { |n| t.start_with?(n) || n.start_with?(t) }
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def basename_variants(name)
|
|
259
|
+
value = name.to_s.downcase
|
|
260
|
+
variants = [value]
|
|
261
|
+
|
|
262
|
+
plural_to_s = value.sub(/ties\z/, "s")
|
|
263
|
+
plural_to_y = value.sub(/ies\z/, "y")
|
|
264
|
+
singular = value.sub(/s\z/, "")
|
|
265
|
+
strip_ruby_prefix = value.sub(/\Aruby/, "")
|
|
266
|
+
|
|
267
|
+
variants << plural_to_s unless plural_to_s == value
|
|
268
|
+
variants << plural_to_y unless plural_to_y == value
|
|
269
|
+
variants << singular unless singular == value
|
|
270
|
+
variants << strip_ruby_prefix unless strip_ruby_prefix == value
|
|
271
|
+
variants.uniq.select { |v| v.length >= 3 }
|
|
272
|
+
end
|
|
167
273
|
end
|
|
168
274
|
end
|
data/lib/scint/cache/layout.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "../fs"
|
|
4
4
|
require_relative "../platform"
|
|
5
|
+
require_relative "../spec_utils"
|
|
5
6
|
require "digest"
|
|
6
7
|
require "uri"
|
|
7
8
|
|
|
@@ -35,7 +36,7 @@ module Scint
|
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
def git_dir
|
|
38
|
-
File.join(
|
|
39
|
+
File.join(inbound_dir, "git")
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
# Isolated gem home used while compiling native extensions during install.
|
|
@@ -45,7 +46,7 @@ module Scint
|
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
def install_ruby_dir
|
|
48
|
-
|
|
49
|
+
Platform.ruby_install_dir(install_env_dir)
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
# -- Per-spec paths ------------------------------------------------------
|
|
@@ -78,23 +79,20 @@ module Scint
|
|
|
78
79
|
end
|
|
79
80
|
|
|
80
81
|
def git_path(uri)
|
|
81
|
-
slug =
|
|
82
|
-
File.join(git_dir, slug)
|
|
82
|
+
slug = git_slug(uri)
|
|
83
|
+
File.join(git_dir, "repos", slug)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def git_checkout_path(uri, revision)
|
|
87
|
+
slug = git_slug(uri)
|
|
88
|
+
rev = revision.to_s.gsub(/[^0-9A-Za-z._-]/, "_")
|
|
89
|
+
File.join(git_dir, "checkouts", slug, rev)
|
|
83
90
|
end
|
|
84
91
|
|
|
85
92
|
# -- Helpers -------------------------------------------------------------
|
|
86
93
|
|
|
87
94
|
def full_name(spec)
|
|
88
|
-
|
|
89
|
-
version = spec.respond_to?(:version) ? spec.version : spec[:version]
|
|
90
|
-
platform = spec.respond_to?(:platform) ? spec.platform : spec[:platform]
|
|
91
|
-
|
|
92
|
-
base = "#{name}-#{version}"
|
|
93
|
-
if platform && platform.to_s != "ruby" && platform.to_s != ""
|
|
94
|
-
"#{base}-#{platform}"
|
|
95
|
-
else
|
|
96
|
-
base
|
|
97
|
-
end
|
|
95
|
+
SpecUtils.full_name(spec)
|
|
98
96
|
end
|
|
99
97
|
|
|
100
98
|
# Ensure a directory exists (thread-safe, cached).
|
|
@@ -126,6 +124,10 @@ module Scint
|
|
|
126
124
|
Digest::SHA256.hexdigest(str)[0, 16]
|
|
127
125
|
end
|
|
128
126
|
end
|
|
127
|
+
|
|
128
|
+
def git_slug(uri)
|
|
129
|
+
Digest::SHA256.hexdigest(uri.to_s)[0, 16]
|
|
130
|
+
end
|
|
129
131
|
end
|
|
130
132
|
end
|
|
131
133
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../fs"
|
|
4
|
+
require_relative "../spec_utils"
|
|
4
5
|
|
|
5
6
|
module Scint
|
|
6
7
|
module Cache
|
|
@@ -30,7 +31,7 @@ module Scint
|
|
|
30
31
|
# Check if a gem is installed. specs_hash keys are "name-version" or "name-version-platform".
|
|
31
32
|
def installed?(name, version, platform = "ruby")
|
|
32
33
|
data = load
|
|
33
|
-
key =
|
|
34
|
+
key = SpecUtils.full_name_for(name, version, platform)
|
|
34
35
|
data.key?(key)
|
|
35
36
|
end
|
|
36
37
|
|
|
@@ -38,7 +39,7 @@ module Scint
|
|
|
38
39
|
def add(name, version, platform = "ruby")
|
|
39
40
|
@mutex.synchronize do
|
|
40
41
|
@data ||= load_from_disk
|
|
41
|
-
key =
|
|
42
|
+
key = SpecUtils.full_name_for(name, version, platform)
|
|
42
43
|
@data[key] = true
|
|
43
44
|
FS.atomic_write(@path, Marshal.dump(@data))
|
|
44
45
|
end
|
|
@@ -48,7 +49,7 @@ module Scint
|
|
|
48
49
|
def remove(name, version, platform = "ruby")
|
|
49
50
|
@mutex.synchronize do
|
|
50
51
|
@data ||= load_from_disk
|
|
51
|
-
key =
|
|
52
|
+
key = SpecUtils.full_name_for(name, version, platform)
|
|
52
53
|
@data.delete(key)
|
|
53
54
|
FS.atomic_write(@path, Marshal.dump(@data))
|
|
54
55
|
end
|
|
@@ -56,14 +57,6 @@ module Scint
|
|
|
56
57
|
|
|
57
58
|
private
|
|
58
59
|
|
|
59
|
-
def cache_key(name, version, platform)
|
|
60
|
-
if platform && platform.to_s != "ruby" && platform.to_s != ""
|
|
61
|
-
"#{name}-#{version}-#{platform}"
|
|
62
|
-
else
|
|
63
|
-
"#{name}-#{version}"
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
60
|
def load_from_disk
|
|
68
61
|
return {} unless File.exist?(@path)
|
|
69
62
|
Marshal.load(File.binread(@path))
|
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
|
|
@@ -306,7 +307,7 @@ module Scint
|
|
|
306
307
|
def dedupe_specs(specs)
|
|
307
308
|
seen = {}
|
|
308
309
|
specs.each do |spec|
|
|
309
|
-
key =
|
|
310
|
+
key = SpecUtils.full_key(spec)
|
|
310
311
|
seen[key] ||= spec
|
|
311
312
|
end
|
|
312
313
|
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,13 +112,7 @@ 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)
|
|
@@ -130,20 +125,13 @@ module Scint
|
|
|
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
|