github 0.1.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/History.txt +37 -0
  2. data/Manifest +33 -12
  3. data/README.md +187 -0
  4. data/Rakefile +44 -0
  5. data/bin/gh +8 -0
  6. data/bin/github +4 -1
  7. data/github.gemspec +29 -34
  8. data/lib/commands/commands.rb +249 -0
  9. data/lib/commands/helpers.rb +486 -0
  10. data/lib/commands/issues.rb +17 -0
  11. data/lib/commands/network.rb +110 -0
  12. data/lib/github.rb +117 -29
  13. data/lib/github/command.rb +69 -14
  14. data/lib/github/extensions.rb +39 -0
  15. data/lib/github/ui.rb +19 -0
  16. data/setup.rb +1551 -0
  17. data/spec/command_spec.rb +82 -0
  18. data/spec/commands/command_browse_spec.rb +36 -0
  19. data/spec/commands/command_clone_spec.rb +87 -0
  20. data/spec/commands/command_create-from-local_spec.rb +7 -0
  21. data/spec/commands/command_fetch_spec.rb +56 -0
  22. data/spec/commands/command_fork_spec.rb +44 -0
  23. data/spec/commands/command_helper.rb +170 -0
  24. data/spec/commands/command_home_spec.rb +20 -0
  25. data/spec/commands/command_info_spec.rb +23 -0
  26. data/spec/commands/command_issues_spec.rb +97 -0
  27. data/spec/commands/command_network_spec.rb +30 -0
  28. data/spec/commands/command_pull-request_spec.rb +51 -0
  29. data/spec/commands/command_pull_spec.rb +82 -0
  30. data/spec/commands/command_search_spec.rb +34 -0
  31. data/spec/commands/command_track_spec.rb +82 -0
  32. data/spec/commands_spec.rb +49 -0
  33. data/spec/extensions_spec.rb +36 -0
  34. data/spec/github_spec.rb +85 -0
  35. data/spec/helper_spec.rb +368 -0
  36. data/spec/spec_helper.rb +160 -4
  37. data/spec/windoze_spec.rb +38 -0
  38. metadata +114 -47
  39. data/README +0 -49
  40. data/commands/commands.rb +0 -54
  41. data/commands/helpers.rb +0 -79
  42. data/spec/helpers/owner_spec.rb +0 -12
  43. data/spec/helpers/project_spec.rb +0 -12
  44. data/spec/helpers/public_url_for_spec.rb +0 -12
  45. data/spec/helpers/repo_for_spec.rb +0 -12
  46. data/spec/helpers/user_and_repo_from_spec.rb +0 -15
  47. data/spec/helpers/user_for_spec.rb +0 -12
