lex-swarm-github 0.3.2 → 0.3.4

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: afb02c0714b2eeeb371b7d0890c04b6a5637be7b8584f62872cec6337baa980e
4
- data.tar.gz: 794646c0b9897d25e18fb22cf156c3f7ee2a5a07408aa23876b17cef00ece198
3
+ metadata.gz: 872fde87c10b7bf7e68f2aab5b56d05d9e66e552511061463e75fcabe714ca62
4
+ data.tar.gz: 72ccbe57f57adabf3ecce969ce4fb721485e9478aabe934e56ed159d6ca31959
5
5
  SHA512:
6
- metadata.gz: 5c20383d9e86e5db3135a6cca8af1b1b226348f9cada58232425e7ae8064c17c2366f6aab167404b2081a567e95e238cfd37fc23cebf338fedb9078c35d5591c
7
- data.tar.gz: 9d494a1404085b965f8bf6d272b9b875fc4bd3c21c291d9d07fdf45452fe356d6e3966f4e5fe6f42c142f5557b18ea80d68092fe33dbc0cd1d5c9628eea9e68e
6
+ metadata.gz: 4331730e59dafc94477cc02309afd0b04f2c56d5556570fe69169c490b62921e31ac6cc4e49606d5f93082dff93d652cfc68992f7a71ac150b1f764098acdffb
7
+ data.tar.gz: 39de677ac295aa04c728f47e16433b7a99c6e6baff36353d48702643b34f15d7dd204f70c1a25003342cc48d6352ac3c58d6ac0dab7439948eb4dce6d2dae4d2
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gemspec
6
6
 
7
7
  gem 'rspec', '~> 3.13'
8
8
  gem 'rubocop', '~> 1.75', require: false
9
+ gem 'rubocop-legion', '~> 0.1'
9
10
  gem 'rubocop-rspec', require: false
10
11
 
11
12
  if File.exist?(File.expand_path('../../legion-gaia', __dir__))
@@ -9,7 +9,7 @@ module Legion
9
9
  class LifecycleSubscriber < Legion::Extensions::Actors::Subscription
10
10
  def runner_class = self.class
11
11
  def runner_function = 'action'
12
- def check_subtask? = false
12
+ def check_subtask? = true
13
13
  def generate_task? = false
14
14
 
15
15
  def action(payload)
@@ -54,11 +54,7 @@ module Legion
54
54
  end
55
55
 
56
56
  def log
57
- return Legion::Logging if defined?(Legion::Logging)
58
-
59
- @log ||= Object.new.tap do |nl|
60
- %i[debug info warn error fatal].each { |m| nl.define_singleton_method(m) { |*| nil } }
61
- end
57
+ Legion::Logging
62
58
  end
63
59
  end
64
60
  end
@@ -6,7 +6,7 @@ module Legion
6
6
  module Extensions
7
7
  module SwarmGithub
8
8
  module Actor
9
- class StaleIssues < Legion::Extensions::Actors::Every
9
+ class StaleIssues < Legion::Extensions::Actors::Every # rubocop:disable Legion/Extension/EveryActorRequiresTime
10
10
  def runner_class
11
11
  Legion::Extensions::SwarmGithub::Runners::GithubSwarm
12
12
  end
@@ -7,7 +7,9 @@ module Legion
7
7
  module ExtensionLifecycle
8
8
  extend self
9
9
 
10
- def run_lifecycle(generation:, review:, review_k: nil, review_models: nil)
10
+ def run_lifecycle(generation: nil, review: nil, review_k: nil, review_models: nil, **payload)
11
+ generation ||= build_generation(payload)
12
+ review ||= build_review(payload)
11
13
  config = github_config
12
14
  return { success: false, error: :github_not_enabled } unless config[:enabled]
13
15
  return { success: false, error: :target_repo_missing } unless config[:target_repo]
@@ -64,7 +66,7 @@ module Legion
64
66
  github_client.commit_files(owner: owner, repo: repo, branch: branch, files: files, message: message)
65
67
  end
66
68
 
