Module: Carson::Runtime::Audit
- Included in:
- Carson::Runtime
- Defined in:
- lib/carson/runtime/audit.rb
Instance Method Summary collapse
- #audit!(json_output: false) ⇒ Object
-
#audit_all! ⇒ Object
Runs audit across all governed repositories.
Instance Method Details
#audit!(json_output: false) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/carson/runtime/audit.rb', line 9 def audit!( json_output: false ) fingerprint_status = block_if_outsider_fingerprints! return fingerprint_status unless fingerprint_status.nil? unless head_exists? if json_output out.puts JSON.pretty_generate( { command: "audit", status: "skipped", reason: "no commits yet", exit_code: EXIT_OK } ) else puts_line "No commits yet — audit skipped for initial commit." end return EXIT_OK end audit_state = "ok" audit_concise_problems = [] puts_verbose "" puts_verbose "[Repository]" puts_verbose "root: #{repo_root}" puts_verbose "current_branch: #{current_branch}" puts_verbose "" puts_verbose "[Working Tree]" puts_verbose git_capture!( "status", "--short", "--branch" ).strip puts_verbose "" puts_verbose "[Hooks]" hooks_ok = hooks_health_report hooks_status = hooks_ok ? "ok" : "mismatch" unless hooks_ok audit_state = "block" audit_concise_problems << "Hooks: mismatch — run carson refresh." end puts_verbose "" puts_verbose "[Main Sync Status]" ahead_count, behind_count, main_error = main_sync_counts main_sync = { ahead: 0, behind: 0, status: "ok" } if main_error puts_verbose "main_vs_remote_main: unknown" puts_verbose "WARN: unable to calculate main sync status (#{main_error})." audit_state = "attention" if audit_state == "ok" audit_concise_problems << "Main sync: unable to determine — check remote connectivity." main_sync = { ahead: 0, behind: 0, status: "unknown", error: main_error } elsif ahead_count.positive? puts_verbose "main_vs_remote_main_ahead: #{ahead_count}" puts_verbose "main_vs_remote_main_behind: #{behind_count}" puts_verbose "ACTION: local #{config.main_branch} is ahead of #{config.git_remote}/#{config.main_branch} by #{ahead_count} commit#{plural_suffix( count: ahead_count )}; reset local drift before commit/push workflows." audit_state = "block" audit_concise_problems << "Main sync (#{config.git_remote}): ahead by #{ahead_count} — git fetch #{config.git_remote}, or carson setup to switch remote." main_sync = { ahead: ahead_count, behind: behind_count, status: "ahead" } elsif behind_count.positive? puts_verbose "main_vs_remote_main_ahead: #{ahead_count}" puts_verbose "main_vs_remote_main_behind: #{behind_count}" puts_verbose "ACTION: local #{config.main_branch} is behind #{config.git_remote}/#{config.main_branch} by #{behind_count} commit#{plural_suffix( count: behind_count )}; run carson sync." audit_state = "attention" if audit_state == "ok" audit_concise_problems << "Main sync (#{config.git_remote}): behind by #{behind_count} — run carson sync." main_sync = { ahead: ahead_count, behind: behind_count, status: "behind" } else puts_verbose "main_vs_remote_main_ahead: 0" puts_verbose "main_vs_remote_main_behind: 0" puts_verbose "ACTION: local #{config.main_branch} is in sync with #{config.git_remote}/#{config.main_branch}." end puts_verbose "" puts_verbose "[PR and Required Checks (gh)]" monitor_report = pr_and_check_report audit_state = "attention" if audit_state == "ok" && !%w[ok skipped].include?( monitor_report.fetch( :status ) ) if monitor_report.fetch( :status ) == "attention" checks = monitor_report.fetch( :checks ) fail_n = checks.fetch( :failing_count ) pend_n = checks.fetch( :pending_count ) total = checks.fetch( :required_total ) fail_names = checks.fetch( :failing ).map { |e| e.fetch( :name ) }.join( ", " ) if fail_n.positive? && pend_n.positive? audit_concise_problems << "Checks: #{fail_n} failing (#{fail_names}), #{pend_n} pending of #{total} required." elsif fail_n.positive? audit_concise_problems << "Checks: #{fail_n} of #{total} failing (#{fail_names})." elsif pend_n.positive? audit_concise_problems << "Checks: pending (#{total - pend_n} of #{total} complete)." end end puts_verbose "" puts_verbose "[Default Branch CI Baseline (gh)]" default_branch_baseline = default_branch_ci_baseline_report audit_state = "attention" if audit_state == "ok" && !%w[ok skipped].include?( default_branch_baseline.fetch( :status ) ) baseline_st = default_branch_baseline.fetch( :status ) if baseline_st == "block" parts = [] if default_branch_baseline.fetch( :failing_count ).positive? names = default_branch_baseline.fetch( :failing ).map { |e| e.fetch( :name ) }.join( ", " ) parts << "#{default_branch_baseline.fetch( :failing_count )} failing (#{names})" end if default_branch_baseline.fetch( :pending_count ).positive? names = default_branch_baseline.fetch( :pending ).map { |e| e.fetch( :name ) }.join( ", " ) parts << "#{default_branch_baseline.fetch( :pending_count )} pending (#{names})" end parts << "no check-runs for active workflows" if default_branch_baseline.fetch( :no_check_evidence ) audit_concise_problems << "Baseline (#{default_branch_baseline.fetch( :default_branch, config.main_branch )}): #{parts.join( ', ' )} — fix before merge." elsif baseline_st == "attention" parts = [] if default_branch_baseline.fetch( :advisory_failing_count ).positive? names = default_branch_baseline.fetch( :advisory_failing ).map { |e| e.fetch( :name ) }.join( ", " ) parts << "#{default_branch_baseline.fetch( :advisory_failing_count )} advisory failing (#{names})" end if default_branch_baseline.fetch( :advisory_pending_count ).positive? names = default_branch_baseline.fetch( :advisory_pending ).map { |e| e.fetch( :name ) }.join( ", " ) parts << "#{default_branch_baseline.fetch( :advisory_pending_count )} advisory pending (#{names})" end audit_concise_problems << "Baseline (#{default_branch_baseline.fetch( :default_branch, config.main_branch )}): #{parts.join( ', ' )}." end if config.template_canonical.nil? || config.template_canonical.to_s.empty? puts_verbose "" puts_verbose "[Canonical Templates]" puts_verbose "HINT: canonical templates not configured — run carson setup to enable." end write_and_print_pr_monitor_report( report: monitor_report.merge( default_branch_baseline: default_branch_baseline, audit_status: audit_state ) ) exit_code = audit_state == "block" ? EXIT_BLOCK : EXIT_OK if json_output result = { command: "audit", status: audit_state, branch: current_branch, hooks: { status: hooks_status }, main_sync: main_sync, pr: monitor_report[ :pr ], checks: monitor_report.fetch( :checks ), baseline: { status: default_branch_baseline.fetch( :status ), repository: default_branch_baseline[ :repository ], failing_count: default_branch_baseline.fetch( :failing_count ), pending_count: default_branch_baseline.fetch( :pending_count ), advisory_failing_count: default_branch_baseline.fetch( :advisory_failing_count ), advisory_pending_count: default_branch_baseline.fetch( :advisory_pending_count ) }, problems: audit_concise_problems, exit_code: exit_code } out.puts JSON.pretty_generate( result ) else puts_verbose "" puts_verbose "[Audit Result]" puts_verbose "status: #{audit_state}" puts_verbose( audit_state == "block" ? "ACTION: local policy block must be resolved before commit/push." : "ACTION: no local hard block detected." ) unless verbose? audit_concise_problems.each { |problem| puts_line problem } puts_line "Audit: #{audit_state}" end end exit_code end |
#audit_all! ⇒ Object
Runs audit across all governed repositories.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/carson/runtime/audit.rb', line 161 def audit_all! repos = config.govern_repos if repos.empty? puts_line "No governed repositories configured." puts_line " Run carson onboard in each repo to register." return EXIT_ERROR end puts_line "" puts_line "Audit all (#{repos.length} repo#{plural_suffix( count: repos.length )})" passed = 0 blocked = 0 failed = 0 repos.each do |repo_path| repo_name = File.basename( repo_path ) unless Dir.exist?( repo_path ) puts_line "#{repo_name}: FAIL (path not found)" failed += 1 next end begin rt = build_scoped_runtime( repo_path: repo_path ) status = rt.audit! case status when EXIT_OK puts_line "#{repo_name}: ok" unless verbose? passed += 1 when EXIT_BLOCK puts_line "#{repo_name}: BLOCK" unless verbose? blocked += 1 else puts_line "#{repo_name}: FAIL" unless verbose? failed += 1 end rescue StandardError => e puts_line "#{repo_name}: FAIL (#{e.})" failed += 1 end end puts_line "" puts_line "Audit all complete: #{passed} ok, #{blocked} blocked, #{failed} failed." blocked.zero? && failed.zero? ? EXIT_OK : EXIT_BLOCK end |