fun_ci 1.0.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 +34 -0
  3. data/README.md +3 -1
  4. data/exe/fun-ci-trigger +6 -6
  5. data/exe/fun-ci-tui +9 -4
  6. data/lib/fun_ci/animations/celebrate.rb +210 -0
  7. data/lib/fun_ci/animations/explosion.rb +138 -0
  8. data/lib/fun_ci/animations/flash.rb +205 -0
  9. data/lib/fun_ci/animations/idle.rb +127 -0
  10. data/lib/fun_ci/animations/leprechauns.rb +240 -0
  11. data/lib/fun_ci/animations/running.rb +129 -0
  12. data/lib/fun_ci/animations/success.rb +281 -0
  13. data/lib/fun_ci/animations/yay.rb +169 -0
  14. data/lib/fun_ci/cli.rb +20 -14
  15. data/lib/fun_ci/persistence/database.rb +52 -0
  16. data/lib/fun_ci/persistence/pipeline_recorder.rb +74 -0
  17. data/lib/fun_ci/persistence/pipeline_run.rb +59 -0
  18. data/lib/fun_ci/persistence/stage_job.rb +46 -0
  19. data/lib/fun_ci/{state_machine.rb → persistence/state_machine.rb} +3 -12
  20. data/lib/fun_ci/pipeline/background_wrapper.rb +27 -0
  21. data/lib/fun_ci/pipeline/pipeline_forker.rb +38 -0
  22. data/lib/fun_ci/pipeline/process_runner.rb +28 -0
  23. data/lib/fun_ci/pipeline/progress_reporter.rb +31 -0
  24. data/lib/fun_ci/pipeline/stage_runner.rb +85 -0
  25. data/lib/fun_ci/pipeline/stale_pipeline_canceller.rb +53 -0
  26. data/lib/fun_ci/pipeline/trigger.rb +153 -0
  27. data/lib/fun_ci/setup/hook_writer.rb +75 -0
  28. data/lib/fun_ci/setup/installer.rb +55 -0
  29. data/lib/fun_ci/setup/maven_linter_detector.rb +26 -0
  30. data/lib/fun_ci/setup/project_config.rb +42 -0
  31. data/lib/fun_ci/setup/project_detector.rb +22 -0
  32. data/lib/fun_ci/setup/setup_checker.rb +30 -0
  33. data/lib/fun_ci/setup/template_writer.rb +53 -0
  34. data/lib/fun_ci/tui/admin_tui.rb +90 -0
  35. data/lib/fun_ci/tui/animation.rb +49 -0
  36. data/lib/fun_ci/tui/animation_compositor.rb +107 -0
  37. data/lib/fun_ci/tui/animation_frames.rb +112 -0
  38. data/lib/fun_ci/tui/animation_library.rb +46 -0
  39. data/lib/fun_ci/tui/animation_renderer.rb +144 -0
  40. data/lib/fun_ci/tui/ansi.rb +34 -0
  41. data/lib/fun_ci/tui/board_data.rb +53 -0
  42. data/lib/fun_ci/tui/board_renderer.rb +105 -0
  43. data/lib/fun_ci/tui/duration_formatter.rb +24 -0
  44. data/lib/fun_ci/tui/header_animation_manager.rb +71 -0
  45. data/lib/fun_ci/tui/header_animation_player.rb +45 -0
  46. data/lib/fun_ci/tui/key_handler.rb +86 -0
  47. data/lib/fun_ci/tui/looping_animation_player.rb +45 -0
  48. data/lib/fun_ci/tui/relative_time.rb +22 -0
  49. data/lib/fun_ci/tui/row_formatter.rb +108 -0
  50. data/lib/fun_ci/tui/screen.rb +103 -0
  51. data/lib/fun_ci/tui/spinner.rb +24 -0
  52. data/lib/fun_ci/tui/stage_change_detector.rb +58 -0
  53. data/lib/fun_ci/tui/streak_counter.rb +29 -0
  54. data/lib/fun_ci/tui/terminal_input.rb +69 -0
  55. data/lib/fun_ci.rb +6 -6
  56. metadata +49 -28
  57. data/lib/fun_ci/admin_tui.rb +0 -226
  58. data/lib/fun_ci/ansi.rb +0 -21
  59. data/lib/fun_ci/background_wrapper.rb +0 -27
  60. data/lib/fun_ci/board_data.rb +0 -51
  61. data/lib/fun_ci/database.rb +0 -50
  62. data/lib/fun_ci/duration_formatter.rb +0 -23
  63. data/lib/fun_ci/hook_writer.rb +0 -73
  64. data/lib/fun_ci/installer.rb +0 -53
  65. data/lib/fun_ci/maven_linter_detector.rb +0 -24
  66. data/lib/fun_ci/pipeline_forker.rb +0 -36
  67. data/lib/fun_ci/pipeline_recorder.rb +0 -72
  68. data/lib/fun_ci/pipeline_run.rb +0 -57
  69. data/lib/fun_ci/progress_reporter.rb +0 -29
  70. data/lib/fun_ci/project_config.rb +0 -40
  71. data/lib/fun_ci/project_detector.rb +0 -18
  72. data/lib/fun_ci/relative_time.rb +0 -20
  73. data/lib/fun_ci/row_formatter.rb +0 -106
  74. data/lib/fun_ci/screen.rb +0 -81
  75. data/lib/fun_ci/setup_checker.rb +0 -28
  76. data/lib/fun_ci/spinner.rb +0 -22
  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
