fun_ci 1.1.0 → 1.2.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/exe/fun-ci-trigger +6 -6
  4. data/exe/fun-ci-tui +6 -6
  5. data/lib/fun_ci/cli.rb +17 -16
  6. data/lib/fun_ci/persistence/database.rb +52 -0
  7. data/lib/fun_ci/persistence/pipeline_recorder.rb +74 -0
  8. data/lib/fun_ci/persistence/pipeline_run.rb +59 -0
  9. data/lib/fun_ci/persistence/stage_job.rb +46 -0
  10. data/lib/fun_ci/{state_machine.rb → persistence/state_machine.rb} +3 -12
  11. data/lib/fun_ci/pipeline/background_wrapper.rb +27 -0
  12. data/lib/fun_ci/pipeline/pipeline_forker.rb +38 -0
  13. data/lib/fun_ci/pipeline/process_runner.rb +28 -0
  14. data/lib/fun_ci/pipeline/progress_reporter.rb +31 -0
  15. data/lib/fun_ci/pipeline/stage_runner.rb +85 -0
  16. data/lib/fun_ci/pipeline/stale_pipeline_canceller.rb +53 -0
  17. data/lib/fun_ci/pipeline/trigger.rb +153 -0
  18. data/lib/fun_ci/setup/hook_writer.rb +75 -0
  19. data/lib/fun_ci/setup/installer.rb +55 -0
  20. data/lib/fun_ci/setup/maven_linter_detector.rb +26 -0
  21. data/lib/fun_ci/setup/project_config.rb +42 -0
  22. data/lib/fun_ci/setup/project_detector.rb +22 -0
  23. data/lib/fun_ci/setup/setup_checker.rb +30 -0
  24. data/lib/fun_ci/setup/template_writer.rb +53 -0
  25. data/lib/fun_ci/tui/admin_tui.rb +90 -0
  26. data/lib/fun_ci/tui/animation.rb +49 -0
  27. data/lib/fun_ci/tui/animation_compositor.rb +107 -0
  28. data/lib/fun_ci/tui/animation_frames.rb +112 -0
  29. data/lib/fun_ci/tui/animation_library.rb +46 -0
  30. data/lib/fun_ci/tui/animation_renderer.rb +144 -0
  31. data/lib/fun_ci/tui/ansi.rb +34 -0
  32. data/lib/fun_ci/tui/board_data.rb +53 -0
  33. data/lib/fun_ci/tui/board_renderer.rb +105 -0
  34. data/lib/fun_ci/tui/duration_formatter.rb +24 -0
  35. data/lib/fun_ci/tui/header_animation_manager.rb +71 -0
  36. data/lib/fun_ci/tui/header_animation_player.rb +45 -0
  37. data/lib/fun_ci/tui/key_handler.rb +86 -0
  38. data/lib/fun_ci/tui/looping_animation_player.rb +45 -0
  39. data/lib/fun_ci/tui/relative_time.rb +22 -0
  40. data/lib/fun_ci/tui/row_formatter.rb +108 -0
  41. data/lib/fun_ci/tui/screen.rb +103 -0
  42. data/lib/fun_ci/tui/spinner.rb +24 -0
  43. data/lib/fun_ci/tui/stage_change_detector.rb +58 -0
  44. data/lib/fun_ci/tui/streak_counter.rb +29 -0
  45. data/lib/fun_ci/tui/terminal_input.rb +69 -0
  46. data/lib/fun_ci.rb +6 -6
  47. metadata +41 -37
  48. data/lib/fun_ci/admin_tui.rb +0 -238
  49. data/lib/fun_ci/animation.rb +0 -47
  50. data/lib/fun_ci/animation_compositor.rb +0 -105
  51. data/lib/fun_ci/animation_frames.rb +0 -111
  52. data/lib/fun_ci/animation_library.rb +0 -44
  53. data/lib/fun_ci/animation_renderer.rb +0 -142
  54. data/lib/fun_ci/ansi.rb +0 -32
  55. data/lib/fun_ci/background_wrapper.rb +0 -27
  56. data/lib/fun_ci/board_data.rb +0 -51
  57. data/lib/fun_ci/database.rb +0 -50
  58. data/lib/fun_ci/duration_formatter.rb +0 -23
  59. data/lib/fun_ci/header_animation_manager.rb +0 -69
  60. data/lib/fun_ci/header_animation_player.rb +0 -43
  61. data/lib/fun_ci/hook_writer.rb +0 -73
  62. data/lib/fun_ci/installer.rb +0 -53
  63. data/lib/fun_ci/looping_animation_player.rb +0 -43
  64. data/lib/fun_ci/maven_linter_detector.rb +0 -24
  65. data/lib/fun_ci/pipeline_forker.rb +0 -36
  66. data/lib/fun_ci/pipeline_recorder.rb +0 -72
  67. data/lib/fun_ci/pipeline_run.rb +0 -57
  68. data/lib/fun_ci/progress_reporter.rb +0 -29
  69. data/lib/fun_ci/project_config.rb +0 -40
  70. data/lib/fun_ci/project_detector.rb +0 -18
  71. data/lib/fun_ci/relative_time.rb +0 -20
  72. data/lib/fun_ci/row_formatter.rb +0 -106
  73. data/lib/fun_ci/screen.rb +0 -95
  74. data/lib/fun_ci/setup_checker.rb +0 -28
  75. data/lib/fun_ci/spinner.rb +0 -22
  76. data/lib/fun_ci/stage_change_detector.rb +0 -56
  77. data/lib/fun_ci/stage_job.rb +0 -44
  78. data/lib/fun_ci/stage_runner.rb +0 -108
  79. data/lib/fun_ci/stale_pipeline_canceller.rb +0 -51
  80. data/lib/fun_ci/streak_counter.rb +0 -30
  81. data/lib/fun_ci/template_writer.rb +0 -51
  82. data/lib/fun_ci/trigger.rb +0 -150
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fun_ci
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Thyge Madsen
@@ -41,12 +41,6 @@ files:
41
41
  - exe/fun-ci-trigger
