robot_lab-audit 0.2.6 → 0.2.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1172ca1cc991073953bedbf72f533175c7436852d201e6645ed65d75d7117807
4
- data.tar.gz: 87aff3fc9ae887a2e774571305e07a6f95ae1837ce1145179cfd6862c90406a9
3
+ metadata.gz: ef178bcab0fa1ddd03a137fb4c2bb97032d66e3e78120a171970c87bcc5ca6af
4
+ data.tar.gz: 0eeb18834c2ef6a1355e528308ef31805d86c48e1557359b75d1372d8ef5e52f
5
5
  SHA512:
6
- metadata.gz: b377176b95d73963e9cda9bc83a985abaf863a0a8478d0c6ee82b67be49e012e28c77bfe5fba0ac6f6ab58dfea648b8182844bcb9797c93a8c4dab5c3e58ed16
7
- data.tar.gz: 9b79363dba9c3d3cf7e5982cc097f9e429e89a0ec262e25af65b45a998d62b05449f10a0ae19a195bc566857fa7956e886c8d95db1565cb5b4d9620d684a2a17
6
+ metadata.gz: 77e6579549b5b4d05cc34162cdae304cd1fa1c38ce6d5257cb3c4344f62bef4fa6adaaa6bd2afddbecaf6dd7b953048c44229e8b3841ff6bb601c7c3fbb11097
7
+ data.tar.gz: 0d1b28b9d41dc6d312f2777752664b91f5f197d765c26bf17d3ca84a42f09cb25e049e4ddad141ced0f6ae2d18e9c687d5e4160a0945ad08f9f83ead419520f2
data/.envrc CHANGED
@@ -1,3 +1,6 @@
1
+ # robot_lab_project/robot_lab-audit/.envrc
2
+
1
3
  source_up
4
+
2
5
  export RR=`pwd`
3
6
  export BUNDLE_GEMFILE=Gemfile
data/CHANGELOG.md CHANGED
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.8] - 2026-09-09
11
+
12
+ Released in lockstep with `robot_lab` core v0.2.8: this gem now resolves the released core gem from RubyGems instead of the local sibling checkout (local-path development remains available via `BUNDLE_GEMFILE=Gemfile.local`). Also in this release: gem lifecycle tasks (build/install/release) moved from the Rakefile to the asgard task runner.
13
+
10
14
  ### Added
11
15
  - `.loki` Asgard task file: `test`, `rubocop`, `rubocop_fix`, `flog`, `flay`, `quality`, `build`, `install`, `release`, and `console` tasks via the Asgard task runner
12
16
  - `flay_check` Rake task: structural code duplication gate (mass threshold 50); integrated into the `quality` Rake task
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/gem_tasks'
4
3
  require 'rake/testtask'
5
4
 
6
5
  Rake::TestTask.new(:test) do |t|
@@ -24,101 +23,6 @@ task :test_file, [:file] do |_t, args|
24
23
  ruby "test/#{args[:file]}"
25
24
  end
26
25
 
27
- desc 'Check code complexity with Flog (warn >=20, fail >=50)'
28
- task :flog_check do
29
- require 'flog'
30
-
31
- method_warn = 20.0
32
- method_fail = 50.0
33
-
34
- flogger = Flog.new(all: true)
35
- flogger.flog(*Dir.glob('lib/**/*.rb'))
36
-
37
- warnings = []
38
- failures = []
39
-
40
- flogger.each_by_score do |method, score|
41
- next if method.end_with?('#none')
42
-
43
- if score > method_fail
44
- failures << "#{format('%.1f', score)}: #{method}"
45
- elsif score > method_warn
46
- warnings << "#{format('%.1f', score)}: #{method}"
47
- end
48
- end
49
-
50
- unless warnings.empty?
51
- puts "\nFlog warnings (#{method_warn}–#{method_fail}) — target for future refactoring:"
52
- warnings.each { |v| puts " #{v}" }
53
- end
54
-
55
- if failures.empty?
56
- puts "\nFlog: no methods exceed the failure threshold (>=#{method_fail})"
57
- else
58
- puts "\nFlog failures (>=#{method_fail}) — must be refactored:"
59
- failures.each { |v| puts " #{v}" }
60
- abort "\nFlog quality gate failed: #{failures.size} method(s) exceed #{method_fail}"
61
- end
62
- end
63
-
64
- desc 'Check for structural code duplication with Flay (mass >= 50)'
65
- task :flay_check do
66
- require 'flay'
67
-
68
- mass_threshold = 50
69
-
70
- flay = Flay.new({ mass: mass_threshold, diff: false, verbose: false, summary: false, timeout: 60 })
71
- flay.process(*Dir.glob('lib/**/*.rb'))
72
- flay.analyze
73
-
74
- if flay.hashes.empty?
75
- puts "\nFlay: no structural duplication detected (mass >= #{mass_threshold})"
76
- else
77
- puts "\nFlay found structural duplication (mass >= #{mass_threshold}):"
78
- flay.report
79
- abort "\nFlay quality gate failed: #{flay.hashes.length} pattern(s) detected"
80
- end
81
- end
82
-
83
- desc 'Run all quality checks: tests (with coverage), RuboCop, Flog, and Flay'
84
- task :quality do
85
- gates = [
86
- ['Tests + Coverage', 'bundle exec rake test'],
87
- ['RuboCop', 'bundle exec rubocop'],
88
- ['Flog Complexity', 'bundle exec rake flog_check'],
89
- ['Flay Duplication', 'bundle exec rake flay_check']
90
- ]
91
-
92
- results = gates.map do |label, command|
93
- puts "\n#{'=' * 60}"
94
- puts "Quality Gate: #{label}"
95
- puts '=' * 60
96
- [label, system(command) ? :pass : :fail]
97
- end
98
-
99
- green = ->(s) { "\e[32m#{s}\e[0m" }
100
- red = ->(s) { "\e[31m#{s}\e[0m" }
101
- width = results.map { |label, _| label.length }.max
102
-
103
- puts "\n#{'=' * 60}"
104
- puts 'Quality Gate Summary'
105
- puts '=' * 60
106
- results.each do |label, status|
107
- badge = status == :pass ? green.call('PASS') : red.call('FAIL')
108
- puts " [#{badge}] #{label.ljust(width)}"
109
- end
110
- puts '-' * 60
111
-
112
- passed = results.count { |_, s| s == :pass }
113
- failed = results.count { |_, s| s == :fail }
114
- tally = "#{passed} passed, #{failed} failed"
115
- puts " #{failed.zero? ? green.call(tally) : red.call(tally)}"
116
- puts '=' * 60
117
-
118
- abort "\n#{red.call('Quality gate failed.')}" unless failed.zero?
119
- puts "\n#{green.call('All quality gates passed.')}"
120
- end
121
-
122
26
  namespace :docs do