67
- def open_pull_request(owner:, repo:, branch:, base:, generation:, review:) # rubocop:disable Metrics/ParameterLists
69
+ def open_pull_request(owner:, repo:, branch:, base:, generation:, review:)
68
70
  return { success: false, error: :github_runner_unavailable } unless github_runner_available?
69
71
 
70
72
  body = build_pr_body(generation: generation, review: review)
@@ -84,7 +86,7 @@ module Legion
84
86
  return { success: true } unless pull_number && labels&.any?
85
87
  return { success: false, error: :github_runner_unavailable } unless github_runner_available?
86
88
 
87
- github_client.add_labels(owner: owner, repo: repo, issue_number: pull_number, labels: labels)
89
+ github_client.add_labels_to_issue(owner: owner, repo: repo, issue_number: pull_number, labels: labels)
88
90
  rescue StandardError => e
89
91
  log.warn("Label failed: #{e.message}")
90
92
  { success: false, error: e.message }
@@ -172,8 +174,9 @@ module Legion
172
174
  Legion::Extensions::SwarmGithub::Runners::PullRequestReviewer.review_pull_request(**kwargs)
173
175
  end
174
176
 
177
+ blocker_severities = %w[error critical].freeze
175
178
  approvals = reviews.count do |r|
176
- r[:status] == 'reviewed' && (r[:comments] || []).none? { |c| %w[error critical].include?(c[:severity]&.to_s) }
179
+ r[:status] == 'reviewed' && (r[:comments] || []).none? { |c| blocker_severities.include?(c[:severity]&.to_s) }
177
180
  end
178
181
  rejections = k - approvals
179
182
 
@@ -190,7 +193,7 @@ module Legion
190
193
  { success: true, skipped: true, reason: :review_error }
191
194
  end
192
195
 
193
- def handle_auto_merge(owner:, repo:, pull_number:, config:, review:, review_result: nil) # rubocop:disable Metrics/ParameterLists
196
+ def handle_auto_merge(owner:, repo:, pull_number:, config:, review:, review_result: nil)
194
197
  return unless config[:auto_merge] && review[:verdict]&.to_sym == :approve
195
198
  return if review_result && review_result[:consensus] == :request_changes
196
199
 
@@ -275,11 +278,31 @@ module Legion
275
278
  end
276
279
 
277
280
  def log
278
- return Legion::Logging if defined?(Legion::Logging)
281
+ Legion::Logging
282
+ end
279
283
 
280
- @log ||= Object.new.tap do |nl|
281
- %i[debug info warn error fatal].each { |m| nl.define_singleton_method(m) { |*| nil } }
282
- end
284
+ def build_generation(payload)
285
+ {
286
+ name: payload[:name],
287
+ generation_id: payload[:generation_id],
288
+ gap_id: payload[:gap_id],
289
+ gap_type: payload[:gap_type],
290
+ tier: payload[:tier],
291
+ code: payload[:code],
292
+ spec_code: payload[:spec_code],
293
+ file_path: payload[:file_path],
294
+ spec_path: payload[:spec_path]
295
+ }.compact
296
+ end
297
+
298
+ def build_review(payload)
299
+ {
300
+ verdict: payload[:verdict],
301
+ confidence: payload[:confidence],
302
+ stages: payload[:stages],
303
+ issues: payload[:issues],
304
+ passed: payload[:passed]
305
+ }.compact
283
306
  end
284
307
  end
285
308
  end
@@ -5,8 +5,8 @@ module Legion
5
5
  module SwarmGithub
6
6
  module Runners
7
7
  module GithubSwarm
8
- include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers) &&
9
- Legion::Extensions::Helpers.const_defined?(:Lex)
8
+ include Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?(:Helpers, false) &&
9
+ Legion::Extensions::Helpers.const_defined?(:Lex, false)
10
10
 
11
11
  def ingest_issue(repo:, issue_number:, title:, labels: [], **)
12
12
  key = issue_tracker.track(repo: repo, issue_number: issue_number, title: title, labels: labels)
@@ -43,7 +43,8 @@ module Legion
43
43
  return unless issue_number
44
44
 
45
45
  review_comments = review_result.dig(:review, :comments) || []
