geet 0.24.0 → 0.25.0

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: eeb2fbfeebdf5fad15d266e1c4b30fb5554f0418057b5fe279a4395149e9b4a2
4
- data.tar.gz: 78b4a9f2de093f896d494de393991980e9c619d10ce979a9b9b0941fff39381d
3
+ metadata.gz: 8f33191668d757cda4abaccb0524277d39923df81e1c17e7175fd6ccd27e0751
4
+ data.tar.gz: 0650f891e46a6c108cd59fd91cae1a396214dda38497eefa1cc6712df42b8971
5
5
  SHA512:
6
- metadata.gz: 17409c6c638abc9e27ffbae3af34bec526484c2baf084ddc7adff5beaf91ef3355377cb8bc515afc54fd97b3dc67090a26422891733638cacf3efadd26e4fdf4
7
- data.tar.gz: f6498c9223f91625dd85117322bef311a74b3bd027b9e4d51cb3e4bdea076418822e9637e56de9a462713eba218aaba33380509d23ddc115f04c7075ec6f3c3f
6
+ metadata.gz: 6748bd9048d5d828bf30e356d134c37f607370102792408535717f97e1a46938e59078c4f331173634e4e694ca05b68b8ae6d19464d02c19493533bc3a52b4fa
7
+ data.tar.gz: 1c27b254f20de196d7e74de519cc47358b9a0c2daf3fe565e7f593b8af6dae109dbab066015eda103c9599480ad1231b5e9b4318c96a3578bb15b002b7e82d17
@@ -3,7 +3,8 @@ name: CI
3
3
  on: [pull_request]
4
4
 
5
5
  jobs:
6
- test:
6
+ test_suite:
7
+ name: Test Suite
7
8
  runs-on: ubuntu-latest
8
9
  env:
9
10
  GITHUB_API_TOKEN: foo
@@ -12,9 +13,9 @@ jobs:
12
13
  matrix:
13
14
  ruby-version: [head, 4.0, 3.4, 3.3, 3.2]
14
15
  fail-fast: false
15
- continue-on-error: ${{ endsWith(matrix['ruby-version'], 'head') || matrix['ruby-version'] == 'debug' }}
16
+ continue-on-error: ${{ endsWith(matrix['ruby-version'], 'head') }}
16
17
  steps:
17
- - uses: actions/checkout@v5
18
+ - uses: actions/checkout@v6
18
19
  - uses: ruby/setup-ruby@v1
19
20
  with:
20
21
  ruby-version: ${{ matrix.ruby-version }}
@@ -22,9 +23,10 @@ jobs:
22
23
  - run: bundle install
23
24
  - run: bundle exec rspec
24
25
  sorbet:
26
+ name: Sorbet typecheck
25
27
  runs-on: ubuntu-latest
26
28
  steps:
27
- - uses: actions/checkout@v5
29
+ - uses: actions/checkout@v6
28
30
  with:
29
31
  fetch-depth: ${{ env.REPO_FETCH_DEPTH }}
30
32
  - uses: ruby/setup-ruby@v1
@@ -32,3 +34,14 @@ jobs:
32
34
  bundler-cache: true
33
35
  - name: Sorbet
34
36
  run: bundle exec srb typecheck
37
+ # Ruby summary job, for branch protection rules
38
+ # Head ruby failures are ignored due to continue-on-error in the test job.
39
+ all-tests:
40
+ name: Ruby (non-head) tests passed
41
+ runs-on: ubuntu-latest
42
+ needs: [test_suite]
43
+ if: always()
44
+ steps:
45
+ - name: Check test suite results
46
+ run: |
47
+ [[ "${{ needs.test_suite.result }}" == "success" ]] || exit 1
@@ -66,6 +66,7 @@ module Geet
66
66
  ]
67
67
 
