usedby 0.4.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 +7 -0
- data/CHANGES.md +21 -0
- data/LICENSE.txt +23 -0
- data/README.md +99 -0
- data/bin/usedby +5 -0
- data/lib/usedby.rb +4 -0
- data/lib/usedby/cli.rb +274 -0
- data/lib/usedby/version.rb +5 -0
- data/lib/usedby/version_ranges_intersection.rb +51 -0
- metadata +96 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 51866979c0e578e1c2c4bda5c7f61e79cd9b6bb0a5366eaaf2c66f474b00c6ea
|
|
4
|
+
data.tar.gz: 54e3aa2e5a415b570ead51e7c004b5a37683f76360c8d084874f0b05812281c7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: da372934168ed4cf0fb96e648c7735631c0068652516fa08142a8666f20be2f57f9a0b35861aec240b6a2b2ee29cc35b5387b3fa9c4a9f152ac2375beadb7b04
|
|
7
|
+
data.tar.gz: 5ad1483db112e0b5cbf819f095de455dae8f90105c9a93fc85fcaf4d2c5bec296f9a9291519274ed03c54eee80e9dc878575961b638bb4a01288fe012442d8d7
|
data/CHANGES.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
## 0.3.0 (2018/07/20)
|
|
4
|
+
|
|
5
|
+
**Added**
|
|
6
|
+
|
|
7
|
+
* Don't include archived repositories.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## 0.2.0 (2018/07/03)
|
|
11
|
+
|
|
12
|
+
**Added**
|
|
13
|
+
|
|
14
|
+
* Require `GITHUB_ORGANIZATION` to be provided on the command line.
|
|
15
|
+
|
|
16
|
+
## 0.1.0 (2018/07/03)
|
|
17
|
+
|
|
18
|
+
**Added**
|
|
19
|
+
|
|
20
|
+
* Provide initial version of the `organization_gem_dependencies` command line
|
|
21
|
+
tool.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Copyright (c) 2018, AppFolio, Inc.
|
|
2
|
+
Copyright (c) 2018, Bryce Boe
|
|
3
|
+
All rights reserved.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
|
12
|
+
and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
15
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
16
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
17
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
18
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
19
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
20
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
21
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
22
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
23
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# usedby
|
|
2
|
+
|
|
3
|
+
Figure out where your gems are actually being used!
|
|
4
|
+
Similar to GitHub's "Used By" feature, but for private repos.
|
|
5
|
+
|
|
6
|
+
This gem installs a command line utility `usedby`, that
|
|
7
|
+
outputs a json file with a reverse dependency tree.
|
|
8
|
+
|
|
9
|
+
This acts more or less like GitHub's "Used By" feature.
|
|
10
|
+
It currently uses GitHub's code search API which has a few limitations:
|
|
11
|
+
https://help.github.com/en/github/searching-for-information-on-github/searching-code
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
gem install usedby
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
usedby GITHUB_ORGANIZATION [--direct] [--gems GEM1,GEM2,GEM3]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
You will be securely prompted for a [GitHub Personal Access Token](https://github.com/settings/tokens).
|
|
26
|
+
|
|
27
|
+
For example, running `usedby rails --direct --gems railties,rake` produces output
|
|
28
|
+
like the following:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"railties": {
|
|
33
|
+
"4.0.0.beta": [
|
|
34
|
+
"routing_concerns/Gemfile.lock"
|
|
35
|
+
],
|
|
36
|
+
"4.0.0": [
|
|
37
|
+
"prototype-rails/Gemfile.lock"
|
|
38
|
+
],
|
|
39
|
+
"4.2.1": [
|
|
40
|
+
"rails-perftest/Gemfile.lock"
|
|
41
|
+
],
|
|
42
|
+
"4.2.10": [
|
|
43
|
+
"rails-docs-server/test/fixtures/releases/v4.2.10/Gemfile.lock"
|
|
44
|
+
],
|
|
45
|
+
"5.1.1": [
|
|
46
|
+
"actioncable-examples/Gemfile.lock"
|
|
47
|
+
],
|
|
48
|
+
"5.2.1": [
|
|
49
|
+
"rails_fast_attributes/Gemfile.lock"
|
|
50
|
+
],
|
|
51
|
+
"5.2.2": [
|
|
52
|
+
"globalid/Gemfile.lock"
|
|
53
|
+
],
|
|
54
|
+
"6.0.1": [
|
|
55
|
+
"webpacker/Gemfile.lock"
|
|
56
|
+
],
|
|
57
|
+
"6.0.2.1": [
|
|
58
|
+
"rails-contributors/Gemfile.lock"
|
|
59
|
+
],
|
|
60
|
+
"6.1.0.alpha": [
|
|
61
|
+
"rails/Gemfile.lock"
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
"rake": {
|
|
65
|
+
"0.9.2.2": [
|
|
66
|
+
"commands/Gemfile.lock",
|
|
67
|
+
"etagger/Gemfile.lock",
|
|
68
|
+
"routing_concerns/Gemfile.lock"
|
|
69
|
+
],
|
|
70
|
+
"10.1.0": [
|
|
71
|
+
"prototype-rails/Gemfile.lock"
|
|
72
|
+
],
|
|
73
|
+
"10.4.2": [
|
|
74
|
+
"jquery-ujs/Gemfile.lock",
|
|
75
|
+
"rails-perftest/Gemfile.lock"
|
|
76
|
+
],
|
|
77
|
+
"10.5.0": [
|
|
78
|
+
"rails_fast_attributes/Gemfile.lock",
|
|
79
|
+
"record_tag_helper/Gemfile.lock"
|
|
80
|
+
],
|
|
81
|
+
"12.0.0": [
|
|
82
|
+
"actioncable-examples/Gemfile.lock",
|
|
83
|
+
"rails-docs-server/test/fixtures/releases/v4.2.10/Gemfile.lock",
|
|
84
|
+
"rails-dom-testing/Gemfile.lock"
|
|
85
|
+
],
|
|
86
|
+
"12.3.2": [
|
|
87
|
+
"globalid/Gemfile.lock"
|
|
88
|
+
],
|
|
89
|
+
"13.0.0": [
|
|
90
|
+
"webpacker/Gemfile.lock"
|
|
91
|
+
],
|
|
92
|
+
"13.0.1": [
|
|
93
|
+
"rails-contributors/Gemfile.lock",
|
|
94
|
+
"rails/Gemfile.lock"
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
```
|
data/bin/usedby
ADDED
data/lib/usedby.rb
ADDED
data/lib/usedby/cli.rb
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'base64'
|
|
4
|
+
require 'io/console'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'optparse'
|
|
7
|
+
|
|
8
|
+
require 'octokit'
|
|
9
|
+
|
|
10
|
+
require 'usedby/version_ranges_intersection'
|
|
11
|
+
|
|
12
|
+
module Usedby
|
|
13
|
+
# Define the command line interface.
|
|
14
|
+
class Cli
|
|
15
|
+
using VersionRangesIntersection
|
|
16
|
+
|
|
17
|
+
GEMFILE_LOCK_SEARCH_TERM = 'org:%s filename:Gemfile.lock'
|
|
18
|
+
GEMSPEC_SEARCH_TERM = 'org:%s extension:gemspec'
|
|
19
|
+
USAGE = <<~USAGE
|
|
20
|
+
Usage: usedby [options] GITHUB_ORGANIZATION
|
|
21
|
+
USAGE
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def run
|
|
25
|
+
parse_options
|
|
26
|
+
if ARGV.size != 1
|
|
27
|
+
STDERR.puts USAGE
|
|
28
|
+
return 1
|
|
29
|
+
end
|
|
30
|
+
github_organization = ARGV[0]
|
|
31
|
+
|
|
32
|
+
access_token = ENV['GITHUB_ACCESS_TOKEN'] || \
|
|
33
|
+
STDIN.getpass('GitHub Personal Access Token: ')
|
|
34
|
+
github = Octokit::Client.new(access_token: access_token)
|
|
35
|
+
|
|
36
|
+
gems = {}
|
|
37
|
+
|
|
38
|
+
remote_search(github, github_organization, GEMFILE_LOCK_SEARCH_TERM) do |gemfile_lock|
|
|
39
|
+
content = remote_file(github, gemfile_lock)
|
|
40
|
+
next unless content
|
|
41
|
+
content = Bundler::LockfileParser.new(content)
|
|
42
|
+
merge!(gems, process_gemfile(content, "#{gemfile_lock.repository.name}/#{gemfile_lock.path}"))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
remote_search(github, github_organization, GEMSPEC_SEARCH_TERM) do |gemspec|
|
|
46
|
+
content = remote_file(github, gemspec)
|
|
47
|
+
next unless content
|
|
48
|
+
merge!(gems, process_gemspec(content, "#{gemspec.repository.name}/#{gemspec.path}"))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
output gems
|
|
52
|
+
|
|
53
|
+
0
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def archived_repositories(github, organization)
|
|
59
|
+
github.organization_repositories(organization)
|
|
60
|
+
last_response = github.last_response
|
|
61
|
+
|
|
62
|
+
repositories = []
|
|
63
|
+
last_response.data.each do |repository|
|
|
64
|
+
repositories << repository.name if repository.archived
|
|
65
|
+
end
|
|
66
|
+
until last_response.rels[:next].nil?
|
|
67
|
+
sleep_time = 0
|
|
68
|
+
begin
|
|
69
|
+
last_response = last_response.rels[:next].get
|
|
70
|
+
rescue StandardError
|
|
71
|
+
sleep_time += 1
|
|
72
|
+
STDERR.puts "Sleeping #{sleep_time} seconds"
|
|
73
|
+
sleep(sleep_time)
|
|
74
|
+
retry
|
|
75
|
+
end
|
|
76
|
+
last_response.data.each do |repository|
|
|
77
|
+
repositories << repository.name if repository.archived
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
repositories
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def build_ignore_paths(ignored_paths, file)
|
|
84
|
+
File.open(file).each do |line|
|
|
85
|
+
cleaned = line.strip
|
|
86
|
+
ignored_paths << cleaned if cleaned != ''
|
|
87
|
+
end
|
|
88
|
+
rescue Errno::ENOENT, Errno::EISDIR
|
|
89
|
+
STDERR.puts "No such file #{file}"
|
|
90
|
+
exit 1
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def filtered?(gemfile_path)
|
|
94
|
+
@options[:ignore_paths].each do |ignore_path|
|
|
95
|
+
return true if gemfile_path.start_with?(ignore_path)
|
|
96
|
+
end
|
|
97
|
+
false
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def remote_search(github, organization, search_term)
|
|
101
|
+
archived = archived_repositories(github, organization)
|
|
102
|
+
github.search_code(search_term % organization, per_page: 1000)
|
|
103
|
+
last_response = github.last_response
|
|
104
|
+
|
|
105
|
+
matches = []
|
|
106
|
+
last_response.data.items.each do |match|
|
|
107
|
+
matches << match unless archived.include? match.repository.name
|
|
108
|
+
end
|
|
109
|
+
until last_response.rels[:next].nil?
|
|
110
|
+
sleep_time = 0
|
|
111
|
+
begin
|
|
112
|
+
last_response = last_response.rels[:next].get
|
|
113
|
+
rescue StandardError
|
|
114
|
+
sleep_time += 1
|
|
115
|
+
STDERR.puts "Sleeping #{sleep_time} seconds"
|
|
116
|
+
sleep(sleep_time)
|
|
117
|
+
retry
|
|
118
|
+
end
|
|
119
|
+
last_response.data.items.each do |match|
|
|
120
|
+
matches << match unless archived.include? match.repository.name
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
matches.sort_by(&:html_url).each do |match|
|
|
125
|
+
yield match
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def remote_file(github, file)
|
|
130
|
+
github_path = "#{file.repository.name}/#{file.path}"
|
|
131
|
+
if filtered?(github_path)
|
|
132
|
+
STDERR.puts "Skipping #{github_path}"
|
|
133
|
+
return
|
|
134
|
+
end
|
|
135
|
+
STDERR.puts "Processing #{github_path}"
|
|
136
|
+
sleep_time = 0
|
|
137
|
+
begin
|
|
138
|
+
content = Base64.decode64(github.get(file.url).content)
|
|
139
|
+
rescue StandardError
|
|
140
|
+
sleep_time += 1
|
|
141
|
+
STDERR.puts "Sleeping #{sleep_time} seconds"
|
|
142
|
+
sleep(sleep_time)
|
|
143
|
+
retry
|
|
144
|
+
end
|
|
145
|
+
content
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def merge!(base, additions)
|
|
149
|
+
additions.each do |gem, versions|
|
|
150
|
+
if base.include? gem
|
|
151
|
+
base_versions = base[gem]
|
|
152
|
+
versions.each do |version, projects|
|
|
153
|
+
if base_versions.include? version
|
|
154
|
+
base_versions[version].concat(projects)
|
|
155
|
+
else
|
|
156
|
+
base_versions[version] = projects
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
else
|
|
160
|
+
base[gem] = versions
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def output(gems)
|
|
166
|
+
sorted_gems = {}
|
|
167
|
+
gems.sort.each do |gem, versions|
|
|
168
|
+
sorted_gems[gem] = {}
|
|
169
|
+
versions.sort.each do |version, projects|
|
|
170
|
+
sorted_gems[gem][version_ranges_to_s(version)] = projects.sort
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
puts JSON.pretty_generate(sorted_gems)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def parse_options
|
|
177
|
+
@options = { direct: false, ignore_paths: [] }
|
|
178
|
+
OptionParser.new do |config|
|
|
179
|
+
config.banner = USAGE
|
|
180
|
+
config.on('-d', '--direct',
|
|
181
|
+
'Consider only direct dependencies.') do |direct|
|
|
182
|
+
@options[:direct] = direct
|
|
183
|
+
end
|
|
184
|
+
config.on('-i', '--ignore-file [FILEPATH]',
|
|
185
|
+
'Ignore projects included in file.') do |ignore_file|
|
|
186
|
+
|
|
187
|
+
build_ignore_paths(@options[:ignore_paths], ignore_file)
|
|
188
|
+
end
|
|
189
|
+
config.on('-g', '--gems [GEM1,GEM2,GEM3]',
|
|
190
|
+
'Consider only given gems.') do |gems|
|
|
191
|
+
|
|
192
|
+
@options[:gems] = gems.split(',')
|
|
193
|
+
end
|
|
194
|
+
config.version = Usedby::VERSION
|
|
195
|
+
end.parse!
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def process_gemfile(gemfile, project)
|
|
199
|
+
dependencies = gemfile.dependencies.map { |dependency, _, _| dependency }
|
|
200
|
+
gems = {}
|
|
201
|
+
|
|
202
|
+
gemfile.specs.each do |spec|
|
|
203
|
+
next if @options[:direct] && !dependencies.include?(spec.name)
|
|
204
|
+
next if @options[:gems] && !@options[:gems].include?(spec.name)
|
|
205
|
+
spec_version_ranges = Bundler::VersionRanges.for(Gem::Requirement.new(spec.version))
|
|
206
|
+
gems[spec.name] = {}
|
|
207
|
+
gems[spec.name][spec_version_ranges] = [project]
|
|
208
|
+
end
|
|
209
|
+
gems
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Process dependencies in gemspec according to:
|
|
213
|
+
# https://guides.rubygems.org/specification-reference/
|
|
214
|
+
# Sample supported formats:
|
|
215
|
+
# s.add_dependency(%q<rspec>.freeze, ["~> 3.2"])
|
|
216
|
+
# spec.add_runtime_dependency "multi_json", "~>1.12", ">=1.12.0"
|
|
217
|
+
# Sample unsupported formats:
|
|
218
|
+
# s.add_development_dependency "rake", "~> 10.5" if on_less_than_1_9_3?
|
|
219
|
+
# s.add_dependency 'sunspot', Sunspot::VERSION
|
|
220
|
+
def process_gemspec(content, project)
|
|
221
|
+
gems = {}
|
|
222
|
+
dummy_spec = Gem::Specification.new
|
|
223
|
+
content.each_line do |line|
|
|
224
|
+
if line =~ /^\s*(\w+)\.add_(development_dependency|runtime_dependency|dependency)\b/
|
|
225
|
+
spec_name = $1
|
|
226
|
+
begin
|
|
227
|
+
eval line.sub(spec_name, 'dummy_spec')
|
|
228
|
+
rescue => e
|
|
229
|
+
$stderr.puts e
|
|
230
|
+
next
|
|
231
|
+
end
|
|
232
|
+
dep = dummy_spec.dependencies.last
|
|
233
|
+
gem_name = dep.name
|
|
234
|
+
next if @options[:gems] && !@options[:gems].include?(gem_name)
|
|
235
|
+
gem_version_ranges = Bundler::VersionRanges.for(dep.requirement)
|
|
236
|
+
gems[gem_name] = {}
|
|
237
|
+
gems[gem_name][gem_version_ranges] = [project]
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
gems
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Uses mathematical notation for ranges
|
|
244
|
+
# The unbounded range is [0, ∞)
|
|
245
|
+
def version_ranges_to_s(gem_version_ranges)
|
|
246
|
+
if !gem_version_ranges.kind_of?(Array) || gem_version_ranges.size != 2
|
|
247
|
+
$stderr.puts "Unknown format for version ranges: #{gem_version_ranges}"
|
|
248
|
+
return ""
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# base case: a specific version
|
|
252
|
+
if gem_version_ranges[0].size == 1
|
|
253
|
+
range = gem_version_ranges[0][0]
|
|
254
|
+
if range.left.version == range.right.version
|
|
255
|
+
return range.left.version.to_s
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
gem_version_ranges[0] = Bundler::VersionRanges::ReqR.reduce(gem_version_ranges[0])
|
|
260
|
+
|
|
261
|
+
arr = []
|
|
262
|
+
gem_version_ranges[0].each do |reqr|
|
|
263
|
+
range_begin = reqr.left.inclusive ? "[" : "("
|
|
264
|
+
range_end = reqr.right.inclusive ? "]" : ")"
|
|
265
|
+
arr << "#{range_begin}#{reqr.left.version.to_s}, #{reqr.right.version.to_s}#{range_end}"
|
|
266
|
+
end
|
|
267
|
+
gem_version_ranges[1].each do |neq|
|
|
268
|
+
# special case: exclude specific version
|
|
269
|
+
arr << "!= #{neq.version.to_s}"
|
|
270
|
+
end
|
|
271
|
+
arr.join(", ")
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module VersionRangesIntersection
|
|
4
|
+
refine Bundler::VersionRanges::ReqR do
|
|
5
|
+
# Checks whether two ranges overlap
|
|
6
|
+
def intersect?(other)
|
|
7
|
+
case self <=> other
|
|
8
|
+
when -1
|
|
9
|
+
return (right <=> other.left) == 1
|
|
10
|
+
when 1
|
|
11
|
+
return (other.right <=> left) == 1
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Returns a new range which is the intersection, or else nil
|
|
18
|
+
def intersection(other)
|
|
19
|
+
return nil unless intersect?(other)
|
|
20
|
+
|
|
21
|
+
inter = clone
|
|
22
|
+
case inter <=> other
|
|
23
|
+
when -1
|
|
24
|
+
inter.left = other.left
|
|
25
|
+
when 1
|
|
26
|
+
inter.right = other.right
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
inter
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Class methods need to be refined on the singleton_class
|
|
34
|
+
refine Bundler::VersionRanges::ReqR.singleton_class do
|
|
35
|
+
def reduce(reqrs = [])
|
|
36
|
+
reduced_reqrs = reqrs.clone
|
|
37
|
+
i = 0
|
|
38
|
+
while i < reduced_reqrs.size - 1
|
|
39
|
+
intersecting_range = reduced_reqrs[i].intersection(reduced_reqrs[i + 1])
|
|
40
|
+
if intersecting_range
|
|
41
|
+
reduced_reqrs[i + 1] = intersecting_range
|
|
42
|
+
reduced_reqrs.delete_at(i)
|
|
43
|
+
else
|
|
44
|
+
i += 1
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
reduced_reqrs
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: usedby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.4.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- AppFolio, Inc.
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2020-02-14 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: minitest
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '5.0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '5.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '12.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '12.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: octokit
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '4.0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '4.0'
|
|
55
|
+
description: 'usedby is a command line tool to discover all dependents of ruby gems
|
|
56
|
+
across a github organization.
|
|
57
|
+
|
|
58
|
+
'
|
|
59
|
+
email: opensource@appfolio.com
|
|
60
|
+
executables:
|
|
61
|
+
- usedby
|
|
62
|
+
extensions: []
|
|
63
|
+
extra_rdoc_files: []
|
|
64
|
+
files:
|
|
65
|
+
- CHANGES.md
|
|
66
|
+
- LICENSE.txt
|
|
67
|
+
- README.md
|
|
68
|
+
- bin/usedby
|
|
69
|
+
- lib/usedby.rb
|
|
70
|
+
- lib/usedby/cli.rb
|
|
71
|
+
- lib/usedby/version.rb
|
|
72
|
+
- lib/usedby/version_ranges_intersection.rb
|
|
73
|
+
homepage: https://github.com/appfolio/usedby
|
|
74
|
+
licenses:
|
|
75
|
+
- BSD-2-Clause
|
|
76
|
+
metadata: {}
|
|
77
|
+
post_install_message:
|
|
78
|
+
rdoc_options: []
|
|
79
|
+
require_paths:
|
|
80
|
+
- lib
|
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
|
+
requirements:
|
|
83
|
+
- - ">="
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: '0'
|
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0'
|
|
91
|
+
requirements: []
|
|
92
|
+
rubygems_version: 3.1.2
|
|
93
|
+
signing_key:
|
|
94
|
+
specification_version: 4
|
|
95
|
+
summary: Discover all dependents of ruby gems across a github organization.
|
|
96
|
+
test_files: []
|