fun_ci 2.0.2-aarch64-linux → 2.1.1-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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -0
  3. data/README.md +34 -8
  4. data/lib/fun_ci/agent/command_support.rb +14 -1
  5. data/lib/fun_ci/agent/commands.rb +1 -0
  6. data/lib/fun_ci/agent/context.rb +3 -2
  7. data/lib/fun_ci/agent/events.rb +19 -5
  8. data/lib/fun_ci/agent/events_command.rb +1 -1
  9. data/lib/fun_ci/agent/exit_code.rb +1 -1
  10. data/lib/fun_ci/agent/next_step.rb +26 -0
  11. data/lib/fun_ci/agent/options.rb +6 -4
  12. data/lib/fun_ci/agent/output.rb +15 -2
  13. data/lib/fun_ci/agent/report_stage.rb +1 -1
  14. data/lib/fun_ci/agent/reports.rb +13 -9
  15. data/lib/fun_ci/agent/run_details.rb +31 -0
  16. data/lib/fun_ci/agent/run_report.rb +4 -3
  17. data/lib/fun_ci/agent/runs_text.rb +7 -4
  18. data/lib/fun_ci/agent/snapshots.rb +13 -3
  19. data/lib/fun_ci/agent/status_command.rb +2 -2
  20. data/lib/fun_ci/agent/status_json.rb +2 -1
  21. data/lib/fun_ci/agent/status_text.rb +13 -2
  22. data/lib/fun_ci/agent/trunk_json.rb +32 -0
  23. data/lib/fun_ci/agent/trunk_reading.rb +53 -0
  24. data/lib/fun_ci/agent/trunk_text.rb +50 -0
  25. data/lib/fun_ci/agent/trunk_verdict.rb +20 -0
  26. data/lib/fun_ci/agent/trunk_why.rb +52 -0
  27. data/lib/fun_ci/agent/trunk_why_json.rb +23 -0
  28. data/lib/fun_ci/agent/wait_command.rb +5 -5
  29. data/lib/fun_ci/agent/why_command.rb +11 -0
  30. data/lib/fun_ci/cli.rb +1 -1
  31. data/lib/fun_ci/cli_help.rb +5 -3
  32. data/lib/fun_ci/console/board_data.rb +22 -8
  33. data/lib/fun_ci/console/cancelled_folding.rb +19 -0
  34. data/lib/fun_ci/console/console_events.rb +5 -2
  35. data/lib/fun_ci/console/console_log.rb +4 -1
  36. data/lib/fun_ci/console/console_state.rb +7 -1
  37. data/lib/fun_ci/console/row_order.rb +31 -0
  38. data/lib/fun_ci/console/run_message.rb +9 -2
  39. data/lib/fun_ci/console/trunk_events.rb +43 -0
  40. data/lib/fun_ci/console/trunk_marks.rb +53 -0
  41. data/lib/fun_ci/console/view.rb +9 -8
  42. data/lib/fun_ci/evidence/caps.rb +7 -2
  43. data/lib/fun_ci/persistence/active_runs.rb +5 -4
  44. data/lib/fun_ci/persistence/database.rb +23 -2
  45. data/lib/fun_ci/persistence/null_recorder.rb +8 -0
  46. data/lib/fun_ci/persistence/pipeline_recorder.rb +3 -0
  47. data/lib/fun_ci/persistence/pipeline_run.rb +24 -3
  48. data/lib/fun_ci/persistence/trunk_checks.rb +68 -0
  49. data/lib/fun_ci/persistence/trunk_fetches.rb +57 -0
  50. data/lib/fun_ci/persistence/trunk_recording.rb +49 -0
  51. data/lib/fun_ci/pipeline/pipeline_forker.rb +17 -2
  52. data/lib/fun_ci/pipeline/process_runner.rb +18 -4
  53. data/lib/fun_ci/pipeline/trigger.rb +10 -1
  54. data/lib/fun_ci/pipeline/trigger_command.rb +9 -5
  55. data/lib/fun_ci/pipeline/trigger_params.rb +5 -3
  56. data/lib/fun_ci/pipeline/trunk_run.rb +81 -0
  57. data/lib/fun_ci/setup/agent_instructions.rb +3 -0
  58. data/lib/fun_ci/setup/project_config.rb +3 -1
  59. data/lib/fun_ci/setup/settings.rb +35 -3
  60. data/lib/fun_ci/setup/setup_checker.rb +7 -2
  61. data/lib/fun_ci/setup/trunk_report.rb +28 -0
  62. data/lib/fun_ci/trunk/check.rb +56 -0
  63. data/lib/fun_ci/trunk/checker.rb +91 -0
  64. data/lib/fun_ci/trunk/description.rb +52 -0
  65. data/lib/fun_ci/trunk/fetch.rb +58 -0
  66. data/lib/fun_ci/trunk/first_fetch.rb +27 -0
  67. data/lib/fun_ci/trunk/git.rb +102 -0
  68. data/lib/fun_ci/trunk/local.rb +58 -0
  69. data/lib/fun_ci/trunk/merge_check.rb +54 -0
  70. data/lib/fun_ci/trunk/regions.rb +38 -0
  71. data/lib/fun_ci/trunk/resolver.rb +64 -0
  72. data/lib/fun_ci/trunk/shown.rb +47 -0
  73. data/lib/fun_ci/trunk/tip_reader.rb +39 -0
  74. data/lib/fun_ci/version.rb +1 -1
  75. data/libexec/fun-ci-renderer +0 -0
  76. metadata +30 -1
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "check"
4
+
5
+ module FunCi
6
+ module Trunk
7
+ # What merging a commit with a trunk tip would do (architecture.md, Checking against the trunk,
8
+ # How the check works), from what git answers: the commits each side has
9
+ # that the other lacks, then, when both have some, a merge in memory.
10
+ class MergeCheck
11
+ # What a git command answered: its exit status and what it printed.
12
+ Answer = Data.define(:status, :out, :err)
13
+ # A merge stopped at its budget, with its process group.
14
+ OVER_BUDGET = Answer.new(status: :over_budget, out: "", err: "")
15
+ STOPPED = "the merge ran over its #{CHECK_BUDGET} s budget; " \
16
+ "set trunk: none in .fun-ci/config to stop checking".freeze
17
+
18
+ def initialize(git)
19
+ @git = git
20
+ end
21
+
22
+ # name: how the trunk is called in a reason, "origin/main".
23
+ def merge(commit, trunk, name)
24
+ counted = @git.counts(commit, trunk)
25
+ return Merge.unknown("git rev-list: #{first_line(counted)}") unless counted.status.zero?
26
+
27
+ ahead, behind = counted.out.split.map(&:to_i)
28
+ return Merge.clean(ahead: ahead, behind: behind) if ahead.zero? || behind.zero?
29
+
30
+ merged(@git.merge_tree(commit, trunk), name, ahead: ahead, behind: behind)
31
+ end
32
+
33
+ private
34
+
35
+ def merged(answer, name, **counts)
36
+ return Merge.unknown(STOPPED) if answer.equal?(OVER_BUDGET)
37
+ return Merge.clean(**counts) if answer.status.zero?
38
+ return Merge.conflicts(MergeOutput.parse(answer.out).paths, **counts) if answer.status == 1
39
+
40
+ Merge.unknown(reason(answer, name))
41
+ end
42
+
43
+ def reason(answer, name)
44
+ return "#{name} shares no history with this commit; set trunk: in .fun-ci/config" if unrelated?(answer)
45
+ return "needs git 2.38 or later; this is #{@git.version}" if answer.status == 129
46
+
47
+ "git merge-tree: #{first_line(answer)}"
48
+ end
49
+
50
+ def unrelated?(answer) = answer.err.include?("unrelated histories")
51
+ def first_line(answer) = answer.err.lines.first.to_s.chomp
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FunCi
4
+ module Trunk
5
+ # The conflicted regions of a file as a merge leaves it (architecture.md, Checking against the trunk,
6
+ # why REV trunk): each conflict from its <<<<<<< line to its >>>>>>> line,
7
+ # with `around` lines either side, regions that touch joined into one.
8
+ module Regions
9
+ # Lines first to last, as [number, text] pairs, numbered from 1.
10
+ Region = Data.define(:first, :last, :lines)
11
+ AROUND = 3
12
+
13
+ def self.of(text, around: AROUND)
14
+ lines = text.lines(chomp: true)
15
+ spans = conflicts(lines).map { |from, to| [[from - around, 0].max, [to + around, lines.size - 1].min] }
16
+ joined(spans).map { |from, to| region(lines, from, to) }
17
+ end
18
+
19
+ def self.region(lines, from, to)
20
+ Region.new(first: from + 1, last: to + 1, lines: (from..to).map { |index| [index + 1, lines[index]] })
21
+ end
22
+
23
+ # [index of <<<<<<<, index of >>>>>>>] for each conflict.
24
+ def self.conflicts(lines)
25
+ marked = ->(marker) { lines.each_index.select { |index| lines[index].start_with?(marker) } }
26
+ marked.call("<<<<<<<").zip(marked.call(">>>>>>>"))
27
+ end
28
+
29
+ def self.joined(spans)
30
+ spans.each_with_object([]) do |(from, to), joined|
31
+ touching = joined.any? && from <= joined.last[1] + 1
32
+ touching ? joined[-1] = [joined.last[0], [joined.last[1], to].max] : joined << [from, to]
33
+ end
34
+ end
35
+ private_class_method :region, :conflicts, :joined
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FunCi
4
+ module Trunk
5
+ # A branch that may be the trunk: on a remote, or local when remote is nil.
6
+ Ref = Data.define(:remote, :branch)
7
+
8
+ class Ref
9
+ def to_s = remote ? "#{remote}/#{branch}" : branch
10
+ end
11
+
12
+ # What a repository has to choose a trunk from: its remotes, its branches
13
+ # (a remote's as "origin/main", local ones by name) and each remote's
14
+ # default branch, where git knows it.
15
+ Refs = Data.define(:remotes, :branches, :heads)
16
+
17
+ # Which ref is the trunk (architecture.md, Checking against the trunk):
18
+ # the one configured, else the remote's default branch, else the first
19
+ # usual trunk name on the remote, else locally; nil when none exists.
20
+ module Resolver
21
+ USUAL = %w[main master develop development trunk].freeze
22
+
23
+ def self.pick(setting, refs)
24
+ return configured(setting, refs.remotes) if setting
25
+
26
+ remote = remote(refs.remotes)
27
+ remote ? on_remote(remote, refs) : on_local(refs.branches)
28
+ end
29
+
30
+ # Why pick chose the trunk it did: :configured, :default (the remote's
31
+ # default branch), :usual (a usual trunk name on it) or :local.
32
+ def self.why(setting, refs)
33
+ return :configured if setting
34
+
35
+ remote = remote(refs.remotes)
36
+ return :local unless remote
37
+
38
+ refs.heads[remote] ? :default : :usual
39
+ end
40
+
41
+ def self.configured(setting, remotes)
42
+ remote = remotes.find { |name| setting.start_with?("#{name}/") }
43
+ Ref.new(remote: remote, branch: remote ? setting.delete_prefix("#{remote}/") : setting)
44
+ end
45
+
46
+ def self.remote(remotes)
47
+ return "origin" if remotes.include?("origin")
48
+
49
+ remotes.first if remotes.size == 1
50
+ end
51
+
52
+ def self.on_remote(remote, refs)
53
+ branch = refs.heads[remote] || USUAL.find { |name| refs.branches.include?("#{remote}/#{name}") }
54
+ branch && Ref.new(remote: remote, branch: branch)
55
+ end
56
+
57
+ def self.on_local(branches)
58
+ branch = USUAL.find { |name| branches.include?(name) }
59
+ branch && Ref.new(remote: nil, branch: branch)
60
+ end
61
+ private_class_method :configured, :remote, :on_remote, :on_local
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "check"
4
+
5
+ module FunCi
6
+ module Trunk
7
+ # A check as the developer and agents are shown it: its state, derived
8
+ # rather than stored, how many seconds ago its trunk tip was seen (nil
9
+ # when it had none), the project's last fetch (a LastFetch, nil if none), and
10
+ # the SHA the trunk has moved to since, if it has.
11
+ # A run whose check hasn't been recorded is checking until its fetch and
12
+ # merge are past their time, and then unknown.
13
+ Shown = Data.define(:state, :check, :age, :fetch, :moved_to)
14
+
15
+ class Shown
16
+ # How old a trunk may be before it is stale.
17
+ FRESH_FOR = 3600
18
+
19
+ def self.of(check, now:, fetch: nil, moved_to: nil)
20
+ new(state: state(check.merge), check: check, age: check.tip && (now - check.tip.seen_at),
21
+ fetch: fetch, moved_to: moved_to)
22
+ end
23
+
24
+ def self.unchecked(started:, now:)
25
+ if now - started <= FETCH_DEADLINE + CHECK_BUDGET
26
+ return new(state: "checking", check: nil, age: nil, fetch: nil, moved_to: nil)
27
+ end
28
+
29
+ of(Check.new(commit: nil, tip: nil, merge: Merge.unknown("the check never finished")), now: now)
30
+ end
31
+
32
+ def self.state(merge)
33
+ return merge.outcome if merge.outcome == "unknown"
34
+ return "in_trunk" if merge.ahead.zero?
35
+
36
+ merge.behind.zero? ? "up_to_date" : merge.outcome
37
+ end
38
+ private_class_method :state
39
+
40
+ # Whether the trunk it was checked against may have moved on unseen.
41
+ def stale? = !fetch_error.nil? || (!age.nil? && age > FRESH_FOR)
42
+
43
+ # Why the project's last fetch failed, or nil.
44
+ def fetch_error = fetch&.error
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "check"
4
+ require_relative "fetch"
5
+ require_relative "resolver"
6
+ require_relative "../persistence/trunk_fetches"
7
+
8
+ module FunCi
9
+ module Trunk
10
+ # The tip of the trunk a check reads, and when it counts as seen
11
+ # (architecture.md, Checking against the trunk): fun-ci's own ref once
12
+ # it has fetched one, seen when it was last fetched; else the developer's
13
+ # ref, seen when it last moved.
14
+ class TipReader
15
+ # clock: answers the time now.
16
+ def initialize(git, clock)
17
+ @git = git
18
+ @clock = clock
19
+ end
20
+
21
+ # fetching: whether fun-ci fetches this trunk; fetched: how this run's
22
+ # fetch went, if it made one; last: the fetch before it. Nil when the
23
+ # trunk doesn't exist.
24
+ def tip(ref, fetching:, fetched: nil, last: nil)
25
+ own = fetching && ref.remote && @git.rev(Fetch.ref_for(ref))
26
+ return at(ref, own, seen(fetched, last) || @git.moved_at(Fetch.ref_for(ref))) if own
27
+
28
+ full = ref.remote ? "refs/remotes/#{ref}" : "refs/heads/#{ref.branch}"
29
+ sha = @git.rev(full)
30
+ sha && at(ref, sha, @git.moved_at(full))
31
+ end
32
+
33
+ private
34
+
35
+ def seen(fetched, last) = fetched && !fetched.error ? @clock.call : last&.fetched_at
36
+ def at(ref, sha, seen_at) = Tip.new(remote: ref.remote, branch: ref.branch, sha: sha, seen_at: seen_at)
37
+ end
38
+ end
39
+ 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.0.2"
5
+ VERSION = "2.1.1"
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.0.2
4
+ version: 2.1.1
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Erik Thyge Madsen
@@ -73,10 +73,12 @@ files:
73
73
  - lib/fun_ci/agent/exit_code.rb