data/spec/command_spec.rb CHANGED
@@ -0,0 +1,82 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe GitHub::Command do
4
+ before(:each) do
5
+ @command = GitHub::Command.new(proc { |x| puts x })
6
+ end
7
+
8
+ it "should return a GitHub::Helper" do
9
+ @command.helper.should be_instance_of(GitHub::Helper)
10
+ end
11
+
12
+ it "should call successfully" do
13
+ @command.should_receive(:puts).with("test").once
14
+ @command.call("test")
15
+ end
16
+
17
+ it "should return options" do
18
+ GitHub.should_receive(:options).with().once.and_return({:ssh => true})
19
+ @command.options.should == {:ssh => true}
20
+ end
21
+
22
+ it "should successfully call out to the shell" do
23
+ unguard(Kernel, :fork)
24
+ unguard(Kernel, :exec)
25
+ hi = @command.sh("echo hi")
26
+ hi.should == "hi"
27
+ hi.out.should == "hi"
28
+ hi.out?.should be(true)
29
+ hi.error.should be_nil
30
+ hi.error?.should be(false)
31
+ hi.command.should == "echo hi"
32
+ if RUBY_PLATFORM =~ /mingw|mswin/
33
+ command = "cmd /c echo bye >&2"
34
+ else
35
+ command = "echo bye >&2"
36
+ end
37
+ bye = @command.sh(command)
38
+ bye.should == "bye"
39
+ bye.out.should be_nil
40
+ bye.out?.should be(false)
41
+ bye.error.should == "bye"
42
+ bye.error?.should be(true)
43
+ bye.command.should == command
44
+ hi_and_bye = @command.sh("echo hi; echo bye >&2")
45
+ hi_and_bye.should == "hi"
46
+ hi_and_bye.out.should == "hi"
47
+ hi_and_bye.out?.should be(true)
48
+ hi_and_bye.error.should == "bye"
49
+ hi_and_bye.error?.should be(true)
50
+ hi_and_bye.command.should == "echo hi; echo bye >&2"
51
+ end
52
+
53
+ it "should return the results of a git operation" do
54
+ GitHub::Command::Shell.should_receive(:new).with("git rev-parse master").once.and_return do |*cmds|
55
+ s = mock("GitHub::Commands::Shell")
56
+ s.should_receive(:run).once.and_return("sha1")
57
+ s
58
+ end
59
+ @command.git("rev-parse master").should == "sha1"
60
+ end
61
+
62
+ it "should print the results of a git operation" do
63
+ @command.should_receive(:puts).with("sha1").once
64
+ GitHub::Command::Shell.should_receive(:new).with("git rev-parse master").once.and_return do |*cmds|
65
+ s = mock("GitHub::Commands::Shell")
66
+ s.should_receive(:run).once.and_return("sha1")
67
+ s
68
+ end
69
+ @command.pgit("rev-parse master")
70
+ end
71
+
72
+ it "should exec a git command" do
73
+ @command.should_receive(:exec).with("git rev-parse master").once
74
+ @command.git_exec "rev-parse master"
75
+ end
76
+
77
+ it "should die" do
78
+ @command.should_receive(:puts).once.with("=> message")
79
+ @command.should_receive(:exit!).once
80
+ @command.die "message"
81
+ end
82
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require File.dirname(__FILE__) + '/command_helper'
3
+
4
+ describe "github browse" do
5
+ include CommandHelper
6
+
7
+ specify "browse should open the project home page with the current branch" do
8
+ running :browse do
9
+ setup_url_for
10
+ setup_user_and_branch("user", "test-branch")
11
+ @helper.should_receive(:open).once.with("https://github.com/user/project/tree/test-branch")
12
+ end
13
+ end
14
+
15
+ specify "browse pending should open the project home page with the 'pending' branch" do
16
+ running :browse, "pending" do
17
+ setup_url_for
18
+ setup_user_and_branch("user", "test-branch")
19
+ @helper.should_receive(:open).once.with("https://github.com/user/project/tree/pending")
20
+ end
21
+ end
22
+
23
+ specify "browse defunkt pending should open the home page of defunkt's fork with the 'pending' branch" do
24
+ running :browse, "defunkt", "pending" do
25
+ setup_url_for
26
+ @helper.should_receive(:open).once.with("https://github.com/defunkt/project/tree/pending")
27
+ end
28
+ end
29
+
30
+ specify "browse defunkt/pending should open the home page of defunkt's fork with the 'pending' branch" do
31
+ running :browse, "defunkt/pending" do
32
+ setup_url_for
33
+ @helper.should_receive(:open).once.with("https://github.com/defunkt/project/tree/pending")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,87 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require File.dirname(__FILE__) + '/command_helper'
3
+
4
+ describe "github clone" do
5
+ include CommandHelper
6
+
7
+ # -- clone --
8
+ specify "clone should die with no args" do
9
+ running :clone do
10
+ @command.should_receive(:die).with("Specify a user to pull from").and_return { raise "Died" }
11
+ self.should raise_error(RuntimeError)
12
+ end
13
+ end
14
+
15
+ specify "clone should fall through with just one arg" do
16
+ running :clone, "git://git.kernel.org/linux.git" do
17
+ @command.should_receive(:git_exec).with("clone git://git.kernel.org/linux.git")
18
+ end
19
+ end
20
+
21
+ specify "clone defunkt github-gem should clone the repo" do
22
+ running :clone, "defunkt", "github-gem" do
23
+ @command.should_receive(:current_user?).and_return(nil)
24
+ @command.should_receive(:git_exec).with("clone git://github.com/defunkt/github-gem.git")
25
+ end
26
+ end
27
+
28
+ specify "clone defunkt/github-gem should clone the repo" do
29
+ running :clone, "defunkt/github-gem" do
30
+ @command.should_receive(:current_user?).and_return(nil)
31
+ @command.should_receive(:git_exec).with("clone git://github.com/defunkt/github-gem.git")
32
+ end
33
+ end
34
+
35
+ specify "clone --ssh defunkt github-gem should clone the repo using the private URL" do
36
+ running :clone, "--ssh", "defunkt", "github-gem" do
37
+ @command.should_receive(:git_exec).with("clone git@github.com:defunkt/github-gem.git")
38
+ end
39
+ end
40
+
41
+ specify "clone defunkt github-gem repo should clone the repo into the dir 'repo'" do
42
+ running :clone, "defunkt", "github-gem", "repo" do
43
+ @command.should_receive(:current_user?).and_return(nil)
44
+ @command.should_receive(:git_exec).with("clone git://github.com/defunkt/github-gem.git repo")
45
+ end
46
+ end
47
+
48
+ specify "clone defunkt/github-gem repo should clone the repo into the dir 'repo'" do
49
+ running :clone, "defunkt/github-gem", "repo" do
50
+ @command.should_receive(:current_user?).and_return(nil)
51
+ @command.should_receive(:git_exec).with("clone git://github.com/defunkt/github-gem.git repo")
52
+ end
53
+ end
54
+
55
+ specify "clone --ssh defunkt github-gem repo should clone the repo using the private URL into the dir 'repo'" do
56
+ running :clone, "--ssh", "defunkt", "github-gem", "repo" do
57
+ @command.should_receive(:git_exec).with("clone git@github.com:defunkt/github-gem.git repo")
58
+ end
59
+ end
60
+
61
+ specify "clone defunkt/github-gem repo should clone the repo into the dir 'repo'" do
62
+ running :clone, "defunkt/github-gem", "repo" do
63
+ @command.should_receive(:current_user?).and_return(nil)
64
+ @command.should_receive(:git_exec).with("clone git://github.com/defunkt/github-gem.git repo")
65
+ end
66
+ end
67
+
68
+ specify "clone a selected repo after showing search results" do
69
+ running :clone, "--search", "github-gem" do
70
+ json = StringIO.new '{"repositories":[' +
71
+ '{"name":"github-gem","size":300,"followers":499,"username":"defunkt","language":"Ruby","fork":false,"id":"repo-1653","type":"repo","pushed":"2008-12-04T03:14:00Z","forks":59,"description":"The official `github` command line helper for simplifying your GitHub experience.","score":3.4152448,"created":"2008-02-28T09:35:34Z"},' +
72
+ '{"name":"github-gem-builder","size":76,"followers":26,"username":"pjhyett","language":"Ruby","fork":false,"id":"repo-67489","type":"repo","pushed":"2008-11-04T04:54:57Z","forks":3,"description":"The scripts used to build RubyGems on GitHub","score":3.4152448,"created":"2008-10-24T22:29:32Z"}' +
73
+ ']}'
74
+ json.rewind
75
+ question_list = <<-LIST.gsub(/^ /, '').split("\n").compact
76
+ defunkt/github-gem # The official `github` command line helper for simplifying your GitHub experience.
77
+ pjhyett/github-gem-builder # The scripts used to build RubyGems on GitHub
78
+ LIST
79
+ @command.should_receive(:open).with("http://github.com/api/v1/json/search/github-gem").and_return(json)
80
+ GitHub::UI.should_receive(:display_select_list).with(question_list).
81
+ and_return("defunkt/github-gem")
82
+ @command.should_receive(:current_user?).and_return(nil)
83
+ @command.should_receive(:git_exec).with("clone git://github.com/defunkt/github-gem.git")
84
+ end
85
+ end
86
+
87
+ end
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require File.dirname(__FILE__) + '/command_helper'
3
+
4
+ describe "github create-from-local" do
5
+ include CommandHelper
6
+
7
+ end
@@ -0,0 +1,56 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require File.dirname(__FILE__) + '/command_helper'
3
+
4
+ describe "github fetch" do
5
+ include CommandHelper
6
+
7
+ specify "fetch should die with no args" do
8
+ running :fetch do
9
+ @command.should_receive(:die).with("Specify a user to pull from").and_return { raise "Died" }
10
+ self.should raise_error(RuntimeError)
11
+ end
12
+ end
13
+
14
+ specify "fetch defunkt should start tracking defunkt if they're not already tracked" do
15
+ running :fetch, "defunkt" do
16
+ setup_remote(:origin, :user => "user", :ssh => true)
17
+ setup_remote(:external, :url => "home:/path/to/project.git")
18
+ GitHub.should_receive(:invoke).with(:track, "defunkt").and_return { raise "Tracked" }
19
+ self.should raise_error("Tracked")
20
+ end
21
+ end
22
+
23
+ specify "fetch defunkt should create defunkt/master and fetch from the defunkt remote" do
24
+ running :fetch, "defunkt" do
25
+ setup_remote(:defunkt)
26
+ @helper.should_receive(:branch_dirty?).and_return false
27
+ @command.should_receive(:git).with("fetch defunkt master:refs/remotes/defunkt/master").ordered
28
+ @command.should_receive(:git).with("update-ref refs/heads/defunkt/master refs/remotes/defunkt/master").ordered
29
+ @command.should_receive(:git_exec).with("checkout defunkt/master").ordered
30
+ stdout.should == "Fetching defunkt/master\n"
31
+ end
32
+ end
33
+
34
+ specify "fetch defunkt/wip should create defunkt/wip and fetch from wip branch on defunkt remote" do
35
+ running :fetch, "defunkt/wip" do
36
+ setup_remote(:defunkt, :remote_branches => ["master", "wip"])
37
+ @helper.should_receive(:branch_dirty?).and_return false
38
+ @command.should_receive(:git).with("fetch defunkt wip:refs/remotes/defunkt/wip").ordered
39
+ @command.should_receive(:git).with("update-ref refs/heads/defunkt/wip refs/remotes/defunkt/wip").ordered
40
+ @command.should_receive(:git_exec).with("checkout defunkt/wip").ordered
41
+ stdout.should == "Fetching defunkt/wip\n"
42
+ end
43
+ end
44
+
45
+ specify "fetch --merge defunkt should fetch from defunkt remote into current branch" do
46
+ running :fetch, "--merge", "defunkt" do
47
+ setup_remote(:defunkt)
48
+ @helper.should_receive(:branch_dirty?).and_return false
49
+ @command.should_receive(:git).with("fetch defunkt master:refs/remotes/defunkt/master").ordered
50
+ @command.should_receive(:git).with("update-ref refs/heads/defunkt/master refs/remotes/defunkt/master").ordered
51
+ @command.should_receive(:git_exec).with("checkout defunkt/master").ordered
52
+ stdout.should == "Fetching defunkt/master\n"
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,44 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require File.dirname(__FILE__) + '/command_helper'
3
+
4
+ describe "github fork" do
5
+ include CommandHelper
6
+
7
+ specify "fork should print out help" do
8
+ running :fork do
9
+ @helper.should_receive(:remotes).and_return({})
10
+ @command.should_receive(:die).with("Specify a user/project to fork, or run from within a repo").and_return { raise "Died" }
11
+ self.should raise_error(RuntimeError)
12
+ end
13
+ end
14
+
15
+ specify "fork this repo should create github fork and replace origin remote" do
16
+ running :fork do
17
+ setup_github_token
18
+ setup_url_for "origin", "defunkt", "github-gem"
19
+ setup_remote "origin", :user => "defunkt", :project => "github-gem"
20
+ setup_user_and_branch
21
+ @command.should_receive(:sh).with("curl -F 'login=drnic' -F 'token=MY_GITHUB_TOKEN' http://github.com/defunkt/github-gem/fork")
22
+ @command.should_receive(:git, "config remote.origin.url git@github.com/drnic/github-gem.git")
23
+ stdout.should == "defunkt/github-gem forked\n"
24
+ end
25
+ end
26
+
27
+ specify "fork a user/project repo" do
28
+ running :fork, "defunkt/github-gem" do
29
+ setup_github_token
30
+ @command.should_receive(:sh).with("curl -F 'login=drnic' -F 'token=MY_GITHUB_TOKEN' http://github.com/defunkt/github-gem/fork")
31
+ @command.should_receive(:git_exec, "clone git://github.com/defunkt/github-gem.git")
32
+ stdout.should == "Giving GitHub a moment to create the fork...\n"
33
+ end
34
+ end
35
+
36
+ specify "fork a user project repo" do
37
+ running :fork, "defunkt", "github-gem" do
38
+ setup_github_token
39
+ @command.should_receive("sh").with("curl -F 'login=drnic' -F 'token=MY_GITHUB_TOKEN' http://github.com/defunkt/github-gem/fork")
40
+ @command.should_receive(:git_exec, "clone git://github.com/defunkt/github-gem.git")
41
+ stdout.should == "Giving GitHub a moment to create the fork...\n"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,170 @@
1
+ module CommandHelper
2
+ def running(cmd, *args, &block)
3
+ Runner.new(self, cmd, *args, &block).run
4
+ end
5
+
6
+ class Runner
7
+ include SetupMethods
8
+
9
+ def initialize(parent, cmd, *args, &block)
10
+ @cmd_name = cmd.to_s
11
+ @command = GitHub.find_command(cmd)
12
+ @helper = @command.helper
13
+ @args = args
14
+ @block = block
15
+ @parent = parent
16
+ end
17
+
18
+ def run
19
+ self.instance_eval &@block
20
+ mock_remotes unless @remotes.nil?
21
+ GitHub.should_receive(:load).with(GitHub::BasePath + "/commands/commands.rb")
22
+ GitHub.should_receive(:load).with(GitHub::BasePath + "/commands/helpers.rb")
23
+ GitHub.should_receive(:load).with(GitHub::BasePath + "/commands/network.rb")
24
+ GitHub.should_receive(:load).with(GitHub::BasePath + "/commands/issues.rb")
25
+ args = @args.clone
26
+ GitHub.parse_options(args) # strip out the flags
27
+ GitHub.should_receive(:invoke).with(@cmd_name, *args).and_return do
28
+ GitHub.send(GitHub.send(:__mock_proxy).send(:munge, :invoke), @cmd_name, *args)
29
+ end
30
+ invoke = lambda { GitHub.activate([@cmd_name, *@args]) }
31
+ if @expected_result
32
+ expectation, result = @expected_result
33
+ case result
34
+ when Spec::Matchers::RaiseError, Spec::Matchers::Change, Spec::Matchers::ThrowSymbol
35
+ invoke.send expectation, result
36
+ else
37
+ invoke.call.send expectation, result
38
+ end
39
+ else
40
+ invoke.call
41
+ end
42
+ @stdout_mock.invoke unless @stdout_mock.nil?
43
+ @stderr_mock.invoke unless @stderr_mock.nil?
44
+ end
45
+
46
+ def setup_remote(remote, options = {:user => nil, :project => "project", :remote_branches => nil})
47
+ @remotes ||= {}
48
+ @remote_branches ||= {}
49
+ user = options[:user] || remote
50
+ project = options[:project]
51
+ ssh = options[:ssh]
52
+ url = options[:url]
53
+ remote_branches = options[:remote_branches] || ["master"]
54
+ if url
55
+ @remotes[remote] = url
56
+ elsif ssh
57
+ @remotes[remote] = "git@github.com:#{user}/#{project}.git"
58
+ else
59
+ @remotes[remote] = "git://github.com/#{user}/#{project}.git"
60
+ end
61
+
62
+ @remote_branches[remote] = (@remote_branches[remote] || Array.new) | remote_branches
63
+ @helper.should_receive(:remote_branch?).any_number_of_times.and_return do |remote, branch|
64
+ @remote_branches.fetch(remote.to_sym,[]).include?(branch)
65
+ end
66
+ end
67
+
68
+ def mock_remotes()
69
+ @helper.should_receive(:remotes).any_number_of_times.and_return(@remotes)
70
+ end
71
+
72
+ def mock_members(members)
73
+ @helper.should_receive(:network_members).any_number_of_times.and_return(members)
74
+ end
75
+
76
+ def should(result)
77
+ @expected_result = [:should, result]
78
+ end
79
+
80
+ def should_not(result)
81
+ @expected_result = [:should_not, result]
82
+ end
83
+
84
+ def stdout
85
+ if @stdout_mock.nil?
86
+ output = ""
87
+ @stdout_mock = DeferredMock.new(output)
88
+ $stdout.should_receive(:write).any_number_of_times do |str|
89
+ output << str
90
+ end
91
+ end
92
+ @stdout_mock
93
+ end
94
+
95
+ def stderr
96
+ if @stderr_mock.nil?
97
+ output = ""
98
+ @stderr_mock = DeferredMock.new(output)
99
+ $stderr.should_receive(:write).any_number_of_times do |str|
100
+ output << str
101
+ end
102
+ end
103
+ @stderr_mock
104
+ end
105
+
106
+ class DeferredMock
107
+ def initialize(obj = nil)
108
+ @obj = obj
109
+ @calls = []
110
+ @expectations = []
111
+ end
112
+
113
+ attr_reader :obj
114
+
115
+ def invoke(obj = nil)
116
+ obj ||= @obj
117
+ @calls.each do |sym, args|
118
+ obj.send sym, *args
119
+ end
120
+ @expectations.each do |exp|
121
+ exp.invoke
122
+ end
123
+ end
124
+
125
+ def should(*args)
126
+ if args.empty?
127
+ exp = Expectation.new(self, :should)
128
+ @expectations << exp
129
+ exp
130
+ else
131
+ @calls << [:should, args]
132
+ end
133
+ end
134
+
135
+ def should_not(*args)
136
+ if args.empty?
137
+ exp = Expectation.new(self, :should_not)
138
+ @expectations << exp
139
+ exp
140
+ else
141
+ @calls << [:should_not, args]
142
+ end
143
+ end
144
+
145
+ class Expectation
146
+ def initialize(mock, call)
147
+ @mock = mock
148
+ @call = call
149
+ @calls = []
150
+ end
151
+
152
+ undef_method *(instance_methods.map { |x| x.to_sym } - [:__id__, :__send__])
153
+
154
+ def invoke
155
+ @calls.each do |sym, args|
156
+ (@mock.obj.send @call).send sym, *args
157
+ end
158
+ end
159
+
160
+ def method_missing(sym, *args)
161
+ @calls << [sym, args]
162
+ end
163
+ end
164
+ end
165
+
166
+ def method_missing(sym, *args)
167
+ @parent.send sym, *args
168
+ end
169
+ end
170
+ end