geet 0.31.1 → 0.32.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/bin/geet +5 -1
- data/geet.gemspec +1 -1
- data/lib/geet/commandline/commands.rb +7 -0
- data/lib/geet/git/repository.rb +10 -6
- data/lib/geet/github/api_interface.rb +4 -4
- data/lib/geet/helpers/services_workflow_helper.rb +1 -5
- data/lib/geet/version.rb +1 -1
- data/spec/integration/open_pr_spec.rb +23 -0
- data/spec/unit/geet_launcher_spec.rb +41 -0
- data/spec/unit/git/repository_spec.rb +23 -0
- data/spec/unit/github/api_interface_spec.rb +31 -0
- data/spec/unit/github/pr_spec.rb +26 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8bb6a30bfcec73ad05020727ad03f85e828059db04e4691050d6a5d715c44640
|
|
4
|
+
data.tar.gz: e4e1b9b5befce16e5d7bfdce45c7b135037b391f81b3d022baf095efc077a7ea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9c723d2004d953f770e7d784f211e48608778b5cf1581c73ee0413cbcdb0888291738da0c2dde348cf694791a4fae785b4c5a2aec2cbc290302064410b3677fd
|
|
7
|
+
data.tar.gz: a57207c68b822d4d8ffc24aa0163b630af9600ce19645365b9c75457989785ccc774fa227ae51382120d378ada0b83be9d81cab0ae5a25d33549141cc2a18a6b
|
data/bin/geet
CHANGED
|
@@ -30,7 +30,11 @@ class GeetLauncher
|
|
|
30
30
|
return
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
repository = Git::Repository.new(
|
|
33
|
+
repository = Git::Repository.new(
|
|
34
|
+
upstream: !!options[:upstream],
|
|
35
|
+
protected_repositories: protected_repositories,
|
|
36
|
+
api_token_required: !TOKEN_OPTIONAL_COMMANDS.include?(command)
|
|
37
|
+
)
|
|
34
38
|
|
|
35
39
|
case command
|
|
36
40
|
when GIST_CREATE_COMMAND
|
data/geet.gemspec
CHANGED
|
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
|
8
8
|
s.platform = Gem::Platform::RUBY
|
|
9
9
|
s.required_ruby_version = ">= 3.3.0"
|
|
10
10
|
s.authors = ["Saverio Miroddi"]
|
|
11
|
-
s.date = "2026-
|
|
11
|
+
s.date = "2026-09-04"
|
|
12
12
|
s.email = ["saverio.pub2@gmail.com"]
|
|
13
13
|
s.homepage = "https://github.com/saveriomiroddi/geet"
|
|
14
14
|
s.summary = "Commandline interface for performing SCM host operations, eg. create a PR on GitHub"
|
|
@@ -20,6 +20,13 @@ module Geet
|
|
|
20
20
|
REPO_ADD_UPSTREAM_COMMAND = T.let("repo.add_upstream", String)
|
|
21
21
|
REPO_CREATE_COMMAND = T.let("repo.create", String)
|
|
22
22
|
REPO_OPEN_COMMAND = T.let("repo.open", String)
|
|
23
|
+
|
|
24
|
+
TOKEN_OPTIONAL_COMMANDS = T.let([
|
|
25
|
+
LABEL_LIST_COMMAND,
|
|
26
|
+
MILESTONE_LIST_COMMAND,
|
|
27
|
+
PR_LIST_COMMAND,
|
|
28
|
+
PR_OPEN_COMMAND,
|
|
29
|
+
].freeze, T::Array[String])
|
|
23
30
|
end
|
|
24
31
|
end
|
|
25
32
|
end
|
data/lib/geet/git/repository.rb
CHANGED
|
@@ -23,14 +23,15 @@ module Geet
|
|
|
23
23
|
upstream: T::Boolean,
|
|
24
24
|
git_client: Utils::GitClient,
|
|
25
25
|
warnings: T::Boolean, # disable all the warnings
|
|
26
|
-
protected_repositories: T::Array[String]
|
|
26
|
+
protected_repositories: T::Array[String], # warn when creating an issue/pr on these repos (format: `owner/repo`)
|
|
27
|
+
api_token_required: T::Boolean
|
|
27
28
|
)
|
|
28
29
|
.void
|
|
29
30
|
}
|
|
30
|
-
def initialize(upstream: false, git_client: DEFAULT_GIT_CLIENT, warnings: true, protected_repositories: [])
|
|
31
|
+
def initialize(upstream: false, git_client: DEFAULT_GIT_CLIENT, warnings: true, protected_repositories: [], api_token_required: true)
|
|
31
32
|
@upstream = upstream
|
|
32
33
|
@git_client = git_client
|
|
33
|
-
@api_token = T.let(extract_env_api_token, String)
|
|
34
|
+
@api_token = T.let(extract_env_api_token(required: api_token_required), T.nilable(String))
|
|
34
35
|
@warnings = warnings
|
|
35
36
|
@protected_repositories = protected_repositories
|
|
36
37
|
end
|
|
@@ -200,9 +201,12 @@ module Geet
|
|
|
200
201
|
@api_interface ||= Github::ApiInterface.new(@api_token, repo_path: @git_client.path(upstream: @upstream), upstream: @upstream)
|
|
201
202
|
end
|
|
202
203
|
|
|
203
|
-
sig { returns(String) }
|
|
204
|
-
def extract_env_api_token
|
|
205
|
-
ENV["GH_TOKEN"]
|
|
204
|
+
sig { params(required: T::Boolean).returns(T.nilable(String)) }
|
|
205
|
+
def extract_env_api_token(required:)
|
|
206
|
+
api_token = ENV["GH_TOKEN"]
|
|
207
|
+
raise "GH_TOKEN not set!" if required && api_token.nil?
|
|
208
|
+
|
|
209
|
+
api_token
|
|
206
210
|
end
|
|
207
211
|
end
|
|
208
212
|
end
|
|
@@ -22,13 +22,13 @@ module Geet
|
|
|
22
22
|
#
|
|
23
23
|
sig {
|
|
24
24
|
params(
|
|
25
|
-
api_token: String,
|
|
25
|
+
api_token: T.nilable(String),
|
|
26
26
|
repo_path: T.nilable(String),
|
|
27
27
|
upstream: T.nilable(T::Boolean)
|
|
28
28
|
).void
|
|
29
29
|
}
|
|
30
30
|
def initialize(api_token, repo_path: nil, upstream: nil)
|
|
31
|
-
@api_token = T.let(api_token, String)
|
|
31
|
+
@api_token = T.let(api_token, T.nilable(String))
|
|
32
32
|
@repository_path = T.let(repo_path, T.nilable(String))
|
|
33
33
|
@upstream = T.let(upstream, T.nilable(T::Boolean))
|
|
34
34
|
end
|
|
@@ -110,7 +110,7 @@ module Geet
|
|
|
110
110
|
|
|
111
111
|
Net::HTTP.start(uri.host, use_ssl: true) do |http|
|
|
112
112
|
request = Net::HTTP::Post.new(uri).tap do
|
|
113
|
-
it.basic_auth API_AUTH_USER, @api_token
|
|
113
|
+
it.basic_auth API_AUTH_USER, @api_token if @api_token
|
|
114
114
|
it["Accept"] = "application/vnd.github.v3+json"
|
|
115
115
|
it.body = {query:, variables:}.to_json
|
|
116
116
|
end
|
|
@@ -169,7 +169,7 @@ module Geet
|
|
|
169
169
|
Net::HTTP.start(uri.host, use_ssl: true) do |http|
|
|
170
170
|
request = http_class.new(uri)
|
|
171
171
|
|
|
172
|
-
request.basic_auth API_AUTH_USER, @api_token
|
|
172
|
+
request.basic_auth API_AUTH_USER, @api_token if @api_token
|
|
173
173
|
request.body = data.to_json if data
|
|
174
174
|
request["Accept"] = "application/vnd.github.v3+json"
|
|
175
175
|
|
|
@@ -26,11 +26,7 @@ module Geet
|
|
|
26
26
|
#
|
|
27
27
|
sig { returns(Github::PR) }
|
|
28
28
|
def checked_find_branch_pr
|
|
29
|
-
owner =
|
|
30
|
-
@repository.authenticated_user.username
|
|
31
|
-
else
|
|
32
|
-
@git_client.owner
|
|
33
|
-
end
|
|
29
|
+
owner = @git_client.owner
|
|
34
30
|
|
|
35
31
|
head = @git_client.current_branch
|
|
36
32
|
|
data/lib/geet/version.rb
CHANGED
|
@@ -39,4 +39,27 @@ describe Geet::Services::OpenPr do
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
end # context 'with github.com'
|
|
42
|
+
|
|
43
|
+
context "when opening an upstream PR" do
|
|
44
|
+
let(:repository) { instance_double(Geet::Git::Repository, upstream?: true) }
|
|
45
|
+
|
|
46
|
+
it "uses the origin owner without requesting the authenticated user" do
|
|
47
|
+
pr = instance_double(
|
|
48
|
+
Geet::Github::PR,
|
|
49
|
+
link: "https://github.com/upstream/repository/pull/1"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
allow(git_client).to receive(:owner).and_return(owner)
|
|
53
|
+
allow(git_client).to receive(:current_branch).and_return(branch)
|
|
54
|
+
allow(repository).to receive(:prs).with(owner:, head: branch).and_return([pr])
|
|
55
|
+
expect(repository).not_to receive(:authenticated_user)
|
|
56
|
+
|
|
57
|
+
expect {
|
|
58
|
+
service_instance = described_class.new(repository, git_client: git_client)
|
|
59
|
+
allow(service_instance).to receive(:open_file_with_default_application)
|
|
60
|
+
|
|
61
|
+
expect(service_instance.execute).to equal(pr)
|
|
62
|
+
}.to output("Finding PR with head (#{owner}:#{branch})...\n").to_stdout
|
|
63
|
+
end
|
|
64
|
+
end
|
|
42
65
|
end
|
|
@@ -45,5 +45,46 @@ describe GeetLauncher do
|
|
|
45
45
|
ensure
|
|
46
46
|
ENV["GH_TOKEN"] = without_token if without_token
|
|
47
47
|
end
|
|
48
|
+
|
|
49
|
+
{
|
|
50
|
+
Geet::Commandline::Commands::LABEL_LIST_COMMAND => Geet::Services::ListLabels,
|
|
51
|
+
Geet::Commandline::Commands::MILESTONE_LIST_COMMAND => Geet::Services::ListMilestones,
|
|
52
|
+
Geet::Commandline::Commands::PR_LIST_COMMAND => Geet::Services::ListPrs,
|
|
53
|
+
Geet::Commandline::Commands::PR_OPEN_COMMAND => Geet::Services::OpenPr,
|
|
54
|
+
}.each do |command, service_class|
|
|
55
|
+
it "dispatches #{command} with an optional API token" do
|
|
56
|
+
launcher = described_class.new
|
|
57
|
+
repository = instance_double(Geet::Git::Repository)
|
|
58
|
+
service = instance_double(service_class)
|
|
59
|
+
|
|
60
|
+
allow_any_instance_of(Geet::Commandline::Configuration).to receive(:decode_argv).and_return([command, {}])
|
|
61
|
+
allow(launcher).to receive(:protected_repositories).and_return([])
|
|
62
|
+
expect(Geet::Git::Repository).to receive(:new).with(
|
|
63
|
+
upstream: false,
|
|
64
|
+
protected_repositories: [],
|
|
65
|
+
api_token_required: false
|
|
66
|
+
).and_return(repository)
|
|
67
|
+
expect(service_class).to receive(:new).with(repository).and_return(service)
|
|
68
|
+
expect(service).to receive(:execute).with(no_args)
|
|
69
|
+
|
|
70
|
+
launcher.launch
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "requires an API token for issue.list" do
|
|
75
|
+
launcher = described_class.new
|
|
76
|
+
|
|
77
|
+
allow_any_instance_of(Geet::Commandline::Configuration).to receive(:decode_argv).and_return([
|
|
78
|
+
Geet::Commandline::Commands::ISSUE_LIST_COMMAND,
|
|
79
|
+
{},
|
|
80
|
+
])
|
|
81
|
+
allow(launcher).to receive(:protected_repositories).and_return([])
|
|
82
|
+
expect(Geet::Services::ListIssues).not_to receive(:new)
|
|
83
|
+
|
|
84
|
+
api_token = ENV.delete("GH_TOKEN")
|
|
85
|
+
expect { launcher.launch }.to raise_error("GH_TOKEN not set!")
|
|
86
|
+
ensure
|
|
87
|
+
ENV["GH_TOKEN"] = api_token if api_token
|
|
88
|
+
end
|
|
48
89
|
end
|
|
49
90
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
describe Geet::Git::Repository do
|
|
6
|
+
describe "API token handling" do
|
|
7
|
+
it "allows the token to be omitted when authentication is not required" do
|
|
8
|
+
api_token = ENV.delete("GH_TOKEN")
|
|
9
|
+
|
|
10
|
+
expect { described_class.new(api_token_required: false) }.not_to raise_error
|
|
11
|
+
ensure
|
|
12
|
+
ENV["GH_TOKEN"] = api_token if api_token
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "requires the token by default" do
|
|
16
|
+
api_token = ENV.delete("GH_TOKEN")
|
|
17
|
+
|
|
18
|
+
expect { described_class.new }.to raise_error("GH_TOKEN not set!")
|
|
19
|
+
ensure
|
|
20
|
+
ENV["GH_TOKEN"] = api_token if api_token
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "webmock/rspec"
|
|
5
|
+
|
|
6
|
+
describe Geet::Github::ApiInterface do
|
|
7
|
+
describe "authentication" do
|
|
8
|
+
let(:request_url) { "https://api.github.com/repos/owner/repository/issues" }
|
|
9
|
+
|
|
10
|
+
it "sends an unauthenticated request when no token is available" do
|
|
11
|
+
request = stub_request(:get, request_url)
|
|
12
|
+
.with { |actual_request| !actual_request.headers.key?("Authorization") }
|
|
13
|
+
.to_return(status: 200, body: "[]")
|
|
14
|
+
|
|
15
|
+
described_class.new(nil, repo_path: "owner/repository").send_request("issues")
|
|
16
|
+
|
|
17
|
+
expect(request).to have_been_requested.once
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "authenticates the request when a token is available" do
|
|
21
|
+
authorization = "Basic #{Base64.strict_encode64(":token")}"
|
|
22
|
+
request = stub_request(:get, request_url)
|
|
23
|
+
.with(headers: {"Authorization" => authorization})
|
|
24
|
+
.to_return(status: 200, body: "[]")
|
|
25
|
+
|
|
26
|
+
described_class.new("token", repo_path: "owner/repository").send_request("issues")
|
|
27
|
+
|
|
28
|
+
expect(request).to have_been_requested.once
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/spec/unit/github/pr_spec.rb
CHANGED
|
@@ -7,6 +7,32 @@ require_relative "../../../lib/geet/github/pr"
|
|
|
7
7
|
require_relative "../../../lib/geet/github/api_interface"
|
|
8
8
|
|
|
9
9
|
describe Geet::Github::PR do
|
|
10
|
+
describe ".list" do
|
|
11
|
+
it "filters upstream PRs by the supplied origin owner and branch" do
|
|
12
|
+
api_interface = instance_double(Geet::Github::ApiInterface, upstream?: true)
|
|
13
|
+
matching_pr = {
|
|
14
|
+
"number" => 1,
|
|
15
|
+
"title" => "Matching PR",
|
|
16
|
+
"html_url" => "https://github.com/upstream/repository/pull/1",
|
|
17
|
+
"head" => {"label" => "origin-owner:topic"},
|
|
18
|
+
}
|
|
19
|
+
other_pr = {
|
|
20
|
+
"number" => 2,
|
|
21
|
+
"title" => "Other PR",
|
|
22
|
+
"html_url" => "https://github.com/upstream/repository/pull/2",
|
|
23
|
+
"head" => {"label" => "other-owner:topic"},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
expect(api_interface).to receive(:send_request)
|
|
27
|
+
.with("pulls", multipage: true)
|
|
28
|
+
.and_return([matching_pr, other_pr])
|
|
29
|
+
|
|
30
|
+
prs = described_class.list(api_interface, owner: "origin-owner", head: "topic")
|
|
31
|
+
|
|
32
|
+
expect(prs.map(&:number)).to eq([1])
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
10
36
|
describe "#fetch_available_merge_method" do
|
|
11
37
|
let(:api_interface) { instance_double(Geet::Github::ApiInterface) }
|
|
12
38
|
let(:pr_number) { 123 }
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: geet
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.32.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Saverio Miroddi
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-09-04 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|
|
@@ -260,6 +260,8 @@ files:
|
|
|
260
260
|
- spec/spec_helper.rb
|
|
261
261
|
- spec/unit/commandline/configuration_spec.rb
|
|
262
262
|
- spec/unit/geet_launcher_spec.rb
|
|
263
|
+
- spec/unit/git/repository_spec.rb
|
|
264
|
+
- spec/unit/github/api_interface_spec.rb
|
|
263
265
|
- spec/unit/github/pr_spec.rb
|
|
264
266
|
- spec/unit/github/repository_spec.rb
|
|
265
267
|
- spec/unit/services/create_repo_spec.rb
|