github-grep 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -8
- data/bin/github-grep +7 -69
- data/lib/github_grep.rb +89 -0
- metadata +4 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bed5f83e7664a3ce11e21935ebc1be098517cd6ab4aa7614b8c7d42309fb0544
|
4
|
+
data.tar.gz: 7b3923830d8148962d77adf72138cd2576d153fcb928a7837f40d3024d29366c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '091a02da44da6fb6b16f01bf82a57a9e690a091da17c702a4c58e24a33f57428dadf0283fa93e92f370f81e857c85e723dacda1b4890543e5fa84fb34aa27cee'
|
7
|
+
data.tar.gz: 0a02ebf19cc013c6ff40f06f70b802875287f81dd32b65c33fe36b48228d96a120a250f9eaa16c1db52c08f516f3d60d793c9d85960975505197e793ec3f87c3
|
data/README.md
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
makes github search grep and pipeable
|
2
2
|
|
3
|
-
|
4
|
-
- clone repo
|
5
|
-
- follow script instructions
|
3
|
+
First create a [application token](https://github.com/settings/applications) with read access + enable SSO if available.
|
6
4
|
|
7
5
|
```
|
8
|
-
|
6
|
+
gem install github-grep
|
9
7
|
|
10
|
-
export GITHUB_TOKEN
|
11
|
-
# or: git config github.token
|
8
|
+
export GITHUB_TOKEN=<your token>
|
9
|
+
# or: git config github.token <your token>
|
12
10
|
|
13
11
|
# search code:
|
14
|
-
|
12
|
+
github-grep 'user:grosser unicorn' | grep 'dictionary' | grep -v 'higher'
|
15
13
|
|
16
14
|
# search issues and PR comments:
|
17
|
-
|
15
|
+
github-grep 'repo:kubernetes/kubernetes network error' --issues | grep 'narrow-it-down' | grep -v 'something good'
|
18
16
|
```
|
19
17
|
|
20
18
|
NOTE: there are random 403 errors on the last page of a search (usually empty anyway), contacted github support about that :/
|
data/bin/github-grep
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require
|
4
|
-
require 'shellwords'
|
5
|
-
require 'open3'
|
2
|
+
$LOAD_PATH << File.expand_path("../../lib", __FILE__)
|
3
|
+
require "github_grep"
|
6
4
|
|
7
5
|
def usage
|
8
|
-
puts
|
6
|
+
puts <<~TEXT
|
9
7
|
Setup
|
10
8
|
-----
|
11
9
|
# create a new token at https://github.com/settings/tokens/new with repo access
|
@@ -18,74 +16,14 @@ def usage
|
|
18
16
|
exit 1
|
19
17
|
end
|
20
18
|
|
21
|
-
def code_items_to_lines(items)
|
22
|
-
items.flat_map do |item|
|
23
|
-
file = item.fetch('repository').fetch('name') + ":" + item.fetch('path')
|
24
|
-
lines(item).map { |l| "#{file}: #{l}" }
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def issue_items_to_lines(items)
|
29
|
-
items.flat_map do |item|
|
30
|
-
number = item.fetch("number")
|
31
|
-
lines(item).map { |l| "##{number}: #{l}" }
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def lines(item)
|
36
|
-
item.fetch("text_matches").flat_map { |match| match.fetch('fragment').split("\n") }
|
37
|
-
end
|
38
|
-
|
39
|
-
def search(q, type)
|
40
|
-
per_page = 100
|
41
|
-
page = 1
|
42
|
-
|
43
|
-
loop do
|
44
|
-
response = page(q, type, page, per_page)
|
45
|
-
if page == 1
|
46
|
-
$stderr.puts "Found #{response.fetch("total_count")}"
|
47
|
-
else
|
48
|
-
$stderr.puts "Page #{page}"
|
49
|
-
end
|
50
|
-
|
51
|
-
items = response.fetch('items')
|
52
|
-
yield items
|
53
19
|
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def page(q, type, page, per_page)
|
60
|
-
github_token = ENV['GITHUB_TOKEN'] || `git config github.token`.strip # TODO: update docs
|
61
|
-
usage if github_token.empty?
|
62
|
-
|
63
|
-
# remove --fail and add -v to see response headers
|
64
|
-
# NOTE: github returns a 403 with a Retry-After: 60 on page 3+ ... talking with support atm but might have to handle it
|
65
|
-
url = "https://api.github.com/search/#{type}?per_page=#{per_page}&page=#{page}&q=#{CGI.escape(q)}"
|
66
|
-
command = ["curl", "-v", "-f", "-H", "Authorization: token #{github_token}", "-H", "Accept: application/vnd.github.v3.text-match+json", url]
|
67
|
-
|
68
|
-
out, err, status = Open3.capture3(*command)
|
69
|
-
if retry_after = err[/Retry-After: (\d+)/, 1] # 403 Abuse rate limit
|
70
|
-
warn "Sleeping #{retry_after} to avoid abuse rate-limit"
|
71
|
-
sleep Integer(retry_after)
|
72
|
-
out, err, status = Open3.capture3(*command)
|
73
|
-
end
|
74
|
-
|
75
|
-
raise "ERROR Request failed\n#{url}\n#{err}\n#{out}" unless status.success?
|
76
|
-
|
77
|
-
JSON.load(out)
|
78
|
-
end
|
20
|
+
github_token = ENV['GITHUB_TOKEN'] || `git config github.token`.strip # TODO: update docs
|
21
|
+
usage if github_token.empty?
|
79
22
|
|
80
23
|
type = (ARGV.delete('--issues') ? :issues : :code)
|
81
24
|
|
82
25
|
q = ARGV.shift
|
83
26
|
usage if ARGV.size != 0
|
84
27
|
|
85
|
-
|
86
|
-
|
87
|
-
puts issue_items_to_lines(items)
|
88
|
-
else
|
89
|
-
puts code_items_to_lines(items)
|
90
|
-
end
|
91
|
-
end
|
28
|
+
grep = GithubGrep.new(github_token)
|
29
|
+
grep.render_search(q, type) { |slice| puts slice }
|
data/lib/github_grep.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'json'
|
3
|
+
require 'shellwords'
|
4
|
+
require 'open3'
|
5
|
+
|
6
|
+
class GithubGrep
|
7
|
+
VERSION = "0.1.0"
|
8
|
+
|
9
|
+
def initialize(token)
|
10
|
+
@token = token
|
11
|
+
end
|
12
|
+
|
13
|
+
def render_search(q, type)
|
14
|
+
search(q, type) do |items|
|
15
|
+
if type == :issues
|
16
|
+
yield issue_items_to_lines(items)
|
17
|
+
else
|
18
|
+
yield code_items_to_lines(items)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def search(q, type, &block)
|
24
|
+
headers = ["-H", "Accept: application/vnd.github.v3.text-match+json"]
|
25
|
+
url = "https://api.github.com/search/#{type}?q=#{CGI.escape(q)}"
|
26
|
+
all_pages(url, per_page: 100, argv: headers, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def code_items_to_lines(items)
|
32
|
+
items.flat_map do |item|
|
33
|
+
file = item.fetch('repository').fetch('name') + ":" + item.fetch('path')
|
34
|
+
lines(item).map { |l| "#{file}: #{l}" }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def issue_items_to_lines(items)
|
39
|
+
items.flat_map do |item|
|
40
|
+
number = item.fetch("number")
|
41
|
+
lines(item).map { |l| "##{number}: #{l}" }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def lines(item)
|
46
|
+
item.fetch("text_matches").flat_map { |match| match.fetch('fragment').split("\n") }
|
47
|
+
end
|
48
|
+
|
49
|
+
def all_pages(url, per_page:, **kwargs)
|
50
|
+
page = 1
|
51
|
+
connector = (url.include?("?") ? "&" : "?")
|
52
|
+
loop do
|
53
|
+
response = request_json("#{url}#{connector}per_page=#{per_page}&page=#{page}", **kwargs)
|
54
|
+
hash = response.is_a?(Hash)
|
55
|
+
if page == 1 && hash && total = response["total_count"]
|
56
|
+
$stderr.puts "Found #{total}"
|
57
|
+
else
|
58
|
+
$stderr.puts "Page #{page}"
|
59
|
+
end
|
60
|
+
|
61
|
+
items = (hash ? response.fetch('items') : response)
|
62
|
+
yield items
|
63
|
+
|
64
|
+
break if items.size < per_page
|
65
|
+
page += 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def request_json(url, argv: [])
|
70
|
+
# NOTE: github returns a 403 with a Retry-After: 60 on page 3+ ... talking with support atm but might have to handle it
|
71
|
+
command = ["curl", "-sSfv", "-H", "Authorization: token #{@token}", *argv, url]
|
72
|
+
|
73
|
+
out, err, status = Open3.capture3(*command)
|
74
|
+
|
75
|
+
# 403 Abuse rate limit often has no Retry-After
|
76
|
+
retry_after = err[/Retry-After: (\d+)/, 1]
|
77
|
+
abuse_limit = err.include?("returned error: 403")
|
78
|
+
if retry_after || abuse_limit
|
79
|
+
retry_after ||= "20"
|
80
|
+
warn "Sleeping #{retry_after} to avoid abuse rate-limit"
|
81
|
+
sleep Integer(retry_after)
|
82
|
+
out, err, status = Open3.capture3(*command)
|
83
|
+
end
|
84
|
+
|
85
|
+
raise "ERROR Request failed\n#{url}\n#{err}\n#{out}" unless status.success?
|
86
|
+
|
87
|
+
JSON.parse(out)
|
88
|
+
end
|
89
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: github-grep
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Grosser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-10-
|
11
|
+
date: 2021-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: json
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rake
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -62,6 +48,7 @@ files:
|
|
62
48
|
- MIT-LICENSE.txt
|
63
49
|
- README.md
|
64
50
|
- bin/github-grep
|
51
|
+
- lib/github_grep.rb
|
65
52
|
homepage: https://github.com/grosser/github-grep
|
66
53
|
licenses:
|
67
54
|
- MIT
|
@@ -74,7 +61,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
74
61
|
requirements:
|
75
62
|
- - ">="
|
76
63
|
- !ruby/object:Gem::Version
|
77
|
-
version:
|
64
|
+
version: '0'
|
78
65
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
66
|
requirements:
|
80
67
|
- - ">="
|