scint 0.7.0 → 0.8.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/FEATURES.md +4 -0
- data/README.md +142 -198
- data/VERSION +1 -1
- data/lib/scint/cache/layout.rb +66 -5
- data/lib/scint/cache/manifest.rb +120 -0
- data/lib/scint/cache/prewarm.rb +445 -33
- data/lib/scint/cache/validity.rb +134 -0
- data/lib/scint/cli/cache.rb +34 -6
- data/lib/scint/cli/exec.rb +1 -1
- data/lib/scint/cli/install.rb +611 -292
- data/lib/scint/fs.rb +175 -28
- data/lib/scint/gem/package.rb +6 -2
- data/lib/scint/gemfile/parser.rb +13 -6
- data/lib/scint/index/client.rb +13 -2
- data/lib/scint/installer/extension_builder.rb +63 -43
- data/lib/scint/installer/linker.rb +43 -2
- data/lib/scint/installer/planner.rb +24 -28
- data/lib/scint/installer/preparer.rb +167 -37
- data/lib/scint/installer/promoter.rb +97 -0
- data/lib/scint/linker.sh +137 -0
- data/lib/scint/spec_utils.rb +79 -0
- data/lib/scint.rb +12 -4
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4b276421c3f10fed0f5211f84930f1280c73b4b087891177b2146eeb25c4ff03
|
|
4
|
+
data.tar.gz: 70c5d92d2d14c747f71499196499a4ddf58a38e578ac7ae4e27deb3d645da2dc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 855cf61d3a05a3bc5e4fa886a8e9243a307ed471051c374196733d71190a88b09ac2119915c0444edb7d3cdcc07f9111b4d8c552208d38ddc4ee1f2cf60adda1
|
|
7
|
+
data.tar.gz: 816ae198c494d1acc655f17bca68f29663d1df1e346c404af65ac90783b02d94473a067055beb0be7ce57c7e00300eddc3962ffc7d2fb2bb5f809c612598b7c4
|
data/FEATURES.md
CHANGED
|
@@ -10,4 +10,8 @@
|
|
|
10
10
|
- the installation process involves compilation. We attempt to have compilation happen while its not blocking other operations, but also only one compilation at a time
|
|
11
11
|
- we have a book keeping object that governs the worker pools and that's present during each step (fetch, extract, compile, install) and recieves the tasks for each phase from the workers.
|
|
12
12
|
- i suspect that we need to fork of a worker for compilation which we then have to communicate with via some rpc format. simple "-> CALL method, <- RESULT:\n...." type line protocol through stdin/out might work well enough there.
|
|
13
|
+
- cache validity is defined by cached tree + spec marshal + versioned manifest, scoped by ABI (with `gem.build_complete` for native extensions)
|
|
14
|
+
- cached manifests are versioned JSON with deterministic ordering and a file list for fast materialization
|
|
15
|
+
- git cache slugs are deterministic hashes of the normalized source URI with collision detection + telemetry
|
|
16
|
+
- legacy cached entries without a manifest remain read-compatible but emit telemetry for deprecation
|
|
13
17
|
|
data/README.md
CHANGED
|
@@ -1,265 +1,209 @@
|
|
|
1
1
|
# Scint
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
```
|
|
4
|
+
tobi ~/t/fizzy ❯❯❯ scint install
|
|
5
|
+
💎 Scintellating Gemfile into ./.bundle (scint 0.7.1, ruby 4.0.1)
|
|
6
|
+
|
|
7
|
+
Fetching index https://rubygems.org
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
162 gems installed total (162 cached). (1.42s, 15 workers used)
|
|
10
|
+
```
|
|
6
11
|
|
|
7
|
-
Scint is
|
|
12
|
+
Scint is an experimental Bundler replacement. Pure Ruby, no dependencies.
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
2. It writes standard `Gemfile.lock`.
|
|
11
|
-
3. It interoperates with Bundler runtime layout. For example, `BUNDLE_PATH=".bundle" bundle exec ...` is expected to work.
|
|
12
|
-
4. The intent is identical behavior with better install and execution throughput.
|
|
14
|
+
It reads your `Gemfile` and `Gemfile.lock`, writes standard `Gemfile.lock`, and materializes into `.bundle/` so `bundle exec` keeps working. Same behavior, much faster installs.
|
|
13
15
|
|
|
14
|
-
The core idea
|
|
16
|
+
The core idea:
|
|
15
17
|
|
|
16
18
|
1. Prepare artifacts once in a global cache (`~/.cache/scint`).
|
|
17
|
-
2. Materialize
|
|
18
|
-
3.
|
|
19
|
-
|
|
20
|
-
## Why Scint
|
|
19
|
+
2. Materialize into `.bundle/` via clonefile/hardlink/copy.
|
|
20
|
+
3. Warm installs are effectively instant because no fetch, extract, or compile happens when the cache is populated.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
## Why "Scint"
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
From *scintillation*: short, high-energy flashes.
|
|
25
25
|
|
|
26
26
|
1. Event-driven scheduling.
|
|
27
27
|
2. Burst parallelism where safe.
|
|
28
28
|
3. Tight phase boundaries with clear handoffs.
|
|
29
29
|
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
scint install
|
|
34
|
-
scint exec <command>
|
|
35
|
-
scint cache list
|
|
36
|
-
scint cache clear
|
|
37
|
-
scint cache dir
|
|
38
|
-
```
|
|
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`
|
|
30
|
+
## Install Pipeline
|
|
54
31
|
|
|
55
|
-
|
|
32
|
+
Every gem flows through a deterministic cache pipeline. Resolution decides *what* to install; the pipeline defines *how*.
|
|
56
33
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
# Ruby-level IO trace (JSONL)
|
|
70
|
-
SCINT_IO_TRACE=/tmp/scint-io.jsonl scint install --force
|
|
71
|
-
|
|
72
|
-
# Summarize high-volume IO operations for quick LLM review
|
|
73
|
-
scint-io-summary /tmp/scint-io.jsonl
|
|
74
|
-
|
|
75
|
-
# Syscall-level trace (Linux strace / macOS dtruss)
|
|
76
|
-
scint-syscall-trace /tmp/scint-sys.log -- scint install --force
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Compatibility example:
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
BUNDLE_PATH=".bundle" bundle exec ruby -v
|
|
34
|
+
```mermaid
|
|
35
|
+
flowchart TD
|
|
36
|
+
A["Fetch + Assemble"]
|
|
37
|
+
B[Compile]
|
|
38
|
+
C[Promote]
|
|
39
|
+
D[Materialize]
|
|
40
|
+
|
|
41
|
+
A -->|"gems: download .gem, unpack\ngit: clone, checkout, export"| B
|
|
42
|
+
B -->|"native extensions built\ninside assembling tree"| C
|
|
43
|
+
C -->|"atomic move to cached/\nwrite .spec.marshal + manifest"| D
|
|
44
|
+
D -->|"clonefile / hardlink / copy\nfrom cached/ to .bundle/"| E[Done]
|
|
83
45
|
```
|
|
84
46
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
1. Local install/runtime directory: `.bundle/`
|
|
88
|
-
2. Global cache root: `~/.cache/scint` (or `XDG_CACHE_HOME`)
|
|
89
|
-
|
|
90
|
-
## Install Architecture (Target)
|
|
91
|
-
|
|
92
|
-
Scint should have one clear cache lifecycle:
|
|
47
|
+
### Phase details
|
|
93
48
|
|
|
94
|
-
1. `inbound`
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
49
|
+
1. **Fetch + Assemble** into `inbound/` and `assembling/`
|
|
50
|
+
- Download gem payloads to `inbound/gems/`.
|
|
51
|
+
- Clone/fetch git repos into `inbound/gits/<sha256-slug>/`.
|
|
52
|
+
- Unpack `.gem` files into `assembling/<abi>/<full_name>/`.
|
|
53
|
+
- For git sources: checkout, submodules, then export the tree into `assembling/<abi>/<full_name>/`.
|
|
54
|
+
2. **Compile** in `assembling/`
|
|
55
|
+
- Native extension builds happen inside the assembling directory so outputs are part of the final tree.
|
|
56
|
+
3. **Promote** atomically to `cached/`
|
|
57
|
+
- Move `assembling/<abi>/<full_name>/` to `cached/<abi>/<full_name>/`.
|
|
58
|
+
- Write `cached/<abi>/<full_name>.spec.marshal` and `.manifest`.
|
|
59
|
+
- Failed builds never reach `cached/`.
|
|
60
|
+
4. **Materialize** to `.bundle/`
|
|
61
|
+
- Clonefile/reflink/hardlink/copy from `cached/<abi>/`.
|
|
62
|
+
- No rebuild if the cached artifact is valid.
|
|
98
63
|
|
|
99
|
-
|
|
64
|
+
One truth source for warm installs: `cached/<abi>`.
|
|
100
65
|
|
|
101
|
-
|
|
66
|
+
## Scheduler
|
|
102
67
|
|
|
103
|
-
|
|
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>`.
|
|
68
|
+
The `Scheduler` is the install session object. It owns the job graph, worker pool, and phase coordination.
|
|
120
69
|
|
|
121
70
|
```mermaid
|
|
122
|
-
flowchart
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
71
|
+
flowchart TD
|
|
72
|
+
subgraph "Early Parallel"
|
|
73
|
+
FI[fetch_index]
|
|
74
|
+
GC[git_clone]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
FI --> R[resolve]
|
|
78
|
+
GC --> R
|
|
79
|
+
|
|
80
|
+
R --> P[plan]
|
|
81
|
+
|
|
82
|
+
subgraph "Install DAG"
|
|
83
|
+
DL[download] --> EX[extract]
|
|
84
|
+
EX --> LN[link]
|
|
85
|
+
LN --> BE[build_ext]
|
|
86
|
+
BE --> BS[binstub]
|
|
87
|
+
LN --> BS
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
P --> DL
|
|
91
|
+
P --> LN2["link (cached)"]
|
|
92
|
+
LN2 --> BS2[binstub]
|
|
129
93
|
```
|
|
130
94
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
The `Scheduler` is more than a queue: it is the install *session object*.
|
|
134
|
-
It owns global execution state and coordinates workers with phase-aware semantics.
|
|
135
|
-
|
|
136
|
-
The scheduler tracks:
|
|
137
|
-
|
|
138
|
-
1. Job graph and dependencies
|
|
139
|
-
2. Priority classes by job type
|
|
140
|
-
3. Worker pool scaling
|
|
141
|
-
4. Job state transitions (`pending`, `running`, `completed`, `failed`)
|
|
142
|
-
5. Follow-up chaining (for phase handoff)
|
|
143
|
-
6. Fail-fast abort state and error collection
|
|
144
|
-
7. Progress/stats snapshots used by reporting
|
|
95
|
+
Job priorities (lower = dispatched first):
|
|
145
96
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
U->>C: scint install
|
|
157
|
-
C->>S: start(max_workers, fail_fast)
|
|
158
|
-
C->>S: enqueue(fetch_index/git/...)
|
|
159
|
-
S->>W: dispatch ready jobs
|
|
160
|
-
W->>T: execute payload
|
|
161
|
-
T-->>S: complete/fail + result
|
|
162
|
-
S-->>C: wait_for phase completion
|
|
163
|
-
C->>S: enqueue next phase (download/link/build_ext)
|
|
164
|
-
S-->>C: stats + errors + aborted?
|
|
165
|
-
C-->>U: summary + lockfile/runtime outputs
|
|
166
|
-
```
|
|
97
|
+
| Priority | Job Type | Concurrency |
|
|
98
|
+
|----------|----------|-------------|
|
|
99
|
+
| 0 | `fetch_index` | all workers |
|
|
100
|
+
| 1 | `git_clone` | all workers |
|
|
101
|
+
| 2 | `resolve` | 1 |
|
|
102
|
+
| 3 | `download` | bounded (default 8) |
|
|
103
|
+
| 4 | `extract` | IO-limited |
|
|
104
|
+
| 5 | `link` | IO-limited |
|
|
105
|
+
| 6 | `build_ext` | CPU-limited (slots x make -j) |
|
|
106
|
+
| 7 | `binstub` | 1 |
|
|
167
107
|
|
|
168
|
-
|
|
108
|
+
Workers start at 1, scale dynamically up to `cpu_count * 2` (max 50). Compile concurrency is tuned separately: a small number of slots each running `make -jN` to keep CPU saturated without thrashing.
|
|
169
109
|
|
|
170
|
-
|
|
171
|
-
stateDiagram-v2
|
|
172
|
-
[*] --> pending
|
|
173
|
-
pending --> running: dependencies satisfied + worker slot available
|
|
174
|
-
running --> completed: success
|
|
175
|
-
running --> failed: exception / command failure
|
|
176
|
-
completed --> [*]
|
|
177
|
-
failed --> [*]
|
|
178
|
-
```
|
|
110
|
+
Fail-fast mode aborts scheduling after the first hard failure.
|
|
179
111
|
|
|
180
|
-
## Data Layout
|
|
112
|
+
## Data Layout
|
|
181
113
|
|
|
182
114
|
Global cache (`~/.cache/scint`):
|
|
183
115
|
|
|
184
|
-
```
|
|
116
|
+
```
|
|
185
117
|
~/.cache/scint/
|
|
186
118
|
inbound/
|
|
187
|
-
gems
|
|
188
|
-
|
|
189
|
-
gits/
|
|
190
|
-
<deterministic_repo_slug>/
|
|
119
|
+
gems/<full_name>.gem
|
|
120
|
+
gits/<sha256-slug>/
|
|
191
121
|
assembling/
|
|
192
|
-
<ruby-abi>/
|
|
193
|
-
<full_name>/
|
|
122
|
+
<ruby-abi>/<full_name>/
|
|
194
123
|
cached/
|
|
195
124
|
<ruby-abi>/
|
|
196
125
|
<full_name>/
|
|
197
126
|
<full_name>.spec.marshal
|
|
198
127
|
<full_name>.manifest
|
|
199
128
|
index/
|
|
129
|
+
<source-slug>/
|
|
200
130
|
```
|
|
201
131
|
|
|
202
|
-
|
|
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`
|
|
132
|
+
ABI key example: `ruby-4.0.1-arm64-darwin25`
|
|
206
133
|
|
|
207
134
|
Project-local runtime (`.bundle/`):
|
|
208
135
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
136
|
+
```
|
|
137
|
+
.bundle/
|
|
138
|
+
ruby/<major.minor.0>/
|
|
139
|
+
gems/ # materialized gem trees
|
|
140
|
+
specifications/ # gemspecs
|
|
141
|
+
bin/ # gem binstubs
|
|
142
|
+
bin/ # project-level wrappers
|
|
143
|
+
scint.lock.marshal
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Cache root precedence: `SCINT_CACHE` > `XDG_CACHE_HOME/scint` > `~/.cache/scint`.
|
|
214
147
|
|
|
215
|
-
##
|
|
148
|
+
## Cache Validity
|
|
216
149
|
|
|
217
|
-
|
|
150
|
+
A cached artifact is valid when:
|
|
218
151
|
|
|
219
|
-
1.
|
|
220
|
-
2.
|
|
221
|
-
3.
|
|
222
|
-
4.
|
|
152
|
+
1. `cached/<abi>/<full_name>/` exists.
|
|
153
|
+
2. `.spec.marshal` exists and loads.
|
|
154
|
+
3. `.manifest` exists, parses, schema version is supported, and fields match.
|
|
155
|
+
4. If the gem has native extensions, the build-complete marker exists.
|
|
223
156
|
|
|
224
|
-
|
|
157
|
+
Invalid entries are rebuilt through the full pipeline. Legacy entries without manifests are read-compatible but emit telemetry for deprecation tracking.
|
|
225
158
|
|
|
226
|
-
|
|
159
|
+
## Warm Path Guarantees
|
|
160
|
+
|
|
161
|
+
1. If `cached/<abi>/<full_name>/` is valid, no fetch/extract/compile occurs.
|
|
162
|
+
2. Deleting `.bundle/` triggers only materialization.
|
|
163
|
+
3. Materialization is IO-bound and close to instantaneous on warm cache.
|
|
164
|
+
4. Incomplete assemblies are never promoted.
|
|
165
|
+
|
|
166
|
+
## CLI
|
|
227
167
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
168
|
+
```bash
|
|
169
|
+
scint install # install from Gemfile.lock
|
|
170
|
+
scint add <gem> # add to Gemfile and install
|
|
171
|
+
scint remove <gem> # remove from Gemfile and install
|
|
172
|
+
scint exec <cmd> # run command in bundle context
|
|
173
|
+
scint cache list # list cached gems
|
|
174
|
+
scint cache clear # clear global cache
|
|
175
|
+
scint cache dir # print cache root
|
|
176
|
+
```
|
|
233
177
|
|
|
234
|
-
|
|
178
|
+
Options: `--jobs N`, `--path P`, `--verbose`, `--force`.
|
|
235
179
|
|
|
236
|
-
|
|
180
|
+
`scint exec` sets `GEM_HOME`/`GEM_PATH`, injects load paths from `scint.lock.marshal`, and execs the command. Bundler compatibility: `BUNDLE_PATH=".bundle" bundle exec` works against the same layout.
|
|
237
181
|
|
|
238
|
-
|
|
239
|
-
2. Native build failures include full captured command output.
|
|
240
|
-
3. Final summary reports installed/failed/skipped counts.
|
|
241
|
-
4. `.gitignore` warning is emitted when `.bundle/` is not ignored.
|
|
182
|
+
## Diagnostics
|
|
242
183
|
|
|
243
|
-
|
|
184
|
+
```bash
|
|
185
|
+
# Ruby sampling profile
|
|
186
|
+
SCINT_PROFILE=/tmp/profile.json scint install --force
|
|
244
187
|
|
|
245
|
-
|
|
188
|
+
# Ruby IO trace
|
|
189
|
+
SCINT_IO_TRACE=/tmp/io.jsonl scint install --force
|
|
246
190
|
|
|
247
|
-
|
|
191
|
+
# Summarize IO trace
|
|
192
|
+
scint-io-summary /tmp/io.jsonl
|
|
248
193
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
4. Rebuilds runtime lock from `Gemfile.lock` + installed gems when possible if missing.
|
|
194
|
+
# Syscall trace (strace/dtruss)
|
|
195
|
+
scint-syscall-trace /tmp/sys.log -- scint install --force
|
|
196
|
+
```
|
|
253
197
|
|
|
254
|
-
##
|
|
198
|
+
## Benchmarking
|
|
255
199
|
|
|
256
|
-
|
|
200
|
+
```bash
|
|
201
|
+
bin/scint-vs-bundler [--force] /path/to/project
|
|
202
|
+
bin/scint-bench-matrix [--force] --root /path/to/projects
|
|
203
|
+
```
|
|
257
204
|
|
|
258
|
-
|
|
259
|
-
2. Isolated compile worker process with a simple line-protocol RPC (`CALL` / `RESULT`) for stronger fault isolation.
|
|
260
|
-
3. More deterministic bulk operations for extraction/linking.
|
|
261
|
-
4. Better per-phase telemetry for latency and saturation analysis.
|
|
205
|
+
`scint-bench-matrix` runs cold/warm benchmarks for every project subdirectory and writes `logs/bench-<timestamp>/summary.tsv` and `table.md`. If `<root>/<project>-test.sh` exists, it runs as a smoke test after the benchmark.
|
|
262
206
|
|
|
263
207
|
## Status
|
|
264
208
|
|
|
265
|
-
|
|
209
|
+
Experimental. Optimized for architecture iteration speed.
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.8.0
|
data/lib/scint/cache/layout.rb
CHANGED
|
@@ -23,10 +23,28 @@ module Scint
|
|
|
23
23
|
File.join(@root, "inbound")
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
def inbound_gems_dir
|
|
27
|
+
File.join(inbound_dir, "gems")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def inbound_gits_dir
|
|
31
|
+
File.join(inbound_dir, "gits")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def assembling_dir
|
|
35
|
+
File.join(@root, "assembling")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def cached_dir
|
|
39
|
+
File.join(@root, "cached")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Legacy extracted cache (read-compat only).
|
|
26
43
|
def extracted_dir
|
|
27
44
|
File.join(@root, "extracted")
|
|
28
45
|
end
|
|
29
46
|
|
|
47
|
+
# Legacy extension cache (read-compat only).
|
|
30
48
|
def ext_dir
|
|
31
49
|
File.join(@root, "ext")
|
|
32
50
|
end
|
|
@@ -36,7 +54,7 @@ module Scint
|
|
|
36
54
|
end
|
|
37
55
|
|
|
38
56
|
def git_dir
|
|
39
|
-
|
|
57
|
+
inbound_gits_dir
|
|
40
58
|
end
|
|
41
59
|
|
|
42
60
|
# Isolated gem home used while compiling native extensions during install.
|
|
@@ -52,17 +70,40 @@ module Scint
|
|
|
52
70
|
# -- Per-spec paths ------------------------------------------------------
|
|
53
71
|
|
|
54
72
|
def inbound_path(spec)
|
|
55
|
-
File.join(
|
|
73
|
+
File.join(inbound_gems_dir, "#{full_name(spec)}.gem")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def assembling_path(spec, abi_key = Platform.abi_key)
|
|
77
|
+
File.join(assembling_dir, abi_key, full_name(spec))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def cached_abi_dir(abi_key = Platform.abi_key)
|
|
81
|
+
File.join(cached_dir, abi_key)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def cached_path(spec, abi_key = Platform.abi_key)
|
|
85
|
+
File.join(cached_dir, abi_key, full_name(spec))
|
|
56
86
|
end
|
|
57
87
|
|
|
88
|
+
def cached_spec_path(spec, abi_key = Platform.abi_key)
|
|
89
|
+
File.join(cached_dir, abi_key, "#{full_name(spec)}.spec.marshal")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def cached_manifest_path(spec, abi_key = Platform.abi_key)
|
|
93
|
+
File.join(cached_dir, abi_key, "#{full_name(spec)}.manifest")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Legacy extracted cache (read-compat only).
|
|
58
97
|
def extracted_path(spec)
|
|
59
98
|
File.join(extracted_dir, full_name(spec))
|
|
60
99
|
end
|
|
61
100
|
|
|
101
|
+
# Legacy extracted gemspec cache (read-compat only).
|
|
62
102
|
def spec_cache_path(spec)
|
|
63
103
|
File.join(extracted_dir, "#{full_name(spec)}.spec.marshal")
|
|
64
104
|
end
|
|
65
105
|
|
|
106
|
+
# Legacy extension cache (read-compat only).
|
|
66
107
|
def ext_path(spec, abi_key = Platform.abi_key)
|
|
67
108
|
File.join(ext_dir, abi_key, full_name(spec))
|
|
68
109
|
end
|
|
@@ -80,13 +121,13 @@ module Scint
|
|
|
80
121
|
|
|
81
122
|
def git_path(uri)
|
|
82
123
|
slug = git_slug(uri)
|
|
83
|
-
File.join(git_dir,
|
|
124
|
+
File.join(git_dir, slug)
|
|
84
125
|
end
|
|
85
126
|
|
|
86
127
|
def git_checkout_path(uri, revision)
|
|
87
128
|
slug = git_slug(uri)
|
|
88
129
|
rev = revision.to_s.gsub(/[^0-9A-Za-z._-]/, "_")
|
|
89
|
-
File.join(git_dir, "checkouts",
|
|
130
|
+
File.join(git_dir, slug, "checkouts", rev)
|
|
90
131
|
end
|
|
91
132
|
|
|
92
133
|
# -- Helpers -------------------------------------------------------------
|
|
@@ -109,10 +150,19 @@ module Scint
|
|
|
109
150
|
private
|
|
110
151
|
|
|
111
152
|
def default_root
|
|
153
|
+
return Scint.cache_root if Scint.respond_to?(:cache_root)
|
|
154
|
+
|
|
155
|
+
explicit = ENV["SCINT_CACHE"]
|
|
156
|
+
return File.expand_path(explicit) unless explicit.nil? || explicit.empty?
|
|
157
|
+
|
|
112
158
|
base = ENV["XDG_CACHE_HOME"] || File.join(Dir.home, ".cache")
|
|
113
159
|
File.join(base, "scint")
|
|
114
160
|
end
|
|
115
161
|
|
|
162
|
+
# Slug rules are defined in README.md (Cache Validity + Manifest Specification).
|
|
163
|
+
# - Index slugs prefer host/path when available, otherwise fall back to a hash.
|
|
164
|
+
# - Hash slugs are deterministic but must be paired with manifest checks for
|
|
165
|
+
# collision detection.
|
|
116
166
|
def slugify_uri(str)
|
|
117
167
|
uri = URI.parse(str) rescue nil
|
|
118
168
|
if uri && uri.host
|
|
@@ -125,8 +175,19 @@ module Scint
|
|
|
125
175
|
end
|
|
126
176
|
end
|
|
127
177
|
|
|
178
|
+
# Git slugs are SHA256 of the normalized URI string (uri.to_s), truncated
|
|
179
|
+
# to 16 hex chars. Callers must validate `source.uri` in the manifest to
|
|
180
|
+
# detect collisions and fall back to a longer hash if needed.
|
|
128
181
|
def git_slug(uri)
|
|
129
|
-
|
|
182
|
+
normalized = normalize_uri(uri)
|
|
183
|
+
Digest::SHA256.hexdigest(normalized)[0, 16]
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def normalize_uri(uri)
|
|
187
|
+
return uri.to_s if uri.is_a?(URI)
|
|
188
|
+
URI.parse(uri.to_s).to_s
|
|
189
|
+
rescue URI::InvalidURIError
|
|
190
|
+
uri.to_s
|
|
130
191
|
end
|
|
131
192
|
end
|
|
132
193
|
end
|