68
68
  PR_CREATE_OPTIONS = [
69
+ ['-a', '--automerge', "Enable automerge (with default strategy)"],
69
70
  ['-o', '--open-browser', "Don't open the PR link in the browser after creation"],
70
71
  ['-b', '--base develop', "Specify the base branch; defaults to the main branch"],
71
72
  ['-d', '--draft', "Create as draft"],
@@ -9,6 +9,7 @@ module Geet
9
9
  class ApiInterface
10
10
  API_AUTH_USER = '' # We don't need the login, as the API key uniquely identifies the user
11
11
  API_BASE_URL = 'https://api.github.com'
12
+ GRAPHQL_API_URL = 'https://api.github.com/graphql'
12
13
 
13
14
  attr_reader :repository_path
14
15
 
@@ -69,6 +70,42 @@ module Geet
69
70
  end
70
71
  end
71
72
 
73
+ # Send a GraphQL request.
74
+ #
75
+ # Returns the parsed response data.
76
+ #
77
+ # params:
78
+ # :query: GraphQL query string
79
+ # :variables: (Hash) GraphQL variables
80
+ #
81
+ def send_graphql_request(query, variables: {})
82
+ uri = URI(GRAPHQL_API_URL)
83
+
84
+ Net::HTTP.start(uri.host, use_ssl: true) do |http|
85
+ request = Net::HTTP::Post.new(uri).tap do
86
+ it.basic_auth API_AUTH_USER, @api_token
87
+ it['Accept'] = 'application/vnd.github.v3+json'
88
+ it.body = {query:, variables:}.to_json
89
+ end
90
+
91
+ response = http.request(request)
92
+
93
+ parsed_response = JSON.parse(response.body) if response.body
94
+
95
+ if error?(response)
96
+ error_message = decode_and_format_error(parsed_response)
97
+ raise Geet::Shared::HttpError.new(error_message, response.code)
98
+ end
99
+
100
+ if parsed_response&.key?('errors')
101
+ error_messages = parsed_response['errors'].map { |err| err['message'] }.join(', ')
102
+ raise Geet::Shared::HttpError.new("GraphQL errors: #{error_messages}", response.code)
103
+ end
104
+
105
+ parsed_response&.fetch('data')
106
+ end
107
+ end
108
+
72
109
  private
73
110
 
74
111
  def api_url(api_path)
@@ -3,6 +3,13 @@
3
3
  module Geet
4
4
  module Github
5
5
  class PR < AbstractIssue
6
+ attr_reader :node_id
7
+
8
+ def initialize(number, api_interface, title, link, node_id: nil)
9
+ super(number, api_interface, title, link)
10
+ @node_id = node_id
11
+ end
12
+
6
13
  # See https://developer.github.com/v3/pulls/#create-a-pull-request
7
14
  #
8
15
  def self.create(title, description, head, api_interface, base, draft: false)
@@ -18,8 +25,9 @@ module Geet
18
25
  response = api_interface.send_request(api_path, data: request_data)
19
26
 
20
27
  number, title, link = response.fetch_values('number', 'title', 'html_url')
28
+ node_id = response['node_id']
21
29
 
22
- new(number, api_interface, title, link)
30
+ new(number, api_interface, title, link, node_id:)
23
31
  end
24
32
 
25
33
  # See https://developer.github.com/v3/pulls/#list-pull-requests
@@ -83,6 +91,28 @@ module Geet
83
91
  @api_interface.send_request(api_path, data: request_data)
84
92
  end
85
93
 
94
+ # Enable auto-merge for this PR using the repository's default merge method.
95
+ # See https://docs.github.com/en/graphql/reference/mutations#enablepullrequestautomerge
96
+ #
97
+ def enable_automerge
98
+ query = <<~GRAPHQL
99
+ mutation($pullRequestId: ID!) {
100
+ enablePullRequestAutoMerge(input: {pullRequestId: $pullRequestId}) {
101
+ pullRequest {
102
+ id
103
+ autoMergeRequest {
104
+ enabledAt
105
+ }
106
+ }
107
+ }
108
+ }
109
+ GRAPHQL
110
+
111
+ variables = { pullRequestId: @node_id }
112
+
113
+ @api_interface.send_graphql_request(query, variables:)
114
+ end
115
+
86
116
  class << self
87
117
  private
88
118
 
@@ -21,10 +21,11 @@ module Geet
21
21
  # :labels
22
22
  # :reviewers
23
23
  # :open_browser
24
+ # :automerge
24
25
  #
25
26
  def execute(
26
27
  title, description, labels: nil, milestone: nil, reviewers: nil,
27
- base: nil, draft: false, open_browser: false, **
28
+ base: nil, draft: false, open_browser: false, automerge: false, **
28
29
  )
29
30
  ensure_clean_tree
30
31
 
@@ -50,6 +51,8 @@ module Geet
50
51
  edit_pr(pr, selected_labels, selected_milestone, selected_reviewers)
51
52
  end
52
53
 
54
+ enable_automerge(pr) if automerge
55
+
53
56
  if open_browser
54
57
  open_file_with_default_application(pr.link)
55
58
  else
@@ -187,6 +190,22 @@ module Geet
187
190
  pr.request_review(reviewer_usernames)
188
191
  end
189
192
  end
193
+
194
+ def enable_automerge(pr)
195
+ if !pr.respond_to?(:enable_automerge)
196
+ raise "Automerge is not supported for this repository provider"
197
+ elsif !pr.respond_to?(:node_id) || pr.node_id.nil?
198
+ raise "Automerge requires node_id from the API (not available in the response)"
199
+ end
200
+
201
+ @out.puts "Enabling automerge..."
202
+
203
+ begin
204
+ pr.enable_automerge
205
+ rescue Geet::Shared::HttpError => e
206
+ @out.puts "Warning: Could not enable automerge: #{e.message}"
207
+ end
208
+ end
190
209
  end
191
210
  end
192
211
  end
data/lib/geet/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Geet
4
- VERSION = '0.24.0'
4
+ VERSION = '0.25.0'
5
5
  end
@@ -194,6 +194,74 @@ describe Geet::Services::CreatePr do
194
194
 
195
195
  expect(actual_output.string).to eql(expected_output)
196
196
  end
197
+
198
+ it 'should enable automerge after creating a PR' do
199
+ allow(git_client).to receive(:working_tree_clean?).and_return(true)
200
+ allow(git_client).to receive(:current_branch).and_return('mybranch')
201
+ allow(git_client).to receive(:main_branch).and_return('master')
202
+ allow(git_client).to receive(:remote_branch).and_return('mybranch')
203
+ allow(git_client).to receive(:remote_branch_diff_commits).and_return([])
204
+ allow(git_client).to receive(:fetch)
205
+ allow(git_client).to receive(:push)
206
+ allow(git_client).to receive(:remote).with(no_args).and_return('git@github.com:donaldduck/testrepo_f')
207
+
208
+ actual_output = StringIO.new
209
+
210
+ # Mock the repository and PR
211
+ allow(repository).to receive(:authenticated_user).and_return(
212
+ double(is_collaborator?: true, has_permission?: true)
213
+ )
214
+
215
+ mock_pr = double(
216
+ 'PR',
217
+ number: 1,
218
+ title: 'Title',
219
+ link: 'https://github.com/donaldduck/testrepo_f/pull/1',
220
+ node_id: 'PR_test123'
221
+ )
222
+ allow(mock_pr).to receive(:enable_automerge)
223
+
224
+ allow(repository).to receive(:create_pr).and_return(mock_pr)
225
+
226
+ service_instance = described_class.new(repository, out: actual_output, git_client: git_client)
227
+ service_instance.execute('Title', 'Description', automerge: true)
228
+
229
+ expect(mock_pr).to have_received(:enable_automerge)
230
+ expect(actual_output.string).to include('Enabling automerge...')
231
+ end
232
+
233
+ it 'should raise an error when automerge is requested but not supported' do
234
+ allow(git_client).to receive(:working_tree_clean?).and_return(true)
235
+ allow(git_client).to receive(:current_branch).and_return('mybranch')
236
+ allow(git_client).to receive(:main_branch).and_return('master')
237
+ allow(git_client).to receive(:remote_branch).and_return('mybranch')
238
+ allow(git_client).to receive(:remote_branch_diff_commits).and_return([])
239
+ allow(git_client).to receive(:fetch)
240
+ allow(git_client).to receive(:push)
241
+ allow(git_client).to receive(:remote).with(no_args).and_return('git@github.com:donaldduck/testrepo_f')
242
+
243
+ actual_output = StringIO.new
244
+
245
+ # Mock the repository and PR without enable_automerge method (simulating GitLab)
246
+ allow(repository).to receive(:authenticated_user).and_return(
247
+ double(is_collaborator?: true, has_permission?: true)
248
+ )
249
+
250
+ mock_pr = double(
251
+ 'PR',
252
+ number: 1,
253
+ title: 'Title',
254
+ link: 'https://github.com/donaldduck/testrepo_f/pull/1'
255
+ )
256
+
257
+ allow(repository).to receive(:create_pr).and_return(mock_pr)
258
+
259
+ service_instance = described_class.new(repository, out: actual_output, git_client: git_client)
260
+
261
+ expect do
262
+ service_instance.execute('Title', 'Description', automerge: true)
263
+ end.to raise_error(RuntimeError, 'Automerge is not supported for this repository provider')
264
+ end
197
265
  end
198
266
  end # context 'with github.com'
199
267
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.0
4
+ version: 0.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Saverio Miroddi
@@ -309,7 +309,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
309
309
  - !ruby/object:Gem::Version
310
310
  version: '0'
311
311
  requirements: []
312
- rubygems_version: 4.0.3
312
+ rubygems_version: 3.6.9
313
313
  specification_version: 4
314
314
  summary: Commandline interface for performing SCM host operations, eg. create a PR
315
315
  on GitHub