42
42
  - exe/fun-ci-tui
43
43
  - lib/fun_ci.rb
44
- - lib/fun_ci/admin_tui.rb
45
- - lib/fun_ci/animation.rb
46
- - lib/fun_ci/animation_compositor.rb
47
- - lib/fun_ci/animation_frames.rb
48
- - lib/fun_ci/animation_library.rb
49
- - lib/fun_ci/animation_renderer.rb
50
44
  - lib/fun_ci/animations/celebrate.rb
51
45
  - lib/fun_ci/animations/explosion.rb
52
46
  - lib/fun_ci/animations/flash.rb
@@ -55,37 +49,47 @@ files:
55
49
  - lib/fun_ci/animations/running.rb
56
50
  - lib/fun_ci/animations/success.rb
57
51
  - lib/fun_ci/animations/yay.rb
58
- - lib/fun_ci/ansi.rb
59
- - lib/fun_ci/background_wrapper.rb
60
- - lib/fun_ci/board_data.rb
61
52
  - lib/fun_ci/cli.rb
62
- - lib/fun_ci/database.rb
63
- - lib/fun_ci/duration_formatter.rb
64
- - lib/fun_ci/header_animation_manager.rb
65
- - lib/fun_ci/header_animation_player.rb
66
- - lib/fun_ci/hook_writer.rb
67
- - lib/fun_ci/installer.rb
68
- - lib/fun_ci/looping_animation_player.rb
69
- - lib/fun_ci/maven_linter_detector.rb
70
- - lib/fun_ci/pipeline_forker.rb
71
- - lib/fun_ci/pipeline_recorder.rb
72
- - lib/fun_ci/pipeline_run.rb
73
- - lib/fun_ci/progress_reporter.rb
74
- - lib/fun_ci/project_config.rb
75
- - lib/fun_ci/project_detector.rb
76
- - lib/fun_ci/relative_time.rb
77
- - lib/fun_ci/row_formatter.rb
78
- - lib/fun_ci/screen.rb
79
- - lib/fun_ci/setup_checker.rb
80
- - lib/fun_ci/spinner.rb
81
- - lib/fun_ci/stage_change_detector.rb
82
- - lib/fun_ci/stage_job.rb
83
- - lib/fun_ci/stage_runner.rb
84
- - lib/fun_ci/stale_pipeline_canceller.rb
85
- - lib/fun_ci/state_machine.rb
86
- - lib/fun_ci/streak_counter.rb
87
- - lib/fun_ci/template_writer.rb
88
- - lib/fun_ci/trigger.rb
53
+ - lib/fun_ci/persistence/database.rb
54
+ - lib/fun_ci/persistence/pipeline_recorder.rb
55
+ - lib/fun_ci/persistence/pipeline_run.rb
56
+ - lib/fun_ci/persistence/stage_job.rb
57
+ - lib/fun_ci/persistence/state_machine.rb
58
+ - lib/fun_ci/pipeline/background_wrapper.rb
59
+ - lib/fun_ci/pipeline/pipeline_forker.rb
60
+ - lib/fun_ci/pipeline/process_runner.rb
61
+ - lib/fun_ci/pipeline/progress_reporter.rb
62
+ - lib/fun_ci/pipeline/stage_runner.rb
63
+ - lib/fun_ci/pipeline/stale_pipeline_canceller.rb
64
+ - lib/fun_ci/pipeline/trigger.rb
65
+ - lib/fun_ci/setup/hook_writer.rb
66
+ - lib/fun_ci/setup/installer.rb
67
+ - lib/fun_ci/setup/maven_linter_detector.rb
68
+ - lib/fun_ci/setup/project_config.rb
69
+ - lib/fun_ci/setup/project_detector.rb
70
+ - lib/fun_ci/setup/setup_checker.rb
71
+ - lib/fun_ci/setup/template_writer.rb
72
+ - lib/fun_ci/tui/admin_tui.rb
73
+ - lib/fun_ci/tui/animation.rb
74
+ - lib/fun_ci/tui/animation_compositor.rb
75
+ - lib/fun_ci/tui/animation_frames.rb
76
+ - lib/fun_ci/tui/animation_library.rb
77
+ - lib/fun_ci/tui/animation_renderer.rb
78
+ - lib/fun_ci/tui/ansi.rb
79
+ - lib/fun_ci/tui/board_data.rb
80
+ - lib/fun_ci/tui/board_renderer.rb
81
+ - lib/fun_ci/tui/duration_formatter.rb
82
+ - lib/fun_ci/tui/header_animation_manager.rb
83
+ - lib/fun_ci/tui/header_animation_player.rb
84
+ - lib/fun_ci/tui/key_handler.rb
85
+ - lib/fun_ci/tui/looping_animation_player.rb
86
+ - lib/fun_ci/tui/relative_time.rb
87
+ - lib/fun_ci/tui/row_formatter.rb
88
+ - lib/fun_ci/tui/screen.rb
89
+ - lib/fun_ci/tui/spinner.rb
90
+ - lib/fun_ci/tui/stage_change_detector.rb
91
+ - lib/fun_ci/tui/streak_counter.rb
92
+ - lib/fun_ci/tui/terminal_input.rb
89
93
  homepage: https://github.com/beatmadsen/fun-ci
