local_ci_plus 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 +17 -0
- data/lib/local_ci_plus/continuous_integration.rb +94 -23
- data/lib/local_ci_plus/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e29292c8c4b406b43f0e1729ffbfbf52bb7931fa8f62dedab7e9631baab45a5f
|
|
4
|
+
data.tar.gz: 685c98aadeb7b563c077a5ebf99da60880403e3a9bcbcc15e646fac6c70f841d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c40a186742fa94b71a099820f73a03feb79f8b347a00bc8d86d2835dec8070c8acd0f9c03ae30cd37d44f1b3a59bdebb3d897c4d8d0844430e75bd6f5fda1088
|
|
7
|
+
data.tar.gz: ac0a1b4b6adf8ca08457571a8f38fb2259e202c8bf5f4f2a47033495afeb5eb1439bebc9eab031d01083bdbbea9913d7fb05e97ce90e0a5e5d4948bc248434ee
|
data/README.md
CHANGED
|
@@ -28,6 +28,23 @@ continues to work without changes. In plain/non-TTY mode, output is ASCII-only.
|
|
|
28
28
|
bin/ci
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
Use `parallel_steps` when you want a parallel phase before sequential follow-up steps.
|
|
32
|
+
If any step in `parallel_steps` fails, later steps are skipped and listed in the summary.
|
|
33
|
+
If no `parallel_steps` block is present, all steps run in parallel (default behavior).
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
CI.run do
|
|
37
|
+
parallel_steps do
|
|
38
|
+
step "Style: Ruby", "bin/standardrb"
|
|
39
|
+
step "JavaScript: lint", "npm run lint"
|
|
40
|
+
step "Security: Gem audit", "bin/bundler-audit"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
step "Tests: Rails", "bin/rspec"
|
|
44
|
+
step "Tests: Seeds", "env RAILS_ENV=test bin/rails db:seed:replant"
|
|
45
|
+
end
|
|
46
|
+
```
|
|
47
|
+
|
|
31
48
|
If your app does not already require the gem in `bin/ci`, run the installer generator:
|
|
32
49
|
|
|
33
50
|
```bash
|
|
@@ -24,7 +24,7 @@ module LocalCiPlus
|
|
|
24
24
|
STATE_FILE = File.join("tmp", "ci_state")
|
|
25
25
|
STATE_FILE_ENV = "CI_STATE_FILE"
|
|
26
26
|
|
|
27
|
-
attr_reader :results
|
|
27
|
+
attr_reader :results, :skipped_steps
|
|
28
28
|
|
|
29
29
|
def self.run(title = "Continuous Integration", subtitle = "Running tests, style checks, and security audits", &block)
|
|
30
30
|
new.tap do |ci|
|
|
@@ -45,6 +45,11 @@ module LocalCiPlus
|
|
|
45
45
|
@results = []
|
|
46
46
|
@step_names = []
|
|
47
47
|
@parallel_steps = []
|
|
48
|
+
@using_explicit_parallel_steps = false
|
|
49
|
+
@inside_parallel_steps_block = false
|
|
50
|
+
@skip_remaining_steps = false
|
|
51
|
+
@skip_remaining_reason = nil
|
|
52
|
+
@skipped_steps = []
|
|
48
53
|
@skip_until = continue_mode? ? load_failed_step : nil
|
|
49
54
|
@skipping = !!@skip_until
|
|
50
55
|
end
|
|
@@ -80,31 +85,48 @@ module LocalCiPlus
|
|
|
80
85
|
if title == @skip_until
|
|
81
86
|
@skipping = false
|
|
82
87
|
else
|
|
83
|
-
|
|
84
|
-
results << [true, title]
|
|
88
|
+
mark_step_skipped(title, "resuming from: #{@skip_until}")
|
|
85
89
|
return
|
|
86
90
|
end
|
|
87
91
|
end
|
|
88
92
|
|
|
89
|
-
if
|
|
93
|
+
if @skip_remaining_steps
|
|
94
|
+
mark_step_skipped(title, @skip_remaining_reason || "parallel steps failed")
|
|
95
|
+
return
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if queue_parallel_step?
|
|
90
99
|
@parallel_steps << {title: title, command: command}
|
|
91
100
|
return
|
|
92
101
|
end
|
|
93
102
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
success = system(*command)
|
|
97
|
-
results << [success, title]
|
|
103
|
+
run_inline_step(title, command)
|
|
104
|
+
end
|
|
98
105
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
def parallel_steps(&block)
|
|
107
|
+
return unless block
|
|
108
|
+
|
|
109
|
+
@using_explicit_parallel_steps = true
|
|
110
|
+
|
|
111
|
+
if parallel?
|
|
112
|
+
if @parallel_steps.any?
|
|
113
|
+
completed = run_parallel_steps!(@parallel_steps)
|
|
114
|
+
@parallel_steps.clear
|
|
115
|
+
mark_remaining_steps_skipped!("parallel_steps failed") if completed.any? { |job| !job[:success] }
|
|
106
116
|
end
|
|
117
|
+
|
|
118
|
+
@inside_parallel_steps_block = true
|
|
119
|
+
queue_start = @parallel_steps.length
|
|
120
|
+
instance_eval(&block)
|
|
121
|
+
batch = @parallel_steps.slice!(queue_start, @parallel_steps.length - queue_start) || []
|
|
122
|
+
completed = run_parallel_steps!(batch)
|
|
123
|
+
mark_remaining_steps_skipped!("parallel_steps failed") if completed.any? { |job| !job[:success] }
|
|
124
|
+
else
|
|
125
|
+
@inside_parallel_steps_block = true
|
|
126
|
+
instance_eval(&block)
|
|
107
127
|
end
|
|
128
|
+
ensure
|
|
129
|
+
@inside_parallel_steps_block = false
|
|
108
130
|
end
|
|
109
131
|
|
|
110
132
|
def success?
|
|
@@ -148,7 +170,7 @@ module LocalCiPlus
|
|
|
148
170
|
|
|
149
171
|
elapsed = timing do
|
|
150
172
|
ci.instance_eval(&block)
|
|
151
|
-
ci.run_parallel_steps! if ci.parallel? && ci.
|
|
173
|
+
ci.run_parallel_steps!(ci.queued_parallel_steps) if ci.parallel? && ci.queued_parallel_steps.any?
|
|
152
174
|
end
|
|
153
175
|
|
|
154
176
|
@skip_until = ci.instance_variable_get(:@skip_until)
|
|
@@ -165,6 +187,12 @@ module LocalCiPlus
|
|
|
165
187
|
echo " #{failure_bullet} #{step_title} failed", type: :error
|
|
166
188
|
end
|
|
167
189
|
end
|
|
190
|
+
|
|
191
|
+
if ci.skipped_steps.any?
|
|
192
|
+
ci.skipped_steps.each do |step_title|
|
|
193
|
+
echo " #{failure_bullet} #{step_title} skipped", type: :skip
|
|
194
|
+
end
|
|
195
|
+
end
|
|
168
196
|
end
|
|
169
197
|
|
|
170
198
|
results.concat ci.results
|
|
@@ -208,24 +236,29 @@ module LocalCiPlus
|
|
|
208
236
|
false
|
|
209
237
|
end
|
|
210
238
|
|
|
211
|
-
|
|
239
|
+
def queued_parallel_steps
|
|
240
|
+
@parallel_steps
|
|
241
|
+
end
|
|
212
242
|
|
|
213
243
|
MAX_OUTPUT_BYTES = 100 * 1024 # 100KB max per output stream
|
|
214
244
|
|
|
215
|
-
def run_parallel_steps!
|
|
216
|
-
|
|
245
|
+
def run_parallel_steps!(steps = @parallel_steps)
|
|
246
|
+
return [] if steps.empty?
|
|
247
|
+
|
|
248
|
+
total = steps.size
|
|
249
|
+
@active_parallel_size = total
|
|
217
250
|
@running_jobs = []
|
|
218
251
|
completed = []
|
|
219
252
|
completed_by_index = {}
|
|
220
253
|
|
|
221
254
|
echo "\n#{status_marker(:pending)} Running #{total} steps in parallel:", type: :subtitle
|
|
222
255
|
unless plain?
|
|
223
|
-
|
|
256
|
+
steps.each do |step|
|
|
224
257
|
echo format_parallel_line(step[:title], :pending), type: :pending
|
|
225
258
|
end
|
|
226
259
|
end
|
|
227
260
|
|
|
228
|
-
|
|
261
|
+
steps.each_with_index do |step_info, idx|
|
|
229
262
|
title = step_info[:title]
|
|
230
263
|
command = step_info[:command]
|
|
231
264
|
|
|
@@ -277,7 +310,7 @@ module LocalCiPlus
|
|
|
277
310
|
end
|
|
278
311
|
|
|
279
312
|
if plain?
|
|
280
|
-
|
|
313
|
+
steps.each_with_index do |step, idx|
|
|
281
314
|
job = completed_by_index[idx]
|
|
282
315
|
type = job[:success] ? :success : :error
|
|
283
316
|
echo format_parallel_line(step[:title], type, duration: job[:duration]), type: type
|
|
@@ -285,7 +318,9 @@ module LocalCiPlus
|
|
|
285
318
|
end
|
|
286
319
|
|
|
287
320
|
print_parallel_summary(completed)
|
|
321
|
+
completed
|
|
288
322
|
ensure
|
|
323
|
+
@active_parallel_size = nil
|
|
289
324
|
cleanup_all_jobs!
|
|
290
325
|
end
|
|
291
326
|
|
|
@@ -337,6 +372,42 @@ module LocalCiPlus
|
|
|
337
372
|
|
|
338
373
|
private
|
|
339
374
|
|
|
375
|
+
def run_inline_step(title, command)
|
|
376
|
+
heading title, command.join(" "), type: :title
|
|
377
|
+
report(title) do
|
|
378
|
+
success = system(*command)
|
|
379
|
+
results << [success, title]
|
|
380
|
+
|
|
381
|
+
if success
|
|
382
|
+
clear_state if recorded_failed_step == title
|
|
383
|
+
else
|
|
384
|
+
record_failed_step(title)
|
|
385
|
+
if fail_fast?
|
|
386
|
+
abort colorize("\n#{status_marker(:error)} #{title} failed (fail-fast enabled)", :error)
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def mark_step_skipped(title, reason)
|
|
393
|
+
heading title, "skipped (#{reason})", type: :skip
|
|
394
|
+
@skipped_steps << title if reason == @skip_remaining_reason
|
|
395
|
+
results << [true, title]
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def queue_parallel_step?
|
|
399
|
+
return false unless parallel?
|
|
400
|
+
return true if @inside_parallel_steps_block
|
|
401
|
+
return false if @using_explicit_parallel_steps
|
|
402
|
+
|
|
403
|
+
true
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def mark_remaining_steps_skipped!(reason)
|
|
407
|
+
@skip_remaining_steps = true
|
|
408
|
+
@skip_remaining_reason = reason
|
|
409
|
+
end
|
|
410
|
+
|
|
340
411
|
def print_parallel_summary(completed)
|
|
341
412
|
failed_jobs = completed.reject { |j| j[:success] }
|
|
342
413
|
return if failed_jobs.empty?
|
|
@@ -446,7 +517,7 @@ module LocalCiPlus
|
|
|
446
517
|
return
|
|
447
518
|
end
|
|
448
519
|
|
|
449
|
-
lines_up = @parallel_steps.size - index
|
|
520
|
+
lines_up = (@active_parallel_size || @parallel_steps.size) - index
|
|
450
521
|
print "\033[s"
|
|
451
522
|
print "\033[#{lines_up}A" if lines_up > 0
|
|
452
523
|
print "\r\033[2K"
|