geet 0.23.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 +4 -4
- data/.github/workflows/ci.yml +31 -4
- data/.gitignore +0 -1
- data/.ruby-version +1 -0
- data/Gemfile +3 -6
- data/bin/geet +2 -8
- data/geet.gemspec +4 -4
- data/lib/geet/commandline/configuration.rb +1 -1
- data/lib/geet/commandline/editor.rb +0 -2
- data/lib/geet/git/repository.rb +12 -21
- data/lib/geet/github/abstract_issue.rb +0 -6
- data/lib/geet/github/api_interface.rb +37 -1
- data/lib/geet/github/issue.rb +0 -3
- data/lib/geet/github/milestone.rb +0 -2
- data/lib/geet/github/pr.rb +31 -4
- data/lib/geet/github/user.rb +0 -3
- data/lib/geet/gitlab/pr.rb +3 -1
- data/lib/geet/helpers/json_helper.rb +4 -0
- data/lib/geet/helpers/os_helper.rb +21 -7
- data/lib/geet/helpers/services_workflow_helper.rb +12 -0
- data/lib/geet/helpers/summary_helper.rb +7 -0
- data/lib/geet/services/abstract_create_issue.rb +5 -5
- data/lib/geet/services/add_upstream_repo.rb +6 -0
- data/lib/geet/services/close_milestones.rb +0 -2
- data/lib/geet/services/comment_pr.rb +0 -3
- data/lib/geet/services/create_gist.rb +0 -4
- data/lib/geet/services/create_issue.rb +0 -4
- data/lib/geet/services/create_pr.rb +24 -7
- data/lib/geet/services/list_issues.rb +0 -3
- data/lib/geet/services/merge_pr.rb +0 -2
- data/lib/geet/services/open_pr.rb +0 -3
- data/lib/geet/services/open_repo.rb +0 -2
- data/lib/geet/shared/http_error.rb +8 -2
- data/lib/geet/shared/repo_permissions.rb +7 -2
- data/lib/geet/shared/selection.rb +3 -2
- data/lib/geet/utils/attributes_selection_manager.rb +15 -4
- data/lib/geet/utils/git_client.rb +4 -1
- data/lib/geet/utils/manual_list_selection.rb +39 -14
- data/lib/geet/utils/string_matching_selection.rb +5 -0
- data/lib/geet/version.rb +1 -1
- data/lib/geet.rb +11 -0
- data/sorbet/config +3 -1
- data/sorbet/rbi/gems/{rbs@3.9.5.rbi → rbs@4.0.0.dev.5.rbi} +2013 -680
- data/sorbet/rbi/gems/require-hooks@0.2.2.rbi +110 -0
- data/sorbet/rbi/gems/{spoom@1.6.3.rbi → spoom@1.7.11.rbi} +1139 -2246
- data/sorbet/rbi/gems/{tapioca@0.16.11.rbi → tapioca@0.17.10.rbi} +721 -835
- data/sorbet/rbi/gems/tsort@0.2.0.rbi +393 -0
- data/sorbet/rbi/gems/tty-prompt@0.23.1.rbi +3300 -2
- data/sorbet/rbi/gems/zeitwerk@2.7.4.rbi +1196 -0
- data/sorbet/rbi/shims/unresolved_gem_constants.rbi +4 -0
- data/spec/integration/create_pr_spec.rb +225 -147
- data/spec/integration/merge_pr_spec.rb +84 -85
- data/spec/spec_helper.rb +1 -1
- metadata +40 -6
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
2
3
|
|
|
3
4
|
require 'io/console' # stdlib
|
|
4
5
|
|
|
5
|
-
require_relative 'abstract_create_issue'
|
|
6
|
-
require_relative '../shared/repo_permissions'
|
|
7
|
-
require_relative '../shared/selection'
|
|
8
|
-
require_relative 'add_upstream_repo'
|
|
9
|
-
|
|
10
6
|
module Geet
|
|
11
7
|
module Services
|
|
12
8
|
class CreatePr < AbstractCreateIssue
|
|
@@ -25,10 +21,11 @@ module Geet
|
|
|
25
21
|
# :labels
|
|
26
22
|
# :reviewers
|
|
27
23
|
# :open_browser
|
|
24
|
+
# :automerge
|
|
28
25
|
#
|
|
29
26
|
def execute(
|
|
30
27
|
title, description, labels: nil, milestone: nil, reviewers: nil,
|
|
31
|
-
base: nil, draft: false, open_browser: false, **
|
|
28
|
+
base: nil, draft: false, open_browser: false, automerge: false, **
|
|
32
29
|
)
|
|
33
30
|
ensure_clean_tree
|
|
34
31
|
|
|
@@ -54,6 +51,8 @@ module Geet
|
|
|
54
51
|
edit_pr(pr, selected_labels, selected_milestone, selected_reviewers)
|
|
55
52
|
end
|
|
56
53
|
|
|
54
|
+
enable_automerge(pr) if automerge
|
|
55
|
+
|
|
57
56
|
if open_browser
|
|
58
57
|
open_file_with_default_application(pr.link)
|
|
59
58
|
else
|
|
@@ -87,7 +86,9 @@ module Geet
|
|
|
87
86
|
selection_manager.select_attributes
|
|
88
87
|
end
|
|
89
88
|
|
|
90
|
-
|
|
89
|
+
# `input` is a Sorbet workaround (error 7001).
|
|
90
|
+
#
|
|
91
|
+
def sync_with_remote_branch(input: T.untyped)
|
|
91
92
|
# Fetching doesn't have a real world case when there isn't a remote branch. It's also not generally
|
|
92
93
|
# useful when there is a remote branch, however, since a force push is an option, it's important
|
|
93
94
|
# to be 100% sure of the current diff.
|
|
@@ -189,6 +190,22 @@ module Geet
|
|
|
189
190
|
pr.request_review(reviewer_usernames)
|
|
190
191
|
end
|
|
191
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
|
|
192
209
|
end
|
|
193
210
|
end
|
|
194
211
|
end
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
3
|
+
|
|
1
4
|
module Geet
|
|
2
5
|
module Shared
|
|
3
6
|
class HttpError < RuntimeError
|
|
4
|
-
|
|
7
|
+
extend T::Sig
|
|
8
|
+
|
|
9
|
+
sig { returns(Integer) }
|
|
5
10
|
attr_reader :code
|
|
6
11
|
|
|
12
|
+
sig { params(message: String, code: T.any(Integer, String)).void }
|
|
7
13
|
def initialize(message, code)
|
|
8
14
|
super(message)
|
|
9
|
-
@code = code.to_i
|
|
15
|
+
@code = T.let(code.to_i, Integer)
|
|
10
16
|
end
|
|
11
17
|
end
|
|
12
18
|
end
|
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
2
3
|
|
|
3
4
|
module Geet
|
|
4
5
|
module Shared
|
|
5
6
|
module RepoPermissions
|
|
7
|
+
extend T::Sig
|
|
8
|
+
|
|
6
9
|
PERMISSION_ADMIN = 'admin'
|
|
7
10
|
PERMISSION_WRITE = 'write'
|
|
8
11
|
PERMISSION_READ = 'read'
|
|
9
12
|
PERMISSION_NONE = 'none'
|
|
10
13
|
|
|
14
|
+
ALL_PERMISSIONS = T.let(T.unsafe(nil), T::Array[String]) if defined?(T::sig)
|
|
11
15
|
ALL_PERMISSIONS = [
|
|
12
16
|
PERMISSION_ADMIN,
|
|
13
17
|
PERMISSION_WRITE,
|
|
14
18
|
PERMISSION_READ,
|
|
15
19
|
PERMISSION_NONE,
|
|
16
|
-
]
|
|
20
|
+
].freeze
|
|
17
21
|
|
|
18
22
|
# Not worth creating a Permission class at this stage.
|
|
19
23
|
#
|
|
24
|
+
sig { params(subject_permission: String, object_permission: String).returns(T::Boolean) }
|
|
20
25
|
def permission_greater_or_equal_to?(subject_permission, object_permission)
|
|
21
|
-
ALL_PERMISSIONS.index(subject_permission) <= ALL_PERMISSIONS.index(object_permission)
|
|
26
|
+
T.must(ALL_PERMISSIONS.index(subject_permission)) <= T.must(ALL_PERMISSIONS.index(object_permission))
|
|
22
27
|
end
|
|
23
28
|
end
|
|
24
29
|
end
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
2
3
|
|
|
3
4
|
module Geet
|
|
4
5
|
module Shared
|
|
5
6
|
module Selection
|
|
6
|
-
MANUAL_LIST_SELECTION_FLAG = '-'
|
|
7
|
+
MANUAL_LIST_SELECTION_FLAG = '-'
|
|
7
8
|
# Don't select anything; return the null value.
|
|
8
|
-
SKIP_LIST_SELECTION_FLAG = ''
|
|
9
|
+
SKIP_LIST_SELECTION_FLAG = ''
|
|
9
10
|
|
|
10
11
|
SELECTION_SINGLE = :single
|
|
11
12
|
SELECTION_MULTIPLE = :multiple
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'manual_list_selection'
|
|
4
|
-
require_relative 'string_matching_selection'
|
|
5
|
-
require_relative '../shared/selection'
|
|
2
|
+
# typed: true
|
|
6
3
|
|
|
7
4
|
module Geet
|
|
8
5
|
module Utils
|
|
@@ -14,6 +11,8 @@ module Geet
|
|
|
14
11
|
# multiple attributes are required (typically, three).
|
|
15
12
|
#
|
|
16
13
|
class AttributesSelectionManager
|
|
14
|
+
extend T::Sig
|
|
15
|
+
|
|
17
16
|
include Geet::Shared::Selection
|
|
18
17
|
|
|
19
18
|
# Workaround for VCR not supporting multithreading; see https://github.com/vcr/vcr/issues/200.
|
|
@@ -24,6 +23,7 @@ module Geet
|
|
|
24
23
|
|
|
25
24
|
# Initialize the instance, and starts the background threads.
|
|
26
25
|
#
|
|
26
|
+
sig { params(repository: T.untyped, out: T.any(IO, StringIO)).void }
|
|
27
27
|
def initialize(repository, out: $stdout)
|
|
28
28
|
@repository = repository
|
|
29
29
|
@out = out
|
|
@@ -32,6 +32,16 @@ module Geet
|
|
|
32
32
|
|
|
33
33
|
# selection_type: SELECTION_SINGLE or SELECTION_MULTIPLE
|
|
34
34
|
#
|
|
35
|
+
sig {
|
|
36
|
+
params(
|
|
37
|
+
repository_call: T.untyped,
|
|
38
|
+
description: T.untyped,
|
|
39
|
+
pattern: T.untyped,
|
|
40
|
+
selection_type: T.untyped,
|
|
41
|
+
name_method: T.untyped,
|
|
42
|
+
pre_selection_hook: T.nilable(T.proc.params(all_reviewers: T::Array[T.untyped]).void)
|
|
43
|
+
).void
|
|
44
|
+
}
|
|
35
45
|
def add_attribute(repository_call, description, pattern, selection_type, name_method: nil, &pre_selection_hook)
|
|
36
46
|
raise "Unrecognized selection type #{selection_type.inspect}" if ![SELECTION_SINGLE, SELECTION_MULTIPLE].include?(selection_type)
|
|
37
47
|
|
|
@@ -42,6 +52,7 @@ module Geet
|
|
|
42
52
|
|
|
43
53
|
# Select and return the attributes, in the same order they've been added.
|
|
44
54
|
#
|
|
55
|
+
sig { returns(T.untyped) }
|
|
45
56
|
def select_attributes
|
|
46
57
|
@selections_data.map do |finder_thread, description, pattern, selection_type, name_method, pre_selection_hook|
|
|
47
58
|
entries = finder_thread.value
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
2
3
|
|
|
3
4
|
require 'English'
|
|
4
5
|
require 'shellwords'
|
|
5
|
-
require_relative '../helpers/os_helper'
|
|
6
6
|
|
|
7
7
|
module Geet
|
|
8
8
|
module Utils
|
|
9
9
|
# Represents the git program interface; used for performing git operations.
|
|
10
10
|
#
|
|
11
11
|
class GitClient
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
12
14
|
include Geet::Helpers::OsHelper
|
|
13
15
|
|
|
14
16
|
ORIGIN_NAME = 'origin'
|
|
@@ -40,6 +42,7 @@ module Geet
|
|
|
40
42
|
|
|
41
43
|
CLEAN_TREE_MESSAGE_REGEX = /^nothing to commit, working tree clean$/
|
|
42
44
|
|
|
45
|
+
sig { params(location: T.untyped ).void }
|
|
43
46
|
def initialize(location: nil)
|
|
44
47
|
@location = location
|
|
45
48
|
end
|
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
2
3
|
|
|
3
4
|
require 'tty-prompt'
|
|
4
5
|
|
|
5
6
|
module Geet
|
|
6
7
|
module Utils
|
|
7
8
|
class ManualListSelection
|
|
9
|
+
extend T::Sig
|
|
10
|
+
|
|
8
11
|
NO_SELECTION_KEY = '(none)'
|
|
9
12
|
|
|
10
13
|
PAGER_SIZE = 16
|
|
11
14
|
|
|
12
15
|
# Shows a prompt for selecting an entry from a list.
|
|
13
16
|
#
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
# returned.
|
|
24
|
-
#
|
|
17
|
+
sig {
|
|
18
|
+
type_parameters(:T).params(
|
|
19
|
+
entry_type: String, # description of the entries type.
|
|
20
|
+
entries: T::Array[T.type_parameter(:T)], # array of objects; if they're not strings, must also pass :name_method.
|
|
21
|
+
name_method: T.nilable(Symbol) # required when non-string objects are passed as entries; its invocation
|
|
22
|
+
# on each object must return a string, which is used as key.
|
|
23
|
+
).returns(T.nilable(T.type_parameter(:T))) # selected entry. nil is returned if the null entry (NO_SELECTION_KEY) is
|
|
24
|
+
# selected, or if there are no entries.
|
|
25
|
+
}
|
|
25
26
|
def select_entry(entry_type, entries, name_method: nil)
|
|
26
27
|
return nil if entries.empty?
|
|
27
28
|
|
|
@@ -37,12 +38,17 @@ module Geet
|
|
|
37
38
|
|
|
38
39
|
# Shows a prompt for selecting an entry from a list.
|
|
39
40
|
#
|
|
40
|
-
# Returns an empty array, without showing the prompt, if there are no entries.
|
|
41
|
-
#
|
|
42
41
|
# See #select_entry for the parameters.
|
|
43
42
|
#
|
|
44
43
|
# returns: array of entries.
|
|
45
44
|
#
|
|
45
|
+
sig {
|
|
46
|
+
type_parameters(:T).params(
|
|
47
|
+
entry_type: String,
|
|
48
|
+
entries: T::Array[T.type_parameter(:T)],
|
|
49
|
+
name_method: T.nilable(Symbol)
|
|
50
|
+
).returns(T::Array[T.type_parameter(:T)]) # empty array, without showing the prompt, if there are no entries.
|
|
51
|
+
}
|
|
46
52
|
def select_entries(entry_type, entries, name_method: nil)
|
|
47
53
|
return [] if entries.empty?
|
|
48
54
|
|
|
@@ -55,10 +61,17 @@ module Geet
|
|
|
55
61
|
|
|
56
62
|
private
|
|
57
63
|
|
|
64
|
+
sig { params(entries: T::Array[T.untyped], entry_type: String).void }
|
|
58
65
|
def check_entries(entries, entry_type)
|
|
59
66
|
raise "No #{entry_type} provided!" if entries.empty?
|
|
60
67
|
end
|
|
61
68
|
|
|
69
|
+
sig {
|
|
70
|
+
params(
|
|
71
|
+
entries: T::Array[T.untyped],
|
|
72
|
+
name_method: T.nilable(Symbol)
|
|
73
|
+
).returns(T::Hash[String, T.untyped])
|
|
74
|
+
}
|
|
62
75
|
def create_entries_map(entries, name_method)
|
|
63
76
|
entries.each_with_object({}) do |entry, current_map|
|
|
64
77
|
key = name_method ? entry.send(name_method) : entry
|
|
@@ -66,17 +79,29 @@ module Geet
|
|
|
66
79
|
end
|
|
67
80
|
end
|
|
68
81
|
|
|
82
|
+
sig {
|
|
83
|
+
type_parameters(:T).params(entries: T::Hash[String, T.type_parameter(:T)]
|
|
84
|
+
).returns(T::Hash[String, T.type_parameter(:T)])
|
|
85
|
+
}
|
|
69
86
|
def add_no_selection_entry(entries)
|
|
70
87
|
{NO_SELECTION_KEY => nil}.merge(entries)
|
|
71
88
|
end
|
|
72
89
|
|
|
90
|
+
sig {
|
|
91
|
+
type_parameters(:T).params(
|
|
92
|
+
invocation_method: Symbol,
|
|
93
|
+
entry_type: String,
|
|
94
|
+
entries: T::Hash[String, T.type_parameter(:T)]
|
|
95
|
+
).returns(T.type_parameter(:T))
|
|
96
|
+
}
|
|
73
97
|
def show_prompt(invocation_method, entry_type, entries)
|
|
74
98
|
# Arguably inexact phrasing for avoiding language complexities.
|
|
75
99
|
prompt_title = "Please select the #{entry_type}(s):"
|
|
76
100
|
|
|
77
|
-
TTY::Prompt.new.send(invocation_method, prompt_title, entries, filter: true, per_page: PAGER_SIZE)
|
|
101
|
+
::TTY::Prompt.new.send(invocation_method, prompt_title, entries, filter: true, per_page: PAGER_SIZE)
|
|
78
102
|
end
|
|
79
103
|
|
|
104
|
+
sig { params(entry: T.untyped).returns(T::Boolean) }
|
|
80
105
|
def no_selection?(entry)
|
|
81
106
|
entry == NO_SELECTION_KEY
|
|
82
107
|
end
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: strict
|
|
2
3
|
|
|
3
4
|
module Geet
|
|
4
5
|
module Utils
|
|
5
6
|
class StringMatchingSelection
|
|
7
|
+
extend T::Sig
|
|
8
|
+
|
|
9
|
+
sig { params(entry_type: String, entries: T::Array[T.untyped], pattern: String, name_method: T.nilable(Symbol)).returns(T.untyped) }
|
|
6
10
|
def select_entry(entry_type, entries, pattern, name_method: nil)
|
|
7
11
|
entries_found = entries.select do |entry|
|
|
8
12
|
entry = entry.send(name_method) if name_method
|
|
@@ -19,6 +23,7 @@ module Geet
|
|
|
19
23
|
end
|
|
20
24
|
end
|
|
21
25
|
|
|
26
|
+
sig { params(entry_type: String, entries: T::Array[T.untyped], raw_patterns: String, name_method: T.nilable(Symbol)).returns(T::Array[T.untyped]) }
|
|
22
27
|
def select_entries(entry_type, entries, raw_patterns, name_method: nil)
|
|
23
28
|
patterns = raw_patterns.split(',')
|
|
24
29
|
|
data/lib/geet/version.rb
CHANGED
data/lib/geet.rb
ADDED
data/sorbet/config
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
--disable-watchman
|
|
2
2
|
--suppress-payload-superclass-redefinition-for=Reline::ANSI
|
|
3
3
|
--dir=.
|
|
4
|
-
--ignore=lib
|
|
5
4
|
--ignore=spec
|
|
5
|
+
# Without a Bundler lockfile, we need to ignore gems vendored in CI, to avoid typechecking gem sources
|
|
6
|
+
# that may clash with those in the repo.
|
|
7
|
+
--ignore=vendor/bundle
|