fun_ci 2.1.1-aarch64-linux → 2.2.0-aarch64-linux

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/README.md +24 -1
  4. data/lib/fun_ci/agent/command_support.rb +7 -0
  5. data/lib/fun_ci/agent/commands.rb +2 -1
  6. data/lib/fun_ci/agent/due_in.rb +21 -0
  7. data/lib/fun_ci/agent/events_command.rb +14 -7
  8. data/lib/fun_ci/agent/job_events.rb +37 -0
  9. data/lib/fun_ci/agent/job_json.rb +16 -0
  10. data/lib/fun_ci/agent/job_report.rb +85 -0
  11. data/lib/fun_ci/agent/job_why.rb +59 -0
  12. data/lib/fun_ci/agent/job_why_json.rb +31 -0
  13. data/lib/fun_ci/agent/job_why_text.rb +51 -0
  14. data/lib/fun_ci/agent/jobs_command.rb +43 -0
  15. data/lib/fun_ci/agent/jobs_text.rb +50 -0
  16. data/lib/fun_ci/agent/live_pipeline.rb +7 -2
  17. data/lib/fun_ci/agent/options.rb +3 -3
  18. data/lib/fun_ci/agent/output.rb +14 -2
  19. data/lib/fun_ci/agent/snapshots.rb +16 -1
  20. data/lib/fun_ci/agent/span.rb +24 -0
  21. data/lib/fun_ci/agent/stage_summary.rb +10 -6
  22. data/lib/fun_ci/agent/status_command.rb +3 -2
  23. data/lib/fun_ci/agent/status_text.rb +19 -5
  24. data/lib/fun_ci/agent/why_command.rb +6 -2
  25. data/lib/fun_ci/cli.rb +1 -1
  26. data/lib/fun_ci/cli_help.rb +3 -1
  27. data/lib/fun_ci/console/board_data.rb +26 -13
  28. data/lib/fun_ci/console/console_state.rb +12 -6
  29. data/lib/fun_ci/console/job_message.rb +33 -0
  30. data/lib/fun_ci/console/job_order.rb +22 -0
  31. data/lib/fun_ci/console/job_rows.rb +21 -0
  32. data/lib/fun_ci/console/key_handler.rb +33 -14
  33. data/lib/fun_ci/console/paging.rb +26 -0
  34. data/lib/fun_ci/console/view.rb +18 -9
  35. data/lib/fun_ci/evidence/settings.rb +14 -7
  36. data/lib/fun_ci/evidence/settings_check.rb +14 -3
  37. data/lib/fun_ci/jobs/due.rb +33 -0
  38. data/lib/fun_ci/jobs/due_jobs.rb +26 -0
  39. data/lib/fun_ci/jobs/folders.rb +50 -0
  40. data/lib/fun_ci/jobs/job.rb +31 -0
  41. data/lib/fun_ci/jobs/job_fork.rb +45 -0
  42. data/lib/fun_ci/jobs/job_run.rb +85 -0
  43. data/lib/fun_ci/jobs/locks.rb +30 -0
  44. data/lib/fun_ci/jobs/site.rb +10 -0
  45. data/lib/fun_ci/jobs/standings.rb +46 -0
  46. data/lib/fun_ci/jobs/state.rb +26 -0
  47. data/lib/fun_ci/persistence/active_jobs.rb +54 -0
  48. data/lib/fun_ci/persistence/database.rb +14 -4
  49. data/lib/fun_ci/persistence/job_recorder.rb +47 -0
  50. data/lib/fun_ci/persistence/job_runs.rb +68 -0
  51. data/lib/fun_ci/persistence/raw_outputs.rb +3 -0
  52. data/lib/fun_ci/pipeline/pipeline_forker.rb +39 -10
  53. data/lib/fun_ci/pipeline/process_runner.rb +15 -5
  54. data/lib/fun_ci/pipeline/run_canceller.rb +24 -0
  55. data/lib/fun_ci/pipeline/slot.rb +1 -1
  56. data/lib/fun_ci/pipeline/stage_execution.rb +9 -4
  57. data/lib/fun_ci/pipeline/trigger_command.rb +7 -1
  58. data/lib/fun_ci/pipeline/worktree_prune.rb +8 -6
  59. data/lib/fun_ci/pipeline/worktrees.rb +8 -4
  60. data/lib/fun_ci/setup/agent_instructions.rb +3 -0
  61. data/lib/fun_ci/setup/project_config.rb +6 -0
  62. data/lib/fun_ci/setup/setup_checker.rb +7 -1
  63. data/lib/fun_ci/version.rb +1 -1
  64. data/libexec/fun-ci-renderer +0 -0
  65. metadata +28 -1