90
94
  licenses:
91
95
  - MIT
@@ -1,238 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "board_data"
4
- require_relative "header_animation_manager"
5
- require_relative "row_formatter"
6
- require_relative "screen"
7
- require_relative "spinner"
8
- require_relative "streak_counter"
9
- require "io/console"
10
-
11
- module FunCi
12
- class AdminTui
13
- FAST_REFRESH = 0.1 # seconds (spinner + timer)
14
- SLOW_REFRESH = 5.0 # seconds (settled board)
15
- HEADER_HEIGHT = HeaderAnimationManager::HEADER_HEIGHT
16
-
17
- def initialize(db:, output: $stdout, input: $stdin, width: 80,
18
- width_provider: nil, page_size: nil, animation_renderer: nil)
19
- @board_data = BoardData.new(db, page_size: page_size)
20
- @screen = Screen.new(output: output, width: width)
21
- @animation_renderer = animation_renderer
22
- @input = input
23
- @width_provider = width_provider
24
- @spinner = Spinner.new
25
- @cursor_index = nil
26
- @running = false
27
- @confirm_cancel = nil
28
- end
29
-
30
- def render_once
31
- update_width_from_provider
32
- runs = @board_data.runs
33
- streak = @board_data.streak
34
- streak_text = StreakCounter.format_text(streak)
35
-
36
- @screen.render_header(streak_text: streak_text)
37
- (HEADER_HEIGHT - 1).times { @screen.println }
38
-
39
- if runs.empty?
40
- @screen.render_empty_state
41
- @screen.render_footer(empty: true)
42
- else
43
- rows = runs.map { |run| format_run(run) }
44
- @screen.render_board(rows, cursor_index: @cursor_index)
45
- @screen.println
46
- @screen.render_footer(empty: false, confirming: confirming?)
47
- end
48
-
49
- @screen.clear_below
50
- render_animations(runs)
51
- end
52
-
53
- def run
54
- @running = true
55
- @screen.clear
56
-
57
- begin
58
- setup_raw_mode
59
- setup_sigwinch_trap
60
- loop do
61
- render_frame
62
- break unless @running
63
- key = read_key_with_timeout(refresh_interval)
64
- handle_key(key) if key
65
- end
66
- ensure
67
- restore_terminal
68
- end
69
- end
70
-
71
- def resize(new_width)
72
- @screen.width = new_width
73
- end
74
-
75
- def confirming?
76
- !@confirm_cancel.nil?
77
- end
78
-
79
- def confirmation_run
80
- @confirm_cancel
81
- end
82
-
83
- def handle_key(key)
84
- if @confirm_cancel
85
- handle_confirm_key(key)
86
- return
87
- end
88
-
89
- case key
90
- when "q"
91
- @running = false
92
- when "j", :down
93
- move_cursor_down
94
- when "k", :up
95
- move_cursor_up
96
- when "c"
97
- initiate_cancel
98
- end
99
- end
100
-
101
- private
102
-
103
- def render_animations(runs)
104
- return unless @animation_renderer
105
-
106
- @animation_renderer.render(@screen, runs)
107
- end
108
-
109
- def format_run(run)
110
- opts = {}
111
- if run[:status] == "running"
112
- active_stage = run[:stages].find { |s| s[:status] == "running" }
113
- if active_stage && active_stage[:started_at]
114
- opts[:elapsed_seconds] = Time.now - Time.parse(active_stage[:started_at])
115
- end
116
- opts[:spinner_frame] = @spinner.current_frame
117
- end
118
- RowFormatter.format(run, **opts)
119
- end
120
-
121
- def render_frame
122
- @screen.move_cursor_home
123
- @spinner.advance!
124
- render_once
125
- end
126
-
127
- def refresh_interval
128
- return FAST_REFRESH if @animation_renderer
129
-
130
- runs = @board_data.runs
131
- any_running = runs.any? { |r| r[:status] == "running" }
132
- any_running ? FAST_REFRESH : SLOW_REFRESH
133
- end
134
-
135
- def move_cursor_down
136
- runs = @board_data.runs
137
- return if runs.empty?
138
-
139
- if @cursor_index.nil?
140
- @cursor_index = 0
141
- elsif @cursor_index < runs.length - 1
142
- @cursor_index += 1
143
- @board_data.load_more if @cursor_index == runs.length - 1
144
- end
145
- end
146
-
147
- def move_cursor_up
148
- return if @cursor_index.nil?
149
-
150
- if @cursor_index > 0
151
- @cursor_index -= 1
152
- end
153
- end
154
-
155
- def initiate_cancel
156
- return if @cursor_index.nil?
157
-
158
- runs = @board_data.runs
159
- return if @cursor_index >= runs.length
160
-
161
- run = runs[@cursor_index]
162
- case run[:status]
163
- when "scheduled"
164
- @board_data.cancel_run(run[:id])
165
- when "running"
166
- @confirm_cancel = run
167
- end
168
- end
169
-
170
- def handle_confirm_key(key)
171
- case key
172
- when "y"
173
- @board_data.cancel_run(@confirm_cancel[:id])
174
- @confirm_cancel = nil
175
- when "n", :escape
176
- @confirm_cancel = nil
177
- end
178
- end
179
-
180
- def update_width_from_provider
181
- return unless @width_provider
182
-
183
- new_width = @width_provider.call
184
- resize(new_width) if new_width
185
- end
186
-
187
- def setup_sigwinch_trap
188
- return unless @width_provider
189
-
190
- @signal_read, @signal_write = IO.pipe
191
- Signal.trap("WINCH") do
192
- @signal_write.write_nonblock(".") rescue nil
193
- end
194
- end
195
-
196
- def setup_raw_mode
197
- @input.raw! if @input.respond_to?(:raw!)
198
- rescue Errno::ENOTTY
199
- # Not a terminal (testing)
200
- end
201
-
202
- def restore_terminal
203
- @input.cooked! if @input.respond_to?(:cooked!)
204
- rescue Errno::ENOTTY
205
- # Not a terminal
206
- end
207
-
208
- def read_key_with_timeout(timeout)
209
- return nil unless @input.respond_to?(:read_nonblock)
210
-
211
- watched = [@input]
212
- watched << @signal_read if @signal_read
213
- ready = IO.select(watched, nil, nil, timeout)
214
- return nil unless ready
215
-
216
- # Drain signal pipe if it woke us
217
- if @signal_read && ready[0].include?(@signal_read)
218
- @signal_read.read_nonblock(1) rescue nil
219
- return nil unless ready[0].include?(@input)
220
- end
221
-
222
- byte = @input.read_nonblock(1)
223
- if byte == "\e"
224
- # Escape sequence
225
- seq = @input.read_nonblock(2) rescue ""
226
- case seq
227
- when "[A" then :up
228
- when "[B" then :down
229
- else :escape
230
- end
231
- else
232
- byte
233
- end
234
- rescue IO::WaitReadable, EOFError
235
- nil
236
- end
237
- end
238
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunCi
4
- class Animation
5
- TYPES = {
6
- failure: { total_frames: 40, priority: 3 },
7
- timeout: { total_frames: 4, priority: 2 },
8
- success: { total_frames: 40, priority: 1 },
9
- stage_pass: { total_frames: 3, priority: 0 }
10
- }.freeze
11
-
12
- attr_reader :type, :run_id, :stage, :frame
13
-
14
- def initialize(type:, run_id:, stage: nil)
15
- raise ArgumentError, "Unknown animation type: #{type}" unless TYPES.key?(type)
16
-
17
- @type = type
18
- @run_id = run_id
19
- @stage = stage
20
- @frame = 0
21
- end
22
-
23
- def advance!
24
- @frame += 1
25
- end
26
-
27
- def finished?
28
- frame >= total_frames
29
- end
30
-
31
- def total_frames
32
- TYPES[@type][:total_frames]
33
- end
34
-
35
- def priority
36
- TYPES[@type][:priority]
37
- end
38
-
39
- def has_header?
40
- %i[failure timeout success].include?(@type)
41
- end
42
-
43
- def has_footer?
44
- %i[failure success].include?(@type)
45
- end
46
- end
47
- end
@@ -1,105 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "ansi"
4
- require_relative "animation_frames"
5
- require_relative "row_formatter"
6
- require_relative "duration_formatter"
7
-
8
- module FunCi
9
- module AnimationCompositor
10
- RESET = Ansi::RESET
11
- STAGE_STAGGER = { "lint" => 0, "build" => 2, "fast" => 4, "slow" => 6 }.freeze
12
-
13
- def self.header_overlay(animation, width)
14
- frames = case animation.type
15
- when :failure then AnimationFrames.failure_header(width)
16
- when :success then AnimationFrames.success_header(width)
17
- when :timeout then AnimationFrames.timeout_header(width)
18
- else return nil
19
- end
20
- frame_at(frames, animation.frame)
21
- end
22
-
23
- def self.footer_overlay(animation, width)
24
- frames = case animation.type
25
- when :failure
26
- AnimationFrames.failure_footer(animation.stage || "stage", width)
27
- when :success then AnimationFrames.success_footer(width)
28
- else return nil
29
- end
30
- frame_at(frames, animation.frame)
31
- end
32
-
33
- def self.stage_column_overlay(animation, stage_text, col)
34
- case animation.type
35
- when :stage_pass then color_flash(animation, stage_text, AnimationFrames.stage_pass_colors)
36
- when :timeout then color_flash(animation, stage_text, AnimationFrames.timeout_colors)
37
- when :failure then failure_flanks(animation, stage_text)
38
- when :success then sparkle_sweep(animation, stage_text)
39
- end
40
- end
41
-
42
- def self.stage_text_for(run, stage_name)
43
- stage = run[:stages]&.find { |s| s[:stage] == stage_name }
44
- return nil unless stage
45
-
46
- name = RowFormatter::STAGE_NAMES[stage[:stage]] || stage[:stage]
47
- dur = stage[:duration] ? DurationFormatter.format(stage[:duration]) : "--"
48
- case stage[:status]
49
- when "completed" then "#{name} #{dur}"
50
- when "failed" then "#{name} FAIL #{dur}"
51
- when "timed_out" then "#{name} TIMEOUT #{dur}"
52
- else "#{name} --"
53
- end
54
- end
55
-
56
- def self.stage_col_for(run, stage_name)
57
- project = run[:project_path] ? " #{File.basename(run[:project_path])}" : ""
58
- prefix_len = 2 + 7 + 2 + run[:branch].to_s.length + project.length + 2
59
- (run[:stages] || []).each do |s|
60
- return prefix_len + 1 if s[:stage] == stage_name
61
- prefix_len += stage_text_for(run, s[:stage]).to_s.length + 2
62
- end
63
- nil
64
- end
65
-
66
- def self.color_flash(animation, stage_text, colors)
67
- idx = [animation.frame, colors.length - 1].min
68
- "#{colors[idx]}#{Ansi.strip(stage_text)}#{RESET}"
69
- end
70
- private_class_method :color_flash
71
-
72
- def self.failure_flanks(animation, stage_text)
73
- particles = AnimationFrames.failure_particles
74
- return nil if animation.frame >= particles.length
75
-
76
- p = particles[animation.frame]
77
- plain = Ansi.strip(stage_text)
78
- return plain if p[:chars].empty?
79
-
80
- left = p[:chars].reverse
81
- right = p[:chars]
82
- "#{p[:color]}#{left}#{RESET} #{Ansi.bold_red(plain)} #{p[:color]}#{right}#{RESET}"
83
- end
84
- private_class_method :failure_flanks
85
-
86
- def self.sparkle_sweep(animation, stage_text)
87
- plain = Ansi.strip(stage_text)
88
- stagger = STAGE_STAGGER[animation.stage] || 0
89
- pos = animation.frame - stagger
90
- return nil if pos < 0 || pos > plain.length
91
-
92
- result = plain.chars.map { |c| "\e[32m#{c}" }
93
- result[pos] = "\e[1;33m#{plain[pos]}" if pos < plain.length
94
- "#{result.join}#{RESET}"
95
- end
96
- private_class_method :sparkle_sweep
97
-
98
- def self.frame_at(frames, index)
99
- return nil if index >= frames.length
100
-
101
- frames[index]
102
- end
103
- private_class_method :frame_at
104
- end
105
- end
@@ -1,111 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "ansi"
4
-
5
- module FunCi
6
- module AnimationFrames
7
- RESET = Ansi::RESET
8
- FOOTER_HOLD = 8
9
-
10
- def self.failure_particles
11
- # Left/right flanking debris per frame (chars expanding outward)
12
- [
13
- { chars: "*", color: "\e[1;31m" },
14
- { chars: ".*", color: "\e[1;31m" },
15
- { chars: ".+*.", color: "\e[38;5;208m" },
16
- { chars: "*.+'", color: "\e[38;5;208m" },
17
- { chars: "' .", color: "\e[38;5;52m" },
18
- { chars: ".", color: "\e[38;5;52m" },
19
- { chars: "", color: "" }
20
- ]
21
- end
22
-
23
- def self.failure_header(width)
24
- boom = "BOOM"
25
- [
26
- nil,
27
- banner(boom, width, "\e[48;5;124m", particles: "* "),
28
- banner(boom, width, "\e[48;5;166m", particles: "* * "),
29
- banner(boom, width, "\e[48;5;52m", particles: ". * * "),
30
- banner(boom, width, "\e[48;5;236m", particles: "' . * "),
31
- banner(boom, width, "\e[48;5;236m", particles: ". "),
32
- nil
33
- ]
34
- end
35
-
36
- def self.failure_footer(stage_name, width)
37
- up = stage_name.upcase
38
- down = stage_name.downcase
39
- hold [
40
- nil,
41
- center("\e[1;31m>>> #{up} FAILED <<<#{RESET}", ">>> #{up} FAILED <<<", width),
42
- center("\e[31m>> #{down} failed <<#{RESET}", ">> #{down} failed <<", width),
43
- center("\e[2;31m> #{down} failed <#{RESET}", "> #{down} failed <", width),
44
- nil
45
- ]
46
- end
47
-
48
- def self.success_header(width)
49
- text = "ALL PASSED"
50
- [
51
- nil,
52
- banner(text, width, "\e[48;5;22m", particles: ""),
53
- banner(text, width, "\e[48;5;22m", particles: "* * "),
54
- banner(text, width, "\e[48;5;22m", particles: "* . . "),
55
- banner(text, width, "\e[48;5;58m", particles: ". ' ' "),
56
- banner(text, width, "\e[48;5;22m", particles: "' "),
57
- banner(text, width, "\e[48;5;22m", particles: ""),
58
- nil
59
- ]
60
- end
61
-
62
- def self.success_footer(width)
63
- hold [
64
- nil,
65
- center("\e[1;32m* * * NICE! * * *#{RESET}", "* * * NICE! * * *", width),
66
- center("\e[32m. + . * NICE! * . + .#{RESET}", ". + . * NICE! * . + .", width),
67
- center("\e[2;32m' . + . nice . + . '#{RESET}", "' . + . nice . + . '", width),
68
- nil
69
- ]
70
- end
71
-
72
- def self.timeout_header(width)
73
- [
74
- nil,
75
- banner("TIMED OUT", width, "\e[48;5;58m", particles: ""),
76
- banner("timed out", width, "\e[48;5;58m", particles: ""),
77
- nil
78
- ]
79
- end
80
-
81
- def self.stage_pass_colors
82
- ["\e[1;33m", "\e[1;32m", "\e[32m"]
83
- end
84
-
85
- def self.timeout_colors
86
- ["\e[1;33m", "\e[33m", "\e[1;33m", "\e[33m"]
87
- end
88
-
89
- def self.banner(text, width, bg_code, particles: "")
90
- padded = if particles.empty?
91
- text.center(width)
92
- else
93
- inner = "#{particles} #{text} #{particles.reverse}"
94
- inner.center(width)
95
- end
96
- "#{bg_code}\e[1;37m#{padded}#{RESET}"
97
- end
98
- private_class_method :banner
99
-
100
- def self.hold(frames)
101
- frames.flat_map { |f| Array.new(FOOTER_HOLD, f) }
102
- end
103
- private_class_method :hold
104
-
105
- def self.center(colored_text, plain_text, width)
106
- pad = [(width - plain_text.length) / 2, 0].max
107
- "#{" " * pad}#{colored_text}"
108
- end
109
- private_class_method :center
110
- end
111
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "animations/explosion"
4
- require_relative "animations/success"
5
- require_relative "animations/celebrate"
6
- require_relative "animations/flash"
7
- require_relative "animations/leprechauns"
8
- require_relative "animations/yay"
9
- require_relative "animations/idle"
10
- require_relative "animations/running"
11
-
12
- module FunCi
13
- module AnimationLibrary
14
- FAILURE = [Animations::Explosion::DATA].freeze
15
-
16
- SUCCESS = [
17
- Animations::Success::DATA,
18
- Animations::Celebrate::DATA,
19
- Animations::Flash::DATA,
20
- Animations::Leprechauns::DATA,
21
- Animations::Yay::DATA
22
- ].freeze
23
-
24
- IDLE = Animations::Idle::DATA
25
-
26
- RUNNING = Animations::Running::DATA
27
-
28
- def self.random_failure
29
- FAILURE.sample
30
- end
31
-
32
- def self.random_success
33
- SUCCESS.sample
34
- end
35
-
36
- def self.idle
37
- IDLE
38
- end
39
-
40
- def self.running
41
- RUNNING
42
- end
43
- end
44
- end