123
27
  desc 'Build MkDocs documentation'
124
28
  task :build do
@@ -44,6 +44,9 @@ module RobotLab
44
44
  @db.execute_batch(SCHEMA)
45
45
  end
46
46
 
47
+ # :reek:LongParameterList -- one required keyword per SCHEMA column, on
48
+ # purpose: a caller that forgets a column raises ArgumentError instead
49
+ # of silently inserting NULL.
47
50
  def record_network_run(run_id:, network_name:, input:, result:, error_class:,
48
51
  error_message:, started_at:, finished_at:, duration_ms:)
49
52
  @db.execute(
@@ -56,6 +59,9 @@ module RobotLab
56
59
  )
57
60
  end
58
61
 
62
+ # :reek:LongParameterList -- one required keyword per SCHEMA column, on
63
+ # purpose: a caller that forgets a column raises ArgumentError instead
64
+ # of silently inserting NULL.
59
65
  def record_event(run_id:, event_type:, robot_name:, tool_name:, input:, output:,
60
66
  error_class:, error_message:, started_at:, finished_at:, duration_ms:)
61
67
  @db.execute(
@@ -70,12 +76,12 @@ module RobotLab
70
76
 
71
77
  def network_runs(limit: 100)
72
78
  rows = @db.execute('SELECT * FROM network_runs ORDER BY started_at DESC LIMIT ?', [limit])
73
- rows.map { |r| row_to_hash(:network_runs, r) }
79
+ rows.map { |r| row_to_hash(NETWORK_RUN_COLUMNS, r) }
74
80
  end
75
81
 
76
82
  def events_for(run_id)
77
83
  rows = @db.execute('SELECT * FROM audit_events WHERE run_id = ? ORDER BY started_at', [run_id])
78
- rows.map { |r| row_to_hash(:audit_events, r) }
84
+ rows.map { |r| row_to_hash(AUDIT_EVENT_COLUMNS, r) }
79
85
  end
80
86
 
81
87
  def recent_errors(limit: 50)
@@ -83,7 +89,7 @@ module RobotLab
83
89
  'SELECT * FROM audit_events WHERE error_class IS NOT NULL ORDER BY started_at DESC LIMIT ?',
84
90
  [limit]
85
91
  )
86
- rows.map { |r| row_to_hash(:audit_events, r) }
92
+ rows.map { |r| row_to_hash(AUDIT_EVENT_COLUMNS, r) }
87
93
  end
88
94
 
89
95
  NETWORK_RUN_COLUMNS = %i[id run_id network_name input result error_class
@@ -93,9 +99,8 @@ module RobotLab
93
99
 
94
100
  private
95
101
 
96
- def row_to_hash(table, row)
97
- cols = table == :network_runs ? NETWORK_RUN_COLUMNS : AUDIT_EVENT_COLUMNS
98
- cols.zip(row).to_h
102
+ def row_to_hash(columns, row)
103
+ columns.zip(row).to_h
99
104
  end
100
105
  end
101
106
  end
@@ -5,6 +5,12 @@ require 'json'
5
5
 
6
6
  module RobotLab
7
7
  module Audit
8
+ # :reek:RepeatedConditional -- each callback independently no-ops when
9
+ # event_log is nil, so the hook can be registered globally (RobotLab.on)
10
+ # before Audit.enable ever runs, or scoped to one robot/network without
11
+ # ever calling enable at all. A Null Object would remove the repeated
12
+ # check but couples every caller to a different `Hook.event_log` contract
13
+ # (an object always present vs. nil-means-disabled); not worth it here.
8
14
  class Hook < RobotLab::Hook
9
15
  self.namespace = :audit
10
16
 
@@ -62,6 +68,9 @@ module RobotLab
62
68
 
63
69
  private
64
70
 
71
+ # :reek:LongParameterList -- private helper with exactly two call
72
+ # sites, each passing every argument explicitly; a wrapper object
73
+ # would add indirection without making either call site clearer.
65
74
  def record_audit_event(event_type, ctx, started, tool_name, input, output, error)
66
75
  event_log.record_event(
67
76
  run_id: current_run_id,
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RobotLab
4
4
  module Audit
5
- VERSION = '0.2.6'
5
+ VERSION = '0.2.8'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: robot_lab-audit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
@@ -90,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
90
  - !ruby/object:Gem::Version
91
91
  version: '0'
92
92
  requirements: []
93
- rubygems_version: 4.0.17
93
+ rubygems_version: 4.0.20
94
94
  specification_version: 4
95
95
  summary: SQLite-backed execution audit log for RobotLab via the Hook system.
96
96
  test_files: []