lex-swarm-github 0.3.0 → 0.3.1

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: a5b852399782262ebab25e020d689b147a55ce6f256134a9d53137b223e6ba8e
4
- data.tar.gz: 8804deca49b8a4bd37a262d90e152ade0e2359fd065f40ea4d32604bb0f885bf
3
+ metadata.gz: a1c767cd01e97e707ce4079c0a3313c723e984143e1e29cfcd580bfd7b391735
4
+ data.tar.gz: abf61ad73ed7875421cef31b0ff713031f5fc99bc61ece84943fdba22cf34169
5
5
  SHA512:
6
- metadata.gz: 798c161250c26ba51ca15c0696d7f97dcc794d78b34fc1696b466e0aba16196368b96f2e5fc9351c0b34244efdbf3fe0c35b0a7cca4bc09d684d1fd014cec361
7
- data.tar.gz: 5ad660b2154048f0974ff1ac444ac0a96f282c9ffa789455bb901a29b3c737cfe103ea0bfb8c177862daec6b11efe2d4606a221edcedb8689005b6c8dea76d38
6
+ metadata.gz: 99ae16ec047585531e0c41eedb26e55d4a4e4b2940562078bbc1c36f4f9eb734c08c1df8aef46aa35032ea68c2199e90d39cef50b7535eb8c179f16f90a689c9
7
+ data.tar.gz: f61a356f212fa8f7b7b250fa6d4fd9ef10446233842cf65410ec3239e76bb689793ef3694978b2e6ee67c00ea09f137b9bcc36d7ec0ad438b626ec5da9fa3875
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ return unless defined?(Legion::Extensions::Actors::Subscription)
4
+
5
+ module Legion
6
+ module Extensions
7
+ module SwarmGithub
8
+ module Actor
9
+ class LifecycleSubscriber < Legion::Extensions::Actors::Subscription
10
+ def runner_class = self.class
11
+ def runner_function = 'action'
12
+ def check_subtask? = false
13
+ def generate_task? = false
14
+
15
+ def action(payload)
16
+ verdict = (payload[:verdict] || payload['verdict']).to_s
17
+
18
+ unless verdict == 'approve' && github_lifecycle_enabled?
19
+ return { skipped: true, reason: verdict == 'approve' ? :github_disabled : :not_approved }
20
+ end
21
+
22
+ generation = payload[:generation] || {}
23
+ review = payload.except(:generation)
24
+
25
+ Runners::ExtensionLifecycle.run_lifecycle(generation: generation, review: review)
26
+ rescue StandardError => e
27
+ log.warn("LifecycleSubscriber failed: #{e.message}")
28
+ { success: false, error: e.message }
29
+ end
30
+
31
+ private
32
+
33
+ def github_lifecycle_enabled?
34
+ return false unless defined?(Legion::Settings)
35
+
36
+ Legion::Settings.dig(:codegen, :self_generate, :github, :enabled) == true
37
+ rescue StandardError => e
38
+ log.warn(e.message)
39
+ false
40
+ end
41
+
42
+ def log
43
+ return Legion::Logging if defined?(Legion::Logging)
44
+
45
+ @log ||= Object.new.tap do |nl|
46
+ %i[debug info warn error fatal].each { |m| nl.define_singleton_method(m) { |*| nil } }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module SwarmGithub
6
+ module Runners
7
+ module ExtensionLifecycle
8
+ extend self
9
+
10
+ def run_lifecycle(generation:, review:)
11
+ config = github_config
12
+ return { success: false, error: :github_not_enabled } unless config[:enabled]
13
+ return { success: false, error: :target_repo_missing } unless config[:target_repo]
14
+
15
+ owner, repo = config[:target_repo].split('/')
16
+ name = generation[:name] || generation[:generation_id]
17
+ branch_name = "#{config[:branch_prefix]}-#{name}-#{Time.now.strftime('%Y%m%d%H%M%S')}"
18
+
19
+ branch = create_lifecycle_branch(owner: owner, repo: repo,
20
+ branch: branch_name, from_ref: config[:target_branch])
21
+ return branch unless branch[:success]
22
+
23
+ commit = commit_generated_files(owner: owner, repo: repo, branch: branch_name,
24
+ generation: generation)
25
+ return commit unless commit[:success]
26
+
27
+ pr = open_pull_request(owner: owner, repo: repo, branch: branch_name,
28
+ base: config[:target_branch], generation: generation, review: review)
29
+ return pr unless pr[:success]
30
+
31
+ label_pull_request(owner: owner, repo: repo, pull_number: pr[:pull_number],
32
+ labels: config[:pr_labels])
33
+
34
+ handle_auto_merge(owner: owner, repo: repo, pull_number: pr[:pull_number],
35
+ config: config, review: review)
36
+
37
+ { success: true, pull_number: pr[:pull_number], html_url: pr[:html_url],
38
+ branch: branch_name, generation_id: generation[:generation_id] }
39
+ rescue StandardError => e
40
+ log.warn("ExtensionLifecycle failed: #{e.message}")
41
+ { success: false, error: e.message }
42
+ end
43
+
44
+ private
45
+
46
+ def create_lifecycle_branch(owner:, repo:, branch:, from_ref:)
47
+ return { success: false, error: :github_runner_unavailable } unless github_runner_available?
48
+
49
+ github_client.create_branch(owner: owner, repo: repo, branch: branch, from_ref: from_ref)
50
+ end
51
+
52
+ def commit_generated_files(owner:, repo:, branch:, generation:)
53
+ return { success: false, error: :github_runner_unavailable } unless github_runner_available?
54
+
55
+ files = build_file_list(generation)
56
+ message = "add auto-generated #{generation[:name]} from gap #{generation[:gap_id]}"
57
+
58
+ github_client.commit_files(owner: owner, repo: repo, branch: branch, files: files, message: message)
59
+ end
60
+
61
+ def open_pull_request(owner:, repo:, branch:, base:, generation:, review:) # rubocop:disable Metrics/ParameterLists
62
+ return { success: false, error: :github_runner_unavailable } unless github_runner_available?
63
+
64
+ body = build_pr_body(generation: generation, review: review)
65
+ result = github_client.create_pull_request(
66
+ owner: owner, repo: repo, title: "auto-generated: #{generation[:name]}",
67
+ head: branch, base: base, body: body
68
+ )
69
+
70
+ pr = result[:result] || {}
71
+ { success: true, pull_number: pr['number'], html_url: pr['html_url'] }
72
+ rescue StandardError => e
73
+ log.warn(e.message)
74
+ { success: false, error: e.message }
75
+ end
76
+
77
+ def label_pull_request(owner:, repo:, pull_number:, labels:)
78
+ return { success: true } unless pull_number && labels&.any?
79
+ return { success: false, error: :github_runner_unavailable } unless github_runner_available?
80
+
81
+ github_client.add_labels(owner: owner, repo: repo, issue_number: pull_number, labels: labels)
82
+ rescue StandardError => e
83
+ log.warn("Label failed: #{e.message}")
84
+ { success: false, error: e.message }
85
+ end
86
+
87
+ def handle_auto_merge(owner:, repo:, pull_number:, config:, review:)
88
+ return unless config[:auto_merge] && review[:verdict]&.to_sym == :approve
89
+
90
+ github_client.merge_pull_request(
91
+ owner: owner, repo: repo, pull_number: pull_number,
92
+ commit_title: 'auto-merge: generated extension'
93
+ )
94
+ rescue StandardError => e
95
+ log.warn("Auto-merge failed: #{e.message}")
96
+ end
97
+
98
+ def build_file_list(generation)
99
+ files = []
100
+ files << { path: generation[:file_path], content: generation[:code] } if generation[:code]
101
+ files << { path: generation[:spec_path], content: generation[:spec_code] } if generation[:spec_code]
102
+ files
103
+ end
104
+
105
+ def build_pr_body(generation:, review:)
106
+ stages = (review[:stages] || {}).map do |name, stage|
107
+ passed = stage.is_a?(Hash) && stage[:passed] ? 'pass' : 'fail'
108
+ "| #{name} | #{passed} | |"
109
+ end.join("\n")
110
+
111
+ <<~BODY
112
+ ## Auto-Generated Extension
113
+
114
+ **Gap**: #{generation[:gap_type]} - "#{generation[:name]}"
115
+ **Tier**: #{generation[:tier]}
116
+ **Generation ID**: #{generation[:generation_id]}
117
+
118
+ ### Validation Results
119
+
120
+ | Stage | Result | Details |
121
+ |-------|--------|---------|
122
+ #{stages}
123
+
124
+ ### Files
125
+
126
+ - `#{generation[:file_path]}` - Runner implementation
127
+ - `#{generation[:spec_path]}` - Specs
128
+ BODY
129
+ end
130
+
131
+ def github_config
132
+ return default_config unless defined?(Legion::Settings)
133
+
134
+ settings = Legion::Settings.dig(:codegen, :self_generate, :github) || {}
135
+ default_config.merge(settings)
136
+ rescue StandardError => e
137
+ log.warn(e.message)
138
+ default_config
139
+ end
140
+
141
+ def default_config
142
+ {
143
+ enabled: false, target_repo: nil, target_branch: 'main',
144
+ auto_merge: false, pr_labels: %w[auto-generated needs-review],
145
+ branch_prefix: 'feature/auto-generated'
146
+ }
147
+ end
148
+
149
+ def github_runner_available?
150
+ defined?(Legion::Extensions::Github::Client)
151
+ end
152
+
153
+ def github_client
154
+ @github_client ||= Legion::Extensions::Github::Client.new(**github_connection_opts)
155
+ end
156
+
157
+ def github_connection_opts
158
+ return {} unless defined?(Legion::Settings)
159
+
160
+ { token: Legion::Settings.dig(:github, :token) }
161
+ rescue StandardError => e
162
+ log.warn(e.message)
163
+ {}
164
+ end
165
+
166
+ def log
167
+ return Legion::Logging if defined?(Legion::Logging)
168
+
169
+ @log ||= Object.new.tap do |nl|
170
+ %i[debug info warn error fatal].each { |m| nl.define_singleton_method(m) { |*| nil } }
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -28,7 +28,8 @@ module Legion
28
28
  Legion::Extensions::Github::Client.new.list_pull_request_files(
29
29
  owner: owner, repo: repo, pull_number: pull_number
30
30
  )[:result] || []