74
74
  - lib/fun_ci/agent/git.rb
75
75
  - lib/fun_ci/agent/live_pipeline.rb
76
+ - lib/fun_ci/agent/next_step.rb
76
77
  - lib/fun_ci/agent/options.rb
77
78
  - lib/fun_ci/agent/output.rb
78
79
  - lib/fun_ci/agent/report_stage.rb
79
80
  - lib/fun_ci/agent/reports.rb
81
+ - lib/fun_ci/agent/run_details.rb
80
82
  - lib/fun_ci/agent/run_report.rb
81
83
  - lib/fun_ci/agent/runs_command.rb
82
84
  - lib/fun_ci/agent/runs_text.rb
@@ -86,6 +88,12 @@ files:
86
88
  - lib/fun_ci/agent/status_json.rb
87
89
  - lib/fun_ci/agent/status_text.rb
88
90
  - lib/fun_ci/agent/system_clock.rb
91
+ - lib/fun_ci/agent/trunk_json.rb
92
+ - lib/fun_ci/agent/trunk_reading.rb
93
+ - lib/fun_ci/agent/trunk_text.rb
94
+ - lib/fun_ci/agent/trunk_verdict.rb
95
+ - lib/fun_ci/agent/trunk_why.rb
96
+ - lib/fun_ci/agent/trunk_why_json.rb
89
97
  - lib/fun_ci/agent/verdict.rb
