fun_ci 1.2.0 → 2.0.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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +241 -0
  3. data/README.md +180 -59
  4. data/lib/fun_ci/agent/age.rb +15 -0
  5. data/lib/fun_ci/agent/command_support.rb +27 -0
  6. data/lib/fun_ci/agent/commands.rb +27 -0
  7. data/lib/fun_ci/agent/context.rb +10 -0
  8. data/lib/fun_ci/agent/digest.rb +49 -0
  9. data/lib/fun_ci/agent/events.rb +47 -0
  10. data/lib/fun_ci/agent/events_command.rb +61 -0
  11. data/lib/fun_ci/agent/evidence_text.rb +46 -0
  12. data/lib/fun_ci/agent/exit_code.rb +11 -0
  13. data/lib/fun_ci/agent/git.rb +31 -0
  14. data/lib/fun_ci/agent/live_pipeline.rb +35 -0
  15. data/lib/fun_ci/agent/options.rb +75 -0
  16. data/lib/fun_ci/agent/output.rb +81 -0
  17. data/lib/fun_ci/agent/report_stage.rb +73 -0
  18. data/lib/fun_ci/agent/reports.rb +47 -0
  19. data/lib/fun_ci/agent/run_report.rb +30 -0
  20. data/lib/fun_ci/agent/runs_command.rb +34 -0
  21. data/lib/fun_ci/agent/runs_text.rb +27 -0
  22. data/lib/fun_ci/agent/snapshots.rb +39 -0
  23. data/lib/fun_ci/agent/stage_summary.rb +30 -0
  24. data/lib/fun_ci/agent/status_command.rb +36 -0
  25. data/lib/fun_ci/agent/status_json.rb +27 -0
  26. data/lib/fun_ci/agent/status_text.rb +37 -0
  27. data/lib/fun_ci/agent/system_clock.rb +11 -0
  28. data/lib/fun_ci/agent/verdict.rb +32 -0
  29. data/lib/fun_ci/agent/wait_command.rb +71 -0
  30. data/lib/fun_ci/agent/waiting.rb +32 -0
  31. data/lib/fun_ci/agent/why_command.rb +50 -0
  32. data/lib/fun_ci/agent/why_json.rb +35 -0
  33. data/lib/fun_ci/agent/why_text.rb +43 -0
  34. data/lib/fun_ci/cli.rb +72 -94
  35. data/lib/fun_ci/cli_help.rb +65 -0
  36. data/lib/fun_ci/console/board_data.rb +66 -0
  37. data/lib/fun_ci/console/board_heartbeat.rb +40 -0
  38. data/lib/fun_ci/console/console_events.rb +20 -0
  39. data/lib/fun_ci/console/console_log.rb +26 -0
  40. data/lib/fun_ci/console/console_loop.rb +41 -0
  41. data/lib/fun_ci/console/console_session.rb +82 -0
  42. data/lib/fun_ci/console/console_state.rb +42 -0
  43. data/lib/fun_ci/console/key_handler.rb +73 -0
  44. data/lib/fun_ci/console/launcher.rb +43 -0
  45. data/lib/fun_ci/console/milestone_events.rb +38 -0
  46. data/lib/fun_ci/console/milestones.rb +25 -0
  47. data/lib/fun_ci/console/renderer_lookup.rb +51 -0
  48. data/lib/fun_ci/console/renderer_process.rb +60 -0
  49. data/lib/fun_ci/console/run_message.rb +33 -0
  50. data/lib/fun_ci/console/stage_change_detector.rb +32 -0
  51. data/lib/fun_ci/console/stage_events.rb +31 -0
  52. data/lib/fun_ci/console/stop_report.rb +39 -0
  53. data/lib/fun_ci/console/streak_counter.rb +19 -0
  54. data/lib/fun_ci/console/view.rb +47 -0
  55. data/lib/fun_ci/evidence/about.rb +12 -0
  56. data/lib/fun_ci/evidence/about_stage.rb +29 -0
  57. data/lib/fun_ci/evidence/caps.rb +77 -0
  58. data/lib/fun_ci/evidence/catalog.rb +83 -0
  59. data/lib/fun_ci/evidence/collector.rb +95 -0
  60. data/lib/fun_ci/evidence/command_output.rb +45 -0
  61. data/lib/fun_ci/evidence/command_reading.rb +38 -0
  62. data/lib/fun_ci/evidence/command_runner.rb +75 -0
  63. data/lib/fun_ci/evidence/context.rb +21 -0
  64. data/lib/fun_ci/evidence/context_document.rb +29 -0
  65. data/lib/fun_ci/evidence/deadline.rb +15 -0
  66. data/lib/fun_ci/evidence/detection.rb +82 -0
  67. data/lib/fun_ci/evidence/document.rb +59 -0
  68. data/lib/fun_ci/evidence/extract_command.rb +83 -0
  69. data/lib/fun_ci/evidence/extract_options.rb +45 -0
  70. data/lib/fun_ci/evidence/extraction.rb +54 -0
  71. data/lib/fun_ci/evidence/extractors/command.rb +52 -0
  72. data/lib/fun_ci/evidence/extractors/grep.rb +49 -0
  73. data/lib/fun_ci/evidence/extractors/json_log.rb +45 -0
  74. data/lib/fun_ci/evidence/extractors/junit_files.rb +32 -0
  75. data/lib/fun_ci/evidence/extractors/last_lines.rb +25 -0
  76. data/lib/fun_ci/evidence/extractors/log_file.rb +62 -0
  77. data/lib/fun_ci/evidence/extractors/process_tree.rb +51 -0
  78. data/lib/fun_ci/evidence/extractors/section.rb +49 -0
  79. data/lib/fun_ci/evidence/findings.rb +14 -0
  80. data/lib/fun_ci/evidence/line_ranges.rb +21 -0
  81. data/lib/fun_ci/evidence/line_scan.rb +22 -0
  82. data/lib/fun_ci/evidence/log_record.rb +58 -0
  83. data/lib/fun_ci/evidence/masking.rb +55 -0
  84. data/lib/fun_ci/evidence/option_check.rb +53 -0
  85. data/lib/fun_ci/evidence/outcome.rb +15 -0
  86. data/lib/fun_ci/evidence/patterns.rb +22 -0
  87. data/lib/fun_ci/evidence/presets/README.md +7 -0
  88. data/lib/fun_ci/evidence/presets/bun-test.yml +6 -0
  89. data/lib/fun_ci/evidence/presets/cargo-test.yml +6 -0
  90. data/lib/fun_ci/evidence/presets/dart-test.yml +6 -0
  91. data/lib/fun_ci/evidence/presets/deno-test.yml +6 -0
  92. data/lib/fun_ci/evidence/presets/dotnet-test.yml +6 -0
  93. data/lib/fun_ci/evidence/presets/ecs.yml +3 -0
  94. data/lib/fun_ci/evidence/presets/eslint.yml +6 -0
  95. data/lib/fun_ci/evidence/presets/exunit.yml +6 -0
  96. data/lib/fun_ci/evidence/presets/gcc.yml +6 -0
  97. data/lib/fun_ci/evidence/presets/go-build.yml +5 -0
  98. data/lib/fun_ci/evidence/presets/go-test.yml +6 -0
  99. data/lib/fun_ci/evidence/presets/gradle.yml +6 -0
  100. data/lib/fun_ci/evidence/presets/gtest.yml +6 -0
  101. data/lib/fun_ci/evidence/presets/jest.yml +6 -0
  102. data/lib/fun_ci/evidence/presets/logstash.yml +3 -0
  103. data/lib/fun_ci/evidence/presets/maven.yml +6 -0
  104. data/lib/fun_ci/evidence/presets/minitest.yml +6 -0
  105. data/lib/fun_ci/evidence/presets/mocha.yml +6 -0
  106. data/lib/fun_ci/evidence/presets/mypy.yml +6 -0
  107. data/lib/fun_ci/evidence/presets/node-test.yml +6 -0
  108. data/lib/fun_ci/evidence/presets/phpunit.yml +6 -0
  109. data/lib/fun_ci/evidence/presets/pino.yml +5 -0
  110. data/lib/fun_ci/evidence/presets/prove.yml +6 -0
  111. data/lib/fun_ci/evidence/presets/pytest.yml +6 -0
  112. data/lib/fun_ci/evidence/presets/rspec.yml +6 -0
  113. data/lib/fun_ci/evidence/presets/rubocop.yml +6 -0
  114. data/lib/fun_ci/evidence/presets/ruff.yml +6 -0
  115. data/lib/fun_ci/evidence/presets/rustc.yml +6 -0
  116. data/lib/fun_ci/evidence/presets/shellcheck.yml +5 -0
  117. data/lib/fun_ci/evidence/presets/structlog.yml +4 -0
  118. data/lib/fun_ci/evidence/presets/swift-test.yml +5 -0
  119. data/lib/fun_ci/evidence/presets/tsc.yml +6 -0
  120. data/lib/fun_ci/evidence/presets/unittest.yml +6 -0
  121. data/lib/fun_ci/evidence/presets/vitest.yml +6 -0
  122. data/lib/fun_ci/evidence/presets.rb +30 -0
  123. data/lib/fun_ci/evidence/problem.rb +9 -0
  124. data/lib/fun_ci/evidence/process_table.rb +40 -0
  125. data/lib/fun_ci/evidence/saved_run.rb +22 -0
  126. data/lib/fun_ci/evidence/section_walk.rb +49 -0
  127. data/lib/fun_ci/evidence/settings.rb +64 -0
  128. data/lib/fun_ci/evidence/settings_check.rb +78 -0
  129. data/lib/fun_ci/evidence/source.rb +34 -0
  130. data/lib/fun_ci/evidence/sources.rb +21 -0
  131. data/lib/fun_ci/evidence/stage_context.rb +30 -0
  132. data/lib/fun_ci/evidence/stamp.rb +10 -0
  133. data/lib/fun_ci/evidence/start.rb +36 -0
  134. data/lib/fun_ci/evidence/worktree.rb +62 -0
  135. data/lib/fun_ci/persistence/active_runs.rb +69 -0
  136. data/lib/fun_ci/persistence/database.rb +59 -30
  137. data/lib/fun_ci/persistence/null_recorder.rb +25 -0
  138. data/lib/fun_ci/persistence/output_tail.rb +24 -0
  139. data/lib/fun_ci/persistence/pipeline_recorder.rb +78 -32
  140. data/lib/fun_ci/persistence/pipeline_run.rb +26 -17
  141. data/lib/fun_ci/persistence/project_runs.rb +52 -0
  142. data/lib/fun_ci/persistence/raw_outputs.rb +46 -0
  143. data/lib/fun_ci/persistence/retention.rb +25 -0
  144. data/lib/fun_ci/persistence/run_status.rb +31 -0
  145. data/lib/fun_ci/persistence/stage_job.rb +58 -13
  146. data/lib/fun_ci/persistence/state_dir.rb +26 -0
  147. data/lib/fun_ci/persistence/write_trouble.rb +46 -0
  148. data/lib/fun_ci/pipeline/background_fork.rb +42 -0
  149. data/lib/fun_ci/pipeline/command_executor.rb +58 -0
  150. data/lib/fun_ci/pipeline/gate.rb +35 -0
  151. data/lib/fun_ci/pipeline/git_environment.rb +16 -0
  152. data/lib/fun_ci/pipeline/head_part.rb +40 -0
  153. data/lib/fun_ci/pipeline/in_place.rb +17 -0
  154. data/lib/fun_ci/pipeline/output_window.rb +73 -0
  155. data/lib/fun_ci/pipeline/pipeline_forker.rb +18 -16
  156. data/lib/fun_ci/pipeline/process_runner.rb +90 -17
  157. data/lib/fun_ci/pipeline/run_canceller.rb +75 -0
  158. data/lib/fun_ci/pipeline/slot.rb +30 -0
  159. data/lib/fun_ci/pipeline/slot_lock.rb +13 -0
  160. data/lib/fun_ci/pipeline/slot_run.rb +86 -0
  161. data/lib/fun_ci/pipeline/stage_dir.rb +50 -0
  162. data/lib/fun_ci/pipeline/stage_end.rb +70 -0
  163. data/lib/fun_ci/pipeline/stage_execution.rb +67 -0
  164. data/lib/fun_ci/pipeline/stage_runner.rb +42 -64
  165. data/lib/fun_ci/pipeline/stale_pipeline_canceller.rb +16 -33
  166. data/lib/fun_ci/pipeline/tail_segments.rb +60 -0
  167. data/lib/fun_ci/pipeline/test_report.rb +48 -0
  168. data/lib/fun_ci/pipeline/trigger.rb +55 -115
  169. data/lib/fun_ci/pipeline/trigger_command.rb +62 -0
  170. data/lib/fun_ci/pipeline/trigger_params.rb +48 -0
  171. data/lib/fun_ci/pipeline/workspaces.rb +22 -0
  172. data/lib/fun_ci/pipeline/worktree_pool.rb +66 -0
  173. data/lib/fun_ci/pipeline/worktree_prune.rb +49 -0
  174. data/lib/fun_ci/pipeline/worktrees.rb +45 -0
  175. data/lib/fun_ci/setup/agent_instructions.rb +31 -0
  176. data/lib/fun_ci/setup/commands.rb +49 -0
  177. data/lib/fun_ci/setup/git_hooks.rb +20 -0
  178. data/lib/fun_ci/setup/hook_script.rb +47 -0
  179. data/lib/fun_ci/setup/hook_writer.rb +15 -33
  180. data/lib/fun_ci/setup/installer.rb +39 -23
  181. data/lib/fun_ci/setup/legacy_hooks.rb +38 -0
  182. data/lib/fun_ci/setup/project_config.rb +43 -17
  183. data/lib/fun_ci/setup/project_detector.rb +39 -7
  184. data/lib/fun_ci/setup/settings.rb +36 -0
  185. data/lib/fun_ci/setup/setup_checker.rb +18 -12
  186. data/lib/fun_ci/setup/stage_templates/javascript.rb +18 -0
  187. data/lib/fun_ci/setup/stage_templates/managed.rb +24 -0
  188. data/lib/fun_ci/setup/stage_templates/native.rb +25 -0
  189. data/lib/fun_ci/setup/stage_templates/scripting.rb +27 -0
  190. data/lib/fun_ci/setup/stage_templates/shell.rb +16 -0
  191. data/lib/fun_ci/setup/stage_templates.rb +25 -0
  192. data/lib/fun_ci/setup/template_writer.rb +11 -36
  193. data/lib/fun_ci/version.rb +6 -0
  194. data/lib/fun_ci.rb +1 -3
  195. metadata +212 -43
  196. data/exe/fun-ci-trigger +0 -19
  197. data/exe/fun-ci-tui +0 -23
  198. data/lib/fun_ci/animations/celebrate.rb +0 -210
  199. data/lib/fun_ci/animations/explosion.rb +0 -138
  200. data/lib/fun_ci/animations/flash.rb +0 -205
  201. data/lib/fun_ci/animations/idle.rb +0 -127
  202. data/lib/fun_ci/animations/leprechauns.rb +0 -240
  203. data/lib/fun_ci/animations/running.rb +0 -129
  204. data/lib/fun_ci/animations/success.rb +0 -281
  205. data/lib/fun_ci/animations/yay.rb +0 -169
  206. data/lib/fun_ci/persistence/state_machine.rb +0 -50
  207. data/lib/fun_ci/pipeline/background_wrapper.rb +0 -27
  208. data/lib/fun_ci/tui/admin_tui.rb +0 -90
  209. data/lib/fun_ci/tui/animation.rb +0 -49
  210. data/lib/fun_ci/tui/animation_compositor.rb +0 -107
  211. data/lib/fun_ci/tui/animation_frames.rb +0 -112
  212. data/lib/fun_ci/tui/animation_library.rb +0 -46
  213. data/lib/fun_ci/tui/animation_renderer.rb +0 -144
  214. data/lib/fun_ci/tui/ansi.rb +0 -34
  215. data/lib/fun_ci/tui/board_data.rb +0 -53
  216. data/lib/fun_ci/tui/board_renderer.rb +0 -105
  217. data/lib/fun_ci/tui/duration_formatter.rb +0 -24
  218. data/lib/fun_ci/tui/header_animation_manager.rb +0 -71
  219. data/lib/fun_ci/tui/header_animation_player.rb +0 -45
  220. data/lib/fun_ci/tui/key_handler.rb +0 -86
  221. data/lib/fun_ci/tui/looping_animation_player.rb +0 -45
  222. data/lib/fun_ci/tui/relative_time.rb +0 -22
  223. data/lib/fun_ci/tui/row_formatter.rb +0 -108
  224. data/lib/fun_ci/tui/screen.rb +0 -103
  225. data/lib/fun_ci/tui/spinner.rb +0 -24
  226. data/lib/fun_ci/tui/stage_change_detector.rb +0 -58
  227. data/lib/fun_ci/tui/streak_counter.rb +0 -29
  228. data/lib/fun_ci/tui/terminal_input.rb +0 -69
