drone-hunter 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +81 -0
- data/bin/drone-hunter +151 -0
- data/drone-hunter.gemspec +24 -0
- data/lib/drone_hunter.rb +107 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 656d8a52817c26e8a3dc5d956cd458b4c3c6ae8354523968bfdfba0acf3d6358
|
4
|
+
data.tar.gz: df60380cea37bcd2817000df50fd60050c1a3e0e8ba6e6675050a74f325007f9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fd3f1e3932c15708c5afcff7405bfd534f7779c7e36d66f305566cfab96ce0820299a4edacb2069df8b8fd95ba6f0c63574c21ad63c8dbd8faa9d707c99ac2b8
|
7
|
+
data.tar.gz: 46525d26161abbbbd93401936ef6a3a38254d693761f2c26884b97baafcef57c5cc778377af8e184d1015ace32eeff8ab99d709a436e60baeac331048cf2809b
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
The MIT License (MIT)
|
3
|
+
Copyright © 2022 Chris Olstrom <chris@olstrom.com>
|
4
|
+
Copyright © 2022 SUSE LLC
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the “Software”), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# drone-hunter - finds your dronefiles when you have too many repositories.
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
`drone-hunter` is a small utility that trawls whatever GitHub accounts you give it, looking for Drone CI configs.
|
6
|
+
|
7
|
+
## Why does this exist?
|
8
|
+
|
9
|
+
Because I needed to search across hundreds of repositories across multiple GitHub organizations, searching for drone configs so I could review and compare them. I considered Terraform for this, but opted for Ruby instead, because caching makes iterative development much faster, and I didn't know how to make Terraform do that. Also the GitHub Provider for Terraform didn't handle full repository names for several data sources.
|
10
|
+
|
11
|
+
## How do I obtain this majestic tool?
|
12
|
+
|
13
|
+
```shell
|
14
|
+
gem install drone-hunter
|
15
|
+
```
|
16
|
+
|
17
|
+
## How do I use it?
|
18
|
+
|
19
|
+
```shell
|
20
|
+
drone-hunter <owner ...>
|
21
|
+
```
|
22
|
+
|
23
|
+
Where `owner` is either a user or an organization on GitHub.
|
24
|
+
|
25
|
+
`drone-hunter` will use the GitHub API to find all the repositories that are visible to you in the accounts you indicated. This means that if you're using private repositories, you'll need a `GITHUB_TOKEN` in the environment that has access to whatever repositories you want it to read from.
|
26
|
+
|
27
|
+
With this list of repositories, `drone-hunter` will find the default branches for everything, fetch an index of the tree for the commit at the tip of that branch, dig around for stuff that looks like it might be a drone config, and download all of them.
|
28
|
+
|
29
|
+
All of this will be cached.
|
30
|
+
|
31
|
+
The current version will output something like this (this may change in future releases):
|
32
|
+
|
33
|
+
```json
|
34
|
+
[
|
35
|
+
{
|
36
|
+
"repository": "rancherlabs/drone-plugin-golangci-lint",
|
37
|
+
"path": ".drone.yml",
|
38
|
+
"sha": "d95f9416c3521bdcb4acc0146d9af9fbe42ff165",
|
39
|
+
"content": "---\nkind: pipeline\nname: golangci-lint\n\nsteps:\n- name: golangci-lint-run\n image: rancher/drone-golangci-lint:latest\n failure: ignore\n\n---\nkind: pipeline\nname: docker\n\nsteps:\n- name: publish\n image: plugins/docker\n settings:\n username:\n from_secret: docker_username\n password:\n from_secret: docker_password\n repo: rancher/drone-golangci-lint\n tags: latest\n when:\n instance:\n - drone-publish.rancher.io\n ref:\n include:\n - \"refs/heads/*\"\n - \"refs/tags/*\"\n - \"refs/pull/*\"\n event:\n - push\n - tag\n\n"
|
40
|
+
}
|
41
|
+
]
|
42
|
+
```
|
43
|
+
|
44
|
+
By default, it produces JSON output. You can change this with the `--output-format` option. Maybe you want YAML, since that's what drone configs are...
|
45
|
+
|
46
|
+
```yaml
|
47
|
+
- repository: rancherlabs/drone-plugin-golangci-lint
|
48
|
+
path: ".drone.yml"
|
49
|
+
sha: d95f9416c3521bdcb4acc0146d9af9fbe42ff165
|
50
|
+
content: |+
|
51
|
+
---kind: pipelinename: golangci-lintsteps:- name: golangci-lint-run image: rancher/drone-golangci-lint:latest failure: ignore---kind: pipelinename: dockersteps:- name: publish image: plugins/docker settings: username: from_secret: docker_username password: from_secret: docker_password repo: rancher/drone-golangci-lint tags: latest when: instance: - drone-publish.rancher.io ref: include: - "refs/heads/*" - "refs/tags/*" - "refs/pull/*" event: - push - tag
|
52
|
+
```
|
53
|
+
|
54
|
+
Or maybe you want to dump a copy of every dronefile onto the filesystem. Set `--output-format=files` and you'll get something like this:
|
55
|
+
|
56
|
+
```shell
|
57
|
+
find drone-hunter.output -type f
|
58
|
+
```
|
59
|
+
|
60
|
+
```text
|
61
|
+
drone-hunter.output/rancherlabs/drone-plugin-golangci-lint/.drone.yml
|
62
|
+
drone-hunter.output/rancherlabs/ssh-pub-keys/.drone.yml
|
63
|
+
drone-hunter.output/rancherlabs/drone-runner-laboratory/.drone_lab.yml
|
64
|
+
drone-hunter.output/rancherlabs/rio-website/.drone.yml
|
65
|
+
drone-hunter.output/rancherlabs/rancher-catalog-stats/.drone.yml
|
66
|
+
drone-hunter.output/rancherlabs/drone-plugin-fossa/.drone.yml
|
67
|
+
drone-hunter.output/rancherlabs/swiss-army-knife/.drone.ci.yml
|
68
|
+
drone-hunter.output/rancherlabs/swiss-army-knife/.drone.yml
|
69
|
+
drone-hunter.output/rancherlabs/huawei-ui/.drone.yml
|
70
|
+
drone-hunter.output/rancherlabs/drone-runner-docker/.drone.yml
|
71
|
+
drone-hunter.output/rancherlabs/k3s-website/.drone.yml
|
72
|
+
drone-hunter.output/rancherlabs/k3os-website/.drone.yml
|
73
|
+
drone-hunter.output/rancherlabs/website-theme/.drone.yml
|
74
|
+
drone-hunter.output/rancherlabs/support-tools/.drone.yml
|
75
|
+
```
|
76
|
+
|
77
|
+
The only limits are your imagination (and the GitHub API Rate Limit).
|
78
|
+
|
79
|
+
## License
|
80
|
+
|
81
|
+
`drone-hunter` is available under the [MIT License](https://tldrlegal.com/license/mit-license). See `LICENSE.txt` for the full text.
|
data/bin/drone-hunter
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# SPDX-License-Identifier: MIT
|
3
|
+
|
4
|
+
#########################
|
5
|
+
# Ruby Standard Library #
|
6
|
+
#########################
|
7
|
+
|
8
|
+
require "logger"
|
9
|
+
require "optparse"
|
10
|
+
|
11
|
+
#########################
|
12
|
+
# Internal Dependencies #
|
13
|
+
#########################
|
14
|
+
|
15
|
+
require_relative "../lib/drone_hunter"
|
16
|
+
|
17
|
+
def github_access_token_from_environment
|
18
|
+
# prefer a token designated for this program
|
19
|
+
ENV.fetch("DRONE_HUNTER_GITHUB_ACCESS_TOKEN") do
|
20
|
+
# but accept one that isn't designated for any particular use
|
21
|
+
ENV.fetch("GITHUB_ACCESS_TOKEN") do
|
22
|
+
# or accept one designated for the underlying library
|
23
|
+
ENV.fetch("OCTOKIT_ACCESS_TOKEN") do
|
24
|
+
# or play nice with Terraform's GitHub Provider
|
25
|
+
ENV.fetch("GITHUB_TOKEN") do
|
26
|
+
# well... a token isn't strictly REQUIRED...
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def output_format_from(input)
|
35
|
+
case input
|
36
|
+
when /j(son)?/i then :JSON
|
37
|
+
when /y(a?ml)?/i then :YAML
|
38
|
+
when /f(ile)?(s(ystem)?)?/i then :FileSystem
|
39
|
+
else raise NotImplementedError
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def log_level_from(input)
|
44
|
+
case input
|
45
|
+
when /warn/i then Logger::WARN
|
46
|
+
when /info/i then Logger::INFO
|
47
|
+
when /debug/i then Logger::DEBUG
|
48
|
+
else raise NotImplementedError
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#########################
|
53
|
+
# Default Configuration #
|
54
|
+
#########################
|
55
|
+
|
56
|
+
config = {
|
57
|
+
hacking: (ENV["HACKING"] ? true : false),
|
58
|
+
cache: {
|
59
|
+
dir: File.expand_path(ENV.fetch("DRONE_HUNTER_CACHE_DIR", './drone-hunter.cache'))
|
60
|
+
},
|
61
|
+
github: {
|
62
|
+
auto_paginate: true,
|
63
|
+
access_token: github_access_token_from_environment
|
64
|
+
},
|
65
|
+
log: {
|
66
|
+
level: log_level_from(ENV.fetch("DRONE_HUNTER_LOG_LEVEL", "info"))
|
67
|
+
},
|
68
|
+
output: {
|
69
|
+
format: output_format_from(ENV.fetch("DRONE_HUNTER_OUTPUT_FORMAT", "json")),
|
70
|
+
path: File.expand_path(ENV.fetch("DRONE_HUNTER_OUTPUT_PATH", "./drone-hunter.output")),
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
################################
|
75
|
+
# Options from the Commandline #
|
76
|
+
################################
|
77
|
+
|
78
|
+
OptionParser.new do |options|
|
79
|
+
options.on("-C", "--cache-dir=DIR", "env: DRONE_HUNTER_CACHE_DIR") { |argument| config[:cache][:dir] = File.expand_path(argument) }
|
80
|
+
options.on( "--github-access-token=TOKEN", "env: DRONE_HUNTER_GITHUB_ACCESS_TOKEN") { |argument| config[:github][:access_token] = argument }
|
81
|
+
options.on("-L", "--log-level=LEVEL", "env: DRONE_HUNTER_LOG_LEVEL") { |argument| config[:log][:level] = log_level_from(argument) }
|
82
|
+
options.on("-o", "--output-format=FORMAT", "env: DRONE_HUNTER_OUTPUT_FORMAT") { |argument| config[:output][:format] = output_format_from(argument) }
|
83
|
+
options.on("-p", "--output-path=PATH", "env: DRONE_HUNTER_OUTPUT_PATH") { |argument| config[:output][:path] = File.expand_path(argument) }
|
84
|
+
end.parse!
|
85
|
+
|
86
|
+
#################
|
87
|
+
# Sanity Checks #
|
88
|
+
#################
|
89
|
+
|
90
|
+
if ARGV.empty?
|
91
|
+
STDERR.puts "#{File.basename($PROGRAM_NAME)} <owner ...>"
|
92
|
+
exit Errno::EINVAL::Errno
|
93
|
+
end
|
94
|
+
|
95
|
+
#################
|
96
|
+
# Logging Setup #
|
97
|
+
#################
|
98
|
+
|
99
|
+
log = Logger::new(STDERR)
|
100
|
+
log.level = config[:log][:level]
|
101
|
+
|
102
|
+
################
|
103
|
+
# GitHub Setup #
|
104
|
+
################
|
105
|
+
|
106
|
+
# the GitHub API has a pretty low rate limit for unauthenticated calls,
|
107
|
+
# and we can exhaust that very quickly when doing what we need to do.
|
108
|
+
# Warn the user about this missing configuration as a courtesy.
|
109
|
+
unless config.dig(:github, :access_token)
|
110
|
+
log.warn("No GitHub Access Token provided. Expect rate limiting.")
|
111
|
+
end
|
112
|
+
|
113
|
+
github = Octokit::Client.new(**config[:github])
|
114
|
+
|
115
|
+
#################
|
116
|
+
# Caching Setup #
|
117
|
+
#################
|
118
|
+
|
119
|
+
cache = Moneta.new(:File, dir: config[:cache][:dir])
|
120
|
+
|
121
|
+
################
|
122
|
+
# Main Program #
|
123
|
+
################
|
124
|
+
|
125
|
+
hunt = DroneHunter.new(owners: ARGV, log: log, github: github, cache: cache)
|
126
|
+
|
127
|
+
if config[:hacking]
|
128
|
+
require "pry"
|
129
|
+
binding.pry
|
130
|
+
else
|
131
|
+
hunt.dronefiles.then do |dronefiles|
|
132
|
+
case config[:output][:format]
|
133
|
+
when :JSON
|
134
|
+
require "json"
|
135
|
+
STDOUT.puts JSON.pretty_generate dronefiles
|
136
|
+
when :YAML
|
137
|
+
require "yaml"
|
138
|
+
STDOUT.puts YAML.dump dronefiles
|
139
|
+
when :FileSystem
|
140
|
+
require "fileutils"
|
141
|
+
Dir.chdir(File.expand_path(*FileUtils.mkdir_p(config[:output][:path]))) do |output|
|
142
|
+
dronefiles.each do |file|
|
143
|
+
Dir.chdir(File.expand_path(*FileUtils.mkdir_p(File.join(output, file[:repository])))) do |repo|
|
144
|
+
File.write(File.join(".", file[:path]), file[:content])
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
else raise NotImplementedError
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
tag = `git describe --tags --abbrev=0`.chomp
|
3
|
+
|
4
|
+
gem.name = 'drone-hunter'
|
5
|
+
gem.homepage = 'https://github.com/colstrom/drone-hunter'
|
6
|
+
gem.summary = 'Hunts for Drone CI files across many repositories'
|
7
|
+
|
8
|
+
gem.version = "#{tag}"
|
9
|
+
gem.licenses = ['MIT']
|
10
|
+
gem.authors = ['Chris Olstrom']
|
11
|
+
gem.email = 'chris@olstrom.com'
|
12
|
+
|
13
|
+
# gem.cert_chain = ['trust/certificates/colstrom.pem']
|
14
|
+
# gem.signing_key = File.expand_path ENV.fetch 'GEM_SIGNING_KEY'
|
15
|
+
|
16
|
+
gem.files = `git ls-files -z`.split("\x0")
|
17
|
+
gem.test_files = `git ls-files -z -- {test,spec,features}/*`.split("\x0")
|
18
|
+
gem.executables = `git ls-files -z -- bin/*`.split("\x0").map { |f| File.basename(f) }
|
19
|
+
|
20
|
+
gem.require_paths = ['lib']
|
21
|
+
|
22
|
+
gem.add_runtime_dependency 'octokit', '~> 4.25', '>= 4.25.1'
|
23
|
+
gem.add_runtime_dependency 'moneta', '~> 1.5', '>= 1.5.1'
|
24
|
+
end
|
data/lib/drone_hunter.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
|
3
|
+
#########################
|
4
|
+
# Ruby Standard Library #
|
5
|
+
#########################
|
6
|
+
|
7
|
+
require "base64"
|
8
|
+
require "logger"
|
9
|
+
require "set"
|
10
|
+
|
11
|
+
#########################
|
12
|
+
# External Dependencies #
|
13
|
+
#########################
|
14
|
+
|
15
|
+
require "moneta"
|
16
|
+
require "octokit"
|
17
|
+
|
18
|
+
class DroneHunter
|
19
|
+
def initialize(**options)
|
20
|
+
@log ||= options.fetch(:log) { Logger.new(STDERR) }
|
21
|
+
@github ||= options.fetch(:client) { Octokit::Client.new(auto_paginate: true) }
|
22
|
+
@cache ||= options.fetch(:cache) { Moneta.new(:File, dir: 'drone-hunter.cache') }
|
23
|
+
@owners ||= Set.new(options.fetch(:owners, []))
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :log
|
27
|
+
attr_reader :github
|
28
|
+
attr_reader :cache
|
29
|
+
attr_reader :owners
|
30
|
+
|
31
|
+
def cached(key, *rest, &block)
|
32
|
+
if cache.key?(key)
|
33
|
+
log.debug("(cache) #{key}")
|
34
|
+
cache.fetch(key)
|
35
|
+
else
|
36
|
+
log.info("(fetch) #{key}")
|
37
|
+
cache.store(key, block.call(*rest))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def repositories(owner = nil)
|
42
|
+
case owner
|
43
|
+
when String then cached("repositories/#{owner}") { github.repositories(owner) }
|
44
|
+
when nil then owners.flat_map { |owner| repositories(owner) }
|
45
|
+
else raise TypeError
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def branches(repo = nil)
|
50
|
+
case repo
|
51
|
+
when String then cached("branches/#{repo}") { github.branches(repo) }
|
52
|
+
when Sawyer::Resource then branches(repo.full_name)
|
53
|
+
when nil then repositories.flat_map { |repo| branches(repo) }
|
54
|
+
else raise TypeError
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def trees
|
59
|
+
repositories.map do |repo|
|
60
|
+
tree_sha = branches(repo.full_name).find do |branch|
|
61
|
+
branch.name == repo.default_branch
|
62
|
+
end&.commit&.sha
|
63
|
+
|
64
|
+
next unless tree_sha
|
65
|
+
repo = repo.full_name
|
66
|
+
|
67
|
+
{
|
68
|
+
repo => cached("tree/#{repo}/#{tree_sha}") { github.tree(repo, tree_sha) }.tree
|
69
|
+
}
|
70
|
+
end.compact.reduce(&:merge)
|
71
|
+
end
|
72
|
+
|
73
|
+
def blobs
|
74
|
+
trees.map do |repo, tree|
|
75
|
+
blobs = tree
|
76
|
+
.select { |entry| entry.path.match?(/ya?ml$/) }
|
77
|
+
.select { |entry| entry.path.match?(/drone/) }
|
78
|
+
.map do |entry|
|
79
|
+
{
|
80
|
+
entry.path => cached("blob/#{repo}/#{entry.sha}") { github.blob(repo, entry.sha) }
|
81
|
+
}
|
82
|
+
end
|
83
|
+
next if blobs.empty?
|
84
|
+
|
85
|
+
{
|
86
|
+
repo => blobs.reduce(&:merge)
|
87
|
+
}
|
88
|
+
end.compact.reduce(&:merge)
|
89
|
+
end
|
90
|
+
|
91
|
+
def dronefiles
|
92
|
+
blobs.flat_map do |repo, blobs|
|
93
|
+
blobs.map do |path, blob|
|
94
|
+
case blob[:encoding]
|
95
|
+
when "base64"
|
96
|
+
{
|
97
|
+
"repository" => repo,
|
98
|
+
"path" => path,
|
99
|
+
"sha" => blob[:sha],
|
100
|
+
"content" => Base64::decode64(blob[:content])
|
101
|
+
}
|
102
|
+
else raise EncodingError
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: drone-hunter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Olstrom
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-08-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: octokit
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.25'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 4.25.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '4.25'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 4.25.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: moneta
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.5'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.5.1
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.5'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.5.1
|
53
|
+
description:
|
54
|
+
email: chris@olstrom.com
|
55
|
+
executables:
|
56
|
+
- drone-hunter
|
57
|
+
extensions: []
|
58
|
+
extra_rdoc_files: []
|
59
|
+
files:
|
60
|
+
- Gemfile
|
61
|
+
- LICENSE.txt
|
62
|
+
- README.md
|
63
|
+
- bin/drone-hunter
|
64
|
+
- drone-hunter.gemspec
|
65
|
+
- lib/drone_hunter.rb
|
66
|
+
homepage: https://github.com/colstrom/drone-hunter
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubygems_version: 3.0.3.1
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Hunts for Drone CI files across many repositories
|
89
|
+
test_files: []
|