bundler-alive 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.
- checksums.yaml +7 -0
- data/.rspec +4 -0
- data/.rubocop.yml +18 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +119 -0
- data/LICENSE +21 -0
- data/README.md +51 -0
- data/Rakefile +12 -0
- data/bin/bundle-alive +11 -0
- data/bin/bundler-alive +4 -0
- data/bin/setup +8 -0
- data/bundler-alive.gemspec +29 -0
- data/lib/bundler/alive/cli.rb +56 -0
- data/lib/bundler/alive/client/gems_api.rb +55 -0
- data/lib/bundler/alive/client/git_hub_api.rb +72 -0
- data/lib/bundler/alive/client/source_code_client.rb +44 -0
- data/lib/bundler/alive/doctor.rb +122 -0
- data/lib/bundler/alive/gem_status.rb +79 -0
- data/lib/bundler/alive/gem_status_collection.rb +49 -0
- data/lib/bundler/alive/source_code_repository.rb +37 -0
- data/lib/bundler/alive/source_code_repository_url.rb +47 -0
- data/lib/bundler/alive/version.rb +7 -0
- data/lib/bundler/alive.rb +11 -0
- metadata +71 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 95401e4f8058b42f643043c89f012ff2c684ad2844ccd8ae8b8f9740d7262b2b
|
4
|
+
data.tar.gz: 5de7bffe8f34c12365916c5835c0321880303c69fde54ea588013548e794f9de
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cf87c1bb0ffa1e5cabb3739024e97e90cdacd94a1540e30454bd16d36b56e6f2a51904b75c3c843decc1a07ec504925d317632dc4113f2a6c385942317ddb670
|
7
|
+
data.tar.gz: ce0786a1ea7ce4418de1640178b76536f852a8b0376327cc0a5d86a26f46b0dea520504c6a2650ce2406c7f800510046d0f6eb37ddcb5cf3abc064f6b965a6c4
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.6
|
3
|
+
NewCops: enable
|
4
|
+
|
5
|
+
Metrics/BlockLength:
|
6
|
+
Exclude:
|
7
|
+
- 'spec/**/*_spec.rb'
|
8
|
+
|
9
|
+
Style/StringLiterals:
|
10
|
+
Enabled: true
|
11
|
+
EnforcedStyle: double_quotes
|
12
|
+
|
13
|
+
Style/StringLiteralsInInterpolation:
|
14
|
+
Enabled: true
|
15
|
+
EnforcedStyle: double_quotes
|
16
|
+
|
17
|
+
Layout/LineLength:
|
18
|
+
Max: 120
|
data/Gemfile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gemspec
|
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
|
+
group :development do
|
16
|
+
gem "yard"
|
17
|
+
end
|
18
|
+
|
19
|
+
group :test do
|
20
|
+
gem "simplecov"
|
21
|
+
gem "vcr"
|
22
|
+
gem "webmock"
|
23
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
bundler-alive (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
addressable (2.8.0)
|
10
|
+
public_suffix (>= 2.0.2, < 5.0)
|
11
|
+
ast (2.4.2)
|
12
|
+
citrus (3.0.2)
|
13
|
+
crack (0.4.5)
|
14
|
+
rexml
|
15
|
+
diff-lcs (1.5.0)
|
16
|
+
docile (1.4.0)
|
17
|
+
faraday (1.10.0)
|
18
|
+
faraday-em_http (~> 1.0)
|
19
|
+
faraday-em_synchrony (~> 1.0)
|
20
|
+
faraday-excon (~> 1.1)
|
21
|
+
faraday-httpclient (~> 1.0)
|
22
|
+
faraday-multipart (~> 1.0)
|
23
|
+
faraday-net_http (~> 1.0)
|
24
|
+
faraday-net_http_persistent (~> 1.0)
|
25
|
+
faraday-patron (~> 1.0)
|
26
|
+
faraday-rack (~> 1.0)
|
27
|
+
faraday-retry (~> 1.0)
|
28
|
+
ruby2_keywords (>= 0.0.4)
|
29
|
+
faraday-em_http (1.0.0)
|
30
|
+
faraday-em_synchrony (1.0.0)
|
31
|
+
faraday-excon (1.1.0)
|
32
|
+
faraday-httpclient (1.0.1)
|
33
|
+
faraday-multipart (1.0.3)
|
34
|
+
multipart-post (>= 1.2, < 3)
|
35
|
+
faraday-net_http (1.0.1)
|
36
|
+
faraday-net_http_persistent (1.2.0)
|
37
|
+
faraday-patron (1.0.0)
|
38
|
+
faraday-rack (1.0.0)
|
39
|
+
faraday-retry (1.0.3)
|
40
|
+
hashdiff (1.0.1)
|
41
|
+
multipart-post (2.1.1)
|
42
|
+
octokit (4.22.0)
|
43
|
+
faraday (>= 0.9)
|
44
|
+
sawyer (~> 0.8.0, >= 0.5.3)
|
45
|
+
parallel (1.22.1)
|
46
|
+
parser (3.1.2.0)
|
47
|
+
ast (~> 2.4.1)
|
48
|
+
public_suffix (4.0.7)
|
49
|
+
rainbow (3.1.1)
|
50
|
+
rake (13.0.6)
|
51
|
+
regexp_parser (2.3.1)
|
52
|
+
rexml (3.2.5)
|
53
|
+
rspec (3.11.0)
|
54
|
+
rspec-core (~> 3.11.0)
|
55
|
+
rspec-expectations (~> 3.11.0)
|
56
|
+
rspec-mocks (~> 3.11.0)
|
57
|
+
rspec-core (3.11.0)
|
58
|
+
rspec-support (~> 3.11.0)
|
59
|
+
rspec-expectations (3.11.0)
|
60
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
61
|
+
rspec-support (~> 3.11.0)
|
62
|
+
rspec-mocks (3.11.1)
|
63
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
64
|
+
rspec-support (~> 3.11.0)
|
65
|
+
rspec-support (3.11.0)
|
66
|
+
rubocop (1.29.0)
|
67
|
+
parallel (~> 1.10)
|
68
|
+
parser (>= 3.1.0.0)
|
69
|
+
rainbow (>= 2.2.2, < 4.0)
|
70
|
+
regexp_parser (>= 1.8, < 3.0)
|
71
|
+
rexml (>= 3.2.5, < 4.0)
|
72
|
+
rubocop-ast (>= 1.17.0, < 2.0)
|
73
|
+
ruby-progressbar (~> 1.7)
|
74
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
75
|
+
rubocop-ast (1.17.0)
|
76
|
+
parser (>= 3.1.1.0)
|
77
|
+
ruby-progressbar (1.11.0)
|
78
|
+
ruby2_keywords (0.0.5)
|
79
|
+
sawyer (0.8.2)
|
80
|
+
addressable (>= 2.3.5)
|
81
|
+
faraday (> 0.8, < 2.0)
|
82
|
+
simplecov (0.21.2)
|
83
|
+
docile (~> 1.1)
|
84
|
+
simplecov-html (~> 0.11)
|
85
|
+
simplecov_json_formatter (~> 0.1)
|
86
|
+
simplecov-html (0.12.3)
|
87
|
+
simplecov_json_formatter (0.1.4)
|
88
|
+
thor (1.2.1)
|
89
|
+
toml-rb (2.1.2)
|
90
|
+
citrus (~> 3.0, > 3.0)
|
91
|
+
unicode-display_width (2.1.0)
|
92
|
+
vcr (6.1.0)
|
93
|
+
webmock (3.14.0)
|
94
|
+
addressable (>= 2.8.0)
|
95
|
+
crack (>= 0.3.2)
|
96
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
97
|
+
yard (0.9.20)
|
98
|
+
|
99
|
+
PLATFORMS
|
100
|
+
arm64-darwin-20
|
101
|
+
ruby
|
102
|
+
x86_64-darwin-20
|
103
|
+
|
104
|
+
DEPENDENCIES
|
105
|
+
bundler-alive!
|
106
|
+
faraday
|
107
|
+
octokit
|
108
|
+
rake (~> 13.0)
|
109
|
+
rspec (~> 3.0)
|
110
|
+
rubocop (~> 1.21)
|
111
|
+
simplecov
|
112
|
+
thor
|
113
|
+
toml-rb
|
114
|
+
vcr
|
115
|
+
webmock
|
116
|
+
yard
|
117
|
+
|
118
|
+
BUNDLED WITH
|
119
|
+
2.3.13
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2022 YOSHIDA Katsuhiko
|
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 all
|
13
|
+
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 THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# bundler-alive
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
`bunder-alive` checks if gems in a RubyGem's `Gemfile.lock` are active.
|
6
|
+
|
7
|
+
Currently only github.com is supported as a source code repository. If the source code repository is archived, then reports as not alive.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
```
|
12
|
+
$ gem install bunlder-alive
|
13
|
+
```
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
```
|
18
|
+
$ bundle-alive
|
19
|
+
Name: journey
|
20
|
+
URL: http://github.com/rails/journey
|
21
|
+
Status: false
|
22
|
+
|
23
|
+
Not alive gems are found!
|
24
|
+
```
|
25
|
+
|
26
|
+
Default `Gemfile.lock` location is in your current directory. You can specify it.
|
27
|
+
|
28
|
+
```
|
29
|
+
$ bundle-alive -G /path/to/Gemfile.lock
|
30
|
+
```
|
31
|
+
|
32
|
+
In most cases, the following error is output.
|
33
|
+
|
34
|
+
```
|
35
|
+
Too many requested! Retry later.
|
36
|
+
```
|
37
|
+
|
38
|
+
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.
|
39
|
+
|
40
|
+
If you run again, it will resume.
|
41
|
+
|
42
|
+
## Contributing
|
43
|
+
|
44
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/kyoshidajp/bunlder-alive.
|
45
|
+
|
46
|
+
## Thanks
|
47
|
+
|
48
|
+
This gem was inspired by the following products.
|
49
|
+
|
50
|
+
- [bundler-audit](https://github.com/rubysec/bundler-audit)
|
51
|
+
- [良いコード/悪いコードで学ぶ設計入門 ―保守しやすい 成長し続けるコードの書き方](https://gihyo.jp/book/2022/978-4-297-12783-1)
|
data/Rakefile
ADDED
data/bin/bundle-alive
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
|
6
|
+
lib_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
|
7
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
8
|
+
|
9
|
+
require "bundler/alive/cli"
|
10
|
+
|
11
|
+
Bundler::Alive::CLI.start
|
data/bin/bundler-alive
ADDED
data/bin/setup
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/bundler/alive/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "bundler-alive"
|
7
|
+
spec.version = Bundler::Alive::VERSION
|
8
|
+
spec.authors = ["Katsuhiko YOSHIDA"]
|
9
|
+
spec.email = ["claddvd@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Are your gems alive?"
|
12
|
+
spec.description = "bundler-alive reports gems are archived or not."
|
13
|
+
spec.homepage = "https://github.com/kyoshidajp/bundler-audit"
|
14
|
+
spec.required_ruby_version = ">= 2.6.0"
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = "https://github.com/kyoshidajp/bundler-audit"
|
17
|
+
spec.metadata["source_code_uri"] = "https://github.com/kyoshidajp/bundler-audit"
|
18
|
+
spec.metadata["changelog_uri"] = "https://github.com/kyoshidajp/bundler-audit"
|
19
|
+
|
20
|
+
spec.files = Dir.chdir(__dir__) do
|
21
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
22
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
23
|
+
end
|
24
|
+
end
|
25
|
+
spec.bindir = "bin"
|
26
|
+
spec.executables = spec.files.grep(%r{\Abin/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/alive"
|
4
|
+
require "bundler/alive/doctor"
|
5
|
+
|
6
|
+
require "thor"
|
7
|
+
|
8
|
+
module Bundler
|
9
|
+
module Alive
|
10
|
+
#
|
11
|
+
# The `bundler-alive` command.
|
12
|
+
#
|
13
|
+
class CLI < ::Thor
|
14
|
+
default_task :check
|
15
|
+
map "--version" => :version
|
16
|
+
|
17
|
+
desc "check [DIR]", "Checks the Gemfile.lock"
|
18
|
+
method_option :gemfile_lock, type: :string, aliases: "-G",
|
19
|
+
default: "Gemfile.lock"
|
20
|
+
|
21
|
+
def check(_dir = Dir.pwd)
|
22
|
+
doctor = check_by_doctor
|
23
|
+
|
24
|
+
if doctor.rate_limit_exceeded_error
|
25
|
+
puts "Too many requested! Retry later."
|
26
|
+
exit 1
|
27
|
+
end
|
28
|
+
|
29
|
+
exit 0 if doctor.all_alive
|
30
|
+
|
31
|
+
puts "Not alive gems are found!"
|
32
|
+
exit 1
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "version", "Prints the bundler-alive version"
|
36
|
+
def version
|
37
|
+
puts "bundler-alive #{VERSION}"
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def check_by_doctor
|
43
|
+
doctor = begin
|
44
|
+
Doctor.new(options[:gemfile_lock])
|
45
|
+
rescue Bundler::GemfileLockNotFound
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
|
49
|
+
doctor.diagnose
|
50
|
+
doctor.report
|
51
|
+
doctor.save_as_file
|
52
|
+
doctor
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module Bundler
|
7
|
+
module Alive
|
8
|
+
module Client
|
9
|
+
#
|
10
|
+
# API Client for RubyGems.org API
|
11
|
+
#
|
12
|
+
# @see https://guides.rubygems.org/rubygems-org-api/
|
13
|
+
#
|
14
|
+
class GemsApi
|
15
|
+
#
|
16
|
+
# Not found in rubygems.org error
|
17
|
+
#
|
18
|
+
class NotFound < StandardError
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Returns repository url
|
23
|
+
#
|
24
|
+
# @param [String] gem_name
|
25
|
+
#
|
26
|
+
# @return [SourceCodeRepositoryUrl]
|
27
|
+
#
|
28
|
+
def get_repository_url(gem_name)
|
29
|
+
url = api_url(gem_name)
|
30
|
+
response = connection.get(url)
|
31
|
+
|
32
|
+
raise NotFound, "#{gem_name} is not found in gems.org." if response.status == 404
|
33
|
+
|
34
|
+
body = JSON.parse(response.body)
|
35
|
+
raw_url = body["source_code_uri"] || body["homepage_uri"]
|
36
|
+
SourceCodeRepositoryUrl.new(raw_url)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def api_url(gem_name)
|
42
|
+
"https://rubygems.org/api/v1/gems/#{gem_name}.json"
|
43
|
+
end
|
44
|
+
|
45
|
+
def connection
|
46
|
+
return @connection if instance_variable_defined?(:@connection)
|
47
|
+
|
48
|
+
@connection = Faraday.new do |connection|
|
49
|
+
connection.adapter :net_http
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "octokit"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module Bundler
|
7
|
+
module Alive
|
8
|
+
module Client
|
9
|
+
#
|
10
|
+
# API Client for GitHub API
|
11
|
+
#
|
12
|
+
module GitHubApi
|
13
|
+
ACCESS_TOKEN_ENV_NAME = "BUNDLER_ALIVE_GITHUB_TOKEN"
|
14
|
+
|
15
|
+
#
|
16
|
+
# Creates a GitHub client
|
17
|
+
#
|
18
|
+
# @return [Octokit::Client]
|
19
|
+
#
|
20
|
+
def create_client
|
21
|
+
Octokit::Client.new(access_token: ENV.fetch(ACCESS_TOKEN_ENV_NAME, nil))
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Returns repository URL is archived?
|
26
|
+
#
|
27
|
+
# @param [SourceCodeRepositoryUrl] repository_url
|
28
|
+
#
|
29
|
+
# @raise [ArgumentError]
|
30
|
+
# when repository_uri is not `SourceCodeRepositoryUrl`
|
31
|
+
#
|
32
|
+
# @raise [Octokit::TooManyRequests]
|
33
|
+
# when too many requested to GitHub.com
|
34
|
+
#
|
35
|
+
# @raise [SourceCodeClient::SearchRepositoryError]
|
36
|
+
# when Error without `Octokit::TooManyRequests`
|
37
|
+
#
|
38
|
+
# @return [Boolean]
|
39
|
+
#
|
40
|
+
# rubocop:disable Metrics/MethodLength
|
41
|
+
def archived?(repository_url)
|
42
|
+
unless repository_url.instance_of?(SourceCodeRepositoryUrl)
|
43
|
+
raise ArgumentError, "UnSupported url: #{repository_url}"
|
44
|
+
end
|
45
|
+
|
46
|
+
query = "repo:#{slug(repository_url.url)}"
|
47
|
+
|
48
|
+
begin
|
49
|
+
result = @client.search_repositories(query)
|
50
|
+
result[:items][0][:archived]
|
51
|
+
rescue Octokit::TooManyRequests => e
|
52
|
+
raise SourceCodeClient::RateLimitExceededError, e.message
|
53
|
+
rescue StandardError => e
|
54
|
+
raise SourceCodeClient::SearchRepositoryError, e.message
|
55
|
+
end
|
56
|
+
end
|
57
|
+
# rubocop:enable Metrics/MethodLength
|
58
|
+
|
59
|
+
#
|
60
|
+
# Returns slug of repository URL
|
61
|
+
#
|
62
|
+
# @param [String] repository_url
|
63
|
+
#
|
64
|
+
# @return [String]
|
65
|
+
#
|
66
|
+
def slug(repository_url)
|
67
|
+
Octokit::Repository.from_url(repository_url).slug
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Alive
|
5
|
+
module Client
|
6
|
+
#
|
7
|
+
# Represents a source code client
|
8
|
+
#
|
9
|
+
class SourceCodeClient
|
10
|
+
class SearchRepositoryError < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
class RateLimitExceededError < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
SERVICE_WITH_STRATEGIES = {
|
17
|
+
SourceCodeRepository::Service::GITHUB => GitHubApi
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
private_constant :SERVICE_WITH_STRATEGIES
|
21
|
+
|
22
|
+
#
|
23
|
+
# A new instance of SourceCodeClient
|
24
|
+
#
|
25
|
+
# @param [Symbol] service_name
|
26
|
+
#
|
27
|
+
# @raise [ArgumentError]
|
28
|
+
#
|
29
|
+
# @return [SourceCodeClient]
|
30
|
+
#
|
31
|
+
def initialize(service_name:)
|
32
|
+
raise ArgumentError, "Unknown service: #{service_name}" unless SERVICE_WITH_STRATEGIES.key?(service_name)
|
33
|
+
|
34
|
+
service = SERVICE_WITH_STRATEGIES[service_name]
|
35
|
+
extend service
|
36
|
+
|
37
|
+
@client = create_client
|
38
|
+
|
39
|
+
super()
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler"
|
4
|
+
require "octokit"
|
5
|
+
require "toml-rb"
|
6
|
+
|
7
|
+
module Bundler
|
8
|
+
module Alive
|
9
|
+
#
|
10
|
+
# Diagnoses a `Gemfile.lock` with a TOML file
|
11
|
+
#
|
12
|
+
class Doctor
|
13
|
+
attr_reader :all_alive, :rate_limit_exceeded_error
|
14
|
+
|
15
|
+
#
|
16
|
+
# A new instance of Doctor
|
17
|
+
#
|
18
|
+
# @param [String] lock_file lock file of gem
|
19
|
+
# @param [String] result_file file of result
|
20
|
+
#
|
21
|
+
def initialize(lock_file, result_file = "result.toml")
|
22
|
+
@lock_file = lock_file
|
23
|
+
@result_file = result_file
|
24
|
+
@gem_client = Client::GemsApi.new
|
25
|
+
@result = nil
|
26
|
+
@all_alive = nil
|
27
|
+
@rate_limit_exceeded_error = false
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Diagnoses gems in lock file of gem
|
32
|
+
#
|
33
|
+
def diagnose
|
34
|
+
@result = gems.each_with_object(GemStatusCollection.new) do |spec, collection|
|
35
|
+
gem_name = spec.name
|
36
|
+
gem_status = diagnose_gem(gem_name)
|
37
|
+
|
38
|
+
collection.add(gem_name, gem_status)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Reports the result
|
44
|
+
#
|
45
|
+
def report
|
46
|
+
need_to_report_gems = result.need_to_report_gems
|
47
|
+
@all_alive = need_to_report_gems.size.zero?
|
48
|
+
need_to_report_gems.each do |_name, gem_status|
|
49
|
+
print gem_status.report
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Saves result to file
|
55
|
+
#
|
56
|
+
def save_as_file
|
57
|
+
body = TomlRB.dump(result.to_h)
|
58
|
+
File.write(result_file, body)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
attr_reader :lock_file, :result_file, :gem_client, :result
|
64
|
+
|
65
|
+
def diagnosed_gem?(gem_status)
|
66
|
+
!gem_status.nil? && !gem_status.unknown?
|
67
|
+
end
|
68
|
+
|
69
|
+
def collection_from_file
|
70
|
+
return @collection_from_file if instance_variable_defined?(:@collection_from_file)
|
71
|
+
|
72
|
+
return GemStatusCollection.new unless File.exist?(result_file)
|
73
|
+
|
74
|
+
toml_hash = TomlRB.load_file(result_file)
|
75
|
+
@collection_from_file = collection_from_hash(toml_hash)
|
76
|
+
end
|
77
|
+
|
78
|
+
def collection_from_hash(hash)
|
79
|
+
hash.each_with_object(GemStatusCollection.new) do |(gem_name, v), collection|
|
80
|
+
url = v["repository_url"]
|
81
|
+
next if url.to_sym == GemStatus::REPOSITORY_URL_UNKNOWN
|
82
|
+
|
83
|
+
gem_status = GemStatus.new(name: gem_name,
|
84
|
+
repository_url: SourceCodeRepositoryUrl.new(url),
|
85
|
+
alive: v["alive"],
|
86
|
+
checked_at: v["checked_at"])
|
87
|
+
collection.add(gem_name, gem_status)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def gems
|
92
|
+
lock_file_body = File.read(@lock_file)
|
93
|
+
lock_file = Bundler::LockfileParser.new(lock_file_body)
|
94
|
+
lock_file.specs.each
|
95
|
+
end
|
96
|
+
|
97
|
+
# rubocop:disable Metrics/MethodLength
|
98
|
+
def diagnose_gem(gem_name)
|
99
|
+
gem_status = collection_from_file.get_unchecked(gem_name)
|
100
|
+
return gem_status if diagnosed_gem?(gem_status)
|
101
|
+
|
102
|
+
unless @rate_limit_exceeded_error
|
103
|
+
begin
|
104
|
+
source_code_url = gem_client.get_repository_url(gem_name)
|
105
|
+
is_alive = SourceCodeRepository.new(url: source_code_url).alive?
|
106
|
+
rescue Client::SourceCodeClient::RateLimitExceededError => e
|
107
|
+
@rate_limit_exceeded_error = true
|
108
|
+
puts e.message
|
109
|
+
rescue StandardError => e
|
110
|
+
puts e.message
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
GemStatus.new(name: gem_name,
|
115
|
+
repository_url: source_code_url,
|
116
|
+
alive: is_alive,
|
117
|
+
checked_at: Time.now)
|
118
|
+
end
|
119
|
+
# rubocop:enable Metrics/MethodLength
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Alive
|
5
|
+
#
|
6
|
+
# Result of gem status
|
7
|
+
#
|
8
|
+
class GemStatus
|
9
|
+
REPOSITORY_URL_UNKNOWN = :unknown
|
10
|
+
ALIVE_UNKNOWN = :unknown
|
11
|
+
|
12
|
+
attr_reader :name, :repository_url, :alive, :checked_at
|
13
|
+
|
14
|
+
def initialize(name:, repository_url:, alive:, checked_at:)
|
15
|
+
repository_url = REPOSITORY_URL_UNKNOWN if repository_url.nil?
|
16
|
+
alive = ALIVE_UNKNOWN if alive.nil?
|
17
|
+
|
18
|
+
@name = name
|
19
|
+
@repository_url = repository_url
|
20
|
+
@alive = alive
|
21
|
+
@checked_at = checked_at
|
22
|
+
|
23
|
+
freeze
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Alive?
|
28
|
+
#
|
29
|
+
# @return [Boolean]
|
30
|
+
#
|
31
|
+
def unknown?
|
32
|
+
alive == ALIVE_UNKNOWN
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# @return [Hash] Hash of status
|
37
|
+
#
|
38
|
+
def to_h
|
39
|
+
{
|
40
|
+
repository_url: decorated_repository_url,
|
41
|
+
alive: decorated_alive,
|
42
|
+
checked_at: checked_at
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Reports not alive gem
|
48
|
+
#
|
49
|
+
# @return [String]
|
50
|
+
#
|
51
|
+
def report
|
52
|
+
<<~REPORT
|
53
|
+
Name: #{name}
|
54
|
+
URL: #{decorated_repository_url}
|
55
|
+
Status: #{decorated_alive}
|
56
|
+
|
57
|
+
REPORT
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def decorated_repository_url
|
63
|
+
if repository_url == REPOSITORY_URL_UNKNOWN
|
64
|
+
REPOSITORY_URL_UNKNOWN.to_s
|
65
|
+
else
|
66
|
+
repository_url.url
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def decorated_alive
|
71
|
+
if alive == ALIVE_UNKNOWN
|
72
|
+
ALIVE_UNKNOWN.to_s
|
73
|
+
else
|
74
|
+
alive
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module Bundler
|
6
|
+
module Alive
|
7
|
+
# Collection of GemStatus
|
8
|
+
class GemStatusCollection
|
9
|
+
extend Forwardable
|
10
|
+
delegate each: :gems
|
11
|
+
|
12
|
+
def initialize(gems = {})
|
13
|
+
@gems = gems
|
14
|
+
freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def add(name, gem_status)
|
18
|
+
gems[name] = gem_status
|
19
|
+
|
20
|
+
self.class.new(gems)
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_unchecked(name)
|
24
|
+
return nil unless gems.key?(name)
|
25
|
+
|
26
|
+
gem_status = gems[name]
|
27
|
+
return nil if gem_status.unknown?
|
28
|
+
|
29
|
+
gem_status
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_h
|
33
|
+
hash = {}
|
34
|
+
gems.each do |k, v|
|
35
|
+
hash[k] = v.to_h
|
36
|
+
end
|
37
|
+
hash
|
38
|
+
end
|
39
|
+
|
40
|
+
def need_to_report_gems
|
41
|
+
gems.find_all { |_name, gem| !!!gem.alive }
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :gems
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Alive
|
5
|
+
# Represents a source code repository
|
6
|
+
class SourceCodeRepository
|
7
|
+
module Service
|
8
|
+
GITHUB = :github
|
9
|
+
end
|
10
|
+
|
11
|
+
#
|
12
|
+
# Creates a `SourceCodeRepository`
|
13
|
+
#
|
14
|
+
# @param [SourceCodeRepositoryUrl] url
|
15
|
+
#
|
16
|
+
def initialize(url:)
|
17
|
+
raise ArgumentError, "Unknown url: #{url}" unless url.instance_of?(SourceCodeRepositoryUrl)
|
18
|
+
|
19
|
+
@url = url
|
20
|
+
@client = Client::SourceCodeClient.new(service_name: url.service_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Returns alive or not
|
25
|
+
#
|
26
|
+
# @return [Boolean]
|
27
|
+
#
|
28
|
+
def alive?
|
29
|
+
!client.archived?(url)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :url, :client
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Alive
|
5
|
+
# Represents a source code repository
|
6
|
+
class SourceCodeRepositoryUrl
|
7
|
+
DOMAIN_WITH_SERVICES = {
|
8
|
+
"github.com" => SourceCodeRepository::Service::GITHUB
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
private_constant :DOMAIN_WITH_SERVICES
|
12
|
+
|
13
|
+
# No supported URL Error
|
14
|
+
class UnSupportedUrl < StandardError
|
15
|
+
def initialize(url)
|
16
|
+
message = "UnSupported URL: #{url}"
|
17
|
+
super(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :url, :service_name
|
22
|
+
|
23
|
+
#
|
24
|
+
# Creates a `SourceCodeRepositoryUrl`
|
25
|
+
#
|
26
|
+
# @param [String] url
|
27
|
+
#
|
28
|
+
# @raise [UnSupportedUrl]
|
29
|
+
#
|
30
|
+
def initialize(url)
|
31
|
+
@url = url
|
32
|
+
@service_name = service(url)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def service(url)
|
38
|
+
uri = URI.parse(url)
|
39
|
+
host = uri.host
|
40
|
+
|
41
|
+
raise UnSupportedUrl, url unless DOMAIN_WITH_SERVICES.key?(host)
|
42
|
+
|
43
|
+
DOMAIN_WITH_SERVICES[host]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "alive/version"
|
4
|
+
require_relative "alive/doctor"
|
5
|
+
require_relative "alive/source_code_repository"
|
6
|
+
require_relative "alive/source_code_repository_url"
|
7
|
+
require_relative "alive/gem_status"
|
8
|
+
require_relative "alive/gem_status_collection"
|
9
|
+
require_relative "alive/client/gems_api"
|
10
|
+
require_relative "alive/client/git_hub_api"
|
11
|
+
require_relative "alive/client/source_code_client"
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bundler-alive
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Katsuhiko YOSHIDA
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-05-08 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: bundler-alive reports gems are archived or not.
|
14
|
+
email:
|
15
|
+
- claddvd@gmail.com
|
16
|
+
executables:
|
17
|
+
- bundle-alive
|
18
|
+
- bundler-alive
|
19
|
+
- setup
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- ".rspec"
|
24
|
+
- ".rubocop.yml"
|
25
|
+
- Gemfile
|
26
|
+
- Gemfile.lock
|
27
|
+
- LICENSE
|
28
|
+
- README.md
|
29
|
+
- Rakefile
|
30
|
+
- bin/bundle-alive
|
31
|
+
- bin/bundler-alive
|
32
|
+
- bin/setup
|
33
|
+
- bundler-alive.gemspec
|
34
|
+
- lib/bundler/alive.rb
|
35
|
+
- lib/bundler/alive/cli.rb
|
36
|
+
- lib/bundler/alive/client/gems_api.rb
|
37
|
+
- lib/bundler/alive/client/git_hub_api.rb
|
38
|
+
- lib/bundler/alive/client/source_code_client.rb
|
39
|
+
- lib/bundler/alive/doctor.rb
|
40
|
+
- lib/bundler/alive/gem_status.rb
|
41
|
+
- lib/bundler/alive/gem_status_collection.rb
|
42
|
+
- lib/bundler/alive/source_code_repository.rb
|
43
|
+
- lib/bundler/alive/source_code_repository_url.rb
|
44
|
+
- lib/bundler/alive/version.rb
|
45
|
+
homepage: https://github.com/kyoshidajp/bundler-audit
|
46
|
+
licenses: []
|
47
|
+
metadata:
|
48
|
+
homepage_uri: https://github.com/kyoshidajp/bundler-audit
|
49
|
+
source_code_uri: https://github.com/kyoshidajp/bundler-audit
|
50
|
+
changelog_uri: https://github.com/kyoshidajp/bundler-audit
|
51
|
+
rubygems_mfa_required: 'true'
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 2.6.0
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubygems_version: 3.1.6
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: Are your gems alive?
|
71
|
+
test_files: []
|