bundler-alive 0.1.2 → 0.1.5
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 +4 -4
- data/.bundler-alive.default.yml +30 -0
- data/.rspec +1 -0
- data/.yardopts +1 -0
- data/Gemfile +5 -9
- data/README.md +47 -7
- data/bundler-alive.gemspec +42 -17
- data/gemspec.yml +35 -0
- data/lib/bundler/alive/cli.rb +9 -14
- data/lib/bundler/alive/client/gems_api_client.rb +138 -0
- data/lib/bundler/alive/client/gems_api_response.rb +29 -0
- data/lib/bundler/alive/client/git_hub_api.rb +128 -20
- data/lib/bundler/alive/client/source_code_client.rb +5 -2
- data/lib/bundler/alive/doctor.rb +60 -78
- data/lib/bundler/alive/report.rb +25 -0
- data/lib/bundler/alive/reportable.rb +79 -0
- data/lib/bundler/alive/source_code_repository.rb +2 -10
- data/lib/bundler/alive/source_code_repository_url.rb +43 -8
- data/lib/bundler/alive/{gem_status.rb → status.rb} +23 -9
- data/lib/bundler/alive/status_collection.rb +127 -0
- data/lib/bundler/alive/status_result.rb +42 -0
- data/lib/bundler/alive/version.rb +1 -1
- data/lib/bundler/alive.rb +7 -3
- metadata +174 -9
- data/Gemfile.lock +0 -119
- data/lib/bundler/alive/client/gems_api.rb +0 -55
- data/lib/bundler/alive/gem_status_collection.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd88b794101cf7f456811a805f287ec314d9ecf202294f5ec13e5e339db5834d
|
4
|
+
data.tar.gz: e0db2f2731c7dd50324900a5564365ff14abf82bc7169d30d9ad7444504dffec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89fec08047da08d6178bae7e2760f22139005dc33cd38201a6c7782a589ab173a7b5b72ee934bb301216fe4a5e1e5814dd69b773a9c937ab7ca2f7c7b61f0d3a
|
7
|
+
data.tar.gz: 0f1930be94cf0401d807ec1e63714dea3243c5bedbe226ff7cd4ff957e9d6d5f043d731f4903ca3dac35aa0403d1269cbc85e153a304894fad63848401ddec07
|
@@ -0,0 +1,30 @@
|
|
1
|
+
---
|
2
|
+
gems:
|
3
|
+
coffee-script-source:
|
4
|
+
url: https://github.com/jashkenas/coffeescript/
|
5
|
+
erubis:
|
6
|
+
url: https://github.com/kwatch/erubis
|
7
|
+
ffi-compiler:
|
8
|
+
url: https://github.com/ffi/ffi
|
9
|
+
foundation-rails:
|
10
|
+
url: https://github.com/foundation/foundation-rails
|
11
|
+
guard-compat:
|
12
|
+
url: https://github.com/guard/guard-compat
|
13
|
+
spoon:
|
14
|
+
url: https://github.com/headius/spoon
|
15
|
+
shellany:
|
16
|
+
url: https://github.com/guard/shellany
|
17
|
+
vegas:
|
18
|
+
url: https://github.com/quirkey/vegas
|
19
|
+
zipruby:
|
20
|
+
url: https://github.com/fjg/zipruby
|
21
|
+
gli:
|
22
|
+
url: https://github.com/davetron5000/gli
|
23
|
+
zen_to_i:
|
24
|
+
url: https://github.com/yoshitsugu/zen_to_i
|
25
|
+
trailblazer-option:
|
26
|
+
url: https://github.com/trailblazer/trailblazer-option
|
27
|
+
fog-sakuracloud:
|
28
|
+
url: https://github.com/fog/fog-sakuracloud
|
29
|
+
ovirt-engine-sdk:
|
30
|
+
url: https://github.com/oVirt/ovirt-engine-sdk-ruby
|
data/.rspec
CHANGED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown --title "bundler-alive Documentation" --protected
|
data/Gemfile
CHANGED
@@ -4,20 +4,16 @@ source "https://rubygems.org"
|
|
4
4
|
|
5
5
|
gemspec
|
6
6
|
|
7
|
-
gem "faraday"
|
8
|
-
gem "octokit"
|
9
|
-
gem "rake", "~> 13.0"
|
10
|
-
gem "rspec", "~> 3.0"
|
11
|
-
gem "rubocop", "~> 1.21"
|
12
|
-
gem "thor"
|
13
|
-
gem "toml-rb"
|
14
|
-
|
15
7
|
group :development do
|
16
8
|
gem "yard"
|
17
9
|
end
|
18
10
|
|
19
11
|
group :test do
|
20
|
-
|
12
|
+
# Workaround for cc-test-reporter with SimpleCov 0.18.
|
13
|
+
# Stop upgrading SimpleCov until the following issue will be resolved.
|
14
|
+
# https://github.com/codeclimate/test-reporter/issues/418
|
15
|
+
gem "factory_bot"
|
16
|
+
gem "simplecov", "~> 0.10", "< 0.18"
|
21
17
|
gem "vcr"
|
22
18
|
gem "webmock"
|
23
19
|
end
|
data/README.md
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# bundler-alive
|
2
2
|
|
3
|
-

