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.
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
  - - ">="