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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -8
  3. data/bin/github-grep +7 -69
  4. data/lib/github_grep.rb +89 -0
  5. metadata +4 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c7dbab452ca85ef021446d8318c1293623ebbc17e9f2a6a3fd74b32aa2a5a0da
4
- data.tar.gz: e5ccd15431cb9f46700aa7f336f68951e71f5b5269d0f97eb13e4a6904632316
3
+ metadata.gz: bed5f83e7664a3ce11e21935ebc1be098517cd6ab4aa7614b8c7d42309fb0544
4
+ data.tar.gz: 7b3923830d8148962d77adf72138cd2576d153fcb928a7837f40d3024d29366c
5
5
  SHA512:
6
- metadata.gz: 994f71f2cd15ca77611c094fb62300fea5446e21939e83e059ff4d15a5cb59bfaec58ecc39c150686dafed72ea4c70225f858c27cad5f7e66e88f233c4b8e3f6
7
- data.tar.gz: 39a96320037e21bcfd77b2399733c4a0f15ce40a7a045cd6c2c7153cced54bff5e4d8d52960a9a8077276fc014cd1275e35bea81579cda0433d5cbdc331a62d6
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
- - create a [application token](https://github.com/settings/applications) with read access
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
- bundle
6
+ gem install github-grep
9
7
 
10
- export GITHUB_TOKEN=xxx
11
- # or: git config github.token xxx
8
+ export GITHUB_TOKEN=<your token>
9
+ # or: git config github.token <your token>
12
10
 
13
11
  # search code:
14
- bin/github-grep 'user:grosser unicorn' | grep 'narrow-it-down' | grep -v 'something good'
12
+ github-grep 'user:grosser unicorn' | grep 'dictionary' | grep -v 'higher'
15
13
 
16
14
  # search issues and PR comments:
17
- bin/github-grep 'repo:kubernetes/kubernetes network error' --issues | grep 'narrow-it-down' | grep -v 'something good'
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
- require 'cgi'
3
- require 'json'
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 <<-TEXT.gsub(/^ /, "")
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
- break if items.size < per_page
55
- page += 1
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
- search(q, type) do |items|
86
- if type == :issues
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 }
@@ -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.0.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-13 00:00:00.000000000 Z
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: 2.0.0
64
+ version: '0'
78
65
  required_rubygems_version: !ruby/object:Gem::Requirement
79
66
  requirements:
80
67
  - - ">="