github-grep 0.0.0 → 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/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
|
- - ">="
|