|
4
3
|
[](https://badge.fury.io/rb/bundler-alive)
|
4
|
+

|
5
|
+
[](https://codeclimate.com/github/kyoshidajp/bundler-alive/maintainability)
|
6
|
+
[](https://codeclimate.com/github/kyoshidajp/bundler-alive/test_coverage)
|
5
7
|
|
6
8
|
`bunder-alive` checks if gems in a RubyGem's `Gemfile.lock` are active.
|
7
9
|
|
8
|
-
Currently only
|
10
|
+
Currently only GitHub is supported as a source code repository. If the source code repository is archived, then reports as not alive.
|
9
11
|
|
10
12
|
## Installation
|
11
13
|
|
@@ -17,10 +19,19 @@ $ gem install bunlder-alive
|
|
17
19
|
|
18
20
|
```
|
19
21
|
$ bundle-alive
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
6 gems are in Gemfile.lock
|
23
|
+
..W....
|
24
|
+
Get all source code repository URLs of gems are done!
|
25
|
+
.....
|
26
|
+
|
27
|
+
Errors:
|
28
|
+
[bundle-alive] Not found in RubyGems.org.
|
23
29
|
|
30
|
+
Archived gems:
|
31
|
+
Name: journey
|
32
|
+
URL: http://github.com/rails/journey
|
33
|
+
|
34
|
+
Total: 6 (Archived: 1, Alive: 4, Unknown: 1)
|
24
35
|
Not alive gems are found!
|
25
36
|
```
|
26
37
|
|
@@ -30,7 +41,9 @@ Default `Gemfile.lock` location is in your current directory. You can specify it
|
|
30
41
|
$ bundle-alive -G /path/to/Gemfile.lock
|
31
42
|
```
|
32
43
|
|
33
|
-
|
44
|
+
## Exceeding rate limit
|
45
|
+
|
46
|
+
In some cases, the following error may be output.
|
34
47
|
|
35
48
|
```
|
36
49
|
Too many requested! Retry later.
|
@@ -38,7 +51,34 @@ Too many requested! Retry later.
|
|
38
51
|
|
39
52
|
In this case, setting [GitHub Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) as `BUNDLER_ALIVE_GITHUB_TOKEN` environment variable may alleviate the error.
|
40
53
|
|
41
|
-
|
54
|
+
## Ignore gems
|
55
|
+
|
56
|
+
You can ignore certain gems.
|
57
|
+
|
58
|
+
```
|
59
|
+
$ bundle-alive -i journey rubocop-junit-formatter
|
60
|
+
```
|
61
|
+
|
62
|
+
## Specifying repository URL
|
63
|
+
|
64
|
+
In some cases, some gems cannot find the URL of their source code repositories. For this case, you can specify a mapping between the gem and its URL.
|
65
|
+
|
66
|
+
Put `.bundler-alive.yml` in your current directory. The following code is the sample.
|
67
|
+
|
68
|
+
```yaml
|
69
|
+
---
|
70
|
+
gems:
|
71
|
+
coffee-script-source:
|
72
|
+
url: https://github.com/jashkenas/coffeescript/
|
73
|
+
```
|
74
|
+
|
75
|
+
You can also specify the file path.
|
76
|
+
|
77
|
+
```
|
78
|
+
$ bundle-alive -c /path/to/.bundler-alive.yml
|
79
|
+
```
|
80
|
+
|
81
|
+
[.bundler-alive.default.yml](https://github.com/kyoshidajp/bundler-alive/blob/main/.bundler-alive.default.yml) may also be helpful. Considering that having these mappings obtained automatically in the future.
|
42
82
|
|
43
83
|
## Contributing
|
44
84
|
|
data/bundler-alive.gemspec
CHANGED
@@ -1,29 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "lib/bundler/alive/version"
|
4
|
+
require "yaml"
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
# rubocop:disable Metrics/BlockLength
|
7
|
+
Gem::Specification.new do |gem|
|
8
|
+
gemspec = YAML.load_file("gemspec.yml")
|
9
|
+
gem.name = gemspec.fetch("name")
|
10
|
+
gem.version = Bundler::Alive::VERSION
|
11
|
+
gem.authors = gemspec.fetch("authors")
|
12
|
+
gem.email = gemspec.fetch("email")
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
gem.summary = gemspec.fetch("summary")
|
15
|
+
gem.description = gemspec.fetch("description")
|
16
|
+
gem.homepage = gemspec.fetch("homepage")
|
17
|
+
gem.required_ruby_version = gemspec.fetch("required_ruby_version")
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
+
metadata = gemspec.fetch("metadata")
|
20
|
+
gem.metadata["homepage_uri"] = metadata["homepage_uri"]
|
21
|
+
gem.metadata["source_code_uri"] = metadata["source_code_uri"]
|
22
|
+
gem.metadata["changelog_uri"] = metadata["changelog_uri"]
|
23
|
+
gem.metadata["rubygems_mfa_required"] = "true"
|
19
24
|
|
20
|
-
|
25
|
+
gem.files = Dir.chdir(__dir__) do
|
21
26
|
`git ls-files -z`.split("\x0").reject do |f|
|
22
27
|
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
23
28
|
end
|
24
29
|
end
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
30
|
+
gem.bindir = gemspec.fetch("bindir")
|
31
|
+
gem.executables = gem.files.grep(%r{\Abin/}) { |f| File.basename(f) }
|
32
|
+
gem.require_paths = gemspec.fetch("require_paths")
|
33
|
+
|
34
|
+
split = lambda do |string|
|
35
|
+
string.nil? ? "" : string.split(/,\s*/)
|
36
|
+
end
|
37
|
+
|
38
|
+
gemspec["dependencies"].each do |name, versions|
|
39
|
+
if versions.nil?
|
40
|
+
gem.add_dependency(name)
|
41
|
+
else
|
42
|
+
gem.add_dependency(name, split[versions])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
gemspec["development_dependencies"].each do |name, versions|
|
47
|
+
if versions.nil?
|
48
|
+
gem.add_development_dependency(name)
|
49
|
+
else
|
50
|
+
gem.add_development_dependency(name, split[versions])
|
51
|
+
end
|
52
|
+
end
|
29
53
|
end
|
54
|
+
# rubocop:enable Metrics/BlockLength
|
data/gemspec.yml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
name: bundler-alive
|
2
|
+
summary: Are your gems alive?
|
3
|
+
description: bundler-alive reports gems are archived or not.
|
4
|
+
authors: Katsuhiko YOSHIDA
|
5
|
+
email: claddvd@gmail.com
|
6
|
+
homepage: https://github.com/kyoshidajp/bundler-alive
|
7
|
+
|
8
|
+
metadata:
|
9
|
+
homepage_uri: https://github.com/kyoshidajp/bundler-alive
|
10
|
+
source_code_uri: https://github.com/kyoshidajp/bundler-alive
|
11
|
+
changelog_uri: https://github.com/kyoshidajp/bundler-alive
|
12
|
+
|
13
|
+
required_ruby_version: ">= 2.6.0"
|
14
|
+
bindir: bin
|
15
|
+
require_paths: lib
|
16
|
+
|
17
|
+
dependencies:
|
18
|
+
faraday:
|
19
|
+
octokit:
|
20
|
+
rake:
|
21
|
+
thor:
|
22
|
+
|
23
|
+
development_dependencies:
|
24
|
+
rspec:
|
25
|
+
rubocop:
|
26
|
+
yard:
|
27
|
+
|
28
|
+
# Workaround for cc-test-reporter with SimpleCov 0.18.
|
29
|
+
# Stop upgrading SimpleCov until the following issue will be resolved.
|
30
|
+
# https://github.com/codeclimate/test-reporter/issues/418
|
31
|
+
simplecov: "~> 0.10, < 0.18"
|
32
|
+
|
33
|
+
factory_bot:
|
34
|
+
vcr:
|
35
|
+
webmock:
|
data/lib/bundler/alive/cli.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "bundler/alive"
|
4
4
|
require "bundler/alive/doctor"
|
5
|
+
require "bundler/alive/reportable"
|
5
6
|
|
6
7
|
require "thor"
|
7
8
|
|
@@ -15,21 +16,18 @@ module Bundler
|
|
15
16
|
map "--version" => :version
|
16
17
|
|
17
18
|
desc "check [DIR]", "Checks the Gemfile.lock"
|
19
|
+
method_option :ignore, type: :array, aliases: "-i", default: []
|
18
20
|
method_option :gemfile_lock, type: :string, aliases: "-G",
|
19
21
|
default: "Gemfile.lock"
|
22
|
+
method_option :config, type: :string, aliases: "-c", default: ".bundler-alive.yml"
|
20
23
|
|
21
24
|
def check(_dir = Dir.pwd)
|
22
|
-
|
25
|
+
extend Reportable
|
26
|
+
report = check_by_doctor
|
27
|
+
print_report(report)
|
23
28
|
|
24
|
-
|
25
|
-
|
26
|
-
exit 1
|
27
|
-
end
|
28
|
-
|
29
|
-
exit 0 if doctor.all_alive
|
30
|
-
|
31
|
-
puts "Not alive gems are found!"
|
32
|
-
exit 1
|
29
|
+
exit_status = report.result.all_alive? ? 0 : 1
|
30
|
+
exit exit_status
|
33
31
|
end
|
34
32
|
|
35
33
|
desc "version", "Prints the bundler-alive version"
|
@@ -41,15 +39,12 @@ module Bundler
|
|
41
39
|
|
42
40
|
def check_by_doctor
|
43
41
|
doctor = begin
|
44
|
-
Doctor.new(options[:gemfile_lock])
|
42
|
+
Doctor.new(options[:gemfile_lock], options[:config], options[:ignore])
|
45
43
|
rescue Bundler::GemfileLockNotFound
|
46
44
|
exit 1
|
47
45
|
end
|
48
46
|
|
49
47
|
doctor.diagnose
|
50
|
-
doctor.report
|
51
|
-
doctor.save_as_file
|
52
|
-
doctor
|
53
48
|
end
|
54
49
|
end
|
55
50
|
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module Bundler
|
7
|
+
module Alive
|
8
|
+
module Client
|
9
|
+
#
|
10
|
+
# RubyGems.org API Client
|
11
|
+
#
|
12
|
+
# @see https://guides.rubygems.org/rubygems-org-api/
|
13
|
+
#
|
14
|
+
class GemsApiClient
|
15
|
+
#
|
16
|
+
# Not found in rubygems.org error
|
17
|
+
#
|
18
|
+
class NotFound < StandardError
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# A new instance of `GemApiClient`
|
23
|
+
#
|
24
|
+
# @param [String] config_path
|
25
|
+
#
|
26
|
+
# @return [GemApiClient]
|
27
|
+
#
|
28
|
+
def initialize(config_path = nil)
|
29
|
+
@error_messages = []
|
30
|
+
@config_gems = get_config_gems(config_path)
|
31
|
+
|
32
|
+
freeze
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Gets gems from RubyGems.org
|
37
|
+
#
|
38
|
+
# @param [Array<String>] gem_names
|
39
|
+
#
|
40
|
+
# @return [Client::GemsApiResponse]
|
41
|
+
#
|
42
|
+
def gems_api_response(gem_names)
|
43
|
+
urls = service_with_urls(gem_names)
|
44
|
+
$stdout.puts <<~MESSAGE
|
45
|
+
|
46
|
+
Get all source code repository URLs of gems are done!
|
47
|
+
MESSAGE
|
48
|
+
Client::GemsApiResponse.new(
|
49
|
+
service_with_urls: urls,
|
50
|
+
error_messages: error_messages
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
attr_accessor :error_messages, :config_gems
|
57
|
+
|
58
|
+
def api_url(gem_name)
|
59
|
+
"https://rubygems.org/api/v1/gems/#{gem_name}.json"
|
60
|
+
end
|
61
|
+
|
62
|
+
def connection
|
63
|
+
Faraday.new do |connection|
|
64
|
+
connection.adapter :net_http
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_config_gems(path)
|
69
|
+
return {} if path.nil? || !File.exist?(path)
|
70
|
+
|
71
|
+
config = YAML.load_file(path)
|
72
|
+
config["gems"]
|
73
|
+
end
|
74
|
+
|
75
|
+
def service_with_urls(gem_names)
|
76
|
+
urls = get_repository_urls(gem_names)
|
77
|
+
urls.each_with_object({}) do |url, hash|
|
78
|
+
service_name = url.service_name
|
79
|
+
hash[service_name] = Array(hash[service_name]) << url
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Returns repository url
|
85
|
+
#
|
86
|
+
# @param [String] gem_name
|
87
|
+
#
|
88
|
+
# @return [SourceCodeRepositoryUrl]
|
89
|
+
#
|
90
|
+
def get_repository_url(gem_name)
|
91
|
+
url_from_config = get_repository_url_from_config(gem_name)
|
92
|
+
return url_from_config unless url_from_config.nil?
|
93
|
+
|
94
|
+
url = api_url(gem_name)
|
95
|
+
response = connection.get(url)
|
96
|
+
|
97
|
+
raise NotFound, "[#{gem_name}] Not found in RubyGems.org." unless response.success?
|
98
|
+
|
99
|
+
body = JSON.parse(response.body)
|
100
|
+
raw_url = source_code_url(body: body, gem_name: gem_name)
|
101
|
+
SourceCodeRepositoryUrl.new(raw_url, gem_name)
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_repository_url_from_config(gem_name)
|
105
|
+
return nil if config_gems.nil?
|
106
|
+
return nil unless config_gems.key?(gem_name)
|
107
|
+
|
108
|
+
gem = config_gems[gem_name]
|
109
|
+
SourceCodeRepositoryUrl.new(gem["url"], gem_name)
|
110
|
+
end
|
111
|
+
|
112
|
+
def source_code_url(body:, gem_name:)
|
113
|
+
url = body["source_code_uri"]
|
114
|
+
return url if SourceCodeRepositoryUrl.support_url?(url)
|
115
|
+
|
116
|
+
url = body["homepage_uri"]
|
117
|
+
return url if SourceCodeRepositoryUrl.support_url?(url)
|
118
|
+
|
119
|
+
message = "[#{gem_name}] Source code repository is not found in RubyGems.org,"\
|
120
|
+
" or not supported. URL: https://rubygems.org/gems/#{gem_name}"
|
121
|
+
raise NotFound, message
|
122
|
+
end
|
123
|
+
|
124
|
+
def get_repository_urls(gem_names)
|
125
|
+
result = gem_names.map do |gem_name|
|
126
|
+
$stdout.write "."
|
127
|
+
get_repository_url(gem_name)
|
128
|
+
rescue StandardError => e
|
129
|
+
$stdout.write "W"
|
130
|
+
error_messages << e.message
|
131
|
+
end
|
132
|
+
|
133
|
+
result.find_all { |obj| obj.instance_of?(Alive::SourceCodeRepositoryUrl) }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Alive
|
5
|
+
module Client
|
6
|
+
#
|
7
|
+
# Represents API Response of RubyGems.org
|
8
|
+
#
|
9
|
+
class GemsApiResponse
|
10
|
+
attr_reader :service_with_urls, :error_messages
|
11
|
+
|
12
|
+
#
|
13
|
+
# Creates a new StatusResult instance
|
14
|
+
#
|
15
|
+
# @param [StatusCollection|nil] :collection
|
16
|
+
# @param [Array] :error_messages
|
17
|
+
#
|
18
|
+
# @return [GemsApiResponse]
|
19
|
+
#
|
20
|
+
def initialize(service_with_urls:, error_messages:)
|
21
|
+
@service_with_urls = service_with_urls
|
22
|
+
@error_messages = error_messages
|
23
|
+
|
24
|
+
freeze
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -9,9 +9,44 @@ module Bundler
|
|
9
9
|
#
|
10
10
|
# API Client for GitHub API
|
11
11
|
#
|
12
|
+
# @see https://docs.github.com/en/rest/search#search-repositories
|
13
|
+
#
|
12
14
|
module GitHubApi
|
15
|
+
# Environment variable name of GitHub Access Token
|
13
16
|
ACCESS_TOKEN_ENV_NAME = "BUNDLER_ALIVE_GITHUB_TOKEN"
|
14
17
|
|
18
|
+
# Separator of query condition
|
19
|
+
QUERY_CONDITION_SEPARATOR = " "
|
20
|
+
|
21
|
+
# Number of attempts to request after too many requests
|
22
|
+
RETRIES_ON_TOO_MANY_REQUESTS = 3
|
23
|
+
|
24
|
+
#
|
25
|
+
# Interval second when retrying request
|
26
|
+
#
|
27
|
+
# @note
|
28
|
+
# This is an empirical value and should
|
29
|
+
# refer to response of Rate Limit API
|
30
|
+
#
|
31
|
+
# @see
|
32
|
+
# https://docs.github.com/en/rest/overview/resources-in-the-rest-api#checking-your-rate-limit-status
|
33
|
+
RETRY_INTERVAL_SEC_ON_TOO_MANY_REQUESTS = 120
|
34
|
+
|
35
|
+
#
|
36
|
+
# Max number of conditional operator at once
|
37
|
+
#
|
38
|
+
# @see https://docs.github.com/en/rest/search#limitations-on-query-length
|
39
|
+
QUERY_MAX_OPERATORS_AT_ONCE = 6
|
40
|
+
|
41
|
+
private_constant :QUERY_MAX_OPERATORS_AT_ONCE
|
42
|
+
|
43
|
+
def self.extended(base)
|
44
|
+
base.instance_eval do
|
45
|
+
@rate_limit_exceeded = false
|
46
|
+
@retries_on_too_many_requests = 0
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
15
50
|
#
|
16
51
|
# Creates a GitHub client
|
17
52
|
#
|
@@ -22,39 +57,112 @@ module Bundler
|
|
22
57
|
end
|
23
58
|
|
24
59
|
#
|
25
|
-
#
|
60
|
+
# Query repository statuses
|
61
|
+
#
|
62
|
+
# @param [Array<RepositoryUrl>] :urls
|
63
|
+
#
|
64
|
+
# @return [StatusResult]
|
65
|
+
#
|
66
|
+
# rubocop:disable Metrics/MethodLength
|
67
|
+
def query(urls:)
|
68
|
+
collection = StatusCollection.new
|
69
|
+
name_with_archived = get_name_with_statuses(urls)
|
70
|
+
urls.each do |url|
|
71
|
+
$stdout.write "."
|
72
|
+
|
73
|
+
gem_name = url.gem_name
|
74
|
+
alive = !name_with_archived[gem_name]
|
75
|
+
status = Status.new(name: gem_name, repository_url: url, alive: alive, checked_at: Time.now)
|
76
|
+
collection = collection.add(gem_name, status)
|
77
|
+
end
|
78
|
+
|
79
|
+
StatusResult.new(collection: collection, error_messages: @error_messages,
|
80
|
+
rate_limit_exceeded: @rate_limit_exceeded)
|
81
|
+
end
|
82
|
+
# rubocop:enable Metrics/MethodLength
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
#
|
87
|
+
# Search status of repositories
|
88
|
+
#
|
89
|
+
# @param [Array<RepositoryUrl>] urls
|
90
|
+
#
|
91
|
+
# @return [Hash<String, Boolean>]
|
92
|
+
# gem name with archived or not
|
93
|
+
#
|
94
|
+
# rubocop:disable Metrics/MethodLength
|
95
|
+
def get_name_with_statuses(urls)
|
96
|
+
raise ArgumentError unless urls.instance_of?(Array)
|
97
|
+
|
98
|
+
name_with_status = {}
|
99
|
+
urls.each_slice(QUERY_MAX_OPERATORS_AT_ONCE) do |sliced_urls|
|
100
|
+
q = search_query(sliced_urls)
|
101
|
+
repositories = search_repositories_with_retry(q)
|
102
|
+
next if repositories.nil?
|
103
|
+
|
104
|
+
repositories.each do |repository|
|
105
|
+
name = repository["name"]
|
106
|
+
name_with_status[name] = repository["archived"]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
name_with_status
|
110
|
+
end
|
111
|
+
# rubocop:enable Metrics/MethodLength
|
112
|
+
|
113
|
+
#
|
114
|
+
# Search query of repositories
|
26
115
|
#
|
27
|
-
# @param [
|
116
|
+
# @param [Array<RepositoryUrl>] urls
|
28
117
|
#
|
29
|
-
# @
|
30
|
-
#
|
118
|
+
# @return [String]
|
119
|
+
#
|
120
|
+
def search_query(urls)
|
121
|
+
urls.map do |url|
|
122
|
+
"repo:#{slug(url.url)}"
|
123
|
+
end.join(QUERY_CONDITION_SEPARATOR)
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Search repositories
|
128
|
+
#
|
129
|
+
# @param [String] query
|
31
130
|
#
|
32
131
|
# @raise [Octokit::TooManyRequests]
|
33
132
|
# when too many requested to GitHub.com
|
34
|
-
#
|
35
133
|
# @raise [SourceCodeClient::SearchRepositoryError]
|
36
134
|
# when Error without `Octokit::TooManyRequests`
|
37
135
|
#
|
38
|
-
# @return [
|
136
|
+
# @return [Array<Sawyer::Resource>|nil]
|
39
137
|
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
138
|
+
def search_repositories(query)
|
139
|
+
result = @client.search_repositories(query)
|
140
|
+
result[:items]
|
141
|
+
rescue Octokit::TooManyRequests => e
|
142
|
+
raise e
|
143
|
+
rescue StandardError => e
|
144
|
+
@error_messages << e.message
|
145
|
+
[]
|
146
|
+
end
|
147
|
+
|
148
|
+
def search_repositories_with_retry(query)
|
149
|
+
search_repositories(query)
|
150
|
+
rescue Octokit::TooManyRequests
|
151
|
+
if @retries_on_too_many_requests < RETRIES_ON_TOO_MANY_REQUESTS
|
152
|
+
@retries_on_too_many_requests += 1
|
153
|
+
sleep_with_message
|
154
|
+
retry
|
44
155
|
end
|
45
156
|
|
46
|
-
|
157
|
+
@rate_limit_exceeded = true
|
158
|
+
[]
|
159
|
+
end
|
47
160
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
raise SourceCodeClient::RateLimitExceededError, e.message
|
53
|
-
rescue StandardError => e
|
54
|
-
raise SourceCodeClient::SearchRepositoryError, e.message
|
55
|
-
end
|
161
|
+
def sleep_with_message
|
162
|
+
puts "Too many requested to GitHub. Sleep #{RETRY_INTERVAL_SEC_ON_TOO_MANY_REQUESTS} sec."
|
163
|
+
sleep RETRY_INTERVAL_SEC_ON_TOO_MANY_REQUESTS
|
164
|
+
puts "Retry request (#{@retries_on_too_many_requests}/#{RETRIES_ON_TOO_MANY_REQUESTS})"
|
56
165
|
end
|
57
|
-
# rubocop:enable Metrics/MethodLength
|
58
166
|
|
59
167
|
#
|
60
168
|
# Returns slug of repository URL
|