ghi 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +180 -0
- data/Manifest.txt +14 -0
- data/README.rdoc +92 -0
- data/bin/ghi +6 -0
- data/lib/ghi.rb +55 -0
- data/lib/ghi/api.rb +106 -0
- data/lib/ghi/cli.rb +608 -0
- data/lib/ghi/issue.rb +29 -0
- data/spec/ghi/api_spec.rb +194 -0
- data/spec/ghi/cli_spec.rb +258 -0
- data/spec/ghi/issue_spec.rb +26 -0
- data/spec/ghi_spec.rb +91 -0
- metadata +70 -0
data/History.rdoc
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
=== 0.2.0 / 2009-11-03
|
2
|
+
|
3
|
+
* Major-minor release!
|
4
|
+
|
5
|
+
* Cleanup! (Better help, warnings and errors.)
|
6
|
+
|
7
|
+
|
8
|
+
=== 0.1.7 / 2009-08-26
|
9
|
+
|
10
|
+
* 2 bugfixes
|
11
|
+
|
12
|
+
* Allow dots in repo names.
|
13
|
+
* Fix bug preventing invocation from non-GitHub repo directories.
|
14
|
+
|
15
|
+
|
16
|
+
=== 0.1.6 / 2009-08-05
|
17
|
+
|
18
|
+
* 1 minor enhancement
|
19
|
+
|
20
|
+
* Support for non-"origin" remotes.
|
21
|
+
|
22
|
+
|
23
|
+
* 1 bugfix
|
24
|
+
|
25
|
+
* Graceful offline error.
|
26
|
+
|
27
|
+
|
28
|
+
=== 0.1.5 / 2009-07-08
|
29
|
+
|
30
|
+
* 2 bugfixes
|
31
|
+
|
32
|
+
* Long titles should wrap in show/verbose issue view.
|
33
|
+
* If a non-OptionParser argument fails early, re-parse when valid.
|
34
|
+
|
35
|
+
|
36
|
+
=== 0.1.4 / 2009-05-15
|
37
|
+
|
38
|
+
* 1 minor enhancement
|
39
|
+
|
40
|
+
* Minor Windows support.
|
41
|
+
|
42
|
+
|
43
|
+
=== 0.1.3 / 2009-05-08
|
44
|
+
|
45
|
+
* 1 minor enhancement
|
46
|
+
|
47
|
+
* Fix bug where `more' was being passed `less' options.
|
48
|
+
* Fix scoping of commands so `ghi user/repo` can run outside of a repo
|
49
|
+
directory.
|
50
|
+
|
51
|
+
|
52
|
+
=== 0.1.2 / 2009-05-07
|
53
|
+
|
54
|
+
* 1 major enhancement
|
55
|
+
|
56
|
+
* Better fallbacks. Enter `ghi open`, `ghi list closed`, `ghi search term`,
|
57
|
+
`ghi show 2`, `ghi user/repo`, etc., it will try to work. Fallbacks do not
|
58
|
+
accept options, though.
|
59
|
+
|
60
|
+
|
61
|
+
=== 0.1.1 / 2009-05-01
|
62
|
+
|
63
|
+
* 3 major enhancements
|
64
|
+
|
65
|
+
* Use `more' (or $GHI_PAGER) to accommodate lengthy output.
|
66
|
+
* Default to "-l" if Dir.pwd is a git repo.
|
67
|
+
* Accept numbered args/flags to shortcut "show" (e.g., "ghi -2", "ghi 2")
|
68
|
+
|
69
|
+
|
70
|
+
* 1 minor enhancement
|
71
|
+
|
72
|
+
* Update --url flag to GitHub's new convention.
|
73
|
+
|
74
|
+
|
75
|
+
=== 0.1.0 / 2009-04-27
|
76
|
+
|
77
|
+
* 2 major enhancements
|
78
|
+
|
79
|
+
* Use tempfiles when we should, the gitdir otherwise.
|
80
|
+
* Now a minor!
|
81
|
+
|
82
|
+
|
83
|
+
* 2 minor enhancement
|
84
|
+
|
85
|
+
* Small ANSI tweaks.
|
86
|
+
* Truncation fix.
|
87
|
+
|
88
|
+
|
89
|
+
=== 0.0.9 / 2009-04-27
|
90
|
+
|
91
|
+
* 1 major enhancement
|
92
|
+
|
93
|
+
* ANSI colors (honors your .gitconfig).
|
94
|
+
|
95
|
+
|
96
|
+
* 1 minor enhancement
|
97
|
+
|
98
|
+
* Bugfixes.
|
99
|
+
|
100
|
+
|
101
|
+
=== 0.0.8 / 2009-04-26
|
102
|
+
|
103
|
+
* 1 major enhancement
|
104
|
+
|
105
|
+
* Flag to return issues URLs.
|
106
|
+
|
107
|
+
|
108
|
+
* 1 minor enhancement
|
109
|
+
|
110
|
+
* Preliminary specs for top-level module and API class.
|
111
|
+
|
112
|
+
|
113
|
+
=== 0.0.7 / 2009-04-25
|
114
|
+
|
115
|
+
* 1 major enhancement
|
116
|
+
|
117
|
+
* Labels! Label and un-label at your whim.
|
118
|
+
|
119
|
+
|
120
|
+
* 1 minor enhancement
|
121
|
+
|
122
|
+
* Tail arguments are sometimes parsed. E.g., ghi -om "Issue message parsed".
|
123
|
+
|
124
|
+
|
125
|
+
=== 0.0.6 / 2009-04-25
|
126
|
+
|
127
|
+
* 2 minor enhancements
|
128
|
+
|
129
|
+
* Accept comments as arguments in more places.
|
130
|
+
* Update error message to accommodate both issues and comments.
|
131
|
+
|
132
|
+
|
133
|
+
=== 0.0.5 / 2009-04-25
|
134
|
+
|
135
|
+
* 3 major enhancements
|
136
|
+
|
137
|
+
* Flag to claim/label issues with your GitHub username (thanks, Jamie).
|
138
|
+
* Flag for commenting on issues.
|
139
|
+
* Prompt for GitHub login and token if absent from gitconfig.
|
140
|
+
|
141
|
+
|
142
|
+
* 1 minor enhancement
|
143
|
+
|
144
|
+
* Opening issues with a title bypasses your $EDITOR.
|
145
|
+
|
146
|
+
|
147
|
+
=== 0.0.4 / 2009-04-24
|
148
|
+
|
149
|
+
* 1 major enhancement
|
150
|
+
|
151
|
+
* Cache messages created in $EDITOR.
|
152
|
+
|
153
|
+
|
154
|
+
* 2 minor enhancements
|
155
|
+
|
156
|
+
* Refactoring and cleanup.
|
157
|
+
* Change editing messages.
|
158
|
+
|
159
|
+
|
160
|
+
=== 0.0.3 / 2009-04-23
|
161
|
+
|
162
|
+
* 2 minor enhancements
|
163
|
+
|
164
|
+
* Typo corrected.
|
165
|
+
* README updated.
|
166
|
+
|
167
|
+
|
168
|
+
=== 0.0.2 / 2009-04-22
|
169
|
+
|
170
|
+
* 1 major enhancement
|
171
|
+
|
172
|
+
* Add --search flag.
|
173
|
+
* Add --repo flag.
|
174
|
+
|
175
|
+
|
176
|
+
=== 0.0.1 / 2009-04-21
|
177
|
+
|
178
|
+
* 1 major enhancement
|
179
|
+
|
180
|
+
* Birthday!
|
data/Manifest.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
= ghi
|
2
|
+
|
3
|
+
http://github.com/stephencelis/ghi
|
4
|
+
|
5
|
+
|
6
|
+
GitHub Issues on the command line. Use your <tt>$EDITOR</tt>, not your
|
7
|
+
browser.
|
8
|
+
|
9
|
+
== HOW?
|
10
|
+
|
11
|
+
Get:
|
12
|
+
|
13
|
+
% gem install stephencelis-ghi --source=http://gems.github.com
|
14
|
+
|
15
|
+
|
16
|
+
Go:
|
17
|
+
|
18
|
+
Usage: ghi [options]
|
19
|
+
-l, --list [state|term|number]
|
20
|
+
--search, --show
|
21
|
+
-v, --verbose
|
22
|
+
-o, --open [title|number]
|
23
|
+
--reopen
|
24
|
+
-c, --closed, --close [number]
|
25
|
+
-e, --edit [number]
|
26
|
+
-r, --repo, --repository [name]
|
27
|
+
-m, --comment [number|comment]
|
28
|
+
-t, --label [number] [label]
|
29
|
+
--claim [number]
|
30
|
+
-d, --unlabel [number] [label]
|
31
|
+
-u, --url [state|number]
|
32
|
+
--[no-]color
|
33
|
+
--[no-]pager
|
34
|
+
-V, --version
|
35
|
+
-h, --help
|
36
|
+
|
37
|
+
|
38
|
+
== EXAMPLE?
|
39
|
+
|
40
|
+
ghi works simply from within a repository. Some short examples:
|
41
|
+
|
42
|
+
ghi -l # Lists all open issues
|
43
|
+
ghi # Shorter shorthand for "ghi -l"
|
44
|
+
ghi -v # Lists all open issues, verbosely (includes body)
|
45
|
+
ghi -lc # Lists all closed issues
|
46
|
+
ghi -l "doesn't work" # Searches for open issues matching "doesn't work"
|
47
|
+
ghi -l invalid -c # Searches for closed issues matching "invalid"
|
48
|
+
ghi -l1 # Shows issue 1
|
49
|
+
ghi -1 # Shorter shorthand for "ghi -l1"
|
50
|
+
ghi 1 # Shorter shorthand still
|
51
|
+
ghi -o # Opens a new issue (in your $EDITOR)
|
52
|
+
ghi -o "New issue" # Opens a new issue with the title "New issue"
|
53
|
+
ghi -o "Title" -m "Body" # Opens a new issue with specified title and body
|
54
|
+
ghi -e1 # Edits issue number 1 (in your $EDITOR)
|
55
|
+
ghi -e1 -m "New body" # Edits issue number 1 with the specified body
|
56
|
+
ghi -c1 # Closes issue 1
|
57
|
+
ghi -c1 -m # Closes issue with comment (from your $EDITOR)
|
58
|
+
ghi -c1 -m "Comment" # Closes issue with specified comment
|
59
|
+
ghi -o1 # Reopens 1 (accepts comments, too)
|
60
|
+
ghi -m1 # Comments on issue 1 (in your $EDITOR)
|
61
|
+
ghi -t1 "tag" # Labels issue 1 with "tag"
|
62
|
+
ghi -d1 "tag" # Removes the label, "tag"
|
63
|
+
ghi --claim 1 # Tags issue 1 with your GitHub username
|
64
|
+
ghi -u # Loads issues in your browser.
|
65
|
+
ghi -u1 # Loads an issue in your browser.
|
66
|
+
|
67
|
+
|
68
|
+
ghi also works anywhere:
|
69
|
+
|
70
|
+
ghi -rghi # Your fork of "ghi"
|
71
|
+
ghi -rstephencelis/ghi # Mine: "stephencelis/ghi"
|
72
|
+
ghi stephencelis/ghi # Shorthand to merely list open.
|
73
|
+
|
74
|
+
|
75
|
+
ghi uses ANSI colors if you use them in git.
|
76
|
+
|
77
|
+
ghi looks for a <tt>$GHI_PAGER</tt> variable for paging.
|
78
|
+
|
79
|
+
|
80
|
+
== CONTRIBUTORS
|
81
|
+
|
82
|
+
* Jamie Macey (http://blog.tracefunc.com)
|
83
|
+
* Hiroshi Nakamura (http://github.com/nahi)
|
84
|
+
|
85
|
+
|
86
|
+
=== CONTRIBUTE?
|
87
|
+
|
88
|
+
ghi is not under currently under the control of any gem packaging system. To
|
89
|
+
build, use RubyGems:
|
90
|
+
|
91
|
+
% gem build ghi.gemspec
|
92
|
+
% sudo gem install ghi*.gem
|
data/bin/ghi
ADDED
data/lib/ghi.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
module GHI
|
5
|
+
VERSION = "0.2"
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def login
|
9
|
+
return @login if defined? @login
|
10
|
+
@login = `git config --get github.user`.chomp
|
11
|
+
if @login.empty?
|
12
|
+
begin
|
13
|
+
print "Please enter your GitHub username: "
|
14
|
+
@login = gets.chomp
|
15
|
+
valid = user? @login
|
16
|
+
warn "invalid username" unless valid
|
17
|
+
end until valid
|
18
|
+
`git config --global github.user #@login`
|
19
|
+
end
|
20
|
+
@login
|
21
|
+
end
|
22
|
+
|
23
|
+
def token
|
24
|
+
return @token if defined? @token
|
25
|
+
@token = `git config --get github.token`.chomp
|
26
|
+
if @token.empty?
|
27
|
+
begin
|
28
|
+
print "GitHub token (https://github.com/account): "
|
29
|
+
@token = gets.chomp
|
30
|
+
valid = token? @token
|
31
|
+
warn "invalid token for #{login}" unless valid
|
32
|
+
end until valid
|
33
|
+
`git config --global github.token #@token`
|
34
|
+
end
|
35
|
+
@token
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def user?(username)
|
41
|
+
url = "http://github.com/api/v2/yaml/user/show/#{username}"
|
42
|
+
!YAML.load(Net::HTTP.get(URI.parse(url)))["user"].nil?
|
43
|
+
rescue ArgumentError, URI::InvalidURIError
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def token?(token)
|
48
|
+
url = "http://github.com/api/v2/yaml/user/show/#{login}"
|
49
|
+
url += "?login=#{login}&token=#{token}"
|
50
|
+
!YAML.load(Net::HTTP.get(URI.parse(url)))["user"]["plan"].nil?
|
51
|
+
rescue ArgumentError, NoMethodError, URI::InvalidURIError
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/ghi/api.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
class GHI::API
|
5
|
+
class InvalidRequest < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
class InvalidConnection < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
class ResponseError < StandardError
|
12
|
+
end
|
13
|
+
|
14
|
+
API_URL = "http://github.com/api/v2/yaml/issues/:action/:user/:repo"
|
15
|
+
|
16
|
+
attr_reader :user, :repo
|
17
|
+
|
18
|
+
def initialize(user, repo)
|
19
|
+
raise InvalidConnection if user.nil? || repo.nil?
|
20
|
+
@user, @repo = user, repo
|
21
|
+
end
|
22
|
+
|
23
|
+
def search(term, state = :open)
|
24
|
+
get(:search, state, term)["issues"].map { |attrs| GHI::Issue.new(attrs) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def list(state = :open)
|
28
|
+
get(:list, state)["issues"].map { |attrs| GHI::Issue.new(attrs) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def show(number)
|
32
|
+
GHI::Issue.new get(:show, number)["issue"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def open(title, body)
|
36
|
+
GHI::Issue.new post(:open, :title => title, :body => body)["issue"]
|
37
|
+
end
|
38
|
+
|
39
|
+
def edit(number, title, body)
|
40
|
+
res = post :edit, number, :title => title, :body => body
|
41
|
+
GHI::Issue.new res["issue"]
|
42
|
+
end
|
43
|
+
|
44
|
+
def close(number)
|
45
|
+
GHI::Issue.new post(:close, number)["issue"]
|
46
|
+
end
|
47
|
+
|
48
|
+
def reopen(number)
|
49
|
+
GHI::Issue.new post(:reopen, number)["issue"]
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_label(label, number)
|
53
|
+
post("label/add", label, number)["labels"]
|
54
|
+
end
|
55
|
+
|
56
|
+
def remove_label(label, number)
|
57
|
+
post("label/remove", label, number)["labels"]
|
58
|
+
end
|
59
|
+
|
60
|
+
def comment(number, comment)
|
61
|
+
post(:comment, number, :comment => comment)["comment"]
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def get(*args)
|
67
|
+
res = YAML.load Net::HTTP.get(URI.parse(url(*args) + auth(true)))
|
68
|
+
raise ResponseError, errors(res) if res["error"]
|
69
|
+
res
|
70
|
+
rescue ArgumentError, URI::InvalidURIError
|
71
|
+
raise ResponseError, "GitHub hiccuped on your request"
|
72
|
+
rescue SocketError
|
73
|
+
raise ResponseError, "couldn't find the internet"
|
74
|
+
end
|
75
|
+
|
76
|
+
def post(*args)
|
77
|
+
params = args.last.is_a?(Hash) ? args.pop : {}
|
78
|
+
params.update auth
|
79
|
+
res = YAML.load Net::HTTP.post_form(URI.parse(url(*args)), params).body
|
80
|
+
raise ResponseError, errors(res) if res["error"]
|
81
|
+
res
|
82
|
+
rescue ArgumentError, URI::InvalidURIError
|
83
|
+
raise ResponseError, "GitHub hiccuped on your request"
|
84
|
+
rescue SocketError
|
85
|
+
raise ResponseError, "couldn't find the internet"
|
86
|
+
end
|
87
|
+
|
88
|
+
def errors(response)
|
89
|
+
[*response["error"]].map { |e| e["error"] } * ", "
|
90
|
+
end
|
91
|
+
|
92
|
+
def auth(query = false)
|
93
|
+
if query
|
94
|
+
"?login=#{GHI.login}&token=#{GHI.token}"
|
95
|
+
else
|
96
|
+
{ :login => GHI.login, :token => GHI.token }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def url(action, *args)
|
101
|
+
@url ||= API_URL.sub(":user", user).sub(":repo", repo)
|
102
|
+
uri = @url.sub ":action", action.to_s
|
103
|
+
uri += "/#{args.join("/")}" unless args.empty?
|
104
|
+
uri
|
105
|
+
end
|
106
|
+
end
|