geet 0.30.0 → 0.31.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 +2 -2
- data/.ruby-version +1 -1
- data/README.md +17 -1
- data/Rakefile +1 -0
- data/bin/geet +11 -3
- data/docs/superpowers/plans/2026-07-12-pre-existing-build-and-ci-fixes.md +89 -0
- data/docs/superpowers/plans/2026-07-12-repo-create.md +435 -0
- data/docs/superpowers/plans/2026-07-12-ruby-4-upgrade.md +111 -0
- data/docs/superpowers/specs/2026-07-12-pre-existing-build-and-ci-fixes-design.md +15 -0
- data/docs/superpowers/specs/2026-07-12-repo-create-design.md +43 -0
- data/docs/superpowers/specs/2026-07-12-ruby-4-upgrade-design.md +17 -0
- data/geet.gemspec +4 -4
- data/lib/geet/commandline/commands.rb +1 -0
- data/lib/geet/commandline/configuration.rb +7 -0
- data/lib/geet/github/repository.rb +88 -0
- data/lib/geet/services/create_repo.rb +108 -0
- data/lib/geet/services/open_repo.rb +2 -4
- data/lib/geet/utils/git_client.rb +15 -4
- data/lib/geet/version.rb +1 -1
- data/sorbet/rbi/gems/{rake@12.3.3.rbi → rake@13.4.2.rbi} +724 -486
- data/spec/integration/open_repo_spec.rb +1 -2
- data/spec/unit/commandline/configuration_spec.rb +34 -0
- data/spec/unit/geet_launcher_spec.rb +38 -0
- data/spec/unit/github/repository_spec.rb +40 -0
- data/spec/unit/services/create_repo_spec.rb +187 -0
- data/spec/unit/services/merge_pr_spec.rb +21 -0
- data/spec/unit/utils/git_client_spec.rb +50 -0
- metadata +23 -9
|
@@ -8,7 +8,6 @@ require_relative "../../lib/geet/services/open_repo"
|
|
|
8
8
|
module Geet
|
|
9
9
|
describe Services::OpenRepo do
|
|
10
10
|
let(:git_client) { Utils::GitClient.new }
|
|
11
|
-
let(:repository) { Git::Repository.new(git_client: git_client) }
|
|
12
11
|
|
|
13
12
|
OWNER = "donaldduck"
|
|
14
13
|
REPOSITORY_NAME = "testrepo"
|
|
@@ -27,7 +26,7 @@ module Geet
|
|
|
27
26
|
expected_output = ""
|
|
28
27
|
|
|
29
28
|
expect {
|
|
30
|
-
service_instance = described_class.new(
|
|
29
|
+
service_instance = described_class.new(git_client: git_client)
|
|
31
30
|
|
|
32
31
|
expect(service_instance).to receive(:open_file_with_default_application).with(expected_url) do
|
|
33
32
|
# do nothing; just don't open the browser
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: false
|
|
3
|
+
|
|
4
|
+
require "spec_helper"
|
|
5
|
+
|
|
6
|
+
describe Geet::Commandline::Configuration do
|
|
7
|
+
describe "#decode_argv" do
|
|
8
|
+
it "decodes long repository creation options" do
|
|
9
|
+
stub_const("ARGV", [
|
|
10
|
+
"repo", "create",
|
|
11
|
+
"--visibility", "private",
|
|
12
|
+
"--upstream", "git@github.com:owner/project.git",
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
expect(described_class.new.decode_argv).to eq([
|
|
16
|
+
"repo.create",
|
|
17
|
+
{visibility: "private", upstream: "git@github.com:owner/project.git"},
|
|
18
|
+
])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "decodes short repository creation options" do
|
|
22
|
+
stub_const("ARGV", [
|
|
23
|
+
"repo", "create",
|
|
24
|
+
"-v", "public",
|
|
25
|
+
"-u", "https://github.com/owner/project.git",
|
|
26
|
+
])
|
|
27
|
+
|
|
28
|
+
expect(described_class.new.decode_argv).to eq([
|
|
29
|
+
"repo.create",
|
|
30
|
+
{visibility: "public", upstream: "https://github.com/owner/project.git"},
|
|
31
|
+
])
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: false
|
|
3
|
+
|
|
4
|
+
require "spec_helper"
|
|
5
|
+
load File.expand_path("../../bin/geet", __dir__)
|
|
6
|
+
|
|
7
|
+
describe GeetLauncher do
|
|
8
|
+
describe "#launch" do
|
|
9
|
+
it "dispatches repository creation before constructing an existing repository" do
|
|
10
|
+
service = instance_double(Geet::Services::CreateRepo)
|
|
11
|
+
allow_any_instance_of(Geet::Commandline::Configuration).to receive(:decode_argv).and_return([
|
|
12
|
+
Geet::Commandline::Commands::REPO_CREATE_COMMAND,
|
|
13
|
+
{visibility: "private"},
|
|
14
|
+
])
|
|
15
|
+
expect(Geet::Services::CreateRepo).to receive(:new).and_return(service)
|
|
16
|
+
expect(service).to receive(:execute).with(visibility: "private")
|
|
17
|
+
expect(Geet::Git::Repository).not_to receive(:new)
|
|
18
|
+
|
|
19
|
+
described_class.new.launch
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "dispatches repository opening without constructing an authenticated repository" do
|
|
23
|
+
service = instance_double(Geet::Services::OpenRepo)
|
|
24
|
+
allow_any_instance_of(Geet::Commandline::Configuration).to receive(:decode_argv).and_return([
|
|
25
|
+
Geet::Commandline::Commands::REPO_OPEN_COMMAND,
|
|
26
|
+
{upstream: true},
|
|
27
|
+
])
|
|
28
|
+
expect(Geet::Services::OpenRepo).to receive(:new).and_return(service)
|
|
29
|
+
expect(service).to receive(:execute).with(upstream: true)
|
|
30
|
+
expect(Geet::Git::Repository).not_to receive(:new)
|
|
31
|
+
|
|
32
|
+
without_token = ENV.delete("GH_TOKEN")
|
|
33
|
+
described_class.new.launch
|
|
34
|
+
ensure
|
|
35
|
+
ENV["GH_TOKEN"] = without_token if without_token
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe Geet::Github::Repository do
|
|
6
|
+
let(:api_interface) { instance_double(Geet::Github::ApiInterface) }
|
|
7
|
+
let(:response) do
|
|
8
|
+
{
|
|
9
|
+
"full_name" => "donald/geet",
|
|
10
|
+
"html_url" => "https://github.com/donald/geet",
|
|
11
|
+
"ssh_url" => "git@github.com:donald/geet.git",
|
|
12
|
+
}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "creates a private repository for the authenticated user" do
|
|
16
|
+
expect(api_interface).to receive(:send_request).with("/user/repos", data: {name: "geet", private: true}).and_return(response)
|
|
17
|
+
repository = described_class.create("geet", "private", api_interface)
|
|
18
|
+
expect([repository.full_name, repository.html_url, repository.ssh_url]).to eq(["donald/geet", "https://github.com/donald/geet", "git@github.com:donald/geet.git"])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "creates a public repository for the authenticated user" do
|
|
22
|
+
expect(api_interface).to receive(:send_request).with("/user/repos", data: {name: "geet", private: false}).and_return(response)
|
|
23
|
+
described_class.create("geet", "public", api_interface)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "requests a named fork" do
|
|
27
|
+
expect(api_interface).to receive(:send_request).with("/repos/upstream/project/forks", data: {name: "geet"}).and_return(response)
|
|
28
|
+
described_class.fork("upstream/project", "geet", api_interface)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "fetches a repository" do
|
|
32
|
+
expect(api_interface).to receive(:send_request).with("/repos/donald/geet").and_return(response)
|
|
33
|
+
expect(described_class.fetch("donald/geet", api_interface).full_name).to eq("donald/geet")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "updates the default branch" do
|
|
37
|
+
expect(api_interface).to receive(:send_request).with("/repos/donald/geet", data: {default_branch: "topic"}, http_method: :patch)
|
|
38
|
+
described_class.new(response, api_interface).update_default_branch("topic")
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: false
|
|
3
|
+
|
|
4
|
+
require "spec_helper"
|
|
5
|
+
|
|
6
|
+
describe Geet::Services::CreateRepo do
|
|
7
|
+
describe "#execute" do
|
|
8
|
+
let(:git_client) { instance_double(Geet::Utils::GitClient) }
|
|
9
|
+
let(:api_interface) { instance_double(Geet::Github::ApiInterface) }
|
|
10
|
+
let(:prompt) { instance_double(TTY::Prompt) }
|
|
11
|
+
let(:out) { StringIO.new }
|
|
12
|
+
|
|
13
|
+
subject(:service) do
|
|
14
|
+
described_class.new(
|
|
15
|
+
directory: "/tmp/geet",
|
|
16
|
+
out:,
|
|
17
|
+
git_client:,
|
|
18
|
+
api_interface:,
|
|
19
|
+
prompt:,
|
|
20
|
+
sleeper: ->(_) {},
|
|
21
|
+
fork_attempts: 3
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
before do
|
|
26
|
+
allow(git_client).to receive(:head_commit?).and_return(true)
|
|
27
|
+
allow(git_client).to receive(:current_branch).and_return("topic")
|
|
28
|
+
allow(git_client).to receive(:remote_defined?).with("origin").and_return(false)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "rejects a repository without commits before making an API request" do
|
|
32
|
+
allow(git_client).to receive(:head_commit?).and_return(false)
|
|
33
|
+
|
|
34
|
+
expect(Geet::Github::Repository).not_to receive(:create)
|
|
35
|
+
expect { service.execute(visibility: "private") }.to raise_error("The repository has no commits!")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "rejects a detached HEAD before prompting or making an API request" do
|
|
39
|
+
allow(git_client).to receive(:current_branch).and_raise("Couldn't find current branch")
|
|
40
|
+
|
|
41
|
+
expect(prompt).not_to receive(:select)
|
|
42
|
+
expect(Geet::Github::Repository).not_to receive(:create)
|
|
43
|
+
expect { service.execute }.to raise_error("Couldn't find current branch")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "rejects an existing origin before making an API request" do
|
|
47
|
+
allow(git_client).to receive(:remote_defined?).with("origin").and_return(true)
|
|
48
|
+
|
|
49
|
+
expect(Geet::Github::Repository).not_to receive(:create)
|
|
50
|
+
expect { service.execute(visibility: "private") }.to raise_error('Remote "origin" already exists!')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "rejects an existing upstream before making an API request" do
|
|
54
|
+
allow(git_client).to receive(:remote_defined?).with("upstream").and_return(true)
|
|
55
|
+
|
|
56
|
+
expect(Geet::Github::Repository).not_to receive(:fork)
|
|
57
|
+
expect { service.execute(upstream: "https://github.com/upstream/project.git") }
|
|
58
|
+
.to raise_error('Remote "upstream" already exists!')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "rejects invalid visibility before making an API request" do
|
|
62
|
+
expect(Geet::Github::Repository).not_to receive(:create)
|
|
63
|
+
expect { service.execute(visibility: "internal") }.to raise_error("Visibility must be private or public!")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "rejects visibility in fork mode before making an API request" do
|
|
67
|
+
allow(git_client).to receive(:remote_defined?).with("upstream").and_return(false)
|
|
68
|
+
|
|
69
|
+
expect(Geet::Github::Repository).not_to receive(:fork)
|
|
70
|
+
expect { service.execute(visibility: "private", upstream: "https://github.com/upstream/project.git") }
|
|
71
|
+
.to raise_error("Visibility can't be specified with upstream!")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "prompts for visibility when it is omitted" do
|
|
75
|
+
repository = instance_double(Geet::Github::Repository, ssh_url: "git@github.com:donald/geet.git", html_url: "https://github.com/donald/geet")
|
|
76
|
+
allow(prompt).to receive(:select).with("Visibility:", ["private", "public"]).and_return("private")
|
|
77
|
+
allow(Geet::Github::Repository).to receive(:create).with("geet", "private", api_interface).and_return(repository)
|
|
78
|
+
allow(git_client).to receive(:add_remote)
|
|
79
|
+
allow(git_client).to receive(:push)
|
|
80
|
+
allow(repository).to receive(:update_default_branch)
|
|
81
|
+
|
|
82
|
+
service.execute
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "does not prompt when public visibility is explicit" do
|
|
86
|
+
repository = instance_double(Geet::Github::Repository, ssh_url: "git@github.com:donald/geet.git", html_url: "https://github.com/donald/geet")
|
|
87
|
+
expect(prompt).not_to receive(:select)
|
|
88
|
+
allow(Geet::Github::Repository).to receive(:create).with("geet", "public", api_interface).and_return(repository)
|
|
89
|
+
allow(git_client).to receive(:add_remote)
|
|
90
|
+
allow(git_client).to receive(:push)
|
|
91
|
+
allow(repository).to receive(:update_default_branch)
|
|
92
|
+
|
|
93
|
+
service.execute(visibility: "public")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "creates a repository, configures origin, pushes, and updates the default branch in order" do
|
|
97
|
+
repository = instance_double(
|
|
98
|
+
Geet::Github::Repository,
|
|
99
|
+
full_name: "donald/geet",
|
|
100
|
+
ssh_url: "git@github.com:donald/geet.git",
|
|
101
|
+
html_url: "https://github.com/donald/geet"
|
|
102
|
+
)
|
|
103
|
+
allow(prompt).to receive(:select).and_return("private")
|
|
104
|
+
expect(Geet::Github::Repository).to receive(:create)
|
|
105
|
+
.with("geet", "private", api_interface).ordered.and_return(repository)
|
|
106
|
+
expect(git_client).to receive(:add_remote)
|
|
107
|
+
.with("origin", "git@github.com:donald/geet.git").ordered
|
|
108
|
+
expect(git_client).to receive(:push).with(remote_branch: "topic").ordered
|
|
109
|
+
expect(repository).to receive(:update_default_branch).with("topic").ordered
|
|
110
|
+
|
|
111
|
+
expect(service.execute).to eq(repository)
|
|
112
|
+
expect(out.string).to include("Repository address: https://github.com/donald/geet")
|
|
113
|
+
expect(out.string).to include("Default branch: topic")
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "waits for a fork, configures both remotes, pushes, and updates the default branch" do
|
|
117
|
+
upstream = "https://github.com/upstream/project.git"
|
|
118
|
+
provisional_repository = instance_double(Geet::Github::Repository, full_name: "donald/geet")
|
|
119
|
+
repository = instance_double(
|
|
120
|
+
Geet::Github::Repository,
|
|
121
|
+
ssh_url: "git@github.com:donald/geet.git",
|
|
122
|
+
html_url: "https://github.com/donald/geet"
|
|
123
|
+
)
|
|
124
|
+
sleeper = proc {}
|
|
125
|
+
service = described_class.new(
|
|
126
|
+
directory: "/tmp/geet", out:, git_client:, api_interface:, prompt:,
|
|
127
|
+
sleeper:, fork_attempts: 3
|
|
128
|
+
)
|
|
129
|
+
allow(git_client).to receive(:remote_defined?).with("upstream").and_return(false)
|
|
130
|
+
expect(git_client).to receive(:remote_path).with(upstream).ordered.and_return("upstream/project")
|
|
131
|
+
expect(Geet::Github::Repository).to receive(:fork)
|
|
132
|
+
.with("upstream/project", "geet", api_interface).ordered.and_return(provisional_repository)
|
|
133
|
+
expect(Geet::Github::Repository).to receive(:fetch)
|
|
134
|
+
.with("donald/geet", api_interface).ordered.and_raise(Geet::Shared::HttpError.new("Not Found", 404))
|
|
135
|
+
expect(sleeper).to receive(:call).with(1).ordered
|
|
136
|
+
expect(Geet::Github::Repository).to receive(:fetch)
|
|
137
|
+
.with("donald/geet", api_interface).ordered.and_return(repository)
|
|
138
|
+
expect(git_client).to receive(:add_remote)
|
|
139
|
+
.with("origin", "git@github.com:donald/geet.git").ordered
|
|
140
|
+
expect(git_client).to receive(:add_remote).with("upstream", upstream).ordered
|
|
141
|
+
expect(git_client).to receive(:push).with(remote_branch: "topic").ordered
|
|
142
|
+
expect(repository).to receive(:update_default_branch).with("topic").ordered
|
|
143
|
+
|
|
144
|
+
expect(service.execute(upstream:)).to eq(repository)
|
|
145
|
+
expect(out.string).to include("Repository address: https://github.com/donald/geet")
|
|
146
|
+
expect(out.string).to include("Default branch: topic")
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "times out after all fork fetch attempts return 404" do
|
|
150
|
+
upstream = "https://github.com/upstream/project.git"
|
|
151
|
+
provisional_repository = instance_double(Geet::Github::Repository, full_name: "donald/geet")
|
|
152
|
+
allow(git_client).to receive(:remote_defined?).with("upstream").and_return(false)
|
|
153
|
+
allow(git_client).to receive(:remote_path).with(upstream).and_return("upstream/project")
|
|
154
|
+
allow(Geet::Github::Repository).to receive(:fork).and_return(provisional_repository)
|
|
155
|
+
allow(Geet::Github::Repository).to receive(:fetch)
|
|
156
|
+
.and_raise(Geet::Shared::HttpError.new("Not Found", 404))
|
|
157
|
+
|
|
158
|
+
expect { service.execute(upstream:) }.to raise_error("Timed out waiting for fork donald/geet")
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "immediately propagates a non-404 fork fetch error" do
|
|
162
|
+
upstream = "https://github.com/upstream/project.git"
|
|
163
|
+
provisional_repository = instance_double(Geet::Github::Repository, full_name: "donald/geet")
|
|
164
|
+
error = Geet::Shared::HttpError.new("Server Error", 500)
|
|
165
|
+
allow(git_client).to receive(:remote_defined?).with("upstream").and_return(false)
|
|
166
|
+
allow(git_client).to receive(:remote_path).with(upstream).and_return("upstream/project")
|
|
167
|
+
allow(Geet::Github::Repository).to receive(:fork).and_return(provisional_repository)
|
|
168
|
+
allow(Geet::Github::Repository).to receive(:fetch).and_raise(error)
|
|
169
|
+
|
|
170
|
+
expect { service.execute(upstream:) }.to raise_error(error)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it "stops before pushing or updating the default branch when adding a remote fails" do
|
|
174
|
+
repository = instance_double(
|
|
175
|
+
Geet::Github::Repository,
|
|
176
|
+
ssh_url: "git@github.com:donald/geet.git",
|
|
177
|
+
html_url: "https://github.com/donald/geet"
|
|
178
|
+
)
|
|
179
|
+
allow(Geet::Github::Repository).to receive(:create).and_return(repository)
|
|
180
|
+
allow(git_client).to receive(:add_remote).and_raise("remote failure")
|
|
181
|
+
expect(git_client).not_to receive(:push)
|
|
182
|
+
expect(repository).not_to receive(:update_default_branch)
|
|
183
|
+
|
|
184
|
+
expect { service.execute(visibility: "private") }.to raise_error("remote failure")
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# typed: false
|
|
3
|
+
|
|
4
|
+
require "spec_helper"
|
|
5
|
+
|
|
6
|
+
require_relative "../../../lib/geet/git/repository"
|
|
7
|
+
require_relative "../../../lib/geet/services/merge_pr"
|
|
8
|
+
|
|
9
|
+
describe Geet::Services::MergePr do
|
|
10
|
+
describe "#execute" do
|
|
11
|
+
let(:repository) { instance_double(Geet::Git::Repository) }
|
|
12
|
+
let(:git_client) { instance_double(Geet::Utils::GitClient) }
|
|
13
|
+
|
|
14
|
+
subject { described_class.new(repository, out: StringIO.new, git_client: git_client) }
|
|
15
|
+
|
|
16
|
+
it "raises when both --rebase and --squash are passed" do
|
|
17
|
+
expect { subject.execute(rebase: true, squash: true) }
|
|
18
|
+
.to raise_error("Can't specify both --rebase and --squash")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
require "tmpdir"
|
|
5
|
+
|
|
6
|
+
describe Geet::Utils::GitClient do
|
|
7
|
+
subject { described_class.new }
|
|
8
|
+
|
|
9
|
+
describe "#head_commit?" do
|
|
10
|
+
it "returns true when HEAD resolves" do
|
|
11
|
+
allow(subject).to receive(:execute_git_command).with("rev-parse --verify HEAD", allow_error: true, silent_stderr: true).and_return("abc123")
|
|
12
|
+
expect(subject.head_commit?).to be(true)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "returns false for an unborn HEAD" do
|
|
16
|
+
allow(subject).to receive(:execute_git_command).with("rev-parse --verify HEAD", allow_error: true, silent_stderr: true).and_return("")
|
|
17
|
+
expect(subject.head_commit?).to be(false)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe "#remote_path" do
|
|
22
|
+
it "extracts an SSH repository path" do
|
|
23
|
+
expect(subject.remote_path("git@github.com:owner/project.git")).to eq("owner/project")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "extracts an HTTPS repository path" do
|
|
27
|
+
expect(subject.remote_path("https://github.com/owner/project.git")).to eq("owner/project")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "rejects an unsupported address" do
|
|
31
|
+
expect { subject.remote_path("owner/project") }.to raise_error(RuntimeError, 'Unexpected remote reference format: "owner/project"')
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "#add_remote" do
|
|
36
|
+
it "passes a URL containing shell metacharacters as a single argument" do
|
|
37
|
+
Dir.mktmpdir do |directory|
|
|
38
|
+
marker = File.join(directory, "injected")
|
|
39
|
+
url = "https://example.com/owner/project.git; touch #{marker}"
|
|
40
|
+
git_client = described_class.new(location: directory)
|
|
41
|
+
system("git", "init", "--quiet", directory, exception: true)
|
|
42
|
+
|
|
43
|
+
git_client.add_remote("origin", url)
|
|
44
|
+
|
|
45
|
+
expect(File).not_to exist(marker)
|
|
46
|
+
expect(`git -C #{directory.shellescape} remote get-url origin`.strip).to eq(url)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
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.31.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-07-24 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: base64
|
|
@@ -43,14 +43,14 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: 0.14
|
|
46
|
+
version: '0.14'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: 0.14
|
|
53
|
+
version: '0.14'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: sorbet-runtime
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -99,14 +99,14 @@ dependencies:
|
|
|
99
99
|
requirements:
|
|
100
100
|
- - "~>"
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '
|
|
102
|
+
version: '13.3'
|
|
103
103
|
type: :development
|
|
104
104
|
prerelease: false
|
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
106
|
requirements:
|
|
107
107
|
- - "~>"
|
|
108
108
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '
|
|
109
|
+
version: '13.3'
|
|
110
110
|
description: Commandline interface for performing SCM host operations, eg. create
|
|
111
111
|
a PR on GitHub, with support for multiple hosts.
|
|
112
112
|
email:
|
|
@@ -128,6 +128,12 @@ files:
|
|
|
128
128
|
- Rakefile
|
|
129
129
|
- bin/geet
|
|
130
130
|
- bin/tapioca
|
|
131
|
+
- docs/superpowers/plans/2026-07-12-pre-existing-build-and-ci-fixes.md
|
|
132
|
+
- docs/superpowers/plans/2026-07-12-repo-create.md
|
|
133
|
+
- docs/superpowers/plans/2026-07-12-ruby-4-upgrade.md
|
|
134
|
+
- docs/superpowers/specs/2026-07-12-pre-existing-build-and-ci-fixes-design.md
|
|
135
|
+
- docs/superpowers/specs/2026-07-12-repo-create-design.md
|
|
136
|
+
- docs/superpowers/specs/2026-07-12-ruby-4-upgrade-design.md
|
|
131
137
|
- extra/anonymize_vcr_data
|
|
132
138
|
- extra/issue_editing.png
|
|
133
139
|
- extra/pr_editing.png
|
|
@@ -145,6 +151,7 @@ files:
|
|
|
145
151
|
- lib/geet/github/label.rb
|
|
146
152
|
- lib/geet/github/milestone.rb
|
|
147
153
|
- lib/geet/github/pr.rb
|
|
154
|
+
- lib/geet/github/repository.rb
|
|
148
155
|
- lib/geet/github/user.rb
|
|
149
156
|
- lib/geet/helpers/json_helper.rb
|
|
150
157
|
- lib/geet/helpers/os_helper.rb
|
|
@@ -159,6 +166,7 @@ files:
|
|
|
159
166
|
- lib/geet/services/create_label.rb
|
|
160
167
|
- lib/geet/services/create_milestone.rb
|
|
161
168
|
- lib/geet/services/create_pr.rb
|
|
169
|
+
- lib/geet/services/create_repo.rb
|
|
162
170
|
- lib/geet/services/list_issues.rb
|
|
163
171
|
- lib/geet/services/list_labels.rb
|
|
164
172
|
- lib/geet/services/list_milestones.rb
|
|
@@ -201,7 +209,7 @@ files:
|
|
|
201
209
|
- sorbet/rbi/gems/public_suffix@6.0.1.rbi
|
|
202
210
|
- sorbet/rbi/gems/racc@1.8.1.rbi
|
|
203
211
|
- sorbet/rbi/gems/rainbow@3.1.1.rbi
|
|
204
|
-
- sorbet/rbi/gems/rake@
|
|
212
|
+
- sorbet/rbi/gems/rake@13.4.2.rbi
|
|
205
213
|
- sorbet/rbi/gems/rbi@0.3.7.rbi
|
|
206
214
|
- sorbet/rbi/gems/rbs@4.0.0.dev.5.rbi
|
|
207
215
|
- sorbet/rbi/gems/regexp_parser@2.11.2.rbi
|
|
@@ -250,7 +258,13 @@ files:
|
|
|
250
258
|
- spec/integration/open_pr_spec.rb
|
|
251
259
|
- spec/integration/open_repo_spec.rb
|
|
252
260
|
- spec/spec_helper.rb
|
|
261
|
+
- spec/unit/commandline/configuration_spec.rb
|
|
262
|
+
- spec/unit/geet_launcher_spec.rb
|
|
253
263
|
- spec/unit/github/pr_spec.rb
|
|
264
|
+
- spec/unit/github/repository_spec.rb
|
|
265
|
+
- spec/unit/services/create_repo_spec.rb
|
|
266
|
+
- spec/unit/services/merge_pr_spec.rb
|
|
267
|
+
- spec/unit/utils/git_client_spec.rb
|
|
254
268
|
- spec/vcr_cassettes/create_gist_private.yml
|
|
255
269
|
- spec/vcr_cassettes/create_gist_public.yml
|
|
256
270
|
- spec/vcr_cassettes/create_issue.yml
|
|
@@ -289,14 +303,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
289
303
|
requirements:
|
|
290
304
|
- - ">="
|
|
291
305
|
- !ruby/object:Gem::Version
|
|
292
|
-
version: 3.
|
|
306
|
+
version: 3.3.0
|
|
293
307
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
294
308
|
requirements:
|
|
295
309
|
- - ">="
|
|
296
310
|
- !ruby/object:Gem::Version
|
|
297
311
|
version: '0'
|
|
298
312
|
requirements: []
|
|
299
|
-
rubygems_version: 4.0.
|
|
313
|
+
rubygems_version: 4.0.10
|
|
300
314
|
specification_version: 4
|
|
301
315
|
summary: Commandline interface for performing SCM host operations, eg. create a PR
|
|
302
316
|
on GitHub
|