90
98
  - lib/fun_ci/agent/wait_command.rb
91
99
  - lib/fun_ci/agent/waiting.rb
@@ -96,6 +104,7 @@ files:
96
104
  - lib/fun_ci/cli_help.rb
97
105
  - lib/fun_ci/console/board_data.rb
98
106
  - lib/fun_ci/console/board_heartbeat.rb
107
+ - lib/fun_ci/console/cancelled_folding.rb
99
108
  - lib/fun_ci/console/console_events.rb
100
109
  - lib/fun_ci/console/console_log.rb
101
110
  - lib/fun_ci/console/console_loop.rb
@@ -107,11 +116,14 @@ files:
107
116
  - lib/fun_ci/console/milestones.rb
108
117
  - lib/fun_ci/console/renderer_lookup.rb
109
118
  - lib/fun_ci/console/renderer_process.rb
119
+ - lib/fun_ci/console/row_order.rb
110
120
  - lib/fun_ci/console/run_message.rb
111
121
  - lib/fun_ci/console/stage_change_detector.rb
112
122
  - lib/fun_ci/console/stage_events.rb
113
123
  - lib/fun_ci/console/stop_report.rb
114
124
  - lib/fun_ci/console/streak_counter.rb
125
+ - lib/fun_ci/console/trunk_events.rb
126
+ - lib/fun_ci/console/trunk_marks.rb
115
127
  - lib/fun_ci/console/view.rb
