loca 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1d72e91059bc88a4090bd9eb2c6c8f1e45c13704
4
+ data.tar.gz: e8fda9734eaf1fc2446fdb85490e792db395d6ee
5
+ SHA512:
6
+ metadata.gz: 71eb60e85d247ce2ee9e14905c685283632262618815a1dccf80af8039a449302f0945af2b40f144358e84ecad73129d72f1adac0183bbd48f6bd4439da611b3
7
+ data.tar.gz: 129bf8eb437ae3a064b5f9a77e284738eb961f8f6a55ba4c9390b6eba4a91623a73b8664fe200394dd414c083ca6b1e0d16ae348dba6686656889555455258ec
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'loca'
4
+
5
+ Loca::CLI.start(ARGV)
@@ -0,0 +1,14 @@
1
+ # Require all runtime dependencies first
2
+ require 'thor'
3
+ require 'colorize'
4
+ require 'mixlib/shellout'
5
+ require 'addressable/uri'
6
+
7
+ # Start by requiring files with no dependencies on other files, then files with
8
+ # only external gem runtime dependencies (specified above), then files with
9
+ # dependencies on other files within the loca project itself
10
+ require 'loca/version'
11
+ require 'loca/error'
12
+ require 'loca/url'
13
+ require 'loca/git'
14
+ require 'loca/cli'
@@ -0,0 +1,47 @@
1
+ module Loca
2
+ class CLI < Thor
3
+ include Thor::Actions # https://github.com/erikhuda/thor/wiki/Actions
4
+
5
+ map %w(--version -v) => :__print_version
6
+
7
+ desc '--version, -v', 'print the version'
8
+ def __print_version
9
+ puts Loca::VERSION
10
+ end
11
+
12
+ desc 'c URL', 'Check out a pull request locally'
13
+ method_option :delete, aliases: '-d', desc: 'Delete the branch instead of creating it'
14
+ def c(pasted_url)
15
+ return d(pasted_url) if options[:delete]
16
+
17
+ git = Loca::Git.new(pasted_url)
18
+ branch_name = git.branch_name
19
+
20
+ if git.first_time_creating? || yes?("WARN: Branch '#{branch_name}' "\
21
+ ' already exists. Overwrite? (n)', :yellow)
22
+ git.fetch
23
+ git.checkout
24
+ say "Checked out #{branch_name}!", :green
25
+ else
26
+ fail Loca::Error::GitAborted, 'Git checkout aborted!'
27
+ end
28
+ end
29
+
30
+ desc 'd URL', 'Delete the local branch for that URL'
31
+ def d(pasted_url)
32
+ git = Loca::Git.new(pasted_url)
33
+ branch_name = git.branch_name
34
+
35
+ git.delete
36
+ say "Deleted #{branch_name}!", :green
37
+ end
38
+
39
+ private # rubocop:disable Lint/UselessAccessModifier
40
+
41
+ no_commands do # Thor primitive(s) that we want to stub in RSpec
42
+ def yes?(*args)
43
+ super
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,21 @@
1
+ # Using http://ablogaboutcode.com/2011/01/03/using-custom-error-messages-for-cleaner-code/
2
+ # and http://www.simonewebdesign.it/how-to-set-default-message-exception/
3
+ module Loca
4
+ module Error
5
+ class Base < StandardError
6
+ def initialize(message)
7
+ # To get the message in red
8
+ $stderr.puts message.red
9
+ @message = message
10
+ end
11
+ end
12
+
13
+ InvalidURL = Class.new(Base)
14
+
15
+ GitStdErrDetected = Class.new(Base)
16
+ UnstashedFilesFound = Class.new(Base)
17
+ OnlyOneBranch = Class.new(Base)
18
+ RemoteNotSet = Class.new(Base)
19
+ GitAborted = Class.new(Base)
20
+ end
21
+ end
@@ -0,0 +1,117 @@
1
+ module Loca
2
+ class Git
3
+ include Thor::Actions
4
+
5
+ attr_reader :branch_name
6
+
7
+ def initialize(url, remote = nil)
8
+ @url = Loca::URL.new(url)
9
+ @branch_name = @url.branch_name
10
+
11
+ ensure_git_repo
12
+ ensure_no_unstashed_files
13
+ @remote_name = remote || extract_remote_name
14
+ end
15
+
16
+ def delete
17
+ # Cannot delete a branch you are currently on:
18
+ checkout_another_branch if @branch_name == current_branch
19
+ git "branch -D #{@branch_name}"
20
+ end
21
+
22
+ def checkout
23
+ git "checkout #{@branch_name}", false # prints to stderr for some reason
24
+ end
25
+
26
+ def fetch
27
+ # To avoid fatal error: Refusing to fetch into current branch
28
+ delete unless first_time_creating?
29
+ # Performs `git fetch upstream pull/PR_NUMBER/head:BRANCH_NAME`
30
+ git "fetch #{@remote_name} pull/#{@url.pr_num}/head:#{@branch_name}", false # shellout has stderr for some reason
31
+ end
32
+
33
+ def first_time_creating? # Keep this a public method so we can prompt the user for overwrite
34
+ branches.include?(@branch_name) ? false : true
35
+ end
36
+
37
+ # Example
38
+ #
39
+ # git_match_http?("https://github.com/smoll/loca.git", "https://github.com/smoll/loca/pull/1")
40
+ # => true
41
+ def git_match_http?(git, http)
42
+ format = lambda do |uri| # Strip off uri scheme & trailing '.git'
43
+ uri.sub('https://', '')
44
+ .sub('http://', '')
45
+ .sub('git://', '')
46
+ .sub(/.git$/, '')
47
+ end
48
+ format.call(http).start_with?(format.call(git))
49
+ end
50
+
51
+ private
52
+
53
+ def git(cmd, fail_on_stderr = true)
54
+ shellout = Mixlib::ShellOut.new "git #{cmd}"
55
+ shellout.run_command
56
+ return shellout.stdout if shellout.stderr.empty?
57
+ if fail_on_stderr
58
+ fail Loca::Error::GitStdErrDetected, "#{shellout.stderr.strip}"
59
+ else
60
+ $stderr.puts shellout.stderr.strip.yellow
61
+ end
62
+ shellout.stdout.strip
63
+ end
64
+
65
+ def branches
66
+ git("for-each-ref refs/heads/ --format='%(refname:short)'").split("\n")
67
+ end
68
+
69
+ def current_branch
70
+ git('rev-parse --abbrev-ref HEAD').strip
71
+ end
72
+
73
+ def ensure_git_repo
74
+ git 'rev-parse'
75
+ end
76
+
77
+ def ensure_no_unstashed_files
78
+ val = git 'status --porcelain'
79
+ fail Loca::Error::UnstashedFilesFound, 'Commit or stash your files before continuing!' unless val.empty?
80
+ end
81
+
82
+ def checkout_another_branch
83
+ another = branches.find { |branch| branch != current_branch }
84
+ fail Loca::Error::OnlyOneBranch, 'No other branch to checkout!' unless another
85
+ git "checkout #{another}", false # prints to stderr for some reason
86
+ end
87
+
88
+ def remote_mapping
89
+ names = git('remote show -n').split("\n")
90
+ mapping = {}
91
+ names.each do |name|
92
+ mapping[name] = git("config --get remote.#{name}.url").strip
93
+ end
94
+ mapping
95
+ end
96
+
97
+ def extract_remote_name
98
+ match = remote_mapping.find { |_name, url| git_match_http?(url, @url.to_s) }
99
+ unless match
100
+ if yes?("Remote #{(@url)} not set. Would you like to set it as 'auto_loca_remote'?")
101
+ set_remote
102
+ else
103
+ fail Loca::Error::RemoteNotSet, "You must set the repo (#{@url}) as a remote "\
104
+ "(see `git remote -v'). All remotes: #{remote_mapping}"
105
+ end
106
+ end
107
+ match.first
108
+ end
109
+
110
+ def set_remote
111
+ uri = Addressable::URI.parse(@url)
112
+ uri.path = uri.path.split('/')[0..2].join('/')
113
+ remote_url = "#{uri}.git"
114
+ git "remote add upstream #{remote_url}"
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,37 @@
1
+ module Loca
2
+ class URL
3
+ attr_reader :branch_name
4
+ attr_reader :pr_num
5
+
6
+ def initialize(url)
7
+ @url = url
8
+ ensure_well_formed
9
+
10
+ @branch_name = extract_branch_name
11
+ @pr_num = extract_pr_num
12
+ end
13
+
14
+ def to_s
15
+ @url
16
+ end
17
+
18
+ def ensure_well_formed
19
+ # TODO: add more checks via Addressable::URI
20
+ segments = URI(@url).path.split('/')
21
+ int = Integer(segments[-1]) rescue false # replace with coercible gem?
22
+ pull = segments[-2] == 'pull'
23
+
24
+ fail Loca::Error::InvalidURL, "Doesn't appear to be a well-formed URL: #{@url}" unless int && pull
25
+ end
26
+
27
+ private
28
+
29
+ def extract_branch_name
30
+ "PULL_#{extract_pr_num}"
31
+ end
32
+
33
+ def extract_pr_num
34
+ URI(@url).path.split('/').last
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module Loca
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,43 @@
1
+ describe 'Checking out a GitHub PR locally' do
2
+ let(:pr_url) { 'https://github.com/octocat/Spoon-Knife/pull/4865' }
3
+ let(:expected_branch_name) { 'PULL_4865' }
4
+
5
+ before(:each) do
6
+ clone_test_repo
7
+ cd_to_cloned_dir
8
+ end
9
+
10
+ after(:each) { teardown }
11
+
12
+ context "the repo exists as the 'upstream' remote" do
13
+ before(:each) { set_upstream }
14
+
15
+ it 'checks out then deletes' do
16
+ shellout! "loca c #{pr_url}"
17
+ expect(current_branch.strip).to eq expected_branch_name
18
+ shellout! "loca c #{pr_url} -d"
19
+ expect(current_branch.strip).to_not eq expected_branch_name
20
+ end
21
+
22
+ it 'checks out then prompts to overwrite' do
23
+ shellout! "loca c #{pr_url}"
24
+ expect(current_branch.strip).to eq expected_branch_name
25
+ shellout! "loca c #{pr_url}", input: 'yes'
26
+ expect(current_branch.strip).to eq expected_branch_name
27
+ end
28
+ end
29
+
30
+ context 'the repo does not exist as a remote' do
31
+ it 'fails to checkout' do
32
+ expect { shellout! "loca c #{pr_url}" }.to raise_error
33
+ expect(current_branch.strip).to_not eq expected_branch_name
34
+ end
35
+ end
36
+
37
+ context 'the wrong URL is supplied' do
38
+ it 'fails to checkout' do
39
+ expect { shellout! 'loca c https://github.com/octocat/Spoon-Knife/wrong/4865' }.to raise_error
40
+ expect(current_branch.strip).to_not eq expected_branch_name
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,69 @@
1
+ describe Loca::CLI do
2
+ let(:invalid_url) { 'https://github.com/fakez/fakezz/pull/1' }
3
+ let(:url) { 'https://github.com/smoll/loca/pull/1' }
4
+ subject { described_class.new }
5
+
6
+ before do # ensure we don't shell out to git
7
+ allow_any_instance_of(Loca::Git).to receive(:fetch)
8
+ allow_any_instance_of(Loca::Git).to receive(:checkout)
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
28
+
29
+ it 'raises an error when the branch exists and the user says no to overwrite' do
30
+ expect_any_instance_of(Loca::Git).to receive(:branch_name).and_return 'PULL_1'
31
+ expect_any_instance_of(Loca::Git).to receive(:first_time_creating?).and_return false
32
+ silence(:stdout) { allow_any_instance_of(described_class).to receive(:yes?).and_return false }
33
+
34
+ expect { silence(:stderr) { described_class.start(%W(c #{url})) } }.to raise_error
35
+ end
36
+
37
+ it 'raises an error when an invalid repo is supplied' do
38
+ expect { silence(:stderr) { described_class.start(%W(c #{invalid_url})) } }.to raise_error
39
+ end
40
+
41
+ it 'deletes when the delete flag is appended' do
42
+ expect_any_instance_of(Loca::Git).to receive(:branch_name).and_return 'PULL_1'
43
+ expect_any_instance_of(Loca::Git).to receive(:delete)
44
+
45
+ output = capture(:stdout) { described_class.start(%W(c #{url} -d)) }.strip
46
+ expect(output).to eq 'Deleted PULL_1!'
47
+ end
48
+ end
49
+
50
+ describe 'd' do
51
+ it 'deletes' do
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
63
+
64
+ describe '--version' do
65
+ it 'returns the correct version' do
66
+ expect(capture(:stdout) { described_class.start(%w(-v)) }.strip).to eq(Loca::VERSION)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,152 @@
1
+ describe Loca::Git do
2
+ let(:pid) { 1 } # Pull request ID
3
+ let(:url) { "https://github.com/smoll/loca/pull/#{pid}" }
4
+ let(:remote_name) { 'loca-fake-remote' }
5
+ let(:remote_url) { 'https://github.com/smoll/loca.git' }
6
+ subject { described_class.new(url, remote_name) }
7
+
8
+ let(:expected_branch_name) { "PULL_#{pid}" }
9
+ let(:branches_without_expected) { ['master'] }
10
+ let(:branches_with_expected) { branches_without_expected << expected_branch_name }
11
+
12
+ describe '#git' do # private method that shells out to git
13
+ it 'raises an error when an invalid git command is supplied' do
14
+ expect { capture(:stderr) { subject.send(:git, 'not-a-git-command') } }.to raise_error
15
+ end
16
+
17
+ it 'prints to stderr when an invalid git command is supplied, but we do not want to fail on stderr' do
18
+ output = capture(:stderr) { subject.send(:git, 'not-a-git-command', false) }
19
+ expect(output).to include "git: 'not-a-git-command' is not a git command"
20
+ end
21
+ end
22
+
23
+ describe '#branches' do # private method
24
+ it 'returns all local branches' do
25
+ cmd = "for-each-ref refs/heads/ --format='%(refname:short)'"
26
+ expect(subject).to receive(:git).with(cmd).and_return 'a_branch'
27
+ expect(subject.send(:branches)).to eq ['a_branch']
28
+ end
29
+ end
30
+
31
+ describe '#remote_mapping' do # private method
32
+ it "returns all of the repo's remotes" do
33
+ expect(subject).to receive(:git).with('remote show -n').and_return remote_name
34
+ expect(subject).to receive(:git).with("config --get remote.#{remote_name}.url").and_return remote_url
35
+ expect(subject.send(:remote_mapping)).to eq(remote_name => remote_url)
36
+ end
37
+ end
38
+
39
+ describe '#extract_remote_name' do # private method
40
+ it 'returns the local repo remote name corresponding to the Pull Request URL' do
41
+ expect(subject).to receive(:remote_mapping).and_return(remote_name => remote_url) # investigate this
42
+
43
+ expect(subject.send(:extract_remote_name)).to eq remote_name
44
+ end
45
+ end
46
+
47
+ describe '#current_branch' do # private method
48
+ it 'returns the current branch' do
49
+ expect(subject).to receive(:git).with('rev-parse --abbrev-ref HEAD').and_return "branch_name\n"
50
+ expect(subject.send(:current_branch)).to eq 'branch_name'
51
+ end
52
+ end
53
+
54
+ describe '#checkout' do # private method
55
+ it 'checks out the expected branch' do
56
+ expect(subject).to receive(:git).with("checkout #{expected_branch_name}", false).once
57
+ subject.send(:checkout)
58
+ end
59
+ end
60
+
61
+ describe '#checkout_another_branch' do # private method
62
+ it 'checks out a branch that is not the current one' do
63
+ allow(subject).to receive(:branches).and_return %w(branch1 branch2)
64
+ allow(subject).to receive(:current_branch).and_return 'branch1'
65
+ expect(subject).to receive(:git).with('checkout branch2', false).once
66
+ silence(:stdout) { subject.send(:checkout_another_branch) }
67
+ end
68
+
69
+ it 'raises an error when there is no other branch to check out' do
70
+ allow(subject).to receive(:branches).and_return ['branch1']
71
+ allow(subject).to receive(:current_branch).and_return 'branch1'
72
+
73
+ expect { silence(:stderr) { subject.send(:checkout_another_branch) } }.to raise_error
74
+ end
75
+ end
76
+
77
+ describe '#fetch' do
78
+ before do
79
+ allow(subject).to receive(:git) # ensure we don't actually shell out to `git`
80
+ end
81
+
82
+ it 'deletes before fetching when the branch already exists' do
83
+ allow(subject).to receive(:branches).and_return branches_with_expected
84
+ expect(subject).to receive(:delete).once
85
+
86
+ subject.fetch
87
+ end
88
+
89
+ it 'fetches the remote pull request' do
90
+ allow(subject).to receive(:branches).and_return branches_without_expected
91
+
92
+ # Investigate why this git command prints to $stderr, i.e. we must pass false as the 2nd arg:
93
+ expect(subject).to receive(:git).with("fetch #{remote_name} pull/#{pid}/head:#{expected_branch_name}", false)
94
+
95
+ subject.fetch
96
+ end
97
+ end
98
+
99
+ describe '#delete' do # should attempt to delete a branch with name 'PULL_1'
100
+ it 'raises an error when the branch does not exist' do
101
+ allow(subject).to receive(:branches).and_return branches_without_expected
102
+ expect { silence(:stderr) { subject.delete } }.to raise_error
103
+ end
104
+
105
+ it 'deletes the branch when it exists' do
106
+ allow(subject).to receive(:branches).and_return branches_with_expected
107
+ allow(subject).to receive(:current_branch).and_return('master')
108
+ expect(subject).to receive(:git).with("branch -D #{expected_branch_name}").once
109
+
110
+ subject.delete
111
+ end
112
+
113
+ it 'checks out another branch if attempting to delete current branch' do
114
+ allow(subject).to receive(:branches).and_return branches_with_expected
115
+ allow(subject).to receive(:current_branch).and_return expected_branch_name
116
+
117
+ expect(subject).to receive(:checkout_another_branch).once
118
+ expect(subject).to receive(:git).with("branch -D #{expected_branch_name}").once
119
+
120
+ subject.delete
121
+ end
122
+ end
123
+
124
+ describe '#first_time_creating?' do
125
+ it 'returns false when the branch already exists' do
126
+ allow(subject).to receive(:branches).and_return branches_with_expected
127
+ expect(subject.first_time_creating?).to eq false
128
+ end
129
+
130
+ it 'returns true when the branch does not exist' do
131
+ allow(subject).to receive(:branches).and_return branches_without_expected
132
+ expect(subject.first_time_creating?).to eq true
133
+ end
134
+ end
135
+
136
+ describe '#git_match_http?' do
137
+ it 'returns true when matching urls are supplied' do
138
+ git_url = 'https://github.com/smoll/loca.git'
139
+ expect(subject.git_match_http?(git_url, url)).to eq true
140
+ end
141
+
142
+ it 'returns true when different URI schemes but matching urls are supplied' do
143
+ git_url = 'git://github.com/smoll/loca.git'
144
+ expect(subject.git_match_http?(git_url, url)).to eq true
145
+ end
146
+
147
+ it 'returns false when non-matching urls are supplied' do
148
+ git_url = 'git://github.com/someoneelse/loca.git'
149
+ expect(subject.git_match_http?(git_url, url)).to eq false
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,47 @@
1
+ describe Loca::URL do
2
+ let(:pid) { 1 } # Pull request ID
3
+ let(:url) { "https://github.com/smoll/loca/pull/#{pid}" }
4
+ subject { described_class.new(url) }
5
+
6
+ let(:expected_branch_name) { "PULL_#{pid}" }
7
+ let(:expected_pr_num) { pid.to_s }
8
+
9
+ describe '#initialize' do
10
+ it 'does not raise an error for a valid URL' do
11
+ expect { Loca::URL.new(url) }.to_not raise_error
12
+ end
13
+ # it 'does not raise an error for a GitHub PR URL with additional segments' do
14
+ # expect { Loca::URL.new("#{url}/something/something") }.to_not raise_error # TODO: technical improvement
15
+ # end
16
+ it 'raises an error for a non-URL' do
17
+ expect { silence(:stderr) { Loca::URL.new('not a URL') } }.to raise_error
18
+ end
19
+ it 'raises an error for a non-GitHub URL' do
20
+ expect { silence(:stderr) { Loca::URL.new('http://bad-url-here.com') } }.to raise_error
21
+ end
22
+ it 'raises an error for a GitHub non-PR URL' do
23
+ expect { silence(:stderr) { Loca::URL.new('https://github.com/smoll/loca/something/something') } }.to raise_error
24
+ end
25
+ it 'raises an error for a GitHub URL without an integer in the PID location' do
26
+ expect { silence(:stderr) { Loca::URL.new('https://github.com/smoll/loca/pull/NaN') } }.to raise_error
27
+ end
28
+ end
29
+
30
+ describe '#to_s' do
31
+ it 'returns the url passed to the constructor' do
32
+ expect(subject.to_s).to eq url
33
+ end
34
+ end
35
+
36
+ describe '#extract_branch_name' do # private method
37
+ it 'returns the expected branch name' do
38
+ expect(subject.send(:extract_branch_name)).to eq expected_branch_name
39
+ end
40
+ end
41
+
42
+ describe '#extract_pr_num' do # private method
43
+ it 'returns the expected branch name' do
44
+ expect(subject.send(:extract_pr_num)).to eq "#{pid}"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,84 @@
1
+ # Using https://raw.githubusercontent.com/bbatsov/rubocop/master/spec/spec_helper.rb as a guide
2
+ require 'simplecov'
3
+ require 'coveralls'
4
+ require 'codeclimate-test-reporter'
5
+
6
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
7
+ SimpleCov::Formatter::HTMLFormatter,
8
+ Coveralls::SimpleCov::Formatter,
9
+ CodeClimate::TestReporter::Formatter
10
+ ]
11
+ SimpleCov.start
12
+
13
+ require 'bundler/setup'
14
+ Bundler.setup # From http://stackoverflow.com/a/4402193
15
+
16
+ require 'loca'
17
+
18
+ # Requires supporting files with custom matchers and macros, etc,
19
+ # in ./support/ and its subdirectories.
20
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
21
+
22
+ RSpec.configure do |config|
23
+ config.include Features::GitHubHelper
24
+
25
+ config.order = :random
26
+
27
+ config.expect_with :rspec do |expectations|
28
+ expectations.syntax = :expect # Disable `should`
29
+ end
30
+
31
+ config.mock_with :rspec do |mocks|
32
+ mocks.syntax = :expect # Disable `should_receive` and `stub`
33
+ mocks.verify_partial_doubles = true # Avoid La La Land,
34
+ # read http://wegowise.github.io/blog/2014/09/03/rspec-verifying-doubles/
35
+ end
36
+
37
+ config.before do
38
+ ARGV.replace []
39
+ 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
+ end
@@ -0,0 +1,55 @@
1
+ require 'mixlib/shellout'
2
+ require 'fileutils'
3
+
4
+ module Features
5
+ module GitHubHelper
6
+ def teardown
7
+ return_to_original_wd
8
+ FileUtils.rm_rf absolute_path
9
+ end
10
+
11
+ def cd_to_cloned_dir
12
+ @original_wd = Dir.pwd
13
+ Dir.chdir(absolute_path)
14
+ end
15
+
16
+ def return_to_original_wd
17
+ Dir.chdir(@original_wd)
18
+ end
19
+
20
+ def clone_test_repo
21
+ shellout! "git clone https://github.com/smoll/Spoon-Knife ./#{rel_path}"
22
+ end
23
+
24
+ def set_upstream
25
+ shellout! 'git remote add upstream https://github.com/octocat/Spoon-Knife.git'
26
+ end
27
+
28
+ def current_branch
29
+ shellout! 'git rev-parse --abbrev-ref HEAD'
30
+ end
31
+
32
+ def shellout!(cmd, opts = {})
33
+ opts = {
34
+ input: nil
35
+ }.merge(opts)
36
+ sh = opts[:input].nil? ? Mixlib::ShellOut.new(cmd.to_s) : Mixlib::ShellOut.new(cmd.to_s, input: opts[:input].to_s)
37
+ sh.run_command
38
+
39
+ sh.error! if opts[:input].nil? # NOTE: CLI exits non-zero if waiting on user input
40
+ sh.stdout
41
+ end
42
+
43
+ private
44
+
45
+ def rel_path
46
+ absolute_path.split(Dir.pwd.to_s)[1].sub('/', '')
47
+ end
48
+
49
+ def absolute_path
50
+ path = File.expand_path('../../../tmp/cloned', __FILE__)
51
+ FileUtils.mkdir_p path
52
+ path
53
+ end
54
+ end
55
+ end
metadata ADDED
@@ -0,0 +1,224 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: loca
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - smoll
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.19.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.19.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: colorize
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.7.5
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.7.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: mixlib-shellout
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 2.0.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 2.0.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: addressable
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.3.7
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.3.7
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.6'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.6'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.4'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 10.4.2
93
+ type: :development
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '10.4'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 10.4.2
103
+ - !ruby/object:Gem::Dependency
104
+ name: rubocop
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 0.29.0
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: 0.29.0
117
+ - !ruby/object:Gem::Dependency
118
+ name: rspec
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: 3.2.0
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: 3.2.0
131
+ - !ruby/object:Gem::Dependency
132
+ name: coveralls
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: 0.7.9
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: 0.7.9
145
+ - !ruby/object:Gem::Dependency
146
+ name: codeclimate-test-reporter
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: 0.4.6
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: 0.4.6
159
+ - !ruby/object:Gem::Dependency
160
+ name: simplecov
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: 0.9.1
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: 0.9.1
173
+ description:
174
+ email:
175
+ - mollah@gmail.com
176
+ executables:
177
+ - loca
178
+ extensions: []
179
+ extra_rdoc_files: []
180
+ files:
181
+ - bin/loca
182
+ - lib/loca.rb
183
+ - lib/loca/cli.rb
184
+ - lib/loca/error.rb
185
+ - lib/loca/git.rb
186
+ - lib/loca/url.rb
187
+ - lib/loca/version.rb
188
+ - spec/e2e/github_spec.rb
189
+ - spec/loca/cli_spec.rb
190
+ - spec/loca/git_spec.rb
191
+ - spec/loca/url_spec.rb
192
+ - spec/spec_helper.rb
193
+ - spec/support/features/github_helper.rb
194
+ homepage: https://github.com/smoll/loca
195
+ licenses:
196
+ - MIT
197
+ metadata: {}
198
+ post_install_message:
199
+ rdoc_options: []
200
+ require_paths:
201
+ - lib
202
+ required_ruby_version: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ version: '0'
207
+ required_rubygems_version: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - ">="
210
+ - !ruby/object:Gem::Version
211
+ version: '0'
212
+ requirements: []
213
+ rubyforge_project:
214
+ rubygems_version: 2.4.6
215
+ signing_key:
216
+ specification_version: 4
217
+ summary: CLI for checking out GitHub Pull Requests locally
218
+ test_files:
219
+ - spec/e2e/github_spec.rb
220
+ - spec/loca/cli_spec.rb
221
+ - spec/loca/git_spec.rb
222
+ - spec/loca/url_spec.rb
223
+ - spec/spec_helper.rb
224
+ - spec/support/features/github_helper.rb