@@ -1,169 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunCi
4
- module Animations
5
- module Yay
6
- # YAY! wide banner — white letters, green and blue celebration
7
- # Run: ruby exe/animation-player animations/yay.rb
8
-
9
- w = "\e[1;37m" # bold white (letters only)
10
- g = "\e[1;32m" # bold green
11
- G = "\e[32m" # green
12
- d = "\e[2;32m" # dim green
13
- c = "\e[1;36m" # bold cyan
14
- C = "\e[36m" # cyan
15
- b = "\e[1;34m" # bold blue
16
- B = "\e[34m" # blue
17
- D = "\e[2;37m" # dim white (fading letters)
18
- x = "\e[0m" # reset
19
-
20
- raw_frames = [
21
- # Frame 1 — dark field, spark
22
- [
23
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
24
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
25
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
26
- " #{d}. . . . . . . . . . . . . . . . . #{w}*#{d} . . . . . . . . . . . . . . .#{x}",
27
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
28
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
29
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
30
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
31
- ],
32
- # Frame 2 — middle rows of letters flash in
33
- [
34
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
35
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
36
- " #{d}. . #{G}. . . . .#{d} . #{G}. . . . . . . . . .#{d} . #{G}. . . . .#{d} . . . . . . . .#{x}",
37
- " #{d}. . . #{G}---#{w}#####{G}---#{d} . #{G}--#{w}#########{G}--#{d} . #{G}---#{w}#####{G}---#{d} . . . . . . . .#{x}",
38
- " #{d}. . . . #{G}--#{w}###{G}--#{d} . . #{G}-#{w}#########{G}-#{d} . . #{G}--#{w}###{G}--#{d} . . . . . . . . .#{x}",
39
- " #{d}. . #{G}' ' ' ' '#{d} . #{G}' ' ' ' ' ' ' ' '#{d} . #{G}' ' ' ' '#{d} . . . . . . . .#{x}",
40
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
41
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
42
- ],
43
- # Frame 3 — letters expand vertically
44
- [
45
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
46
- " #{d}. #{G}*#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{d} . . . .#{x}",
47
- " #{d}. . #{w}\\###{d} . #{w}/\\###{d} . #{w}#####{d} . #{w}#####{d} . #{w}\\###{d} . #{w}/\\###{d} . #{w}#####{d} . . . . .#{x}",
48
- " #{d}. . #{G}*#{g} *#{w}#####{g} *#{G} *#{g} *#{w}#########{g} *#{G} *#{g} *#{w}#####{g} *#{G} *#{w}#####{d} . . . .#{x}",
49
- " #{d}. . . . #{G}*#{g} #{w}###{g} *#{G} *#{d} . #{w}###{d} . . . . #{w}###{d} . . #{G}*#{g} #{w}###{g} *#{G} *#{d} . . . . . . .#{x}",
50
- " #{d}. . #{w}/###{d} . . #{w}\\###{d} . #{w}###{d} . . . . #{w}###{d} . #{w}/###{d} . . #{w}\\###{d} . #{w}#####{d} . . . .#{x}",
51
- " #{d}. #{G}'#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{d} . . . .#{x}",
52
- " #{d}. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .#{x}",
53
- ],
54
- # Frame 4 — full letters revealed, green/cyan border
55
- [
56
- " #{g}*#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{x}",
57
- " #{c}*#{G} . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} . . . #{w}###{G} . . . . #{w}###{G} . #{w}#####{G} . #{c}*#{x}",
58
- " #{c}*#{G} . . #{w}###{G} . . #{w}/\\###{G} . #{w}###{G} . . #{w}###{G} . . #{w}###{G} . . #{w}/\\###{G} . #{w}#####{G} . #{c}*#{x}",
59
- " #{c}*#{G} . . . #{w}#####{G} . . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} . . . #{w}#####{G} . #{c}*#{x}",
60
- " #{c}*#{G} . . . . #{w}###{G} . . #{w}#########{G} . . . . #{w}###{G} . . . . . . . . . #{c}*#{x}",
61
- " #{c}*#{G} . . . . #{w}###{G} . . #{w}###{G} . . . . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} . #{c}*#{x}",
62
- " #{c}*#{G} . . . . #{w}###{G} . . #{w}###{G} . . . . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} . #{c}*#{x}",
63
- " #{g}'#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{x}",
64
- ],
65
- # (hold with colour shift)
66
- [
67
- " #{c}*#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{x}",
68
- " #{g}*#{G} . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} . . . #{w}###{G} . . . . #{w}###{G} . #{w}#####{G} . #{g}*#{x}",
69
- " #{g}*#{G} . . #{w}###{G} . . #{w}/\\###{G} . #{w}###{G} . . #{w}###{G} . . #{w}###{G} . . #{w}/\\###{G} . #{w}#####{G} . #{g}*#{x}",
70
- " #{g}*#{G} . . . #{w}#####{G} . . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} . . . #{w}#####{G} . #{g}*#{x}",
71
- " #{g}*#{G} . . . . #{w}###{G} . . #{w}#########{G} . . . . #{w}###{G} . . . . . . . . . #{g}*#{x}",
72
- " #{g}*#{G} . . . . #{w}###{G} . . #{w}###{G} . . . . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} . #{g}*#{x}",
73
- " #{g}*#{G} . . . . #{w}###{G} . . #{w}###{G} . . . . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} . #{g}*#{x}",
74
- " #{c}'#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{c} '#{g} '#{x}",
75
- ],
76
- # Frame 5 — colour explosion, green/blue/cyan ring
77
- [
78
- " #{b}*#{B} *#{b} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{b} *#{B} *#{b} *#{B} *#{x}",
79
- " #{B}*#{g} *#{c} #{w}###{c} *#{g} *#{c} *#{w}###{c} *#{g} *#{c} #{w}#####{c} *#{g} *#{c} #{w}###{c} *#{g} *#{c} *#{w}###{c} * #{w}#####{c} *#{B} *#{x}",
80
- " #{b}*#{c} *#{C} *#{w}###{C} *#{c} #{w}/\\###{c} *#{w}###{C} *#{c} #{w}###{C} *#{c} #{w}###{C} *#{c} #{w}/\\###{C} * #{w}#####{C} *#{b} *#{x}",
81
- " #{g}*#{c} *#{G} *#{g} #{w}#####{g} *#{G} #{w}###{G} *#{g} *#{G} *#{w}###{G} *#{g} *#{w}#####{g} *#{G} *#{w}#####{g} *#{c} *#{x}",
82
- " #{c}*#{g} *#{c} *#{g} *#{w}###{g} *#{c} #{w}#########{c} *#{g} *#{c} #{w}###{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{x}",
83
- " #{b}*#{c} *#{C} *#{g} #{w}###{C} *#{c} *#{w}###{C} *#{c} *#{C} *#{w}###{C} *#{c} *#{C} *#{w}###{C} *#{c} * #{w}#####{C} *#{b} *#{x}",
84
- " #{B}*#{g} *#{c} *#{C} *#{w}###{c} *#{g} *#{w}###{c} *#{g} *#{c} *#{w}###{c} *#{g} *#{c} *#{w}###{c} *#{g} * #{w}#####{c} *#{B} *#{x}",
85
- " #{b}*#{B} *#{b} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{b} *#{B} *#{b} *#{B} *#{x}",
86
- ],
87
- # Frame 6 — FULL CELEBRATION, dense green/blue, white letters
88
- [
89
- " #{b}*#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{b} *#{B} *#{x}",
90
- " #{g}*#{c} *#{g} #{w}###{g} *#{c} *#{g} *#{w}###{g} *#{c} *#{g} #{w}#####{g} *#{c} *#{g} #{w}###{g} *#{c} *#{g} *#{w}###{g} * #{w}#####{g} *#{c} *#{x}",
91
- " #{c}*#{g} *#{c} *#{w}###{c} *#{g} #{w}/\\###{c} *#{w}###{g} *#{c} #{w}###{g} *#{c} #{w}###{c} *#{g} #{w}/\\###{g} * #{w}#####{c} *#{g} *#{x}",
92
- " #{g}*#{c} *#{g} *#{c} #{w}#####{c} *#{g} #{w}###{g} *#{c} *#{g} *#{w}###{g} *#{c} *#{w}#####{c} *#{g} *#{w}#####{c} *#{c} *#{x}",
93
- " #{c}*#{g} *#{c} *#{g} *#{w}###{g} *#{c} #{w}#########{c} *#{g} *#{c} #{w}###{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{x}",
94
- " #{g}*#{c} *#{g} *#{c} #{w}###{c} *#{g} *#{w}###{g} *#{c} *#{g} *#{w}###{g} *#{c} *#{g} *#{w}###{g} *#{c} * #{w}#####{c} *#{c} *#{x}",
95
- " #{c}*#{g} *#{c} *#{g} #{w}###{g} *#{c} *#{w}###{c} *#{g} *#{c} *#{w}###{c} *#{g} *#{c} *#{w}###{g} *#{c} * #{w}#####{g} *#{g} *#{x}",
96
- " #{b}*#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{b} *#{B} *#{x}",
97
- ],
98
- # (hold — green/cyan shimmer)
99
- [
100
- " #{c}*#{g} *#{c} *#{g} *#{b} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{b} *#{g} *#{c} *#{g} *#{c} *#{b} *#{x}",
101
- " #{g}*#{G} *#{c} #{w}###{G} *#{g} *#{G} *#{w}###{g} *#{G} *#{g} #{w}#####{G} *#{g} *#{G} #{w}###{g} *#{G} *#{g} *#{w}###{G} * #{w}#####{g} *#{g} *#{x}",
102
- " #{c}*#{C} *#{g} *#{w}###{g} *#{C} #{w}/\\###{g} *#{w}###{C} *#{g} #{w}###{C} *#{g} #{w}###{g} *#{C} #{w}/\\###{C} * #{w}#####{g} *#{c} *#{x}",
103
- " #{g}*#{G} *#{c} *#{g} #{w}#####{g} *#{G} #{w}###{G} *#{g} *#{G} *#{w}###{G} *#{g} *#{w}#####{g} *#{G} *#{w}#####{g} *#{g} *#{x}",
104
- " #{c}*#{C} *#{g} *#{c} *#{w}###{c} *#{C} #{w}#########{C} *#{c} *#{C} #{w}###{c} *#{C} *#{c} *#{C} *#{c} *#{C} *#{c} *#{x}",
105
- " #{g}*#{G} *#{c} *#{g} #{w}###{g} *#{G} *#{w}###{G} *#{g} *#{G} *#{w}###{G} *#{g} *#{G} *#{w}###{G} *#{g} * #{w}#####{g} *#{g} *#{x}",
106
- " #{c}*#{C} *#{g} *#{c} #{w}###{c} *#{C} *#{w}###{C} *#{c} *#{C} *#{w}###{C} *#{c} *#{C} *#{w}###{c} *#{C} * #{w}#####{c} *#{c} *#{x}",
107
- " #{c}*#{g} *#{c} *#{g} *#{b} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{b} *#{g} *#{c} *#{g} *#{c} *#{b} *#{x}",
108
- ],
109
- # (hold — blue accent shift)
110
- [
111
- " #{B}*#{b} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{b} *#{B} *#{b} *#{B} *#{x}",
112
- " #{b}*#{c} *#{g} #{w}###{c} *#{g} *#{c} *#{w}###{g} *#{c} *#{g} #{w}#####{c} *#{g} *#{c} #{w}###{g} *#{c} *#{g} *#{w}###{c} * #{w}#####{g} *#{b} *#{x}",
113
- " #{g}*#{c} *#{c} *#{w}###{g} *#{c} #{w}/\\###{g} *#{w}###{c} *#{g} #{w}###{c} *#{g} #{w}###{g} *#{c} #{w}/\\###{c} * #{w}#####{g} *#{g} *#{x}",
114
- " #{c}*#{g} *#{g} *#{c} #{w}#####{c} *#{g} #{w}###{g} *#{c} *#{g} *#{w}###{g} *#{c} *#{w}#####{c} *#{g} *#{w}#####{c} *#{c} *#{x}",
115
- " #{g}*#{c} *#{c} *#{g} *#{w}###{g} *#{c} #{w}#########{c} *#{g} *#{c} #{w}###{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{x}",
116
- " #{c}*#{g} *#{g} *#{c} #{w}###{c} *#{g} *#{w}###{g} *#{c} *#{g} *#{w}###{g} *#{c} *#{g} *#{w}###{g} *#{c} * #{w}#####{c} *#{c} *#{x}",
117
- " #{b}*#{c} *#{c} *#{g} #{w}###{g} *#{c} *#{w}###{c} *#{g} *#{c} *#{w}###{c} *#{g} *#{c} *#{w}###{c} *#{g} * #{w}#####{g} *#{b} *#{x}",
118
- " #{B}*#{b} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{b} *#{B} *#{b} *#{B} *#{x}",
119
- ],
120
- # Frame 7 — settling, letters still bright
121
- [
122
- " #{d}. #{b}*#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{b} *#{d} . . .#{x}",
123
- " #{b}*#{c} *#{C} #{w}###{C} *#{c} *#{C} *#{w}###{C} *#{c} *#{C} #{w}#####{C} *#{c} *#{C} #{w}###{c} *#{C} *#{c} *#{w}###{C} * #{w}#####{c} *#{b} *#{x}",
124
- " #{g}*#{G} *#{g} *#{w}###{g} *#{G} #{w}/\\###{g} *#{w}###{G} *#{g} #{w}###{G} *#{g} #{w}###{g} *#{G} #{w}/\\###{G} * #{w}#####{g} *#{g} *#{x}",
125
- " #{c}*#{C} *#{g} *#{c} #{w}#####{c} *#{C} #{w}###{C} *#{c} *#{C} *#{w}###{C} *#{c} *#{w}#####{c} *#{C} *#{w}#####{c} *#{c} *#{x}",
126
- " #{g}*#{G} *#{c} *#{g} *#{w}###{g} *#{G} #{w}#########{G} *#{g} *#{G} #{w}###{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{x}",
127
- " #{c}*#{C} *#{g} *#{c} #{w}###{C} *#{c} *#{w}###{C} *#{c} *#{C} *#{w}###{C} *#{c} *#{C} *#{w}###{C} *#{c} * #{w}#####{C} *#{c} *#{x}",
128
- " #{b}*#{c} *#{C} *#{g} #{w}###{C} *#{c} *#{w}###{C} *#{c} *#{C} *#{w}###{C} *#{c} *#{C} *#{w}###{c} *#{C} * #{w}#####{C} *#{b} *#{x}",
129
- " #{d}. #{b}*#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{g} *#{c} *#{b} *#{d} . . .#{x}",
130
- ],
131
- # Frame 8 — fading, letters on green field
132
- [
133
- " #{d}. . #{g}*#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{d} . . .#{x}",
134
- " #{d}. #{g}*#{G} . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} . . . #{w}###{G} . . . . #{w}###{G} . #{w}#####{G} .#{g} *#{d} .#{x}",
135
- " #{d}. #{g}*#{G} . . #{w}###{G} . . #{w}/\\###{G} . #{w}###{G} . . #{w}###{G} . . #{w}###{G} . . #{w}/\\###{G} . #{w}#####{G} .#{g} *#{d} .#{x}",
136
- " #{d}. . #{g}*#{G} . . #{w}#####{G} . . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} . . . #{w}#####{G} .#{g} *#{d} .#{x}",
137
- " #{d}. . #{g}*#{G} . . . #{w}###{G} . . #{w}#########{G} . . . . #{w}###{G} . . . . . . . . . .#{g} *#{d} .#{x}",
138
- " #{d}. #{g}*#{G} . . . #{w}###{G} . . #{w}###{G} . . . . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} .#{g} *#{d} .#{x}",
139
- " #{d}. #{g}*#{G} . . . #{w}###{G} . . #{w}###{G} . . . . #{w}###{G} . . . . #{w}###{G} . . . #{w}#####{G} .#{g} *#{d} .#{x}",
140
- " #{d}. . #{g}*#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{d} . . .#{x}",
141
- ],
142
- # Frame 9 — almost gone, dim letters
143
- [
144
- " #{d}. . . . #{G}*#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{G} *#{g} *#{d} . . . . .#{x}",
145
- " #{d}. . . #{G}. #{D}###{G} . . . . #{D}###{G} . . . #{D}#####{G} . . . #{D}###{G} . . . . #{D}###{G} . #{D}#####{G} . . .#{x}",
146
- " #{d}. . . . #{D}\\###{d} . . #{D}/\\###{d} . #{D}###{d} . . #{D}###{d} . . #{D}\\###{d} . . #{D}/\\###{d} . #{D}#####{d} . . .#{x}",
147
- " #{d}. . . . . #{D}#####{d} . . #{D}###{d} . . . . #{D}###{d} . . . #{D}#####{d} . . . #{D}#####{d} . . .#{x}",
148
- " #{d}. . . . . . #{D}###{d} . . #{D}#########{d} . . . . #{D}###{d} . . . . . . . . . . . .#{x}",
149
- " #{d}. . . . . #{D}###{d} . . #{D}###{d} . . . . #{D}###{d} . . . . #{D}###{d} . . . #{D}#####{d} . . . .#{x}",
150
- " #{d}. . . #{G}. #{D}\\###{G} . . #{D}\\###{G} . #{D}###{G} . . #{D}###{G} . #{D}\\###{G} . . #{D}\\###{G} . #{D}#####{G} . . .#{x}",
151
- " #{d}. . . . #{G}'#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{g} '#{G} '#{g} '#{d} . . . . .#{x}",
152
- ],
153
- ]
154
-
155
- # Pad lines so all rows in each frame have equal stripped length —
156
- # prevents the player's per-line centering from misaligning the art.
157
- max = raw_frames.flatten.map { |l| l.gsub(/\e\[[0-9;]*m/, "").length }.max
158
- raw_frames.each do |frame|
159
- frame.map! { |l| l + " " * (max - l.gsub(/\e\[[0-9;]*m/, "").length) }
160
- end
161
-
162
- DATA = {
163
- name: "YAY!",
164
- fps: 8,
165
- frames: raw_frames
166
- }.freeze
167
- end
168
- end
169
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunCi
4
- module Persistence
5
- class StateMachine
6
- class InvalidTransition < StandardError; end
7
-
8
- STATES = %i[scheduled running completed failed timed_out cancelled].freeze
9
-
10
- TRANSITIONS = {
11
- scheduled: %i[running cancelled],
12
- running: %i[completed failed timed_out cancelled],
13
- completed: [],
14
- failed: [],
15
- timed_out: [],
16
- cancelled: []
17
- }.freeze
18
-
19
- TERMINAL_STATES = %i[completed failed timed_out cancelled].freeze
20
-
21
- attr_reader :current_state
22
-
23
- def initialize(initial_state)
24
- validate_state!(initial_state)
25
- @current_state = initial_state
26
- end
27
-
28
- def transition_to!(new_state)
29
- validate_state!(new_state)
30
- unless TRANSITIONS.fetch(@current_state).include?(new_state)
31
- raise InvalidTransition,
32
- "Cannot transition from #{@current_state} to #{new_state}"
33
- end
34
- @current_state = new_state
35
- end
36
-
37
- def terminal?
38
- TERMINAL_STATES.include?(@current_state)
39
- end
40
-
41
- private
42
-
43
- def validate_state!(state)
44
- return if STATES.include?(state)
45
-
46
- raise ArgumentError, "Unknown state: #{state}. Valid states: #{STATES.join(", ")}"
47
- end
48
- end
49
- end
50
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunCi
4
- module Pipeline
5
- class BackgroundWrapper
6
- def initialize(recorder:, job_id:, executor:)
7
- @recorder = recorder
8
- @job_id = job_id
9
- @executor = executor
10
- end
11
-
12
- def run
13
- _output, status, timed_out = @executor.call
14
- if timed_out
15
- @recorder.end_stage(@job_id, "timed_out")
16
- @recorder.fail_run
17
- elsif status.success?
18
- @recorder.end_stage(@job_id, "completed")
19
- @recorder.complete_run
20
- else
21
- @recorder.end_stage(@job_id, "failed")
22
- @recorder.fail_run
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "board_data"
4
- require_relative "board_renderer"
5
- require_relative "key_handler"
6
- require_relative "screen"
7
- require_relative "spinner"
8
- require_relative "terminal_input"
9
- require "io/console"
10
-
11
- module FunCi
12
- module Tui
13
- class AdminTui
14
- FAST_REFRESH = 0.1
15
- SLOW_REFRESH = 5.0
16
-
17
- def initialize(db:, output: $stdout, input: $stdin, width: 80,
18
- width_provider: nil, height_provider: nil,
19
- page_size: nil, animation_renderer: nil)
20
- @board_data = BoardData.new(db, page_size: page_size)
21
- @terminal_input = TerminalInput.new(input: input, width_provider: width_provider)
22
- @key_handler = KeyHandler.new(board_data: @board_data)
23
- @renderer = BoardRenderer.new(
24
- screen: Screen.new(output: output, width: width),
25
- spinner: Spinner.new,
26
- animation_renderer: animation_renderer,
27
- height_provider: height_provider
28
- )
29
- end
30
-
31
- def render_once
32
- update_width_from_provider
33
- @renderer.render(
34
- runs: @board_data.runs,
35
- streak: @board_data.streak,
36
- cursor_index: @key_handler.cursor_index,
37
- confirming: @key_handler.confirming?
38
- )
39
- end
40
-
41
- def run
42
- @renderer.clear
43
-
44
- begin
45
- @terminal_input.setup_raw_mode
46
- @terminal_input.setup_signal_trap
47
- loop do
48
- @renderer.begin_frame
49
- render_once
50
- key = @terminal_input.read_key_with_timeout(refresh_interval)
51
- break if key && @key_handler.handle_key(key) == :quit
52
- end
53
- ensure
54
- @terminal_input.restore_terminal
55
- end
56
- end
57
-
58
- def resize(new_width)
59
- @renderer.resize(new_width)
60
- end
61
-
62
- def confirming?
63
- @key_handler.confirming?
64
- end
65
-
66
- def confirmation_run
67
- @key_handler.confirmation_run
68
- end
69
-
70
- def handle_key(key)
71
- @key_handler.handle_key(key)
72
- end
73
-
74
- private
75
-
76
- def refresh_interval
77
- return FAST_REFRESH if @renderer.animation_renderer?
78
-
79
- runs = @board_data.runs
80
- any_running = runs.any? { |r| r[:status] == "running" }
81
- any_running ? FAST_REFRESH : SLOW_REFRESH
82
- end
83
-
84
- def update_width_from_provider
85
- new_width = @terminal_input.check_width
86
- @renderer.resize(new_width) if new_width
87
- end
88
- end
89
- end
90
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunCi
4
- module Tui
5
- class Animation
6
- TYPES = {
7
- failure: { total_frames: 40, priority: 3 },
8
- timeout: { total_frames: 4, priority: 2 },
9
- success: { total_frames: 40, priority: 1 },
10
- stage_pass: { total_frames: 3, priority: 0 }
11
- }.freeze
12
-
13
- attr_reader :type, :run_id, :stage, :frame
14
-
15
- def initialize(type:, run_id:, stage: nil)
16
- raise ArgumentError, "Unknown animation type: #{type}" unless TYPES.key?(type)
17
-
18
- @type = type
19
- @run_id = run_id
20
- @stage = stage
21
- @frame = 0
22
- end
23
-
24
- def advance!
25
- @frame += 1
26
- end
27
-
28
- def finished?
29
- frame >= total_frames
30
- end
31
-
32
- def total_frames
33
- TYPES[@type][:total_frames]
34
- end
35
-
36
- def priority
37
- TYPES[@type][:priority]
38
- end
39
-
40
- def has_header?
41
- %i[failure timeout success].include?(@type)
42
- end
43
-
44
- def has_footer?
45
- %i[failure success].include?(@type)
46
- end
47
- end
48
- end
49
- end
@@ -1,107 +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 Tui
10
- module AnimationCompositor
11
- RESET = Ansi::RESET
12
- STAGE_STAGGER = { "lint" => 0, "build" => 2, "fast" => 4, "slow" => 6 }.freeze
13
-
14
- def self.header_overlay(animation, width)
15
- frames = case animation.type
16
- when :failure then AnimationFrames.failure_header(width)
17
- when :success then AnimationFrames.success_header(width)
18
- when :timeout then AnimationFrames.timeout_header(width)
19
- else return nil
20
- end
21
- frame_at(frames, animation.frame)
22
- end
23
-
24
- def self.footer_overlay(animation, width)
25
- frames = case animation.type
26
- when :failure
27
- AnimationFrames.failure_footer(animation.stage || "stage", width)
28
- when :success then AnimationFrames.success_footer(width)
29
- else return nil
30
- end
31
- frame_at(frames, animation.frame)
32
- end
33
-
34
- def self.stage_column_overlay(animation, stage_text, col)
35
- case animation.type
36
- when :stage_pass then color_flash(animation, stage_text, AnimationFrames.stage_pass_colors)
37
- when :timeout then color_flash(animation, stage_text, AnimationFrames.timeout_colors)
38
- when :failure then failure_flanks(animation, stage_text)
39
- when :success then sparkle_sweep(animation, stage_text)
40
- end
41
- end
42
-
43
- def self.stage_text_for(run, stage_name)
44
- stage = run[:stages]&.find { |s| s[:stage] == stage_name }
45
- return nil unless stage
46
-
47
- name = RowFormatter::STAGE_NAMES[stage[:stage]] || stage[:stage]
48
- dur = stage[:duration] ? DurationFormatter.format(stage[:duration]) : "--"
49
- case stage[:status]
50
- when "completed" then "#{name} #{dur}"
51
- when "failed" then "#{name} FAIL #{dur}"
52
- when "timed_out" then "#{name} TIMEOUT #{dur}"
53
- else "#{name} --"
54
- end
55
- end
56
-
57
- def self.stage_col_for(run, stage_name)
58
- project = run[:project_path] ? " #{File.basename(run[:project_path])}" : ""
59
- prefix_len = 2 + 7 + 2 + run[:branch].to_s.length + project.length + 2
60
- (run[:stages] || []).each do |s|
61
- return prefix_len + 1 if s[:stage] == stage_name
62
- prefix_len += stage_text_for(run, s[:stage]).to_s.length + 2
63
- end
64
- nil
65
- end
66
-
67
- def self.color_flash(animation, stage_text, colors)
68
- idx = [animation.frame, colors.length - 1].min
69
- "#{colors[idx]}#{Ansi.strip(stage_text)}#{RESET}"
70
- end
71
- private_class_method :color_flash
72
-
73
- def self.failure_flanks(animation, stage_text)
74
- particles = AnimationFrames.failure_particles
75
- return nil if animation.frame >= particles.length
76
-
77
- p = particles[animation.frame]
78
- plain = Ansi.strip(stage_text)
79
- return plain if p[:chars].empty?
80
-
81
- left = p[:chars].reverse
82
- right = p[:chars]
83
- "#{p[:color]}#{left}#{RESET} #{Ansi.bold_red(plain)} #{p[:color]}#{right}#{RESET}"
84
- end
85
- private_class_method :failure_flanks
86
-
87
- def self.sparkle_sweep(animation, stage_text)
88
- plain = Ansi.strip(stage_text)
89
- stagger = STAGE_STAGGER[animation.stage] || 0
90
- pos = animation.frame - stagger
91
- return nil if pos < 0 || pos > plain.length
92
-
93
- result = plain.chars.map { |c| "\e[32m#{c}" }
94
- result[pos] = "\e[1;33m#{plain[pos]}" if pos < plain.length
95
- "#{result.join}#{RESET}"
96
- end
97
- private_class_method :sparkle_sweep
98
-
99
- def self.frame_at(frames, index)
100
- return nil if index >= frames.length
101
-
102
- frames[index]
103
- end
104
- private_class_method :frame_at
105
- end
106
- end
107
- end
@@ -1,112 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "ansi"
4
-
5
- module FunCi
6
- module Tui
7
- module AnimationFrames
8
- RESET = Ansi::RESET
9
- FOOTER_HOLD = 8
10
-
11
- def self.failure_particles
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
112
- end
@@ -1,46 +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 Tui
14
- module AnimationLibrary
15
- FAILURE = [Animations::Explosion::DATA].freeze
16
-
17
- SUCCESS = [
18
- Animations::Success::DATA,
19
- Animations::Celebrate::DATA,
20
- Animations::Flash::DATA,
21
- Animations::Leprechauns::DATA,
22
- Animations::Yay::DATA
23
- ].freeze
24
-
25
- IDLE = Animations::Idle::DATA
26
-
27
- RUNNING = Animations::Running::DATA
28
-
29
- def self.random_failure
30
- FAILURE.sample
31
- end
32
-
33
- def self.random_success
34
- SUCCESS.sample
35
- end
36
-
37
- def self.idle
38
- IDLE
39
- end
40
-
41
- def self.running
42
- RUNNING
43
- end
44
- end
45
- end
46
- end