gh-search 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a300dcaf4a8dc6474fe1478e29eec71b0729447385d56cb1c6827b56b7936d81
4
+ data.tar.gz: 646036281be686eb397262d9eb3ecaf7cb0e5a1554af01a7df3eaa3466920cf7
5
+ SHA512:
6
+ metadata.gz: c2680949249fa38fa5aa89f6206b3c181f5cd4bdb564ce8a85095ba4829ecadd28301bf31203674f248e973b500e63b80664c91dfaddfce4a2872864868e65b3
7
+ data.tar.gz: be7281c5d48a213e420237cd7b7c574bcba9826e3a75fcafe4be9b23154d2163306aeecf08ba00fcb1cffb28b401003be3d579e42e2295b390414d74f8d40bcc
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Dave Elliott
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "gh_search"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/gh-search ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require "gh_search/cli"
3
+
4
+ # ARGV.unshift(GhSearch::CLI.default_task) unless GhSearch::CLI.all_tasks.has_key?(ARGV[0]) || GhSearch::CLI.instance_variable_get(:@map).has_key?(ARGV[0])
5
+ # GhSearch::CLI.new(ARGV).search
6
+ GhSearch::CLI.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/gh_search.rb ADDED
@@ -0,0 +1,17 @@
1
+ require "gh_search/version"
2
+ require "gh_search/github_client"
3
+ require "gh_search/formatters"
4
+
5
+ module GhSearch
6
+ class Error < StandardError; end
7
+
8
+ def self.run(search_text, options={})
9
+ client = GithubClient.new(search_text, options)
10
+
11
+ Formatters.with(options.fetch(:format, 'plain')) do |formatter|
12
+ client.each do |match|
13
+ formatter.write(match)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,89 @@
1
+ require 'gh_search'
2
+ require 'getoptlong'
3
+
4
+ module GhSearch
5
+ class CLI
6
+ def self.start
7
+ opts = GetoptLong.new(
8
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
9
+ [ '--version', '-v', GetoptLong::NO_ARGUMENT ],
10
+ [ '--org', GetoptLong::REQUIRED_ARGUMENT ],
11
+ [ '--format', '-f', GetoptLong::REQUIRED_ARGUMENT ],
12
+ [ '--file', GetoptLong::REQUIRED_ARGUMENT ],
13
+ [ '--lang', GetoptLong::REQUIRED_ARGUMENT ],
14
+ [ '--output', '-o', GetoptLong::REQUIRED_ARGUMENT ],
15
+ )
16
+
17
+
18
+ options = {}
19
+
20
+ begin
21
+ opts.each do |opt, arg|
22
+ case opt
23
+ when '--help'
24
+ print_help
25
+ exit 0
26
+ when '--version'
27
+ puts "gh-search #{GhSearch::VERSION}"
28
+ exit 0
29
+ when '--org'
30
+ options[:org] = arg
31
+ when '--lang'
32
+ options[:lang] = arg
33
+ when '--file'
34
+ options[:file] = arg
35
+ when '--format'
36
+ options[:format ]= arg
37
+ when '--output'
38
+ options[:output] = arg
39
+ end
40
+ end
41
+ rescue GetoptLong::Error => e
42
+ print_help
43
+ exit 1
44
+ end
45
+ if ARGV.length != 1
46
+ puts "Missing SEARCH_TEXT argument (try --help)"
47
+ puts
48
+ print_help
49
+ exit 0
50
+ end
51
+
52
+ search_text = ARGV.shift
53
+
54
+ GhSearch.run(search_text, options)
55
+ end
56
+
57
+
58
+ private
59
+
60
+ def self.print_usage
61
+ puts "Usage: gh-search SEARCH_TEXT [OPTIONS]"
62
+ puts
63
+ end
64
+
65
+ def self.print_help
66
+ print_usage
67
+ puts <<-EOF
68
+ SEARCH_TEXT: The alphanumeric text to find matches of.
69
+
70
+ Search Options:
71
+ --org GitHub Organisation
72
+ --file Filename to search (e.g. package.json)
73
+ --lang Language type to search (e.g. yml)
74
+
75
+ Output Options:
76
+ --output Destination to write file
77
+ --format Output format (e.g. json, csv)
78
+
79
+ Misc Options:
80
+ -v, --version Prints version
81
+ -h, --help Prints this message
82
+
83
+ For searching private repositories you will need to set the GITHUB_API_TOKEN environment variable.
84
+ The GitHub documentation provides steps for this (https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token).
85
+ EOF
86
+ end
87
+ end
88
+ end
89
+
@@ -0,0 +1,30 @@
1
+ module GhSearch
2
+ module Formatters
3
+ require "gh_search/formatters/base"
4
+ require "gh_search/formatters/plain"
5
+ require "gh_search/formatters/json"
6
+ require "gh_search/formatters/csv"
7
+
8
+ def self.with(type, options={})
9
+ formatter_class = case type
10
+ when 'plain'
11
+ Formatters::Plain
12
+ when 'csv'
13
+ Formatters::Csv
14
+ when 'json'
15
+ Formatters::Json
16
+ when 'stdout'
17
+ Formatters::Stdout
18
+ else
19
+ raise 'unknown format type'
20
+ end
21
+
22
+ # puts "[DEBUG] Loaded #{formatter_class.name}"
23
+ formatter = formatter_class.new(options)
24
+ yield formatter
25
+
26
+ formatter.write_file!
27
+ puts
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ module GhSearch
2
+ module Formatters
3
+ class Base
4
+ attr_reader :output_file
5
+ def initialize(options={})
6
+ @output_file = options[:output]
7
+ end
8
+
9
+ def write!(match)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def write_file!
14
+ # used for writing out a file
15
+ end
16
+
17
+ protected
18
+
19
+ def print_and_flush(str)
20
+ print str
21
+ $stdout.flush
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ require 'csv'
2
+
3
+ module GhSearch
4
+ module Formatters
5
+ class Csv < Base
6
+ def write(match)
7
+
8
+ if output_file
9
+ @file ||= CSV.open(output_file, "w")
10
+ unless @headers
11
+ @headers = match.keys
12
+ @file << @headers
13
+ end
14
+
15
+ @file << match.values
16
+ print_and_flush('.')
17
+ else
18
+ unless @headers
19
+ @headers = match.keys
20
+ puts @headers.join(',')
21
+ end
22
+ puts match.values.join(',')
23
+ end
24
+ end
25
+
26
+ def write_file!
27
+ @file.close if @file
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ require 'json'
2
+
3
+ module GhSearch
4
+ module Formatters
5
+ class Json < Base
6
+ def write(match)
7
+ @data ||= []
8
+ @data << match
9
+ print_and_flush('.') if output_file
10
+ end
11
+
12
+ def write_file!
13
+ if output_file
14
+ @file ||= File.open(output_file, "w")
15
+ @file.write @data.to_json
16
+ @file.close
17
+ else
18
+ puts @data
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ module GhSearch
2
+ module Formatters
3
+ class Plain < Base
4
+ def write(match)
5
+ text = [match[:repo_name], match[:path], match[:linenumber], match[:text], "\n"].join(' ')
6
+
7
+ if output_file
8
+ @file ||= File.open(output_file, "w")
9
+ @file.write text
10
+ print_and_flush('.')
11
+ else
12
+ puts text
13
+ end
14
+ end
15
+
16
+ def write_file!
17
+ @file.close if @file
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/ruby
2
+ # Simple utilitiy that uses the GitHub API for search
3
+ #
4
+ # Example endpoint it uses: https://api.github.com/search/code?q=HACK+in:file+org:Sage
5
+
6
+ module GhSearch
7
+ require 'net/http'
8
+ require "base64"
9
+ require 'json'
10
+
11
+ class GithubClient
12
+ GITHUB_URL = "https://api.github.com"
13
+ ENDPOINT = "/search/code"
14
+
15
+ attr_reader :org, :search_text, :filename
16
+
17
+ attr_accessor :pagenum
18
+
19
+ def initialize(search_text, options={})
20
+ @search_text = search_text
21
+ @org = options[:org]
22
+ @filename = options[:file]
23
+ @pagenum = 1
24
+ end
25
+
26
+ def each
27
+ has_results = true
28
+
29
+ while has_results do
30
+ json_response = make_request(uri)['items']
31
+
32
+ if has_results = json_response.any?
33
+ json_response.each do |item|
34
+ each_match(URI(item['url'])) do |match|
35
+
36
+ result = match.merge({
37
+ repo_name: item['repository']['full_name'],
38
+ html_url: item['html_url'],
39
+ })
40
+
41
+ yield result
42
+ end
43
+ end
44
+ end
45
+
46
+ @pagenum += 1
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def make_request(request_uri)
53
+ # puts uri # debug
54
+ req = Net::HTTP::Get.new(request_uri)
55
+
56
+ if ENV['GITHUB_API_TOKEN']
57
+ req['AUTHORIZATION'] = "token #{github_token}"
58
+ else
59
+ puts "[WARN] unauthenticated"
60
+ end
61
+
62
+ http = Net::HTTP.new(request_uri.hostname, request_uri.port)
63
+ http.use_ssl = true
64
+
65
+ response = http.request(req)
66
+
67
+ if response.code != "200"
68
+ puts "[ERROR]: #{response.code}"
69
+ puts "[DEBUG] failed to call #{request_uri}"
70
+ puts response.body
71
+ exit 1
72
+ end
73
+
74
+ JSON.parse(response.body)
75
+ end
76
+
77
+ def each_match(request_uri)
78
+ response = make_request(request_uri)
79
+
80
+ raise 'Error dont know how to decode content' if response['encoding'] != "base64"
81
+ plain = Base64.decode64(response['content'])
82
+
83
+ begin
84
+ # TODO get line number
85
+ plain.match(/(.*#{search_text}.*)/i).captures.each do |capture|
86
+ match = {
87
+ text: capture,
88
+ path: response['path'],
89
+ commit_sha: response['sha'],
90
+ }
91
+ yield match
92
+ end
93
+ rescue
94
+ # could not find match
95
+ return nil
96
+ end
97
+ end
98
+
99
+ def github_token
100
+ ENV.fetch('GITHUB_API_TOKEN')
101
+ end
102
+
103
+ def uri
104
+ query = "q=#{search_text}"
105
+ query += "+in:file"
106
+ query += "+org:#{org}" if org
107
+ query += "+filename:#{filename}" if filename
108
+
109
+ url = [GITHUB_URL, ENDPOINT, "?page=#{@pagenum}&", query].join
110
+ # puts "[DEBUG] url: '#{url}'"
111
+
112
+ URI(url)
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,3 @@
1
+ module GhSearch
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gh-search
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dave Elliott
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-03-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.14'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.14'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: Uses the GitHub API to perform searches for given string
70
+ email:
71
+ - ddazza@gmail.com
72
+ executables:
73
+ - gh-search
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - LICENSE.txt
78
+ - bin/console
79
+ - bin/gh-search
80
+ - bin/setup
81
+ - lib/gh_search.rb
82
+ - lib/gh_search/cli.rb
83
+ - lib/gh_search/formatters.rb
84
+ - lib/gh_search/formatters/base.rb
85
+ - lib/gh_search/formatters/csv.rb
86
+ - lib/gh_search/formatters/json.rb
87
+ - lib/gh_search/formatters/plain.rb
88
+ - lib/gh_search/github_client.rb
89
+ - lib/gh_search/version.rb
90
+ homepage: https://github.com/DDAZZA/gh-search/blob/master/README.md
91
+ licenses:
92
+ - MIT
93
+ metadata:
94
+ homepage_uri: https://github.com/DDAZZA/gh-search/blob/master/README.md
95
+ source_code_uri: https://github.com/DDAZZA/gh-search
96
+ changelog_uri: https://github.com/DDAZZA/gh-search/blob/master/CHANGE_LOG.md
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubygems_version: 3.1.2
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: Tool to search GitHub for matching text
116
+ test_files: []