@@ -5,24 +5,41 @@ require_relative "../persistence/pipeline_recorder"
5
5
  require_relative "trigger_params"
6
6
  require_relative "../setup/project_config"
7
7
  require_relative "../trunk/first_fetch"
8
+ require_relative "../jobs/due_jobs"
9
+ require_relative "../jobs/job_fork"
8
10
 
9
11
  module FunCi
10
12
  module Pipeline
11
13
  class PipelineForker
12
- # A run started, and what to say before the project's first fetch of its trunk, if this is it.
13
- Forked = Data.define(:notice)
14
+ # A run started, what to say before the project's first fetch of its
15
+ # trunk, if this is it, and why the daily and weekly jobs didn't start,
16
+ # if they didn't.
17
+ Forked = Data.define(:notice, :jobs)
18
+
19
+ class Forked
20
+ def initialize(notice:, jobs: nil) = super
21
+ end
14
22
 
15
23
  # Answers a Forked, or false in a project not set up for fun-ci. What the
16
- # run prints goes nowhere, so the notice is worked out here, before the fork.
17
- def self.fork_pipeline(commit_hash:, branch:, db_path:)
24
+ # run prints goes nowhere, so the notice is worked out here, before the
25
+ # fork. The project's due jobs start beside the run, each in a process
26
+ # of its own (design.md, Daily and weekly jobs), once the run has
27
+ # started, and trouble starting them never stops it. jobs: what starts
28
+ # them, given the commit and the database's path.
29
+ def self.fork_pipeline(commit_hash:, branch:, db_path:, jobs: method(:start_due_jobs))
18
30
  return false unless Setup::ProjectConfig.new(Dir.pwd).validate.empty?
19
31
 
20
32
  notice = first_fetch(db_path)
21
- pid = fork do
22
- run_in_child(commit_hash: commit_hash, branch: branch, db_path: db_path)
23
- end
24
- Process.detach(pid)
25
- Forked.new(notice: notice)
33
+ Process.detach(fork { run_in_child(commit_hash: commit_hash, branch: branch, db_path: db_path) })
34
+ Forked.new(notice: notice, jobs: trouble { jobs.call(Commit.new(sha: commit_hash, branch: branch), db_path) })
35
+ end
36
+
37
+ # What went wrong starting the jobs, or nil.
38
+ def self.trouble
39
+ yield
40
+ nil
41
+ rescue StandardError => e
42
+ "fun-ci: the daily and weekly jobs didn't start: #{e.message}"
26
43
  end
27
44
 
28
45
  # The connection closes before the fork, which must not inherit it.
@@ -33,6 +50,18 @@ module FunCi
33
50
  db&.close
34
51
  end
35
52
 
53
+ # Which jobs are due is read before any fork, which must not inherit the connection.
54
+ def self.start_due_jobs(commit, db_path)
55
+ due_jobs(db_path).each { |job| Jobs::JobFork.start(job, commit, project: Dir.pwd, db_path: db_path) }
56
+ end
57
+
58
+ def self.due_jobs(db_path)
59
+ db = Persistence::Database.connection(db_path)
60
+ Jobs::DueJobs.new(Dir.pwd, db, now: Time.now).list
61
+ ensure
62
+ db&.close
63
+ end
64
+
36
65
  # Trigger requires this file, through TriggerCommand, so it is loaded
37
66
  # here, where it is used, rather than above.
38
67
  def self.run_in_child(commit_hash:, branch:, db_path:)
@@ -49,7 +78,7 @@ module FunCi
49
78
  Trigger.new(project: Dir.pwd, commit: Commit.new(sha: commit_hash, branch: branch),
50
79
  io: Io.new(stdout: File.open(File::NULL, "w")), seams: Seams.new(recorder: recorder))
51
80
  end
52
- private_class_method :trigger, :first_fetch
81
+ private_class_method :trigger, :first_fetch, :start_due_jobs, :due_jobs, :trouble
53
82
  end
54
83
  end
55
84
  end
@@ -23,16 +23,24 @@ module FunCi
23
23
  DRAIN_SECONDS = 1
24
24
 
25
25
  # Where the command runs, what it finds in its environment besides