31
- rescue StandardError
31
+ rescue StandardError => e
32
+ log.warn(e.message) if respond_to?(:log, true)
32
33
  []
33
34
  end
34
35
 
@@ -42,7 +43,7 @@ module Legion
42
43
  end
43
44
  merge_chunk_reviews(reviews)
44
45
  rescue StandardError => e
45
- Legion::Logging::Logger.warn "Review generation failed: #{e.message}"
46
+ log.warn("Review generation failed: #{e.message}") if respond_to?(:log, true)
46
47
  { summary: 'Review generation failed', comments: [] }
47
48
  end
48
49
 
@@ -56,8 +57,9 @@ module Legion
56
57
  end
57
58
 
58
59
  def parse_review_response(response)
59
- Legion::JSON.load(response)
60
- rescue StandardError
60
+ Legion::JSON.parse(response)
61
+ rescue StandardError => e
62
+ log.warn(e.message) if respond_to?(:log, true)
61
63
  { summary: response.to_s, comments: [] }
62
64
  end
63
65
 
@@ -17,6 +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
21
  { notified: false, reason: "slack error: #{e.message}" }
21
22
  end
22
23
 
@@ -22,6 +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
26
  { posted: false, reason: "post failed: #{e.message}" }
26
27
  end
27
28
 
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module SwarmGithub
6
- VERSION = '0.3.0'
6
+ VERSION = '0.3.1'
7
7
  end
