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 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
@@ -0,0 +1,4 @@
1
+ --format documentation
2
+ --color
3
+ --warnings
4
+ --require spec_helper
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
+ ![bundler-alive](https://github.com/kyoshidajp/bundler-alive/actions/workflows/ci.yml/badge.svg)
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
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
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
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ load File.expand_path("bundle-alive", __dir__)
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
@@ -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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ module Alive
5
+ VERSION = "0.1.0"
6
+ end
7
+ 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: []