26
- # fun-ci's own, the window what it prints is written to, and what to do
27
- # with its pid when it runs over budget, before it is killed.
28
- Launch = Data.define(:chdir, :env, :output, :before_kill)
26
+ # fun-ci's own, the window what it prints is written to, what to do
27
+ # with its pid when it runs over budget, before it is killed, and the
28
+ # open files it holds as long as it runs, such as a lock.
29
+ Launch = Data.define(:chdir, :env, :output, :before_kill, :held)
29
30
 
30
31
  class Launch
31
32
  def initialize(**given)
32
- super(chdir: Dir.pwd, env: {}, output: OutputWindow.in_memory, before_kill: nil, **given)
33
+ super(chdir: Dir.pwd, env: {}, output: OutputWindow.in_memory, before_kill: nil, held: [], **given)
33
34
  end
34
35
  end
35
36
 
37
+ # Where the first file a command holds is open in it: past the gate's 9.
38
+ FIRST_HELD = 10
39
+
40
+ # { descriptor in the command => file } for the files it holds. Not their
41
+ # own descriptors, which in a process with few files open may be the gate's.
42
+ def self.held_descriptors(files) = files.each_with_index.to_h { |file, index| [FIRST_HELD + index, file] }
43
+
36
44
  # A command started and let go, and the pipe that carries what it prints.
37
45
  Started = Data.define(:pid, :reader)
38
46
 
@@ -70,9 +78,11 @@ module FunCi
70
78
 
71
79
  def spawn_gated(cmd, writer, gate, launch)
72
80
  Process.spawn(GitEnvironment::CLEAN.merge(launch.env), format(GATED, cmd),
73
- out: writer, err: writer, 9 => gate.child_end, pgroup: true, chdir: launch.chdir)
81
+ out: writer, err: writer, 9 => gate.child_end, pgroup: true, chdir: launch.chdir, **held(launch))
74
82
  end
75
83
 
84
+ def held(launch) = ProcessRunner.held_descriptors(launch.held)
85
+
76
86
  # The reader may be closed while this thread still reads, once a killed
77
87
  # command's output has drained or stopped coming.
78
88
  def read_until_closed(reader, printed)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../persistence/active_runs"
4
+ require_relative "../persistence/active_jobs"
4
5
  require_relative "../persistence/run_status"
5
6
  require_relative "../persistence/write_trouble"
6
7
  require_relative "slot_lock"
@@ -36,6 +37,26 @@ module FunCi
36
37
  # The next poll looks again.
37
38
  end
38
39
 
40
+ # Stops a daily or weekly job's processes and records it cancelled,
41
+ # unless it has finished meanwhile; a group its runner recorded while
42
+ # this looked is stopped once the runner is dead, as a run's late stages are.
43
+ def cancel_job(db, id)
44
+ Persistence::ActiveJobs.with_id(db, id).each do |job|
45
+ stop(job)
46
+ Persistence::ActiveJobs.with_id(db, id).each { |now| kill_late(job, now) }
47
+ Persistence::ActiveJobs.cancelled(db, id)
48
+ end
49
+ end
50
+
51
+ # Records failed each daily or weekly job still running whose lock
52
+ # nobody holds: its process is gone (acceptance-tests.md, AT-13.6).
53
+ def record_dead_jobs(db)
54
+ Persistence::ActiveJobs.running(db).filter_map { |id, lock| id unless @slot_held.call(lock) }
55
+ .each { |id| Persistence::ActiveJobs.died(db, id) }
56
+ rescue SQLite3::BusyException, *Persistence::WriteTrouble::UNWRITABLE
57
+ # The next poll looks again.
58
+ end
59
+
39
60
  def stop(run)
40
61
  return unless alive?(run)
41
62
 
@@ -53,6 +74,9 @@ module FunCi
53
74
  end
54
75
  end
55
76
 
77
+ # The groups recorded since `before` was read.
78
+ def kill_late(before, now) = (now.stage_groups - before.stage_groups).each { |group| kill(-group) }
79
+
56
80
  def alive?(run) = run.slot_lock.nil? || @slot_held.call(run.slot_lock)
57
81
 
58
82
  # Whether a process with this pid exists; one this user may not signal does.
@@ -6,7 +6,7 @@ module FunCi
6
6
  # file. The fast suite and the slow suite each hold it; it frees when the
7
7
  # last of them lets go. A process that dies lets go with it.
8
8
  class Slot