8
8
  end
9
9
  end
@@ -10,6 +10,8 @@ require 'legion/extensions/swarm_github/helpers/diff_chunker'
10
10
  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
+ require 'legion/extensions/swarm_github/runners/extension_lifecycle'
14
+ require 'legion/extensions/swarm_github/actors/lifecycle_subscriber' if defined?(Legion::Extensions::Actors::Subscription)
13
15
 
14
16
  module Legion
15
17
  module Extensions
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Actors
6
+ class Subscription # rubocop:disable Lint/EmptyClass
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ $LOADED_FEATURES << 'legion/extensions/actors/subscription'
13
+
14
+ require_relative '../../../../../lib/legion/extensions/swarm_github/runners/extension_lifecycle'
15
+ require_relative '../../../../../lib/legion/extensions/swarm_github/actors/lifecycle_subscriber'
16
+
17
+ RSpec.describe Legion::Extensions::SwarmGithub::Actor::LifecycleSubscriber do
18
+ subject(:actor) { described_class.new }
19
+
20
+ describe '#runner_function' do
21
+ it { expect(actor.runner_function).to eq('action') }
22
+ end
23
+
24
+ describe '#check_subtask?' do
25
+ it { expect(actor.check_subtask?).to be false }
26
+ end
27
+
28
+ describe '#generate_task?' do
29
+ it { expect(actor.generate_task?).to be false }
30
+ end
31
+
32
+ describe '#action' do
33
+ let(:generation) do
34
+ { name: 'lex-foo', generation_id: 'gen-1', file_path: 'lib/foo.rb', spec_path: 'spec/foo_spec.rb' }
35
+ end
36
+
37
+ context 'when verdict is not approve' do
38
+ it 'skips with :not_approved reason' do
39
+ result = actor.action({ verdict: 'reject', generation: generation })
40
+ expect(result[:skipped]).to be true
41
+ expect(result[:reason]).to eq(:not_approved)
42
+ end
43
+
44
+ it 'handles string verdict' do
45
+ result = actor.action({ 'verdict' => 'pending', 'generation' => {} })
46
+ expect(result[:skipped]).to be true
47
+ expect(result[:reason]).to eq(:not_approved)
48
+ end
49
+ end
50
+
51
+ context 'when verdict is approve but github lifecycle is disabled' do
52
+ before do
53
+ allow(actor).to receive(:github_lifecycle_enabled?).and_return(false)
54
+ end
55
+
56
+ it 'skips with :github_disabled reason' do
57
+ result = actor.action({ verdict: 'approve', generation: generation })
58
+ expect(result[:skipped]).to be true
59
+ expect(result[:reason]).to eq(:github_disabled)
60
+ end
61
+ end
62
+
63
+ context 'when verdict is approve and github lifecycle is enabled' do
64
+ let(:lifecycle_result) { { success: true, pull_number: 99, html_url: 'https://github.com/org/repo/pull/99' } }
65
+
66
+ before do
67
+ allow(actor).to receive(:github_lifecycle_enabled?).and_return(true)
68
+ allow(Legion::Extensions::SwarmGithub::Runners::ExtensionLifecycle)
69
+ .to receive(:run_lifecycle).and_return(lifecycle_result)
70
+ end
71
+
72
+ it 'calls ExtensionLifecycle.run_lifecycle' do
73
+ actor.action({ verdict: 'approve', generation: generation })
74
+ expect(Legion::Extensions::SwarmGithub::Runners::ExtensionLifecycle)
75
+ .to have_received(:run_lifecycle)
76
+ end
77
+
78
+ it 'passes generation and review payload correctly' do
79
+ payload = { verdict: 'approve', generation: generation, extra_key: 'value' }
80
+ actor.action(payload)
81
+ expect(Legion::Extensions::SwarmGithub::Runners::ExtensionLifecycle)
82
+ .to have_received(:run_lifecycle).with(
83
+ generation: generation,
84
+ review: hash_including(verdict: 'approve')
85
+ )
86
+ end
87
+
88
+ it 'returns the lifecycle result' do
89
+ result = actor.action({ verdict: 'approve', generation: generation })
90
+ expect(result).to eq(lifecycle_result)
91
+ end
92
+ end
93
+
94
+ context 'when a StandardError is raised' do
95
+ before do
96
+ allow(actor).to receive(:github_lifecycle_enabled?).and_return(true)
97
+ allow(Legion::Extensions::SwarmGithub::Runners::ExtensionLifecycle)
98
+ .to receive(:run_lifecycle).and_raise(RuntimeError, 'boom')
99
+ end
100
+
101
+ it 'returns success false with error message' do
102
+ result = actor.action({ verdict: 'approve', generation: {} })
103
+ expect(result[:success]).to be false
104
+ expect(result[:error]).to eq('boom')
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'legion/extensions/swarm_github/runners/extension_lifecycle'
5
+
6
+ RSpec.describe Legion::Extensions::SwarmGithub::Runners::ExtensionLifecycle do
7
+ subject(:runner) { described_class }
8
+
9
+ after { described_class.instance_variable_set(:@github_client, nil) }
10
+
11
+ let(:generation) do
12
+ {
13
+ name: 'lex-foo',
14
+ generation_id: 'gen-abc123',
15
+ gap_id: 'gap-001',
16
+ gap_type: 'missing_runner',
17
+ tier: 'utility',
18
+ file_path: 'lib/legion/extensions/foo.rb',
19
+ spec_path: 'spec/legion/extensions/foo_spec.rb',
20
+ code: '# runner code',
21
+ spec_code: '# spec code'
22
+ }
23
+ end
24
+
25
+ let(:review) { { verdict: 'approve', stages: { syntax: { passed: true }, quality: { passed: true } } } }
26
+
27
+ describe '#run_lifecycle' do
28
+ context 'when github is not enabled' do
29
+ it 'returns github_not_enabled error' do
30
+ result = runner.run_lifecycle(generation: generation, review: review)
31
+ expect(result).to eq({ success: false, error: :github_not_enabled })
32
+ end
33
+ end
34
+
35
+ context 'when github is enabled but target_repo is missing' do
36
+ before do
37
+ allow(runner).to receive(:github_config).and_return(
38
+ { enabled: true, target_repo: nil, target_branch: 'main',
39
+ auto_merge: false, pr_labels: [], branch_prefix: 'feature/auto-generated' }
40
+ )
41
+ end
42
+
43
+ it 'returns target_repo_missing error' do
44
+ result = runner.run_lifecycle(generation: generation, review: review)
45
+ expect(result).to eq({ success: false, error: :target_repo_missing })
46
+ end
47
+ end
48
+
49
+ context 'when github runner is unavailable' do
50
+ before do
51
+ allow(runner).to receive(:github_config).and_return(
52
+ { enabled: true, target_repo: 'org/repo', target_branch: 'main',
53
+ auto_merge: false, pr_labels: [], branch_prefix: 'feature/auto-generated' }
54
+ )
55
+ allow(runner).to receive(:github_runner_available?).and_return(false)
56
+ end
57
+
58
+ it 'returns github_runner_unavailable on branch creation' do
59
+ result = runner.run_lifecycle(generation: generation, review: review)
60
+ expect(result).to eq({ success: false, error: :github_runner_unavailable })
61
+ end
62
+ end
63
+
64
+ context 'when all pipeline steps succeed' do
65
+ let(:github_client) { double('Github::Client') }
66
+
67
+ before do
68
+ runner.instance_variable_set(:@github_client, nil)
69
+ allow(runner).to receive(:github_config).and_return(
70
+ { enabled: true, target_repo: 'org/repo', target_branch: 'main',
71
+ auto_merge: false, pr_labels: %w[auto-generated needs-review],
72
+ branch_prefix: 'feature/auto-generated' }
73
+ )
74
+ stub_const('Legion::Extensions::Github::Client', Class.new)
75
+ allow(Legion::Extensions::Github::Client).to receive(:new).and_return(github_client)
76
+ allow(github_client).to receive(:create_branch).and_return({ success: true })
77
+ allow(github_client).to receive(:commit_files).and_return({ success: true })
78
+ allow(github_client).to receive(:create_pull_request).and_return(
79
+ { result: { 'number' => 42, 'html_url' => 'https://github.com/org/repo/pull/42' } }
80
+ )
81
+ allow(github_client).to receive(:add_labels).and_return({ success: true })
82
+ end
83
+
84
+ it 'returns success with PR details' do
85
+ result = runner.run_lifecycle(generation: generation, review: review)
86
+ expect(result[:success]).to be true
87
+ expect(result[:pull_number]).to eq(42)
88
+ expect(result[:html_url]).to eq('https://github.com/org/repo/pull/42')
89
+ expect(result[:generation_id]).to eq('gen-abc123')
90
+ end
91
+
92
+ it 'calls create_branch' do
93
+ runner.run_lifecycle(generation: generation, review: review)
94
+ expect(github_client).to have_received(:create_branch)
95
+ end
96
+
97
+ it 'calls commit_files with file list' do
98
+ runner.run_lifecycle(generation: generation, review: review)
99
+ expect(github_client).to have_received(:commit_files).with(
100
+ hash_including(files: array_including(hash_including(path: 'lib/legion/extensions/foo.rb')))
101
+ )
102
+ end
103
+
104
+ it 'calls create_pull_request' do
105
+ runner.run_lifecycle(generation: generation, review: review)
106
+ expect(github_client).to have_received(:create_pull_request).with(
107
+ hash_including(title: 'auto-generated: lex-foo')
108
+ )
109
+ end
110
+
111
+ it 'calls add_labels' do
112
+ runner.run_lifecycle(generation: generation, review: review)
113
+ expect(github_client).to have_received(:add_labels).with(
114
+ hash_including(labels: %w[auto-generated needs-review])
115
+ )
116
+ end
117
+ end
118
+
119
+ context 'when branch creation fails' do
120
+ before do
121
+ runner.instance_variable_set(:@github_client, nil)
122
+ allow(runner).to receive(:github_config).and_return(
123
+ { enabled: true, target_repo: 'org/repo', target_branch: 'main',
124
+ auto_merge: false, pr_labels: [], branch_prefix: 'feature/auto-generated' }
125
+ )
126
+ stub_const('Legion::Extensions::Github::Client', Class.new)
127
+ allow(Legion::Extensions::Github::Client).to receive(:new).and_return(double(
128
+ create_branch: { success: false, error: 'branch exists' }
129
+ ))
130
+ end
131
+
132
+ it 'propagates the branch error' do
133
+ result = runner.run_lifecycle(generation: generation, review: review)
134
+ expect(result[:success]).to be false
135
+ expect(result[:error]).to eq('branch exists')
136
+ end
137
+ end
138
+
139
+ context 'when auto_merge is enabled and verdict is approve' do
140
+ let(:github_client) { double('Github::Client') }
141
+ let(:review_approve) { { verdict: 'approve', stages: {} } }
142
+
143
+ before do
144
+ runner.instance_variable_set(:@github_client, nil)
145
+ allow(runner).to receive(:github_config).and_return(
146
+ { enabled: true, target_repo: 'org/repo', target_branch: 'main',
147
+ auto_merge: true, pr_labels: [], branch_prefix: 'feature/auto-generated' }
148
+ )
149
+ stub_const('Legion::Extensions::Github::Client', Class.new)
150
+ allow(Legion::Extensions::Github::Client).to receive(:new).and_return(github_client)
151
+ allow(github_client).to receive(:create_branch).and_return({ success: true })
152
+ allow(github_client).to receive(:commit_files).and_return({ success: true })
153
+ allow(github_client).to receive(:create_pull_request).and_return(
154
+ { result: { 'number' => 7, 'html_url' => 'https://github.com/org/repo/pull/7' } }
155
+ )
156
+ allow(github_client).to receive(:merge_pull_request).and_return({ success: true })
157
+ end
158
+
159
+ it 'calls merge_pull_request' do
160
+ runner.run_lifecycle(generation: generation, review: review_approve)
161
+ expect(github_client).to have_received(:merge_pull_request).with(
162
+ hash_including(pull_number: 7)
163
+ )
164
+ end
165
+ end
166
+
167
+ context 'when a StandardError is raised' do
168
+ before do
169
+ allow(runner).to receive(:github_config).and_raise(RuntimeError, 'unexpected failure')
170
+ end
171
+
172
+ it 'returns success false with error message' do
173
+ result = runner.run_lifecycle(generation: generation, review: review)
174
+ expect(result[:success]).to be false
175
+ expect(result[:error]).to eq('unexpected failure')
176
+ end
177
+ end
178
+ end
179
+ end
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.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -118,6 +118,7 @@ files:
118
118
  - Gemfile
