scint 0.1.0 → 0.6.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/VERSION +1 -0
- data/lib/scint/cli/install.rb +206 -36
- data/lib/scint/fs.rb +46 -1
- data/lib/scint/index/client.rb +1 -1
- data/lib/scint/installer/extension_builder.rb +37 -2
- data/lib/scint/installer/linker.rb +1 -1
- data/lib/scint/installer/planner.rb +7 -6
- data/lib/scint/lockfile/writer.rb +1 -7
- data/lib/scint/progress.rb +128 -73
- data/lib/scint/resolver/provider.rb +56 -5
- data/lib/scint/vendor/pub_grub/version.rb +5 -1
- data/lib/scint/version.rb +5 -0
- data/lib/scint.rb +2 -2
- metadata +4 -7
- data/bin/bundler-vs-scint +0 -233
- data/bin/scint-io-summary +0 -46
- data/bin/scint-syscall-trace +0 -41
data/lib/scint/progress.rb
CHANGED
|
@@ -18,15 +18,22 @@ module Scint
|
|
|
18
18
|
}.freeze
|
|
19
19
|
BUILD_TAIL_MAX = 6
|
|
20
20
|
BUILD_TAIL_PREVIEW_LINES = 4
|
|
21
|
-
RENDER_HZ =
|
|
21
|
+
RENDER_HZ = 10
|
|
22
22
|
RENDER_INTERVAL = 1.0 / RENDER_HZ
|
|
23
23
|
MAX_DETAIL_ROWS_PER_PHASE = 4
|
|
24
24
|
MAX_LINE_LEN = 220
|
|
25
25
|
MIN_RENDER_WIDTH = 40
|
|
26
26
|
MAX_PANEL_ROWS = 14
|
|
27
|
+
SLOW_OPERATION_THRESHOLD_SECONDS = 1.0
|
|
27
28
|
SPINNER_FRAMES = %w[⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏].freeze
|
|
28
|
-
IDLE_MARK = "
|
|
29
|
-
PANEL_PHASE_ORDER = %i[download extract build_ext
|
|
29
|
+
IDLE_MARK = "•".freeze
|
|
30
|
+
PANEL_PHASE_ORDER = %i[download extract link build_ext].freeze
|
|
31
|
+
COMPLETION_LOG_TYPES = {
|
|
32
|
+
download: true,
|
|
33
|
+
extract: true,
|
|
34
|
+
link: true,
|
|
35
|
+
build_ext: true,
|
|
36
|
+
}.freeze
|
|
30
37
|
|
|
31
38
|
PHASE_LABELS = {
|
|
32
39
|
fetch_index: "Fetching index",
|
|
@@ -43,6 +50,12 @@ module Scint
|
|
|
43
50
|
link: "Installing",
|
|
44
51
|
build_ext: "Compiling",
|
|
45
52
|
}.freeze
|
|
53
|
+
COMPLETION_LABELS = {
|
|
54
|
+
download: "Downloaded",
|
|
55
|
+
extract: "Extracted",
|
|
56
|
+
link: "Installed",
|
|
57
|
+
build_ext: "Compiled",
|
|
58
|
+
}.freeze
|
|
46
59
|
|
|
47
60
|
def initialize(output: $stderr)
|
|
48
61
|
@output = output
|
|
@@ -55,6 +68,7 @@ module Scint
|
|
|
55
68
|
@failed = Hash.new(0)
|
|
56
69
|
@total = Hash.new(0)
|
|
57
70
|
@active_jobs = {}
|
|
71
|
+
@job_started_at = {}
|
|
58
72
|
@build_tail = []
|
|
59
73
|
@build_tail_by_name = {}
|
|
60
74
|
@spinner_idx = 0
|
|
@@ -67,6 +81,7 @@ module Scint
|
|
|
67
81
|
@phase_reserved_detail_rows = Hash.new(0)
|
|
68
82
|
@setup_lines_printed = false
|
|
69
83
|
@setup_gap_printed = false
|
|
84
|
+
@pending_log_lines = []
|
|
70
85
|
@render_stop = false
|
|
71
86
|
@render_thread = nil
|
|
72
87
|
end
|
|
@@ -90,8 +105,9 @@ module Scint
|
|
|
90
105
|
|
|
91
106
|
@mutex.synchronize do
|
|
92
107
|
begin
|
|
108
|
+
flush_pending_logs_locked
|
|
93
109
|
mark_completed_phase_elapsed_locked
|
|
94
|
-
render_live_locked if
|
|
110
|
+
render_live_locked if any_active_stream_jobs?
|
|
95
111
|
if @interactive && @live_rows.positive?
|
|
96
112
|
move_cursor_down(@live_rows - 1)
|
|
97
113
|
@output.print "\r\n"
|
|
@@ -119,11 +135,16 @@ module Scint
|
|
|
119
135
|
|
|
120
136
|
@mutex.synchronize do
|
|
121
137
|
@active_jobs[job_id] = { type: type, name: name }
|
|
138
|
+
@job_started_at[job_id] = Process.clock_gettime(Process::CLOCK_MONOTONIC) if completion_log_type?(type)
|
|
122
139
|
@started += 1
|
|
123
140
|
emit_setup_gap_if_needed(type)
|
|
124
141
|
@phase_started_at[type] ||= Process.clock_gettime(Process::CLOCK_MONOTONIC) if stream_type?(type)
|
|
125
|
-
if @interactive
|
|
142
|
+
if @interactive
|
|
126
143
|
start_render_thread_locked
|
|
144
|
+
unless stream_type?(type)
|
|
145
|
+
clear_live_block_locked
|
|
146
|
+
emit_setup_or_log_line(type, name)
|
|
147
|
+
end
|
|
127
148
|
else
|
|
128
149
|
clear_live_block_locked
|
|
129
150
|
emit_setup_or_log_line(type, name)
|
|
@@ -136,6 +157,8 @@ module Scint
|
|
|
136
157
|
@completed[type] += 1
|
|
137
158
|
active = @active_jobs.delete(job_id)
|
|
138
159
|
@build_tail_by_name.delete(active[:name]) if active
|
|
160
|
+
elapsed = consume_job_elapsed(job_id)
|
|
161
|
+
emit_task_completion_locked(type, name, elapsed) if @interactive
|
|
139
162
|
if !@interactive || !stream_type?(type)
|
|
140
163
|
emit_phase_completion_locked(type)
|
|
141
164
|
end
|
|
@@ -147,11 +170,22 @@ module Scint
|
|
|
147
170
|
|
|
148
171
|
@mutex.synchronize do
|
|
149
172
|
active = @active_jobs.delete(job_id)
|
|
173
|
+
elapsed = consume_job_elapsed(job_id)
|
|
150
174
|
@build_tail_by_name.delete(active[:name]) if active
|
|
151
175
|
@failed[type] += 1
|
|
152
|
-
clear_live_block_locked
|
|
153
176
|
label = PHASE_LABELS[type] || type.to_s
|
|
154
|
-
|
|
177
|
+
failed_timing = if elapsed && elapsed >= SLOW_OPERATION_THRESHOLD_SECONDS
|
|
178
|
+
" #{DIM}#{format_phase_elapsed(elapsed)}#{RESET}"
|
|
179
|
+
else
|
|
180
|
+
""
|
|
181
|
+
end
|
|
182
|
+
failed_line = "#{RED}FAILED#{RESET} #{label} #{BOLD}#{name}#{RESET}: #{error.message}#{failed_timing}"
|
|
183
|
+
if @interactive
|
|
184
|
+
queue_log_line_locked(failed_line)
|
|
185
|
+
else
|
|
186
|
+
clear_live_block_locked
|
|
187
|
+
@output.puts failed_line
|
|
188
|
+
end
|
|
155
189
|
if !@interactive
|
|
156
190
|
emit_phase_completion_locked(type)
|
|
157
191
|
end
|
|
@@ -197,9 +231,68 @@ module Scint
|
|
|
197
231
|
HIDDEN_TYPES[type] == true
|
|
198
232
|
end
|
|
199
233
|
|
|
234
|
+
def completion_log_type?(type)
|
|
235
|
+
COMPLETION_LOG_TYPES[type] == true
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def consume_job_elapsed(job_id)
|
|
239
|
+
started_at = @job_started_at.delete(job_id)
|
|
240
|
+
return nil unless started_at
|
|
241
|
+
|
|
242
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def emit_task_completion_locked(type, name, elapsed)
|
|
246
|
+
return unless completion_log_type?(type)
|
|
247
|
+
|
|
248
|
+
label = COMPLETION_LABELS[type] || (PHASE_LABELS[type] || type.to_s)
|
|
249
|
+
timing = if elapsed && elapsed >= SLOW_OPERATION_THRESHOLD_SECONDS
|
|
250
|
+
" #{DIM}#{format_phase_elapsed(elapsed)}#{RESET}"
|
|
251
|
+
else
|
|
252
|
+
""
|
|
253
|
+
end
|
|
254
|
+
line = "#{GREEN}#{IDLE_MARK}#{RESET} #{DIM}#{label} #{BOLD}#{name}#{RESET}#{timing}"
|
|
255
|
+
|
|
256
|
+
queue_log_line_locked(line)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def queue_log_line_locked(line)
|
|
260
|
+
@pending_log_lines << line
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def flush_pending_logs_locked
|
|
264
|
+
return if @pending_log_lines.empty?
|
|
265
|
+
|
|
266
|
+
lines = @pending_log_lines.dup
|
|
267
|
+
@pending_log_lines.clear
|
|
268
|
+
lines.each_with_index do |line, idx|
|
|
269
|
+
write_scrollback_line_locked(line, flush: idx == lines.length - 1)
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def write_scrollback_line_locked(line, flush: true)
|
|
274
|
+
rendered = fit_line(line, @render_width)
|
|
275
|
+
|
|
276
|
+
unless @interactive && @live_rows.positive?
|
|
277
|
+
@output.print "\r"
|
|
278
|
+
@output.puts rendered
|
|
279
|
+
@output.flush if flush && @output.respond_to?(:flush)
|
|
280
|
+
return
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Insert one scrollback line above the active block without clearing
|
|
284
|
+
# existing rows first. This reduces visible flicker during fast updates.
|
|
285
|
+
move_cursor_up(@live_rows - 1)
|
|
286
|
+
@output.print "\e[1L"
|
|
287
|
+
@output.print "\r#{rendered}\e[K\n"
|
|
288
|
+
move_cursor_down(@live_rows - 1)
|
|
289
|
+
@output.print "\r"
|
|
290
|
+
@output.flush if flush && @output.respond_to?(:flush)
|
|
291
|
+
end
|
|
292
|
+
|
|
200
293
|
def render_live_locked
|
|
201
294
|
return unless @interactive
|
|
202
|
-
return unless
|
|
295
|
+
return unless any_active_stream_jobs? || any_active_setup_jobs?
|
|
203
296
|
|
|
204
297
|
mark_completed_phase_elapsed_locked
|
|
205
298
|
|
|
@@ -207,7 +300,11 @@ module Scint
|
|
|
207
300
|
spinner = SPINNER_FRAMES[@spinner_idx % SPINNER_FRAMES.length]
|
|
208
301
|
@spinner_idx += 1
|
|
209
302
|
|
|
210
|
-
|
|
303
|
+
if any_active_stream_jobs?
|
|
304
|
+
lines.concat(phase_lines(spinner))
|
|
305
|
+
else
|
|
306
|
+
lines << "#{GREEN}#{spinner}#{RESET} Processing..."
|
|
307
|
+
end
|
|
211
308
|
lines = clamp_panel_rows(lines)
|
|
212
309
|
|
|
213
310
|
redraw_live_block_locked(lines)
|
|
@@ -238,7 +335,8 @@ module Scint
|
|
|
238
335
|
end
|
|
239
336
|
|
|
240
337
|
def phase_lines(spinner)
|
|
241
|
-
PANEL_PHASE_ORDER.
|
|
338
|
+
active_types = PANEL_PHASE_ORDER.select { |type| !active_stream_jobs_for(type).empty? }
|
|
339
|
+
active_types.flat_map do |type|
|
|
242
340
|
phase_rows_for(type, spinner)
|
|
243
341
|
end
|
|
244
342
|
end
|
|
@@ -252,69 +350,21 @@ module Scint
|
|
|
252
350
|
label = PHASE_SUMMARY_LABELS[type] || (PHASE_LABELS[type] || type.to_s)
|
|
253
351
|
line = phase_status_line(type, active, label, completed, total, spinner)
|
|
254
352
|
detail_rows = phase_detail_rows_for(type, active)
|
|
255
|
-
[line, *
|
|
353
|
+
[line, *detail_rows.first(MAX_DETAIL_ROWS_PER_PHASE)]
|
|
256
354
|
end
|
|
257
355
|
|
|
258
356
|
def phase_status_line(type, active_jobs, label, completed, total, spinner)
|
|
259
357
|
base = "#{label}... (#{completed}/#{total})"
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
elapsed_text = elapsed ? " · #{format_phase_elapsed(elapsed)}" : ""
|
|
263
|
-
"#{DIM}#{IDLE_MARK} #{base}#{elapsed_text}#{RESET}"
|
|
264
|
-
else
|
|
265
|
-
name = active_jobs.first[:name]
|
|
266
|
-
"#{spinner} #{base} · #{BOLD}#{name}#{RESET}"
|
|
267
|
-
end
|
|
358
|
+
name = active_jobs.first[:name]
|
|
359
|
+
"#{GREEN}#{spinner}#{RESET} #{base} · #{BOLD}#{name}#{RESET}"
|
|
268
360
|
end
|
|
269
361
|
|
|
270
362
|
def phase_detail_rows_for(type, active_jobs)
|
|
271
363
|
return [] if active_jobs.empty?
|
|
272
364
|
|
|
273
|
-
if type == :build_ext
|
|
274
|
-
compile_tail_rows_for(active_jobs.first[:name])
|
|
275
|
-
else
|
|
276
|
-
active_rows_for(type, active_jobs)
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
def reserve_phase_detail_space(type, details, total, completed, active_jobs)
|
|
281
|
-
if phase_complete?(type, total, completed, active_jobs)
|
|
282
|
-
@phase_reserved_detail_rows[type] = 0
|
|
283
|
-
return []
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
desired = [details.length, MAX_DETAIL_ROWS_PER_PHASE].min
|
|
287
|
-
@phase_reserved_detail_rows[type] = [@phase_reserved_detail_rows[type], desired].max
|
|
288
|
-
reserved = @phase_reserved_detail_rows[type]
|
|
365
|
+
return compile_tail_rows_for(active_jobs.first[:name]) if type == :build_ext
|
|
289
366
|
|
|
290
|
-
|
|
291
|
-
while rows.length < reserved
|
|
292
|
-
rows << "#{DIM} #{RESET}"
|
|
293
|
-
end
|
|
294
|
-
rows
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
def phase_complete?(type, total, completed, active_jobs)
|
|
298
|
-
return false if total.zero?
|
|
299
|
-
|
|
300
|
-
if completed >= total && active_jobs.empty?
|
|
301
|
-
mark_phase_elapsed_locked(type)
|
|
302
|
-
return true
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
false
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
def active_rows_for(_type, active_jobs)
|
|
309
|
-
return [] if active_jobs.empty?
|
|
310
|
-
if active_jobs.length <= MAX_DETAIL_ROWS_PER_PHASE
|
|
311
|
-
return active_jobs.map { |job| " #{BOLD}#{job[:name]}#{RESET}" }
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
rows = active_jobs.first(MAX_DETAIL_ROWS_PER_PHASE - 1).map { |job| " #{BOLD}#{job[:name]}#{RESET}" }
|
|
315
|
-
remaining = active_jobs.length - rows.length
|
|
316
|
-
rows << "#{DIM} +#{remaining} more#{RESET}"
|
|
317
|
-
rows
|
|
367
|
+
[]
|
|
318
368
|
end
|
|
319
369
|
|
|
320
370
|
def compile_tail_rows_for(name)
|
|
@@ -330,14 +380,21 @@ module Scint
|
|
|
330
380
|
@active_jobs.values.select { |job| job[:type] == type }
|
|
331
381
|
end
|
|
332
382
|
|
|
383
|
+
def any_active_stream_jobs?
|
|
384
|
+
STREAM_TYPES.keys.any? { |type| !active_stream_jobs_for(type).empty? }
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def any_active_setup_jobs?
|
|
388
|
+
@active_jobs.values.any? { |job| SETUP_TYPES[job[:type]] == true }
|
|
389
|
+
end
|
|
390
|
+
|
|
333
391
|
def clear_live_block_locked
|
|
334
392
|
return unless @interactive
|
|
335
393
|
return if @live_rows.zero?
|
|
336
394
|
|
|
337
395
|
move_cursor_up(@live_rows - 1)
|
|
338
396
|
@live_rows.times do |idx|
|
|
339
|
-
|
|
340
|
-
@output.print "\r#{' ' * prev_width}"
|
|
397
|
+
@output.print "\r\e[K"
|
|
341
398
|
@output.print "\n" if idx < (@live_rows - 1)
|
|
342
399
|
end
|
|
343
400
|
move_cursor_up(@live_rows - 1)
|
|
@@ -350,7 +407,6 @@ module Scint
|
|
|
350
407
|
def redraw_live_block_locked(lines)
|
|
351
408
|
hide_cursor_locked
|
|
352
409
|
previous_rows = @live_rows
|
|
353
|
-
previous_widths = @rendered_widths.dup
|
|
354
410
|
|
|
355
411
|
move_cursor_up(previous_rows - 1) if previous_rows.positive?
|
|
356
412
|
|
|
@@ -359,12 +415,8 @@ module Scint
|
|
|
359
415
|
|
|
360
416
|
max_rows.times do |idx|
|
|
361
417
|
line = idx < lines.length ? fit_line(lines[idx], @render_width) : ""
|
|
362
|
-
prev_width = previous_widths[idx] || 0
|
|
363
418
|
line_width = visible_width(line)
|
|
364
|
-
|
|
365
|
-
pad = pad_len.positive? ? (" " * pad_len) : ""
|
|
366
|
-
|
|
367
|
-
@output.print "\r#{line}#{pad}"
|
|
419
|
+
@output.print "\r#{line}\e[K"
|
|
368
420
|
@output.print "\n" if idx < (max_rows - 1)
|
|
369
421
|
new_widths << line_width
|
|
370
422
|
end
|
|
@@ -405,7 +457,8 @@ module Scint
|
|
|
405
457
|
@mutex.synchronize do
|
|
406
458
|
should_stop = @render_stop
|
|
407
459
|
unless should_stop
|
|
408
|
-
|
|
460
|
+
flush_pending_logs_locked
|
|
461
|
+
render_live_locked if any_active_stream_jobs?
|
|
409
462
|
should_stop = render_done_locked?
|
|
410
463
|
end
|
|
411
464
|
end
|
|
@@ -502,7 +555,9 @@ module Scint
|
|
|
502
555
|
end
|
|
503
556
|
|
|
504
557
|
def render_done_locked?
|
|
505
|
-
|
|
558
|
+
!any_active_stream_jobs? && !any_active_setup_jobs? &&
|
|
559
|
+
@pending_log_lines.empty? &&
|
|
560
|
+
(!any_stream_activity? || !stream_active_or_pending?)
|
|
506
561
|
end
|
|
507
562
|
|
|
508
563
|
def format_phase_elapsed(seconds)
|
|
@@ -65,8 +65,9 @@ module Scint
|
|
|
65
65
|
else
|
|
66
66
|
entries = info_for(name)
|
|
67
67
|
versions = {}
|
|
68
|
-
entries.each do |_name, version, platform, _deps,
|
|
68
|
+
entries.each do |_name, version, platform, _deps, reqs|
|
|
69
69
|
next unless platform_match?(platform)
|
|
70
|
+
next unless requirements_match?(reqs)
|
|
70
71
|
ver = Gem::Version.new(version)
|
|
71
72
|
versions[version] ||= ver
|
|
72
73
|
end
|
|
@@ -93,8 +94,9 @@ module Scint
|
|
|
93
94
|
entries = info_for(name)
|
|
94
95
|
deps = {}
|
|
95
96
|
|
|
96
|
-
entries.each do |_name, ver, platform, dep_hash,
|
|
97
|
+
entries.each do |_name, ver, platform, dep_hash, reqs|
|
|
97
98
|
next unless ver == version_str && platform_match?(platform)
|
|
99
|
+
next unless requirements_match?(reqs)
|
|
98
100
|
dep_hash.each do |dep_name, dep_req_str|
|
|
99
101
|
# Merge constraints from all matching platform entries
|
|
100
102
|
req = Gem::Requirement.new(dep_req_str.split(", "))
|
|
@@ -119,8 +121,9 @@ module Scint
|
|
|
119
121
|
version_str = version.to_s
|
|
120
122
|
entries = info_for(name)
|
|
121
123
|
has_platform_specific = false
|
|
122
|
-
entries.each do |_name, ver, platform, _deps,
|
|
124
|
+
entries.each do |_name, ver, platform, _deps, reqs|
|
|
123
125
|
next unless ver == version_str
|
|
126
|
+
next unless requirements_match?(reqs)
|
|
124
127
|
has_platform_specific = true if platform != "ruby"
|
|
125
128
|
end
|
|
126
129
|
has_platform_specific
|
|
@@ -132,8 +135,8 @@ module Scint
|
|
|
132
135
|
return "ruby" if @path_gems.key?(name)
|
|
133
136
|
|
|
134
137
|
version_str = version.to_s
|
|
135
|
-
entries = info_for(name).select do |_n, ver, platform, _deps,
|
|
136
|
-
ver == version_str && platform_match?(platform)
|
|
138
|
+
entries = info_for(name).select do |_n, ver, platform, _deps, reqs|
|
|
139
|
+
ver == version_str && platform_match?(platform) && requirements_match?(reqs)
|
|
137
140
|
end
|
|
138
141
|
return "ruby" if entries.empty?
|
|
139
142
|
|
|
@@ -225,6 +228,54 @@ module Scint
|
|
|
225
228
|
combined = req1.requirements + req2.requirements
|
|
226
229
|
Gem::Requirement.new(combined.map { |op, v| "#{op} #{v}" })
|
|
227
230
|
end
|
|
231
|
+
|
|
232
|
+
def requirements_match?(reqs)
|
|
233
|
+
return true unless reqs.is_a?(Hash)
|
|
234
|
+
|
|
235
|
+
ruby_req = reqs["ruby"] || reqs[:ruby]
|
|
236
|
+
return false unless requirement_satisfied?(ruby_req, Gem::Version.new(RUBY_VERSION), ignore_upper: ignore_ruby_upper_bounds?) if ruby_req
|
|
237
|
+
|
|
238
|
+
rubygems_req = reqs["rubygems"] || reqs[:rubygems]
|
|
239
|
+
return false unless requirement_satisfied?(rubygems_req, Gem::Version.new(Gem::VERSION), ignore_upper: false) if rubygems_req
|
|
240
|
+
|
|
241
|
+
true
|
|
242
|
+
rescue StandardError
|
|
243
|
+
# Keep resolver tolerant of malformed compact-index requirement fields.
|
|
244
|
+
true
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def ignore_ruby_upper_bounds?
|
|
248
|
+
raw = ENV["SCINT_IGNORE_RUBY_UPPER_BOUNDS"]
|
|
249
|
+
return true if raw.nil?
|
|
250
|
+
|
|
251
|
+
!%w[0 false no off].include?(raw.strip.downcase)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def requirement_satisfied?(requirement_str, current_version, ignore_upper:)
|
|
255
|
+
req = normalize_requirement(requirement_str, ignore_upper: ignore_upper)
|
|
256
|
+
req.satisfied_by?(current_version)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def normalize_requirement(requirement_str, ignore_upper:)
|
|
260
|
+
parts = requirement_str.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
261
|
+
req = Gem::Requirement.new(*(parts.empty? ? [">= 0"] : parts))
|
|
262
|
+
return req unless ignore_upper
|
|
263
|
+
|
|
264
|
+
filtered = req.requirements.filter_map do |op, version|
|
|
265
|
+
case op
|
|
266
|
+
when "<", "<="
|
|
267
|
+
nil
|
|
268
|
+
when "~>"
|
|
269
|
+
[">=", version]
|
|
270
|
+
else
|
|
271
|
+
[op, version]
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
return Gem::Requirement.default if filtered.empty?
|
|
276
|
+
|
|
277
|
+
Gem::Requirement.new(filtered.map { |op, version| "#{op} #{version}" })
|
|
278
|
+
end
|
|
228
279
|
end
|
|
229
280
|
end
|
|
230
281
|
end
|
data/lib/scint.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
VERSION = "0.1.0"
|
|
3
|
+
require_relative "scint/version"
|
|
5
4
|
|
|
5
|
+
module Scint
|
|
6
6
|
# Color support: respects NO_COLOR (https://no-color.org) and TERM=dumb.
|
|
7
7
|
COLOR = !ENV.key?("NO_COLOR") && ENV["TERM"] != "dumb" && $stderr.tty?
|
|
8
8
|
|
metadata
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: scint
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- Tobi Lutke
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
@@ -16,17 +16,13 @@ email:
|
|
|
16
16
|
- maintainers@example.com
|
|
17
17
|
executables:
|
|
18
18
|
- scint
|
|
19
|
-
- scint-io-summary
|
|
20
|
-
- scint-syscall-trace
|
|
21
19
|
extensions: []
|
|
22
20
|
extra_rdoc_files: []
|
|
23
21
|
files:
|
|
24
22
|
- FEATURES.md
|
|
25
23
|
- README.md
|
|
26
|
-
-
|
|
24
|
+
- VERSION
|
|
27
25
|
- bin/scint
|
|
28
|
-
- bin/scint-io-summary
|
|
29
|
-
- bin/scint-syscall-trace
|
|
30
26
|
- lib/bundler.rb
|
|
31
27
|
- lib/bundler/setup.rb
|
|
32
28
|
- lib/scint.rb
|
|
@@ -90,6 +86,7 @@ files:
|
|
|
90
86
|
- lib/scint/vendor/pub_grub/version_range.rb
|
|
91
87
|
- lib/scint/vendor/pub_grub/version_solver.rb
|
|
92
88
|
- lib/scint/vendor/pub_grub/version_union.rb
|
|
89
|
+
- lib/scint/version.rb
|
|
93
90
|
- lib/scint/worker_pool.rb
|
|
94
91
|
homepage: https://example.com/scint
|
|
95
92
|
licenses:
|