9
- attr_reader :path, :lock_file
9
+ attr_reader :path, :lock_file, :lock
10
10
 
11
11
  # +lock_file+ names the lock on disk; nil for a slot with nothing to lock.
12
12
  def initialize(path, lock, lock_file = nil)
@@ -7,12 +7,18 @@ module FunCi
7
7
  module Pipeline
8
8
  # Runs a stage that has been started, in `dir`, with a stage directory of
9
9
  # its own, and records how it ended while it still holds both. The same
10
- # for every stage, the slow suite in its forked child included.
10
+ # for every stage, the slow suite in its forked child included, and for
11
+ # a daily or weekly job.
11
12
  class StageExecution
12
- def initialize(seams:, dir:, commit:)
13
+ # launching: what the script is launched with besides its output window
14
+ # (ProcessRunner::Launch): what it is told about itself, by default its
15
+ # stage's name in FUN_CI_STAGE (acceptance-tests.md, AT-10.7), and the
16
+ # files it holds open.
17
+ def initialize(seams:, dir:, commit:, launching: {})
13
18
  @seams = seams
14
19
  @dir = dir
15
20
  @commit = commit
21
+ @launching = launching
16
22
  end
17
23
 
18
24
  # Answers [the outcome recorded, what the stage printed].
@@ -36,10 +42,9 @@ module FunCi
36
42
  StageEnd::Finished.new(output: output, status: status, timed_out: timed_out, overrun: overrun)
37
43
  end
38
44
 
39
- # The stage learns its name from FUN_CI_STAGE (acceptance-tests.md, AT-10.7).
40
45
  def execute(stage, command, stage_dir, before_kill, &)
41
46
  window = stage_dir.window