46
- approved = review_comments.none? { |c| %w[critical high].include?(c[:severity]&.to_s&.downcase) }
46
+ blocker_severities = %w[critical high].freeze
47
+ approved = review_comments.none? { |c| blocker_severities.include?(c[:severity]&.to_s&.downcase) }
47
48
  @issue_tracker&.record_validation(
48
49
  "#{repo}##{issue_number}",
49
50
  validator: :code_review,
@@ -31,7 +31,7 @@ module Legion
31
31
  owner: owner, repo: repo, pull_number: pull_number
32
32
  )[:result] || []
33
33
  rescue StandardError => e
34
- log.warn(e.message) if respond_to?(:log, true)
34
+ log.warn(e.message) if respond_to?(:log, true) # rubocop:disable Legion/HelperMigration/LoggingGuard
35
35
  []
36
36
  end
37
37
 
@@ -43,18 +43,18 @@ module Legion
43
43
  llm_kwargs = { message: prompt, caller: { extension: 'lex-swarm-github' } }
44
44
  llm_kwargs[:model] = model if model
45
45
  llm_kwargs[:provider] = provider if provider
46
- response = Legion::LLM.chat(**llm_kwargs)
46
+ response = Legion::LLM.chat(**llm_kwargs) # rubocop:disable Legion/HelperMigration/DirectLlm
47
47
  parse_review_response(response)
48
48
  end
49
49
  merge_chunk_reviews(reviews)
50
50
  rescue StandardError => e
51
- log.warn("Review generation failed: #{e.message}") if respond_to?(:log, true)
51
+ log.warn("Review generation failed: #{e.message}") if respond_to?(:log, true) # rubocop:disable Legion/HelperMigration/LoggingGuard
52
52
  { summary: 'Review generation failed', comments: [] }
53
53
  end
54
54
 
55
55
  def merge_chunk_reviews(reviews)
56
56
  merged_comments = reviews.flat_map { |r| r[:comments] || [] }
57
- summaries = reviews.map { |r| r[:summary] }.compact
57
+ summaries = reviews.filter_map { |r| r[:summary] }
58
58
  {
59
59
  summary: summaries.join("\n\n"),
60
60
  comments: merged_comments
@@ -62,9 +62,9 @@ module Legion
62
62
  end
63
63
 
64
64
  def parse_review_response(response)
65
- Legion::JSON.parse(response)
65
+ json_parse(response)
66
66
  rescue StandardError => e
67
- log.warn(e.message) if respond_to?(:log, true)
67
+ log.warn(e.message) if respond_to?(:log, true) # rubocop:disable Legion/HelperMigration/LoggingGuard
68
68
  { summary: response.to_s, comments: [] }
69
69
  end
70
70
 
@@ -17,7 +17,7 @@ module Legion
17
17
 
18
18
  { notified: result[:ok] == true, channel: channel, ts: result[:ts] }
19
19
  rescue StandardError => e
20
- log.warn(e.message) if respond_to?(:log, true)
20
+ log.warn(e.message) if respond_to?(:log, true) # rubocop:disable Legion/HelperMigration/LoggingGuard
21
21
  { notified: false, reason: "slack error: #{e.message}" }
22
22
  end
23
23
 
@@ -22,7 +22,7 @@ module Legion
22
22
  review_id = result.dig(:result, 'id') || result.dig(:result, :id)
23
23
  { posted: true, review_id: review_id, comments_count: inline_comments.size }
24
24
  rescue StandardError => e
25
- log.warn(e.message) if respond_to?(:log, true)
25
+ log.warn(e.message) if respond_to?(:log, true) # rubocop:disable Legion/HelperMigration/LoggingGuard
26
26
  { posted: false, reason: "post failed: #{e.message}" }
27
27
  end
28
28
 
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module SwarmGithub
6
- VERSION = '0.3.2'
6
+ VERSION = '0.3.4'
7
7
  end
8
8
  end
9
9
  end
@@ -11,12 +11,12 @@ require 'legion/extensions/swarm_github/runners/review_poster'
11
11
  require 'legion/extensions/swarm_github/runners/review_notifier'
12
12
  require 'legion/extensions/swarm_github/runners/pr_pipeline'
13
13
  require 'legion/extensions/swarm_github/runners/extension_lifecycle'
14
- require 'legion/extensions/swarm_github/actors/lifecycle_subscriber' if defined?(Legion::Extensions::Actors::Subscription)
14
+ require 'legion/extensions/swarm_github/actors/lifecycle_subscriber' if defined?(Legion::Extensions::Actors::Subscription) # rubocop:disable Legion/HelperMigration/RequireDefinedGuard
15
15
 
16
16
  module Legion
17
17
  module Extensions
18
18
  module SwarmGithub
19
- extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
19
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core, false
20
20
  end
21
21
  end
22
22
  end
@@ -22,7 +22,7 @@ RSpec.describe Legion::Extensions::SwarmGithub::Actor::LifecycleSubscriber do
22
22
  end
23
23
 
24
24
  describe '#check_subtask?' do
25
- it { expect(actor.check_subtask?).to be false }
25
+ it { expect(actor.check_subtask?).to be true }
26
26
  end
27
27
 
28
28
  describe '#generate_task?' do
@@ -78,7 +78,7 @@ RSpec.describe Legion::Extensions::SwarmGithub::Runners::ExtensionLifecycle do
78
78
  allow(github_client).to receive(:create_pull_request).and_return(
79
79
  { result: { 'number' => 42, 'html_url' => 'https://github.com/org/repo/pull/42' } }
80
80
  )
81
- allow(github_client).to receive(:add_labels).and_return({ success: true })
81
+ allow(github_client).to receive(:add_labels_to_issue).and_return({ success: true })
82
82
  allow(runner).to receive(:pr_reviewer_available?).and_return(false)
83
83
  end
84
84
 
@@ -109,14 +109,46 @@ RSpec.describe Legion::Extensions::SwarmGithub::Runners::ExtensionLifecycle do
109
109
  )
