loca 0.0.1 → 0.1.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/loca +31 -2
- data/lib/loca.rb +5 -9
- data/lib/loca/cli.rb +59 -33
- data/lib/loca/error.rb +7 -14
- data/lib/loca/git/branch_creator.rb +55 -0
- data/lib/loca/git/branch_deleter.rb +32 -0
- data/lib/loca/git/common.rb +42 -0
- data/lib/loca/url/parser.rb +61 -0
- data/lib/loca/url/validator.rb +45 -0
- data/lib/loca/utils.rb +12 -0
- data/lib/loca/version.rb +1 -1
- data/spec/loca/cli_spec.rb +33 -55
- data/spec/loca/git/branch_creator_spec.rb +18 -0
- data/spec/loca/git/common_spec.rb +46 -0
- data/spec/loca/url/parser_spec.rb +88 -0
- data/spec/loca/url/validator_spec.rb +26 -0
- data/spec/loca/utils_spec.rb +40 -0
- data/spec/spec_helper.rb +13 -59
- data/spec/support/matchers/terminate.rb +37 -0
- metadata +63 -41
- data/lib/loca/git.rb +0 -117
- data/lib/loca/url.rb +0 -37
- data/spec/e2e/github_spec.rb +0 -43
- data/spec/loca/git_spec.rb +0 -152
- data/spec/loca/url_spec.rb +0 -47
- data/spec/support/features/github_helper.rb +0 -55
data/lib/loca/utils.rb
ADDED
data/lib/loca/version.rb
CHANGED
data/spec/loca/cli_spec.rb
CHANGED
@@ -1,69 +1,47 @@
|
|
1
1
|
describe Loca::CLI do
|
2
|
-
let(:
|
3
|
-
let(:
|
4
|
-
|
2
|
+
let(:cli) { described_class.new(argv, stdin, stdout, stderr) }
|
3
|
+
let(:stdin) { StringIO.new } # from http://stackoverflow.com/a/16507814/3456726
|
4
|
+
let(:stdout) { StringIO.new }
|
5
|
+
let(:stderr) { StringIO.new }
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
describe 'c' do
|
12
|
-
it 'fetches and checks out the branch locally' do
|
13
|
-
expect_any_instance_of(Loca::Git).to receive(:branch_name).and_return 'PULL_1'
|
14
|
-
expect_any_instance_of(Loca::Git).to receive(:first_time_creating?).and_return true
|
15
|
-
|
16
|
-
output = capture(:stdout) { described_class.start(%W(c #{url})) }.strip
|
17
|
-
expect(output).to end_with 'Checked out PULL_1!'
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'fetches and overwrites existing branch if the user says yes to overwrite' do
|
21
|
-
expect_any_instance_of(Loca::Git).to receive(:branch_name).and_return 'PULL_1'
|
22
|
-
expect_any_instance_of(Loca::Git).to receive(:first_time_creating?).and_return false
|
23
|
-
silence(:stdout) { allow_any_instance_of(described_class).to receive(:yes?).and_return true }
|
24
|
-
|
25
|
-
output = capture(:stdout) { described_class.start(%W(c #{url})) }.strip
|
26
|
-
expect(output).to end_with 'Checked out PULL_1!'
|
27
|
-
end
|
7
|
+
describe "#execute!" do
|
8
|
+
context "-v" do
|
9
|
+
let(:argv) { ["-v"] }
|
28
10
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
11
|
+
it "returns the version" do
|
12
|
+
expect do
|
13
|
+
cli.execute!
|
14
|
+
expect(stdout.string).to eq Loca::VERSION
|
15
|
+
end.to raise_error(SystemExit) # because execute! calls `exit 0` (or 1, etc.)
|
16
|
+
# from http://stackoverflow.com/a/28047771/3456726
|
17
|
+
end
|
33
18
|
|
34
|
-
|
19
|
+
it "exits cleanly" do
|
20
|
+
expect { cli.execute! }.to terminate.with_code 0
|
21
|
+
end
|
35
22
|
end
|
36
23
|
|
37
|
-
|
38
|
-
|
39
|
-
end
|
24
|
+
context "-h" do
|
25
|
+
let(:argv) { ["-h"] }
|
40
26
|
|
41
|
-
|
42
|
-
|
43
|
-
|
27
|
+
it "returns the version" do
|
28
|
+
expect do
|
29
|
+
cli.execute!
|
30
|
+
expect(stdout.string).to include "Usage: loca <url> [options]"
|
31
|
+
end.to raise_error(SystemExit)
|
32
|
+
end
|
44
33
|
|
45
|
-
|
46
|
-
|
34
|
+
it "exits cleanly" do
|
35
|
+
expect { cli.execute! }.to terminate.with_code 0
|
36
|
+
end
|
47
37
|
end
|
48
|
-
end
|
49
38
|
|
50
|
-
|
51
|
-
|
52
|
-
expect_any_instance_of(Loca::Git).to receive(:branch_name).and_return 'PULL_1'
|
53
|
-
expect_any_instance_of(Loca::Git).to receive(:delete)
|
54
|
-
|
55
|
-
output = capture(:stdout) { described_class.start(%W(d #{url})) }.strip
|
56
|
-
expect(output).to eq 'Deleted PULL_1!'
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'raises an error when an invalid repo is supplied' do
|
60
|
-
expect { silence(:stderr) { described_class.start(%W(d #{invalid_url})) } }.to raise_error
|
61
|
-
end
|
62
|
-
end
|
39
|
+
context "invalid option" do
|
40
|
+
let(:argv) { ["--invalid-option"] }
|
63
41
|
|
64
|
-
|
65
|
-
|
66
|
-
|
42
|
+
it "exits 1" do
|
43
|
+
expect { cli.execute! }.to terminate.with_code 1
|
44
|
+
end
|
67
45
|
end
|
68
46
|
end
|
69
47
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
describe Loca::Git::BranchCreator do
|
2
|
+
subject { described_class.new("1337", "PULL_1337", "loca_r_smoll", "https://github.com/smoll/loca") }
|
3
|
+
|
4
|
+
# This is probably better off being functionally tested
|
5
|
+
# Otherwise, we end up stubbing out specific `git` commands which is very brittle
|
6
|
+
# See https://sethvargo.com/unit-and-functional-testing-git-with-rspec/
|
7
|
+
|
8
|
+
describe "#add_remote" do
|
9
|
+
it "uses and avoids duplicating a preexisting remote" do
|
10
|
+
expect(subject).to receive(:git).with("remote show -n").and_return "origin\n"
|
11
|
+
expect(subject).to receive(:git)
|
12
|
+
.with("config --get remote.origin.url")
|
13
|
+
.and_return "https://github.com/smoll/loca\n"
|
14
|
+
|
15
|
+
subject.send :add_remote
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
describe Loca::Git::Common do
|
2
|
+
let(:dummy_class) { Class.new { include Loca::Git::Common } }
|
3
|
+
subject { dummy_class.new }
|
4
|
+
|
5
|
+
describe "#git" do
|
6
|
+
context "nonexistent command" do
|
7
|
+
let(:command) { "fakegitcmd" }
|
8
|
+
|
9
|
+
it "raises an error" do
|
10
|
+
expect { subject.git(command) }.to raise_error
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#git_urls_match?" do
|
16
|
+
# These should all equate with one another:
|
17
|
+
let(:remote_url) { "https://github.com/smoll/loca" }
|
18
|
+
let(:remote_url_with_git) { "https://github.com/smoll/loca.git" }
|
19
|
+
let(:remote_url_with_slash) { "https://github.com/smoll/loca/" }
|
20
|
+
|
21
|
+
it "matches when both have trailing '.git'" do
|
22
|
+
result = subject.git_urls_match?(remote_url_with_git, remote_url_with_git)
|
23
|
+
expect(result).to eq true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "matches when both lack trailing '.git'" do
|
27
|
+
result = subject.git_urls_match?(remote_url, remote_url)
|
28
|
+
expect(result).to eq true
|
29
|
+
end
|
30
|
+
|
31
|
+
it "matches when only one has trailing '.git'" do
|
32
|
+
result = subject.git_urls_match?(remote_url_with_git, remote_url)
|
33
|
+
expect(result).to eq true
|
34
|
+
end
|
35
|
+
|
36
|
+
it "matches when one has a trailing slash" do
|
37
|
+
result = subject.git_urls_match?(remote_url_with_git, remote_url_with_slash)
|
38
|
+
expect(result).to eq true
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns false when they do not match" do
|
42
|
+
result = subject.git_urls_match?(remote_url, "https://github.com/smoll/locaz")
|
43
|
+
expect(result).to eq false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
describe Loca::URL::Parser do
|
2
|
+
subject { described_class.new(url) }
|
3
|
+
|
4
|
+
describe "#to_s" do
|
5
|
+
let(:url) { "https://github.com/smoll/loca/pull/1337/" }
|
6
|
+
it "returns the URL passed to the constructor" do
|
7
|
+
expect(subject.to_s).to eq url
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#parse" do
|
12
|
+
let(:parsed) { subject.parse }
|
13
|
+
|
14
|
+
context "valid commits url" do
|
15
|
+
let(:url) { "https://github.com/smoll/loca/pull/1337/commits" }
|
16
|
+
|
17
|
+
it "returns a hash" do
|
18
|
+
expect(parsed).to be_a Hash
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns the correct pull num" do
|
22
|
+
expect(parsed[:pull][:num].to_s).to eq "1337"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns the correct pull url" do
|
26
|
+
expect(parsed[:pull][:url]).to eq "https://github.com/smoll/loca/pull/1337"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns the correct remote url" do
|
30
|
+
expect(parsed[:remote][:url]).to eq "https://github.com/smoll/loca.git"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns the expected branch name" do
|
34
|
+
expect(parsed[:branch_name]).to eq "PULL_1337"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "valid files url" do
|
39
|
+
let(:url) { "https://github.com/smoll/loca/pull/1337/files" }
|
40
|
+
|
41
|
+
it "returns the correct remote url" do
|
42
|
+
expect(parsed[:remote][:url]).to eq "https://github.com/smoll/loca.git"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "returns the expected branch name" do
|
46
|
+
expect(parsed[:branch_name]).to eq "PULL_1337"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "valid pull url" do
|
51
|
+
let(:url) { "https://github.com/smoll/loca/pull/1337" }
|
52
|
+
|
53
|
+
it "returns the correct remote url" do
|
54
|
+
expect(parsed[:remote][:url]).to eq "https://github.com/smoll/loca.git"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns the expected branch name" do
|
58
|
+
expect(parsed[:branch_name]).to eq "PULL_1337"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "valid pull url with trailing slash" do
|
63
|
+
let(:url) { "https://github.com/smoll/loca/pull/1337/" }
|
64
|
+
|
65
|
+
it "returns the correct remote url" do
|
66
|
+
expect(parsed[:remote][:url]).to eq "https://github.com/smoll/loca.git"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "returns the expected branch name" do
|
70
|
+
expect(parsed[:branch_name]).to eq "PULL_1337"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "invalid host" do
|
75
|
+
let(:url) { "https://badhub.com/smoll/loca/pull/1337" }
|
76
|
+
it "raises an error" do
|
77
|
+
expect { parsed }.to raise_error
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "invalid url" do
|
82
|
+
let(:url) { "badhub.com" }
|
83
|
+
it "raises an error" do
|
84
|
+
expect { parsed }.to raise_error
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
describe Loca::URL::Validator do
|
2
|
+
subject { described_class.new(url) }
|
3
|
+
|
4
|
+
describe "#validate" do
|
5
|
+
context "good url" do
|
6
|
+
let(:url) { "https://github.com/smoll/loca/pull/1337/commits" }
|
7
|
+
it "does not raise an error" do
|
8
|
+
expect { subject.validate }.not_to raise_error
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "bad url" do
|
13
|
+
let(:url) { "bad-url.com" }
|
14
|
+
it "raises an error" do
|
15
|
+
expect { subject.validate }.to raise_error
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "invalid pull number" do
|
20
|
+
let(:url) { "https://github.com/smoll/loca/pull/xyz/commits" }
|
21
|
+
it "raises an error" do
|
22
|
+
expect { subject.validate }.to raise_error
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
describe Loca::Utils do
|
2
|
+
describe "#non_empty_string?" do
|
3
|
+
subject { Loca::Utils.non_empty_string?(param) }
|
4
|
+
|
5
|
+
context "nonempty string passed" do
|
6
|
+
let(:param) { "hi" }
|
7
|
+
it { is_expected.to eq true }
|
8
|
+
end
|
9
|
+
|
10
|
+
context "nonempty text after newline passed" do
|
11
|
+
let(:param) { "\nOK" }
|
12
|
+
it { is_expected.to eq true }
|
13
|
+
end
|
14
|
+
|
15
|
+
context "whitespace string passed" do
|
16
|
+
let(:param) { " " }
|
17
|
+
it { is_expected.to eq false }
|
18
|
+
end
|
19
|
+
|
20
|
+
context "newline string passed" do
|
21
|
+
let(:param) { "\n" }
|
22
|
+
it { is_expected.to eq false }
|
23
|
+
end
|
24
|
+
|
25
|
+
context "integer passed" do
|
26
|
+
let(:param) { 1 }
|
27
|
+
it { is_expected.to eq false }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "float passed" do
|
31
|
+
let(:param) { 1.0 }
|
32
|
+
it { is_expected.to eq false }
|
33
|
+
end
|
34
|
+
|
35
|
+
context "nil passed" do
|
36
|
+
let(:param) { nil }
|
37
|
+
it { is_expected.to eq false }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,27 +1,25 @@
|
|
1
1
|
# Using https://raw.githubusercontent.com/bbatsov/rubocop/master/spec/spec_helper.rb as a guide
|
2
|
-
require
|
3
|
-
require
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
]
|
11
|
-
SimpleCov.start
|
12
|
-
|
13
|
-
require
|
2
|
+
require "simplecov"
|
3
|
+
require "codeclimate-test-reporter"
|
4
|
+
|
5
|
+
# Sending to Coveralls requires no set up, is done by rake task
|
6
|
+
# Sending to Code Climate is set up here, but pushed in a rake task
|
7
|
+
# ref: https://github.com/codeclimate/ruby-test-reporter#using-with-parallel_tests
|
8
|
+
# ref: https://coveralls.zendesk.com/hc/en-us/articles/201769485-Ruby-Rails
|
9
|
+
SimpleCov.add_filter "vendor"
|
10
|
+
SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter]
|
11
|
+
SimpleCov.start CodeClimate::TestReporter.configuration.profile
|
12
|
+
|
13
|
+
require "bundler/setup"
|
14
14
|
Bundler.setup # From http://stackoverflow.com/a/4402193
|
15
15
|
|
16
|
-
require
|
16
|
+
require "loca"
|
17
17
|
|
18
18
|
# Requires supporting files with custom matchers and macros, etc,
|
19
19
|
# in ./support/ and its subdirectories.
|
20
20
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
21
21
|
|
22
22
|
RSpec.configure do |config|
|
23
|
-
config.include Features::GitHubHelper
|
24
|
-
|
25
23
|
config.order = :random
|
26
24
|
|
27
25
|
config.expect_with :rspec do |expectations|
|
@@ -37,48 +35,4 @@ RSpec.configure do |config|
|
|
37
35
|
config.before do
|
38
36
|
ARGV.replace []
|
39
37
|
end
|
40
|
-
|
41
|
-
# From https://raw.githubusercontent.com/erikhuda/thor/master/spec/helper.rb
|
42
|
-
# Captures the output for analysis later
|
43
|
-
#
|
44
|
-
# @example Capture `$stderr`
|
45
|
-
#
|
46
|
-
# output = capture(:stderr) { $stderr.puts "this is captured" }
|
47
|
-
#
|
48
|
-
# @param [Symbol] stream `:stdout` or `:stderr`
|
49
|
-
# @yield The block to capture stdout/stderr for.
|
50
|
-
# @return [String] The contents of $stdout or $stderr
|
51
|
-
# rubocop:disable Lint/Eval
|
52
|
-
def capture(stream)
|
53
|
-
begin
|
54
|
-
stream = stream.to_s
|
55
|
-
eval "$#{stream} = StringIO.new"
|
56
|
-
yield
|
57
|
-
result = eval("$#{stream}").string
|
58
|
-
ensure
|
59
|
-
eval("$#{stream} = #{stream.upcase}")
|
60
|
-
end
|
61
|
-
|
62
|
-
result
|
63
|
-
end
|
64
|
-
# rubocop:enable Lint/Eval
|
65
|
-
|
66
|
-
# This code was adapted from Ruby on Rails, available under MIT-LICENSE
|
67
|
-
# Copyright (c) 2004-2013 David Heinemeier Hansson
|
68
|
-
def silence_warnings
|
69
|
-
old_verbose, $VERBOSE = $VERBOSE, nil
|
70
|
-
yield
|
71
|
-
ensure
|
72
|
-
$VERBOSE = old_verbose
|
73
|
-
end
|
74
|
-
|
75
|
-
# Silences the output stream
|
76
|
-
#
|
77
|
-
# @example Silence `$stdout`
|
78
|
-
#
|
79
|
-
# silence(:stdout) { $stdout.puts "hi" }
|
80
|
-
#
|
81
|
-
# @param [IO] stream The stream to use such as $stderr or $stdout
|
82
|
-
# @return [nil]
|
83
|
-
alias :silence :capture # rubocop:disable Style/Alias
|
84
38
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
RSpec::Matchers.define :terminate do |_code|
|
2
|
+
actual = nil
|
3
|
+
|
4
|
+
def supports_block_expectations?
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
match do |block|
|
9
|
+
begin
|
10
|
+
block.call
|
11
|
+
rescue SystemExit => e
|
12
|
+
actual = e.status
|
13
|
+
end
|
14
|
+
actual && actual == status_code
|
15
|
+
end
|
16
|
+
|
17
|
+
chain :with_code do |status_code|
|
18
|
+
@status_code = status_code
|
19
|
+
end
|
20
|
+
|
21
|
+
failure_message do |_block|
|
22
|
+
"expected block to call exit(#{status_code}) but exit" +
|
23
|
+
(actual.nil? ? " not called" : "(#{actual}) was called")
|
24
|
+
end
|
25
|
+
|
26
|
+
failure_message_when_negated do |_block|
|
27
|
+
"expected block not to call exit(#{status_code})"
|
28
|
+
end
|
29
|
+
|
30
|
+
description do
|
31
|
+
"expect block to call exit(#{status_code})"
|
32
|
+
end
|
33
|
+
|
34
|
+
def status_code
|
35
|
+
@status_code ||= 0
|
36
|
+
end
|
37
|
+
end
|