119
119
  - lex-swarm-github.gemspec
120
120
  - lib/legion/extensions/swarm_github.rb
121
+ - lib/legion/extensions/swarm_github/actors/lifecycle_subscriber.rb
121
122
  - lib/legion/extensions/swarm_github/actors/pr_webhook.rb
122
123
  - lib/legion/extensions/swarm_github/actors/stale_issues.rb
123
124
  - lib/legion/extensions/swarm_github/client.rb
@@ -125,12 +126,14 @@ files:
125
126
  - lib/legion/extensions/swarm_github/helpers/issue_tracker.rb
126
127
  - lib/legion/extensions/swarm_github/helpers/mesh_integration.rb
127
128
  - lib/legion/extensions/swarm_github/helpers/pipeline.rb
129
+ - lib/legion/extensions/swarm_github/runners/extension_lifecycle.rb
128
130
  - lib/legion/extensions/swarm_github/runners/github_swarm.rb
129
131
  - lib/legion/extensions/swarm_github/runners/pr_pipeline.rb
130
132
  - lib/legion/extensions/swarm_github/runners/pull_request_reviewer.rb
131
133
  - lib/legion/extensions/swarm_github/runners/review_notifier.rb
132
134
  - lib/legion/extensions/swarm_github/runners/review_poster.rb
133
135
  - lib/legion/extensions/swarm_github/version.rb
136
+ - spec/legion/extensions/swarm_github/actors/lifecycle_subscriber_spec.rb
134
137
  - spec/legion/extensions/swarm_github/actors/pr_webhook_spec.rb
135
138
  - spec/legion/extensions/swarm_github/actors/stale_issues_spec.rb
136
139
  - spec/legion/extensions/swarm_github/client_spec.rb
@@ -138,6 +141,7 @@ files:
138
141
  - spec/legion/extensions/swarm_github/helpers/issue_tracker_spec.rb
139
142
  - spec/legion/extensions/swarm_github/helpers/mesh_integration_spec.rb
140
143
  - spec/legion/extensions/swarm_github/helpers/pipeline_spec.rb
144
+ - spec/legion/extensions/swarm_github/runners/extension_lifecycle_spec.rb
141
145
  - spec/legion/extensions/swarm_github/runners/github_swarm_spec.rb
142
146
  - spec/legion/extensions/swarm_github/runners/pr_pipeline_spec.rb
143
147
  - spec/legion/extensions/swarm_github/runners/pull_request_reviewer_spec.rb