110
110
  end
111
111
 
112
- it 'calls add_labels' do
112
+ it 'calls add_labels_to_issue' do
113
113
  runner.run_lifecycle(generation: generation, review: review)
114
- expect(github_client).to have_received(:add_labels).with(
114
+ expect(github_client).to have_received(:add_labels_to_issue).with(
115
115
  hash_including(labels: %w[auto-generated needs-review])
116
116
  )
117
117
  end
118
118
  end
119
119
 
120
+ context 'when called with a flat payload (no generation/review keys)' do
121
+ let(:github_client) { double('Github::Client') }
122
+
123
+ before do
124
+ runner.instance_variable_set(:@github_client, nil)
125
+ allow(runner).to receive(:github_config).and_return(
126
+ { enabled: true, target_repo: 'org/repo', target_branch: 'main',
127
+ auto_merge: false, pr_labels: [], branch_prefix: 'feature/auto-generated' }
128
+ )
129
+ stub_const('Legion::Extensions::Github::Client', Class.new)
130
+ allow(Legion::Extensions::Github::Client).to receive(:new).and_return(github_client)
131
+ allow(github_client).to receive(:create_branch).and_return({ success: true })
132
+ allow(github_client).to receive(:commit_files).and_return({ success: true })
133
+ allow(github_client).to receive(:create_pull_request).and_return(
134
+ { result: { 'number' => 99, 'html_url' => 'https://github.com/org/repo/pull/99' } }
135
+ )
136
+ allow(runner).to receive(:pr_reviewer_available?).and_return(false)
137
+ end
138
+
139
+ it 'builds generation and review from flat keys and returns success' do
140
+ result = runner.run_lifecycle(
141
+ name: 'lex-bar', generation_id: 'gen-flat', gap_id: 'gap-002',
142
+ gap_type: 'missing_runner', tier: 'utility',
143
+ code: '# code', spec_code: '# spec', file_path: 'lib/foo.rb', spec_path: 'spec/foo_spec.rb',
144
+ verdict: 'approve', stages: {}
145
+ )
146
+ expect(result[:success]).to be true
147
+ expect(result[:pull_number]).to eq(99)
148
+ expect(result[:generation_id]).to eq('gen-flat')
149
+ end
150
+ end
151
+
120
152
  context 'when branch creation fails' do
121
153
  before do
122
154
  runner.instance_variable_set(:@github_client, nil)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-swarm-github
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity