bundler-alive 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2a89085af5b9f1ee492cdbbe0e51fc27026c90760f77ebe0e7aa219b038d423
4
- data.tar.gz: 619562e866a25a255516cbaaedf1a0ccc08c741d3f00bd6c5bd86455793d9db5
3
+ metadata.gz: cf64468693c247f87a2ffbce58960dd82d1313614d1433d0d032210f080e8fd4
4
+ data.tar.gz: a3d9f48ce1e8a932ae954897448acf7ab5cc82123992bd82b72ab33b7a8902b0
5
5
  SHA512:
6
- metadata.gz: 5929a845890c5b50b6bd629296f4bd3f1c457791c6938f53203f464f99ac86634122500cd2e0dc5fc4276e42ef04ba30898ad91ede8546eb2c2cdf8fc9f6ac18
7
- data.tar.gz: 4845907f1e0eedab0c1746a186dc613d31f2e680b8b0f24c08aa660dbd9467840c42a240cb22ab97e9ba46594be185158ae8c8f69752f4f7150585b849e239da
6
+ metadata.gz: 0c4d5747e3b1a133bcc8e1d757fb0ca790b280c667b7f2aa865c95363db66a878f2b0383d436a3b83cb43c19b28d06815c226d7fde09411d626ecdbc0508836f
7
+ data.tar.gz: e4ad9d70fb50258517e8cfbde02aea45435575e0e41435a26e9c5e35141b6df5e71bbebd85f11837afc6fdaa2b76c89508191234417214471f64c18b43d5f8d9
data/.rspec CHANGED
@@ -2,3 +2,4 @@
2
2
  --color
3
3
  --warnings
4
4
  --require spec_helper
5
+ --backtrace
data/Gemfile CHANGED
@@ -17,7 +17,11 @@ group :development do
17
17
  end
18
18
 
19
19
  group :test do
20
- gem "simplecov"
20
+ # Workaround for cc-test-reporter with SimpleCov 0.18.
21
+ # Stop upgrading SimpleCov until the following issue will be resolved.
22
+ # https://github.com/codeclimate/test-reporter/issues/418
23
+ gem "factory_bot"
24
+ gem "simplecov", "~> 0.10", "< 0.18"
21
25
  gem "vcr"
22
26
  gem "webmock"
23
27
  end
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # bundler-alive
2
2
 
3
- ![bundler-alive](https://github.com/kyoshidajp/bundler-alive/actions/workflows/ci.yml/badge.svg)
4
3
  [![Gem Version](https://badge.fury.io/rb/bundler-alive.svg)](https://badge.fury.io/rb/bundler-alive)
4
+ ![bundler-alive](https://github.com/kyoshidajp/bundler-alive/actions/workflows/ci.yml/badge.svg)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/a79d53257bc5e93842f6/maintainability)](https://codeclimate.com/github/kyoshidajp/bundler-alive/maintainability)
6
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/a79d53257bc5e93842f6/test_coverage)](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
 
@@ -17,10 +19,17 @@ $ gem install bunlder-alive
17
19
 
18
20
  ```
19
21
  $ bundle-alive
22
+ 6 gems are in Gemfile.lock
23
+ ..W....
24
+ Get all source code repository URLs of gems are done!
25
+ .....
20
26
  Name: journey
21
27
  URL: http://github.com/rails/journey
22
28
  Status: false
23
29
 
30
+ Gem: bundle-alive is not found in RubyGems.org.
31
+
32
+ Total: 6 (Dead: 1, Alive: 4, Unknown: 1)
24
33
  Not alive gems are found!
25
34
  ```
26
35
 
@@ -30,7 +39,7 @@ Default `Gemfile.lock` location is in your current directory. You can specify it
30
39
  $ bundle-alive -G /path/to/Gemfile.lock
31
40
  ```
32
41
 
33
- In most cases, the following error is output.
42
+ In some cases, the following error is output.
34
43
 
35
44
  ```
36
45
  Too many requested! Retry later.
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler"
4
+
5
+ module Bundler
6
+ module Alive
7
+ #
8
+ # Announces check progress
9
+ #
10
+ class Announcer
11
+ DOT = "."
12
+
13
+ private_constant :DOT
14
+
15
+ #
16
+ # A new instance of Reporter
17
+ #
18
+ def initialize
19
+ @output = $stdout
20
+ end
21
+
22
+ def announce
23
+ output.write DOT
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :output
29
+ end
30
+ end
31
+ end
@@ -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
 
@@ -17,19 +18,17 @@ module Bundler
17
18
  desc "check [DIR]", "Checks the Gemfile.lock"
18
19
  method_option :gemfile_lock, type: :string, aliases: "-G",
19
20
  default: "Gemfile.lock"
21
+ method_option :result, type: :string, aliases: "-r",
22
+ default: "result.toml"
20
23
 
21
24
  def check(_dir = Dir.pwd)
22
- doctor = check_by_doctor
25
+ extend Reportable
26
+ report = check_by_doctor
27
+ report.save_as_file(options[:result])
28
+ print_report(report)
23
29
 
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
30
+ exit_status = report.result.all_alive? ? 0 : 1
31
+ exit exit_status
33
32
  end
34
33
 
35
34
  desc "version", "Prints the bundler-alive version"
@@ -41,15 +40,12 @@ module Bundler
41
40
 
42
41
  def check_by_doctor
43
42
  doctor = begin
44
- Doctor.new(options[:gemfile_lock])
43
+ Doctor.new(options[:gemfile_lock], options[:result])
45
44
  rescue Bundler::GemfileLockNotFound
46
45
  exit 1
47
46
  end
48
47
 
49
48
  doctor.diagnose
50
- doctor.report
51
- doctor.save_as_file
52
- doctor
53
49
  end
54
50
  end
55
51
  end
@@ -0,0 +1,108 @@
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
+ def initialize
22
+ @error_messages = []
23
+ end
24
+
25
+ #
26
+ # Gets gems from RubyGems.org
27
+ #
28
+ # @param [Array<String>] gem_names
29
+ #
30
+ # @return [Client::GemsApiResponse]
31
+ #
32
+ def gems_api_response(gem_names, &block)
33
+ urls = service_with_urls(gem_names, &block)
34
+ $stdout.puts <<~MESSAGE
35
+
36
+ Get all source code repository URLs of gems are done!
37
+ MESSAGE
38
+ Client::GemsApiResponse.new(
39
+ service_with_urls: urls,
40
+ error_messages: error_messages
41
+ )
42
+ end
43
+
44
+ private
45
+
46
+ attr_accessor :error_messages
47
+
48
+ def api_url(gem_name)
49
+ "https://rubygems.org/api/v1/gems/#{gem_name}.json"
50
+ end
51
+
52
+ def connection
53
+ Faraday.new do |connection|
54
+ connection.adapter :net_http
55
+ end
56
+ end
57
+
58
+ def service_with_urls(gem_names, &block)
59
+ urls = get_repository_urls(gem_names, &block)
60
+ urls.each_with_object({}) do |url, hash|
61
+ service_name = url.service_name
62
+ hash[service_name] = Array(hash[service_name]) << url
63
+ end
64
+ end
65
+
66
+ #
67
+ # Returns repository url
68
+ #
69
+ # @param [String] gem_name
70
+ #
71
+ # @return [SourceCodeRepositoryUrl]
72
+ #
73
+ def get_repository_url(gem_name)
74
+ url = api_url(gem_name)
75
+ response = connection.get(url)
76
+
77
+ raise NotFound, "Gem: #{gem_name} is not found in RubyGems.org." unless response.success?
78
+
79
+ body = JSON.parse(response.body)
80
+ raw_url = source_code_url(body: body, gem_name: gem_name)
81
+ SourceCodeRepositoryUrl.new(raw_url, gem_name)
82
+ end
83
+
84
+ def source_code_url(body:, gem_name:)
85
+ url = body["source_code_uri"]
86
+ return url if SourceCodeRepositoryUrl.support_url?(url)
87
+
88
+ url = body["homepage_uri"]
89
+ return url if SourceCodeRepositoryUrl.support_url?(url)
90
+
91
+ raise NotFound, "[#{gem_name}] source code repository is not found in RubyGems.org."
92
+ end
93
+
94
+ def get_repository_urls(gem_names)
95
+ result = gem_names.map do |gem_name|
96
+ yield if block_given?
97
+ get_repository_url(gem_name)
98
+ rescue StandardError => e
99
+ $stdout.write "W"
100
+ error_messages << e.message
101
+ end
102
+
103
+ result.find_all { |obj| obj.instance_of?(Alive::SourceCodeRepositoryUrl) }
104
+ end
105
+ end
106
+ end
107
+ end
108
+ 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,36 @@ 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
+ # Interval second when retrying request
25
+ RETRY_INTERVAL_SEC_ON_TOO_MANY_REQUESTS = 120
26
+
27
+ #
28
+ # Max number of conditional operator at once
29
+ #
30
+ # @see https://docs.github.com/en/rest/search#limitations-on-query-length
31
+ QUERY_MAX_OPERATORS_AT_ONCE = 6
32
+
33
+ private_constant :QUERY_MAX_OPERATORS_AT_ONCE
34
+
35
+ def self.extended(base)
36
+ base.instance_eval do
37
+ @rate_limit_exceeded = false
38
+ @retries_on_too_many_requests = 0
39
+ end
40
+ end
41
+
15
42
  #
16
43
  # Creates a GitHub client
17
44
  #
@@ -22,39 +49,112 @@ module Bundler
22
49
  end
23
50
 
24
51
  #
25
- # Returns repository URL is archived?
52
+ # Query repository statuses
53
+ #
54
+ # @param [Array<RepositoryUrl>] :urls
55
+ #
56
+ # @return [StatusResult]
57
+ #
58
+ # rubocop:disable Metrics/MethodLength
59
+ def query(urls:)
60
+ collection = StatusCollection.new
61
+ name_with_archived = get_name_with_statuses(urls)
62
+ urls.each do |url|
63
+ yield if block_given?
64
+
65
+ gem_name = url.gem_name
66
+ alive = !name_with_archived[gem_name]
67
+ status = Status.new(name: gem_name, repository_url: url, alive: alive, checked_at: Time.now)
68
+ collection = collection.add(gem_name, status)
69
+ end
70
+
71
+ StatusResult.new(collection: collection, error_messages: @error_messages,
72
+ rate_limit_exceeded: @rate_limit_exceeded)
73
+ end
74
+ # rubocop:enable Metrics/MethodLength
75
+
76
+ private
77
+
78
+ #
79
+ # Search status of repositories
26
80
  #
27
- # @param [SourceCodeRepositoryUrl] repository_url
81
+ # @param [Array<RepositoryUrl>] urls
28
82
  #
29
- # @raise [ArgumentError]
30
- # when repository_uri is not `SourceCodeRepositoryUrl`
83
+ # @return [Hash<String, Boolean>]
84
+ # gem name with archived or not
85
+ #
86
+ # rubocop:disable Metrics/MethodLength
87
+ def get_name_with_statuses(urls)
88
+ raise ArgumentError unless urls.instance_of?(Array)
89
+
90
+ name_with_status = {}
91
+ urls.each_slice(QUERY_MAX_OPERATORS_AT_ONCE) do |sliced_urls|
92
+ q = search_query(sliced_urls)
93
+ repositories = search_repositories_with_retry(q)
94
+ next if repositories.nil?
95
+
96
+ repositories.each do |repository|
97
+ name = repository["name"]
98
+ name_with_status[name] = repository["archived"]
99
+ end
100
+ end
101
+ name_with_status
102
+ end
103
+ # rubocop:enable Metrics/MethodLength
104
+
105
+ #
106
+ # Search query of repositories
107
+ #
108
+ # @param [Array<RepositoryUrl>] urls
109
+ #
110
+ # @return [String]
111
+ #
112
+ def search_query(urls)
113
+ urls.map do |url|
114
+ "repo:#{slug(url.url)}"
115
+ end.join(QUERY_CONDITION_SEPARATOR)
116
+ end
117
+
118
+ #
119
+ # Search repositories
120
+ #
121
+ # @param [String] query
31
122
  #
32
123
  # @raise [Octokit::TooManyRequests]
33
124
  # when too many requested to GitHub.com
34
- #
35
125
  # @raise [SourceCodeClient::SearchRepositoryError]
36
126
  # when Error without `Octokit::TooManyRequests`
37
127
  #
38
- # @return [Boolean]
128
+ # @return [Array<Sawyer::Resource>|nil]
39
129
  #
40
- # rubocop:disable Metrics/MethodLength
41
- def archived?(repository_url)
42
- unless repository_url.instance_of?(SourceCodeRepositoryUrl)
43
- raise ArgumentError, "UnSupported url: #{repository_url}"
130
+ def search_repositories(query)
131
+ result = @client.search_repositories(query)
132
+ result[:items]
133
+ rescue Octokit::TooManyRequests => e
134
+ raise e
135
+ rescue StandardError => e
136
+ @error_messages << e.message
137
+ []
138
+ end
139
+
140
+ def search_repositories_with_retry(query)
141
+ search_repositories(query)
142
+ rescue Octokit::TooManyRequests
143
+ if @retries_on_too_many_requests < RETRIES_ON_TOO_MANY_REQUESTS
144
+ @retries_on_too_many_requests += 1
145
+ sleep_with_message
146
+ retry
44
147
  end
45
148
 
46
- query = "repo:#{slug(repository_url.url)}"
149
+ @rate_limit_exceeded = true
150
+ []
151
+ end
47
152
 
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
153
+ def sleep_with_message
154
+ puts "Too many requested. Sleep #{RETRY_INTERVAL_SEC_ON_TOO_MANY_REQUESTS} sec."
155
+ sleep RETRY_INTERVAL_SEC_ON_TOO_MANY_REQUESTS
156
+ puts "Retry request (#{@retries_on_too_many_requests}/#{RETRIES_ON_TOO_MANY_REQUESTS})"
56
157
  end
57
- # rubocop:enable Metrics/MethodLength
58
158
 
59
159
  #
60
160
  # Returns slug of repository URL
@@ -7,9 +7,11 @@ module Bundler
7
7
  # Represents a source code client
8
8
  #
9
9
  class SourceCodeClient
10
+ # Error of searching repository
10
11
  class SearchRepositoryError < StandardError
11
12
  end
12
13
 
14
+ # Error of rate limit exceeded
13
15
  class RateLimitExceededError < StandardError
14
16
  end
15
17
 
@@ -31,10 +33,11 @@ module Bundler
31
33
  def initialize(service_name:)
32
34
  raise ArgumentError, "Unknown service: #{service_name}" unless SERVICE_WITH_STRATEGIES.key?(service_name)
33
35
 
34
- service = SERVICE_WITH_STRATEGIES[service_name]
35
- extend service
36
+ strategy = SERVICE_WITH_STRATEGIES[service_name]
37
+ extend strategy
36
38
 
37
39
  @client = create_client
40
+ @error_messages = []
38
41
 
39
42
  super()
40
43
  end
@@ -10,113 +10,118 @@ module Bundler
10
10
  # Diagnoses a `Gemfile.lock` with a TOML file
11
11
  #
12
12
  class Doctor
13
- attr_reader :all_alive, :rate_limit_exceeded_error
14
-
15
13
  #
16
14
  # A new instance of Doctor
17
15
  #
18
16
  # @param [String] lock_file lock file of gem
19
17
  # @param [String] result_file file of result
20
18
  #
21
- def initialize(lock_file, result_file = "result.toml")
19
+ def initialize(lock_file, result_file)
22
20
  @lock_file = lock_file
23
21
  @result_file = result_file
24
- @gem_client = Client::GemsApi.new
22
+ @gem_client = Client::GemsApiClient.new
25
23
  @result = nil
26
- @all_alive = nil
27
- @rate_limit_exceeded_error = false
24
+ @rate_limit_exceeded = false
25
+ @announcer = Announcer.new
26
+ @error_messages = []
28
27
  end
29
28
 
30
29
  #
31
30
  # Diagnoses gems in lock file of gem
32
31
  #
32
+ # @raise [Client::SourceCodeClient::RateLimitExceededError]
33
+ # When exceeded access rate limit
34
+ #
35
+ # @raise [StandardError]
36
+ # When raised unexpected error
37
+ #
38
+ # @return [Report]
39
+ #
33
40
  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
41
+ $stdout.puts "#{collection_from_gemfile.total_size} gems are in Gemfile.lock"
42
+ result = _diagnose
43
+ Report.new(result)
40
44
  end
41
45
 
46
+ private
47
+
48
+ attr_reader :lock_file, :result_file, :gem_client, :announcer,
49
+ :result, :error_messages, :rate_limit_exceeded
50
+
42
51
  #
43
- # Reports the result
52
+ # @return [Array<String>]
44
53
  #
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
54
+ def no_need_to_get_gems
55
+ return [] unless File.exist?(result_file)
56
+
57
+ toml_hash = TomlRB.load_file(result_file)
58
+ toml_hash.each_with_object([]) do |(gem_name, v), array|
59
+ alive = v["alive"]
60
+ array << gem_name unless alive
50
61
  end
51
62
  end
52
63
 
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)
64
+ def diagnose_by_service(service, urls)
65
+ client = Client::SourceCodeClient.new(service_name: service)
66
+ client.query(urls: urls) do
67
+ announcer.announce
68
+ end
59
69
  end
60
70
 
61
- private
71
+ def result_by_search(collection)
72
+ gems_api_response = gem_client.gems_api_response(collection.names) do
73
+ announcer.announce
74
+ end
62
75
 
63
- attr_reader :lock_file, :result_file, :gem_client, :result
76
+ service_with_urls = gems_api_response.service_with_urls
77
+ error_messages.concat(gems_api_response.error_messages)
64
78
 
65
- def diagnosed_gem?(gem_status)
66
- !gem_status.nil? && !gem_status.unknown?
79
+ result = StatusResult.new
80
+ service_with_urls.each do |service, urls|
81
+ result = result.merge(diagnose_by_service(service, urls))
82
+ end
83
+ result
67
84
  end
68
85
 
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)
86
+ def fetch_target_collection(base_collection, gem_names)
87
+ collection = StatusCollection.new
88
+ base_collection.each do |name, status|
89
+ next if gem_names.include?(name)
73
90
 
74
- toml_hash = TomlRB.load_file(result_file)
75
- @collection_from_file = collection_from_hash(toml_hash)
91
+ collection = collection.add(name, status)
92
+ end
93
+ collection
76
94
  end
77
95
 
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)
96
+ def collection_from_gemfile
97
+ gems_from_lockfile.each_with_object(StatusCollection.new) do |gem, collection|
98
+ gem_name = gem.name
99
+ status = Status.new(name: gem_name,
100
+ repository_url: nil,
101
+ alive: nil,
102
+ checked_at: nil)
103
+ collection.add(gem_name, status)
88
104
  end
89
105
  end
90
106
 
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
107
+ def _diagnose
108
+ collection = fetch_target_collection(collection_from_gemfile, no_need_to_get_gems)
109
+ result = result_by_search(collection)
110
+ collection_from_toml_file = StatusCollection.new_from_toml_file(result_file)
111
+ new_collection = collection_from_gemfile.merge(collection_from_toml_file)
112
+ .merge(result.collection)
113
+
114
+ messages = error_messages.concat(result.error_messages)
115
+ StatusResult.new(collection: new_collection,
116
+ error_messages: messages,
117
+ rate_limit_exceeded: result.rate_limit_exceeded)
95
118
  end
96
119
 
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)
120
+ def gems_from_lockfile
121
+ lock_file_body = File.read(@lock_file)
122
+ lock_file = Bundler::LockfileParser.new(lock_file_body)
123
+ lock_file.specs
118
124
  end
119
- # rubocop:enable Metrics/MethodLength
120
125
  end
121
126
  end
122
127
  end