116
128
  - lib/fun_ci/evidence/about.rb
117
129
  - lib/fun_ci/evidence/about_stage.rb
@@ -205,6 +217,9 @@ files:
205
217
  - lib/fun_ci/persistence/run_status.rb
206
218
  - lib/fun_ci/persistence/stage_job.rb
207
219
  - lib/fun_ci/persistence/state_dir.rb
220
+ - lib/fun_ci/persistence/trunk_checks.rb
221
+ - lib/fun_ci/persistence/trunk_fetches.rb
222
+ - lib/fun_ci/persistence/trunk_recording.rb
208
223
  - lib/fun_ci/persistence/write_trouble.rb
209
224
  - lib/fun_ci/pipeline/background_fork.rb
210
225
  - lib/fun_ci/pipeline/command_executor.rb
@@ -230,6 +245,7 @@ files:
230
245
  - lib/fun_ci/pipeline/trigger.rb
231
246
  - lib/fun_ci/pipeline/trigger_command.rb
232
247
  - lib/fun_ci/pipeline/trigger_params.rb
248
+ - lib/fun_ci/pipeline/trunk_run.rb
233
249
  - lib/fun_ci/pipeline/workspaces.rb
234
250
  - lib/fun_ci/pipeline/worktree_pool.rb
235
251
  - lib/fun_ci/pipeline/worktree_prune.rb
@@ -255,6 +271,19 @@ files:
255
271
  - lib/fun_ci/setup/stage_templates/scripting.rb
256
272
  - lib/fun_ci/setup/stage_templates/shell.rb
257
273
  - lib/fun_ci/setup/template_writer.rb
274
+ - lib/fun_ci/setup/trunk_report.rb
275
+ - lib/fun_ci/trunk/check.rb
276
+ - lib/fun_ci/trunk/checker.rb
277
+ - lib/fun_ci/trunk/description.rb
278
+ - lib/fun_ci/trunk/fetch.rb
279
+ - lib/fun_ci/trunk/first_fetch.rb
280
+ - lib/fun_ci/trunk/git.rb
281
+ - lib/fun_ci/trunk/local.rb
282
+ - lib/fun_ci/trunk/merge_check.rb
283
+ - lib/fun_ci/trunk/regions.rb
284
+ - lib/fun_ci/trunk/resolver.rb
285
+ - lib/fun_ci/trunk/shown.rb
286
+ - lib/fun_ci/trunk/tip_reader.rb
258
287
  - lib/fun_ci/version.rb
259
288
  - libexec/fun-ci-renderer
260
289
  homepage: https://github.com/beatmadsen/fun-ci