@@ -1,226 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "board_data"
4
- require_relative "row_formatter"
5
- require_relative "screen"
6
- require_relative "spinner"
7
- require_relative "streak_counter"
8
- require "io/console"
9
-
10
- module FunCi
11
- class AdminTui
12
- FAST_REFRESH = 0.1 # seconds (spinner + timer)
13
- SLOW_REFRESH = 5.0 # seconds (settled board)
14
-
15
- def initialize(db:, output: $stdout, input: $stdin, width: 80, width_provider: nil, page_size: nil)
16
- @board_data = BoardData.new(db, page_size: page_size)
17
- @screen = Screen.new(output: output, width: width)
18
- @output = output
19
- @input = input
20
- @width_provider = width_provider
21
- @spinner = Spinner.new
22
- @cursor_index = nil
23
- @running = false
24
- @confirm_cancel = nil
25
- end
26
-
27
- def render_once
28
- update_width_from_provider
29
- runs = @board_data.runs
30
- streak = @board_data.streak
31
- streak_text = StreakCounter.format_text(streak)
32
-
33
- @screen.render_header(streak_text: streak_text)
34
- @screen.println
35
-
36
- if runs.empty?
37
- @screen.render_empty_state
38
- @screen.render_footer(empty: true)
39
- else
40
- rows = runs.map { |run| format_run(run) }
41
- @screen.render_board(rows, cursor_index: @cursor_index)
42
- @screen.println
43
- @screen.render_footer(empty: false, confirming: confirming?)
44
- end
45
-
46
- @screen.clear_below
47
- end
48
-
49
- def run
50
- @running = true
51
- @screen.clear
52
-
53
- begin
54
- setup_raw_mode
55
- setup_sigwinch_trap
56
- loop do
57
- render_frame
58
- break unless @running
59
- key = read_key_with_timeout(refresh_interval)
60
- handle_key(key) if key
61
- end
62
- ensure
63
- restore_terminal
64
- end
65
- end
66
-
67
- def resize(new_width)
68
- @screen.width = new_width
69
- end
70
-
71
- def confirming?
72
- !@confirm_cancel.nil?
73
- end
74
-
75
- def confirmation_run
76
- @confirm_cancel
77
- end
78
-
79
- def handle_key(key)
80
- if @confirm_cancel
81
- handle_confirm_key(key)
82
- return
83
- end
84
-
85
- case key
86
- when "q"
87
- @running = false
88
- when "j", :down
89
- move_cursor_down
90
- when "k", :up
91
- move_cursor_up
92
- when "c"
93
- initiate_cancel
94
- end
95
- end
96
-
97
- private
98
-
99
- def format_run(run)
100
- opts = {}
101
- if run[:status] == "running"
102
- active_stage = run[:stages].find { |s| s[:status] == "running" }
103
- if active_stage && active_stage[:started_at]
104
- opts[:elapsed_seconds] = Time.now - Time.parse(active_stage[:started_at])
105
- end
106
- opts[:spinner_frame] = @spinner.current_frame
107
- end
108
- RowFormatter.format(run, **opts)
109
- end
110
-
111
- def render_frame
112
- @screen.move_cursor_home
113
- @spinner.advance!
114
- render_once
115
- end
116
-
117
- def refresh_interval
118
- runs = @board_data.runs
119
- any_running = runs.any? { |r| r[:status] == "running" }
120
- any_running ? FAST_REFRESH : SLOW_REFRESH
121
- end
122
-
123
- def move_cursor_down
124
- runs = @board_data.runs
125
- return if runs.empty?
126
-
127
- if @cursor_index.nil?
128
- @cursor_index = 0
129
- elsif @cursor_index < runs.length - 1
130
- @cursor_index += 1
131
- @board_data.load_more if @cursor_index == runs.length - 1
132
- end
133
- end
134
-
135
- def move_cursor_up
136
- return if @cursor_index.nil?
137
-
138
- if @cursor_index > 0
139
- @cursor_index -= 1
140
- end
141
- end
142
-
143
- def initiate_cancel
144
- return if @cursor_index.nil?
145
-
146
- runs = @board_data.runs
147
- return if @cursor_index >= runs.length
148
-
149
- run = runs[@cursor_index]
150
- case run[:status]
151
- when "scheduled"
152
- @board_data.cancel_run(run[:id])
153
- when "running"
154
- @confirm_cancel = run
155
- end
156
- end
157
-
158
- def handle_confirm_key(key)
159
- case key
160
- when "y"
161
- @board_data.cancel_run(@confirm_cancel[:id])
162
- @confirm_cancel = nil
163
- when "n", :escape
164
- @confirm_cancel = nil
165
- end
166
- end
167
-
168
- def update_width_from_provider
169
- return unless @width_provider
170
-
171
- new_width = @width_provider.call
172
- resize(new_width) if new_width
173
- end
174
-
175
- def setup_sigwinch_trap
176
- return unless @width_provider
177
-
178
- @signal_read, @signal_write = IO.pipe
179
- Signal.trap("WINCH") do
180
- @signal_write.write_nonblock(".") rescue nil
181
- end
182
- end
183
-
184
- def setup_raw_mode
185
- @input.raw! if @input.respond_to?(:raw!)
186
- rescue Errno::ENOTTY
187
- # Not a terminal (testing)
188
- end
189
-
190
- def restore_terminal
191
- @input.cooked! if @input.respond_to?(:cooked!)
192
- rescue Errno::ENOTTY
193
- # Not a terminal
194
- end
195
-
196
- def read_key_with_timeout(timeout)
197
- return nil unless @input.respond_to?(:read_nonblock)
198
-
199
- watched = [@input]
200
- watched << @signal_read if @signal_read
201
- ready = IO.select(watched, nil, nil, timeout)
202
- return nil unless ready
203
-
204
- # Drain signal pipe if it woke us
205
- if @signal_read && ready[0].include?(@signal_read)
206
- @signal_read.read_nonblock(1) rescue nil
207
- return nil unless ready[0].include?(@input)
208
- end
209
-
210
- byte = @input.read_nonblock(1)
211
- if byte == "\e"
212
- # Escape sequence
213
- seq = @input.read_nonblock(2) rescue ""
214
- case seq
215
- when "[A" then :up
216
- when "[B" then :down
217
- else :escape
218
- end
219
- else
220
- byte
221
- end
222
- rescue IO::WaitReadable, EOFError
223
- nil
224
- end
225
- end
226
- end
data/lib/fun_ci/ansi.rb DELETED
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunCi
4
- module Ansi
5
- RESET = "\e[0m"
6
-
7
- def self.green(text) = "\e[32m#{text}#{RESET}"
8
- def self.bold_green(text) = "\e[1;32m#{text}#{RESET}"
9
- def self.bold_red(text) = "\e[1;31m#{text}#{RESET}"
10
- def self.bold_yellow(text) = "\e[1;33m#{text}#{RESET}"
11
- def self.cyan(text) = "\e[36m#{text}#{RESET}"
12
- def self.bold_cyan(text) = "\e[1;36m#{text}#{RESET}"
13
- def self.dim(text) = "\e[2m#{text}#{RESET}"
14
- def self.white(text) = "\e[37m#{text}#{RESET}"
15
- def self.bg_charcoal(text) = "\e[48;5;236m#{text}#{RESET}"
16
-
17
- def self.strip(text)
18
- text.gsub(/\e\[[0-9;]*[A-Za-z]/, "")
19
- end
20
- end
21
- end
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "timeout"
4
-
5
- module FunCi
6
- class BackgroundWrapper
7
- def initialize(recorder:, job_id:, executor:)
8
- @recorder = recorder
9
- @job_id = job_id
10
- @executor = executor
11
- end
12
-
13
- def run
14
- _output, status = @executor.call
15
- if status.success?
16
- @recorder.end_stage(@job_id, "completed")
17
- @recorder.complete_run
18
- else
19
- @recorder.end_stage(@job_id, "failed")
20
- @recorder.fail_run
21
- end
22
- rescue Timeout::Error
23
- @recorder.end_stage(@job_id, "timed_out")
24
- @recorder.fail_run
25
- end
26
- end
27
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "time"
4
- require_relative "pipeline_run"
5
- require_relative "stage_job"
6
- require_relative "streak_counter"
7
-
8
- module FunCi
9
- class BoardData
10
- def initialize(db, limit: 15, page_size: nil)
11
- @db = db
12
- @page_size = page_size || limit
13
- @limit = @page_size
14
- end
15
-
16
- def load_more
17
- @limit += @page_size
18
- end
19
-
20
- def runs
21
- pipeline_runs = PipelineRun.recent(@db, limit: @limit)
22
- pipeline_runs.map { |run| enrich_with_stages(run) }
23
- end
24
-
25
- def streak
26
- pipeline_runs = PipelineRun.recent(@db, limit: @limit)
27
- StreakCounter.count(pipeline_runs)
28
- end
29
-
30
- def cancel_run(run_id)
31
- PipelineRun.update_status(@db, run_id, "cancelled")
32
- end
33
-
34
- private
35
-
36
- def enrich_with_stages(run)
37
- rows = @db.execute(
38
- "SELECT id, pipeline_run_id, stage, status, started_at, completed_at FROM stage_jobs WHERE pipeline_run_id = ? ORDER BY id",
39
- [run[:id]]
40
- )
41
-
42
- stages = rows.map do |row|
43
- job = { id: row[0], pipeline_run_id: row[1], stage: row[2], status: row[3], started_at: row[4], completed_at: row[5] }
44
- duration = StageJob.elapsed_duration(job)
45
- { stage: job[:stage], status: job[:status], duration: duration, started_at: job[:started_at] }
46
- end
47
-
48
- run.merge(stages: stages)
49
- end
50
- end
51
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "sqlite3"
4
-
5
- module FunCi
6
- module Database
7
- def self.connection(db_path)
8
- db = SQLite3::Database.new(db_path)
9
- db.busy_timeout = 5000
10
- db.execute("PRAGMA journal_mode=WAL")
11
- db.execute("PRAGMA foreign_keys=ON")
12
- db
13
- end
14
-
15
- def self.migrate!(db)
16
- db.execute(<<~SQL)
17
- CREATE TABLE IF NOT EXISTS pipeline_runs (
18
- id INTEGER PRIMARY KEY,
19
- commit_hash TEXT,
20
- branch TEXT,
21
- status TEXT DEFAULT 'scheduled',
22
- pid INTEGER,
23
- created_at TEXT,
24
- updated_at TEXT
25
- )
26
- SQL
27
-
28
- add_column_if_missing(db, "pipeline_runs", "pid", "INTEGER")
29
- add_column_if_missing(db, "pipeline_runs", "project_path", "TEXT")
30
-
31
- db.execute(<<~SQL)
32
- CREATE TABLE IF NOT EXISTS stage_jobs (
33
- id INTEGER PRIMARY KEY,
34
- pipeline_run_id INTEGER REFERENCES pipeline_runs(id),
35
- stage TEXT,
36
- status TEXT DEFAULT 'scheduled',
37
- started_at TEXT,
38
- completed_at TEXT
39
- )
40
- SQL
41
- end
42
-
43
- def self.add_column_if_missing(db, table, column, type)
44
- columns = db.execute("PRAGMA table_info(#{table})").map { |row| row[1] }
45
- return if columns.include?(column)
46
- db.execute("ALTER TABLE #{table} ADD COLUMN #{column} #{type}")
47
- end
48
- private_class_method :add_column_if_missing
49
- end
50
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunCi
4
- module DurationFormatter
5
- def self.format(seconds)
6
- if seconds >= 60
7
- mins = (seconds / 60).to_i
8
- secs = (seconds % 60).to_i
9
- "#{mins}m#{secs.to_s.rjust(2, "0")}"
10
- elsif seconds == seconds.to_i
11
- "#{seconds.to_i}s"
12
- else
13
- "#{format_decimal(seconds)}s"
14
- end
15
- end
16
-
17
- def self.format_decimal(value)
18
- # Show one decimal place, remove trailing zeros
19
- sprintf("%.1f", value)
20
- end
21
- private_class_method :format_decimal
22
- end
23
- end
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "fileutils"
4
-
5
- module FunCi
6
- class HookWriter
7
- ALLOWED_HOOKS = %w[pre-commit pre-push].freeze
8
- MARKER = "# fun-ci-managed-hook"
9
-
10
- HOOK_COMMANDS = {
11
- "pre-commit" => "fun-ci trigger --no-validate",
12
- "pre-push" => "fun-ci trigger"
13
- }.freeze
14
-
15
- HOOK_TEMPLATE = <<~SH
16
- #!/bin/sh
17
- #{MARKER}
18
- COMMIT=$(git rev-parse HEAD 2>/dev/null || echo "0000000000000000000000000000000000000000")
19
- BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
20
- %<command>s "$COMMIT" "$BRANCH"
21
- SH
22
-
23
- def self.run(project_root:, hook_type:, stdout: $stdout)
24
- new(project_root: project_root, hook_type: hook_type, stdout: stdout).run
25
- end
26
-
27
- def initialize(project_root:, hook_type:, stdout:)
28
- @project_root = project_root
29
- @hook_type = hook_type
30
- @stdout = stdout
31
- end
32
-
33
- def run
34
- return reject("Not a git repository — no .git/ found.") unless git_repo?
35
- return reject("Unknown hook type: #{@hook_type}") unless ALLOWED_HOOKS.include?(@hook_type)
36
- return skip("Hook #{@hook_type} already exists from another tool — skipping.") if foreign_hook?
37
-
38
- write_hook
39
- @stdout.puts "Installed #{@hook_type} hook."
40
- 0
41
- end
42
-
43
- private
44
-
45
- def git_repo?
46
- Dir.exist?(File.join(@project_root, ".git"))
47
- end
48
-
49
- def hook_path
50
- File.join(@project_root, ".git", "hooks", @hook_type)
51
- end
52
-
53
- def foreign_hook?
54
- File.exist?(hook_path) && !File.read(hook_path).include?(MARKER)
55
- end
56
-
57
- def write_hook
58
- FileUtils.mkdir_p(File.join(@project_root, ".git", "hooks"))
59
- File.write(hook_path, format(HOOK_TEMPLATE, command: HOOK_COMMANDS[@hook_type]))
60
- File.chmod(0o755, hook_path)
61
- end
62
-
63
- def reject(message)
64
- @stdout.puts message
65
- 1
66
- end
67
-
68
- def skip(message)
69
- @stdout.puts message
70
- 0
71
- end
72
- end
73
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "project_detector"
4
- require_relative "template_writer"
5
- require_relative "maven_linter_detector"
6
-
7
- module FunCi
8
- class Installer
9
- def self.run(project_root:, stdout: $stdout, pom_reader: nil)
10
- new(project_root: project_root, stdout: stdout, pom_reader: pom_reader).run
11
- end
12
-
13
- def initialize(project_root:, stdout:, pom_reader: nil)
14
- @project_root = project_root
15
- @stdout = stdout
16
- @pom_reader = pom_reader || ->(path) { File.read(path) }
17
- end
18
-
19
- def run
20
- if Dir.exist?(File.join(@project_root, ".fun-ci"))
21
- @stdout.puts ".fun-ci/ already exists — skipping init."
22
- return 0
23
- end
24
-
25
- filenames = Dir.children(@project_root)
26
- detected = ProjectDetector.new(filenames).detect
27
-
28
- if detected == :unknown
29
- @stdout.puts "Could not detect project type. Create .fun-ci/ manually."
30
- return 1
31
- end
32
-
33
- @stdout.puts "Detected: #{detected.to_s.tr("_", " ")}"
34
-
35
- lint_override = detect_maven_linter(detected)
36
- TemplateWriter.new(detected, @project_root, lint_override: lint_override).write
37
-
38
- @stdout.puts "Created .fun-ci/ with template scripts."
39
- 0
40
- end
41
-
42
- private
43
-
44
- def detect_maven_linter(detected)
45
- return nil unless detected == :jvm_maven
46
-
47
- pom_path = File.join(@project_root, "pom.xml")
48
- pom_content = @pom_reader.call(pom_path)
49
- command = MavenLinterDetector.new(pom_content).lint_command
50
- command == MavenLinterDetector::DEFAULT_COMMAND ? nil : command
51
- end
52
- end
53
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module FunCi
4
- class MavenLinterDetector
5
- LINTERS = [
6
- { artifact_id: "detekt-maven-plugin", command: "mvn detekt:check" },
7
- { artifact_id: "ktlint-maven-plugin", command: "mvn ktlint:check" },
8
- { artifact_id: "maven-checkstyle-plugin", command: "mvn checkstyle:check" },
9
- { artifact_id: "spotbugs-maven-plugin", command: "mvn spotbugs:check" },
10
- { artifact_id: "maven-pmd-plugin", command: "mvn pmd:check" }
11
- ].freeze
12
-
13
- DEFAULT_COMMAND = "mvn verify -DskipTests"
14
-
15
- def initialize(pom_content)
16
- @pom_content = pom_content
17
- end
18
-
19
- def lint_command
20
- match = LINTERS.find { |linter| @pom_content.include?(linter[:artifact_id]) }
21
- match ? match[:command] : DEFAULT_COMMAND
22
- end
23
- end
24
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "database"
4
- require_relative "pipeline_recorder"
5
- require_relative "background_wrapper"
6
-
7
- module FunCi
8
- class PipelineForker
9
- def self.fork_pipeline(commit_hash:, branch:, db_path:)
10
- pid = fork do
11
- run_in_child(commit_hash: commit_hash, branch: branch, db_path: db_path)
12
- end
13
- Process.detach(pid)
14
- end
15
-
16
- def self.run_in_child(commit_hash:, branch:, db_path:)
17
- db = Database.connection(db_path)
18
- recorder = DbRecorder.new(db)
19
- Trigger.new(
20
- project_root: Dir.pwd,
21
- commit_hash: commit_hash,
22
- branch: branch,
23
- stdout: File.open(File::NULL, "w"),
24
- recorder: recorder,
25
- background_launcher: method(:sync_launcher)
26
- ).run
27
- recorder.close
28
- end
29
-
30
- def self.sync_launcher(db_path:, pipeline_run_id:, job_id:, executor:)
31
- recorder = DbRecorder.for_background(db_path, pipeline_run_id)
32
- BackgroundWrapper.new(recorder: recorder, job_id: job_id, executor: executor).run
33
- recorder.close
34
- end
35
- end
36
- end
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "pipeline_run"
4
- require_relative "stage_job"
5
-
6
- module FunCi
7
- class NullRecorder
8
- def create_run(commit_hash:, branch:, project_path: nil) = nil
9
- def start_stage(stage) = nil
10
- def end_stage(job_id, status) = nil
11
- def complete_run = nil
12
- def fail_run = nil
13
- def db = nil
14
- def db_path = nil
15
- def pipeline_run_id = nil
16
- def close = nil
17
- end
18
-
19
- class DbRecorder
20
- attr_reader :db, :db_path, :pipeline_run_id
21
-
22
- def self.for_background(db_path, pipeline_run_id)
23
- db = Database.connection(db_path)
24
- new(db, pipeline_run_id: pipeline_run_id)
25
- end
26
-
27
- def initialize(db, pipeline_run_id: nil)
28
- @db = db
29
- @db_path = db.filename("main")
30
- @pipeline_run_id = pipeline_run_id
31
- end
32
-
33
- def close
34
- @db.close rescue nil
35
- end
36
-
37
- def create_run(commit_hash:, branch:, project_path: nil)
38
- @pipeline_run_id = PipelineRun.create(@db, commit_hash: commit_hash, branch: branch, project_path: project_path)
39
- end
40
-
41
- def start_stage(stage)
42
- return nil unless @pipeline_run_id
43
- ensure_running
44
- job_id = StageJob.create(@db, pipeline_run_id: @pipeline_run_id, stage: stage)
45
- StageJob.update_status(@db, job_id, "running")
46
- job_id
47
- end
48
-
49
- def end_stage(job_id, status)
50
- return unless job_id
51
- StageJob.update_status(@db, job_id, status)
52
- end
53
-
54
- def complete_run
55
- return unless @pipeline_run_id
56
- PipelineRun.update_status(@db, @pipeline_run_id, "completed")
57
- end
58
-
59
- def fail_run
60
- return unless @pipeline_run_id
61
- PipelineRun.update_status(@db, @pipeline_run_id, "failed")
62
- end
63
-
64
- private
65
-
66
- def ensure_running
67
- run = PipelineRun.find(@db, @pipeline_run_id)
68
- return unless run && run[:status] == "scheduled"
69
- PipelineRun.update_status(@db, @pipeline_run_id, "running")
70
- end
71
- end
72
- end