42
- launch = ProcessRunner::Launch.new(env: { "FUN_CI_STAGE" => stage }, output: window,
47
+ launch = ProcessRunner::Launch.new(env: { "FUN_CI_STAGE" => stage }, **@launching, output: window,
43
48
  before_kill: before_kill)
44
49
  @seams.executor(@dir).call(command, @seams.budgets[stage], launch, &)
45
50
  ensure
@@ -56,10 +56,16 @@ module FunCi
56
56
  return 0 unless forked
57
57
 
58
58
  say_how_to_wait(commit.sha[0, 7])
59
- @io.stdout.puts(forked.notice) if forked.notice
59
+ say(forked)
60
60
  0
61
61
  end
62
62
 
63
+ # The first fetch's notice, and on stderr, why the jobs didn't start.
64
+ def say(forked)
65
+ @io.stdout.puts(forked.notice) if forked.notice
66
+ @io.stderr.puts(forked.jobs) if forked.jobs
67
+ end
68
+
63
69
  def say_how_to_wait(sha) = @io.stdout.puts("fun-ci: testing #{sha}. Verdict: fun-ci wait #{sha} --need all")
64
70
  end
65
71
  end
@@ -5,8 +5,9 @@ require_relative "worktrees"
5
5
 
6
6
  module FunCi
7
7
  module Pipeline
8
- # Removes every slot of a project's worktree pool, and git's entries for
9
- # them, when no run holds one. It takes each slot's lock first, so a run
8
+ # Removes every slot of a project's worktree pool and every daily or
9
+ # weekly job's worktree, and git's entries for them, when no run or job
10
+ # holds one. It takes each slot's lock first, so a run
10
11
  # starting meanwhile waits until the slot is gone and then checks it out
11
12
  # afresh.
12
13
  class WorktreePrune
@@ -16,8 +17,8 @@ module FunCi
16
17
  @worktrees = worktrees
17
18
  end
18
19
 
19
- # How many slots it removed. Raises Busy, removing nothing, while a run
20
- # holds a slot.
20
+ # How many worktrees it removed. Raises Busy, removing nothing, while a
21
+ # run or a job holds one.
21
22
  def run
22
23
  locks = take_locks
23
24
  locks.each { |lock| FileUtils.rm_rf(lock.path.delete_suffix(".lock")) }
@@ -37,12 +38,13 @@ module FunCi
37
38
  next if locks.last.flock(File::LOCK_EX | File::LOCK_NB)
38
39
 
39
40
  locks.each(&:close)
40
- raise Busy, "a pipeline is running in #{path}"
41
+ raise Busy, "fun-ci is running something in #{path}"
41
42
  end
42
43
  end
43
44
 
44
45
  def slot_paths
45
- Dir.glob(File.join(@worktrees.root, "slot-*")).map { |path| path.delete_suffix(".lock") }.uniq
46
+ (Dir.glob(File.join(@worktrees.root, "slot-*")) + Dir.glob(File.join(@worktrees.jobs_root, "*")))
47
+ .map { |path| path.delete_suffix(".lock") }.uniq
46
48
  end
47
49
  end
48
50
  end
@@ -13,10 +13,10 @@ module FunCi
13
13
  @project_root = project_root
14
14
  end
15
15
 
16
- def root
17
- File.join(File.expand_path(git(@project_root, "rev-parse", "--git-common-dir").strip, @project_root),
18
- "fun-ci", "worktrees")
19
- end
16
+ def root = File.join(fun_ci_dir, "worktrees")
17
+
18
+ # Where each daily and weekly job has a worktree of its own (Jobs::Locks).
19
+ def jobs_root = File.join(fun_ci_dir, "jobs")
20
20
 
21
21
  # Leaves +path+ as a clean detached checkout of +sha+. Ignored files stay,
22
22
  # so caches such as vendor/bundle survive between runs in the same slot.
@@ -32,6 +32,10 @@ module FunCi
32
32
 
33
33
  private
34
34
 
35
+ def fun_ci_dir
36
+ File.join(File.expand_path(git(@project_root, "rev-parse", "--git-common-dir").strip, @project_root), "fun-ci")
37
+ end
38
+
35
39
  def add(path, sha) = git(@project_root, "worktree", "add", "--detach", "--force", path, sha)
36
40
 
37
41
  def git(dir, *args)
@@ -19,6 +19,9 @@ module FunCi
19
19
  the commit conflicts with the trunk, which has to be integrated first,
20
20
  as it prints.
21
21
  `fun-ci runs` lists recent runs, and `fun-ci status` says where one stands.
22
+ `fun-ci jobs` lists the daily and weekly jobs, which run beside the
23
+ pipeline and never hold up a verdict, and `fun-ci why --job NAME` says
24
+ why one failed.
22
25
  MD
23
26
 
24
27
  def self.file_for(names) = names.include?("CLAUDE.md") && !names.include?("AGENTS.md") ? "CLAUDE.md" : "AGENTS.md"
@@ -4,6 +4,7 @@ require "shellwords"
4
4
  require_relative "settings"
5
5
  require_relative "../evidence/settings"
6
6
  require_relative "../evidence/start"
7
+ require_relative "../jobs/folders"
7
8
 
8
9
  module FunCi
9
10
  module Setup
@@ -37,6 +38,11 @@ module FunCi
37
38
  # may hold, such as JSON logs.
38
39
  def any_project_presets = preset_candidates.reject(&:because).map { |found| found.preset.name }
39
40
 
41
+ # The daily and weekly jobs that can run, and why any other can't
42
+ # (design.md, Daily and weekly jobs); neither stops a pipeline.
43
+ def jobs = Jobs::Folders.new(@project_root).jobs
44
+ def job_errors = Jobs::Folders.new(@project_root).errors
45
+
40
46
  def worktree_slots = settings.worktree_slots
41
47
  def trunk = settings.trunk
42
48
  def trunk_fetch = settings.trunk_fetch
@@ -22,10 +22,11 @@ module FunCi
22
22
 
23
23
  # Warnings are printed but don't fail the check.
24
24
  def run
25
- errors = @config.validate + @config.evidence_errors
25
+ errors = @config.validate + @config.evidence_errors + @config.job_errors
26
26
  @stdout.puts(errors.empty? ? "All OK. The project is configured." : errors)
27
27
  @stdout.puts(@trunk.lines)
28
28
  list_presets
29
+ list_jobs
29
30
  @hooks.warnings.each { |warning| @stdout.puts "Warning: #{warning}" }
30
31
  errors.empty? ? 0 : 1
31
32
  end
@@ -36,6 +37,11 @@ module FunCi
36
37
  lists = { "this project" => @config.presets, "any project's output" => @config.any_project_presets }
37
38
  lists.each { |what, names| @stdout.puts "Evidence presets for #{what}: #{names.join(", ")}" if names.any? }
38
39
  end
40
+
41
+ def list_jobs
42
+ jobs = @config.jobs
43
+ @stdout.puts "Jobs: #{jobs.map { |job| "#{job.name} (#{job.cadence})" }.join(", ")}" if jobs.any?
44
+ end
39
45
  end
40
46
  end
41
47
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Loaded on its own by the gemspec, so it must not require anything.
4
4
  module FunCi
5
- VERSION = "2.1.1"
5
+ VERSION = "2.2.0"
6
6
  end
Binary file
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: 2.1.1
4
+ version: 2.2.0
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Erik Thyge Madsen
@@ -67,11 +67,20 @@ files:
67
67
  - lib/fun_ci/agent/commands.rb
68
68
  - lib/fun_ci/agent/context.rb
69
69
  - lib/fun_ci/agent/digest.rb
70
+ - lib/fun_ci/agent/due_in.rb
70
71
  - lib/fun_ci/agent/events.rb
71
72
  - lib/fun_ci/agent/events_command.rb
72
73
  - lib/fun_ci/agent/evidence_text.rb
73
74
  - lib/fun_ci/agent/exit_code.rb
74
75
  - lib/fun_ci/agent/git.rb
76
+ - lib/fun_ci/agent/job_events.rb
77
+ - lib/fun_ci/agent/job_json.rb
78
+ - lib/fun_ci/agent/job_report.rb
79
+ - lib/fun_ci/agent/job_why.rb
80
+ - lib/fun_ci/agent/job_why_json.rb
81
+ - lib/fun_ci/agent/job_why_text.rb
82
+ - lib/fun_ci/agent/jobs_command.rb
83
+ - lib/fun_ci/agent/jobs_text.rb
75
84
  - lib/fun_ci/agent/live_pipeline.rb
76
85
  - lib/fun_ci/agent/next_step.rb
77
86
  - lib/fun_ci/agent/options.rb
@@ -83,6 +92,7 @@ files:
83
92
  - lib/fun_ci/agent/runs_command.rb
84
93
  - lib/fun_ci/agent/runs_text.rb
85
94
  - lib/fun_ci/agent/snapshots.rb
95
+ - lib/fun_ci/agent/span.rb
86
96
  - lib/fun_ci/agent/stage_summary.rb
87
97
  - lib/fun_ci/agent/status_command.rb
88
98
  - lib/fun_ci/agent/status_json.rb
@@ -110,10 +120,14 @@ files:
110
120
  - lib/fun_ci/console/console_loop.rb
111
121
  - lib/fun_ci/console/console_session.rb
112
122
  - lib/fun_ci/console/console_state.rb
123
+ - lib/fun_ci/console/job_message.rb
124
+ - lib/fun_ci/console/job_order.rb
125
+ - lib/fun_ci/console/job_rows.rb
113
126
  - lib/fun_ci/console/key_handler.rb
114
127
  - lib/fun_ci/console/launcher.rb
115
128
  - lib/fun_ci/console/milestone_events.rb
116
129
  - lib/fun_ci/console/milestones.rb
130
+ - lib/fun_ci/console/paging.rb
117
131
  - lib/fun_ci/console/renderer_lookup.rb
118
132
  - lib/fun_ci/console/renderer_process.rb
119
133
  - lib/fun_ci/console/row_order.rb
@@ -205,8 +219,21 @@ files:
205
219
  - lib/fun_ci/evidence/stamp.rb
206
220
  - lib/fun_ci/evidence/start.rb
207
221
  - lib/fun_ci/evidence/worktree.rb
222
+ - lib/fun_ci/jobs/due.rb
223
+ - lib/fun_ci/jobs/due_jobs.rb
224
+ - lib/fun_ci/jobs/folders.rb
225
+ - lib/fun_ci/jobs/job.rb
226
+ - lib/fun_ci/jobs/job_fork.rb
227
+ - lib/fun_ci/jobs/job_run.rb
228
+ - lib/fun_ci/jobs/locks.rb
229
+ - lib/fun_ci/jobs/site.rb
230
+ - lib/fun_ci/jobs/standings.rb
231
+ - lib/fun_ci/jobs/state.rb
232
+ - lib/fun_ci/persistence/active_jobs.rb
208
233
  - lib/fun_ci/persistence/active_runs.rb
209
234
  - lib/fun_ci/persistence/database.rb
235
+ - lib/fun_ci/persistence/job_recorder.rb
236
+ - lib/fun_ci/persistence/job_runs.rb
210
237
  - lib/fun_ci/persistence/null_recorder.rb
211
238
  - lib/fun_ci/persistence/output_tail.rb
212
239
  - lib/fun_ci/persistence/pipeline_recorder.rb