bundler-alive 0.1.4 → 0.1.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29049e60565e16c7e828304b8bd98f6a3b16b7f0afdcc5d9043349896f31acf9
4
- data.tar.gz: 969ce8eb67b823d8dad051addc685d86db571c4ac8fdbba926f54505e8f6b61a
3
+ metadata.gz: 4fb0738fac56612f49d0fcc2dadf41f00caa5c34b8e49923185326f3cc6ab744
4
+ data.tar.gz: e89352dbbdec31b5e82e4f42135113d1c464441bcc0d4443709e24437fffb87f
5
5
  SHA512:
6
- metadata.gz: 3fad0ab06a408d151591063307eb460f09995086c5f63d7b4f5f5fbcc94aef889b5495940837b1f46b0719c235a3fa4e1de5a994aac38f489ff27a033c0d0f4e
7
- data.tar.gz: d3442abf4e2e0f454a0a9fa6980e299c04963f09b76df7f17912fddc12a50ed06ad53a8962ba45b2e3c00f8165fc53fccbde5181a4c6fb4cee5f7b0719cfdcac
6
+ metadata.gz: 6692e5114833643c5038b41a8bbff0282837b4e70c05449f76cc810e499751a3f7f8585f5360f31b503376662e6a91f6617a335e59686a9841af65a2917b0d2f
7
+ data.tar.gz: 393f54894aabd833bd0249a54cfab165bbcacb6a9d10cae9ab446b6e81a92c9de83f5e0fc25d38327999b7c2e06fa902a730bbec5eb04b37a476af0067b882c4
@@ -0,0 +1,70 @@
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
31
+ declarative_policy:
32
+ url: https://gitlab.com/gitlab-org/ruby/gems/declarative-policy
33
+ spamcheck:
34
+ url: https://gitlab.com/gitlab-com/gl-security/engineering-and-research/automation-team/spam/spamcheck
35
+ tanuki_emoji:
36
+ url: https://gitlab.com/gitlab-org/ruby/gems/tanuki_emoji
37
+ cbor:
38
+ url: https://github.com/cabo/cbor-ruby
39
+ expression_parser:
40
+ url: https://github.com/nricciar/expression_parser
41
+ extended-markdown-filter:
42
+ url: https://github.com/gjtorikian/extended-markdown-filter
43
+ gettext:
44
+ url: https://github.com/ruby-gettext/gettext
45
+ png_quantizator:
46
+ url: https://github.com/rogercampos/png_quantizator
47
+ redis-actionpack:
48
+ url: https://github.com/redis-store/redis-actionpack
49
+ redis-rack:
50
+ url: https://github.com/redis-store/redis-rack
51
+ sixarm_ruby_unaccent:
52
+ url: https://github.com/SixArm/sixarm_ruby_unaccent
53
+ thrift:
54
+ url: https://github.com/szechyjs/thrift-gem
55
+ vmstat:
56
+ url: https://github.com/threez/ruby-vmstat
57
+ mongoid-rspec:
58
+ url: https://github.com/mongoid/mongoid-rspec
59
+ rack-test:
60
+ url: https://github.com/rack/rack-test
61
+ jmespath:
62
+ url: https://github.com/jmespath/jmespath.rb
63
+ slack-notifier:
64
+ url: https://github.com/slack-notifier/slack-notifier
65
+ ipaddress:
66
+ url: https://github.com/ipaddress-gem/ipaddress
67
+ fog-powerdns:
68
+ url: https://github.com/fog/fog-powerdns
69
+ html_tokenizer:
70
+ url: https://github.com/EiNSTeiN-/html_tokenizer
data/.rspec CHANGED
@@ -1,5 +1,4 @@
1
1
  --format documentation
2
2
  --color
3
- --warnings
4
3
  --require spec_helper
5
4
  --backtrace
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown --title "bundler-alive Documentation" --protected
data/README.md CHANGED
@@ -5,14 +5,14 @@
5
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/a79d53257bc5e93842f6/maintainability)](https://codeclimate.com/github/kyoshidajp/bundler-alive/maintainability)
6
6
  [![Test Coverage](https://api.codeclimate.com/v1/badges/a79d53257bc5e93842f6/test_coverage)](https://codeclimate.com/github/kyoshidajp/bundler-alive/test_coverage)
7
7
 
8
- `bunder-alive` checks if gems in a RubyGem's `Gemfile.lock` are active.
8
+ `bundler-alive` checks if gems in a RubyGem's `Gemfile.lock` are active.
9
9
 
10
- Currently only github.com is supported as a source code repository. If the source code repository is archived, then reports as not alive.
10
+ Currently, GitHub.com and GitLab.com are supported as a source code repository. If the source code repository is archived, then reports as not alive.
11
11
 
12
12
  ## Installation
13
13
 
14
14
  ```
15
- $ gem install bunlder-alive
15
+ $ gem install bundler-alive
16
16
  ```
17
17
 
18
18
  ## Usage
@@ -23,13 +23,15 @@ $ bundle-alive
23
23
  ..W....
24
24
  Get all source code repository URLs of gems are done!
25
25
  .....
26
- Name: journey
27
- URL: http://github.com/rails/journey
28
- Status: false
29
26
 
30
- Gem: bundle-alive is not found in RubyGems.org.
27
+ Errors:
28
+ [bundle-alive] Not found in RubyGems.org.
31
29
 
32
- Total: 6 (Dead: 1, Alive: 4, Unknown: 1)
30
+ Archived gems:
31
+ Name: journey
32
+ URL: http://github.com/rails/journey
33
+
34
+ Total: 6 (Archived: 1, Alive: 4, Unknown: 1)
33
35
  Not alive gems are found!
34
36
  ```
35
37
 
@@ -39,15 +41,51 @@ Default `Gemfile.lock` location is in your current directory. You can specify it
39
41
  $ bundle-alive -G /path/to/Gemfile.lock
40
42
  ```
41
43
 
42
- In some cases, the following error is output.
44
+ ## Access Token
45
+
46
+ You MUST set environment variables to access source code repository services.
47
+
48
+ | Repository service | ENV variable |
49
+ | ------- |---- |
50
+ | GitHub | [`BUNDLER_ALIVE_GITHUB_TOKEN`](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) |
51
+ | GitLab | [`BUNDLER_ALIVE_GITLAB_TOKEN`](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) |
52
+
53
+ ## Ignore gems
54
+
55
+ You can ignore certain gems.
56
+
57
+ ```
58
+ $ bundle-alive -i journey rubocop-junit-formatter
59
+ ```
60
+
61
+ ## Following redirect on RubyGems.org
62
+
63
+ The URL for some gems in RubyGems.org may have changed. You can follow the URL (default: doesn't follow).
43
64
 
44
65
  ```
45
- Too many requested! Retry later.
66
+ $ bundle-alive --follow_redirect
46
67
  ```
47
68
 
48
- 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.
69
+ ## Specifying repository URL
70
+
71
+ 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.
72
+
73
+ Put `.bundler-alive.yml` in your current directory. The following code is the sample.
74
+
75
+ ```yaml
76
+ ---
77
+ gems:
78
+ coffee-script-source:
79
+ url: https://github.com/jashkenas/coffeescript/
80
+ ```
81
+
82
+ You can also specify the file path.
83
+
84
+ ```
85
+ $ bundle-alive -c /path/to/.bundler-alive.yml
86
+ ```
49
87
 
50
- If you run again, it will resume.
88
+ [.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.
51
89
 
52
90
  ## Contributing
53
91
 
data/gemspec.yml CHANGED
@@ -8,7 +8,7 @@ homepage: https://github.com/kyoshidajp/bundler-alive
8
8
  metadata:
9
9
  homepage_uri: https://github.com/kyoshidajp/bundler-alive
10
10
  source_code_uri: https://github.com/kyoshidajp/bundler-alive
11
- changelog_uri: https://github.com/kyoshidajp/bundler-alive
11
+ changelog_uri: https://github.com/kyoshidajp/bundler-alive/commits/main
12
12
 
13
13
  required_ruby_version: ">= 2.6.0"
14
14
  bindir: bin
@@ -16,10 +16,10 @@ require_paths: lib
16
16
 
17
17
  dependencies:
18
18
  faraday:
19
- octokit:
19
+ graphql-client:
20
20
  rake:
21
21
  thor:
22
- toml-rb:
22
+ gitlab:
23
23
 
24
24
  development_dependencies:
25
25
  rspec:
@@ -16,15 +16,15 @@ module Bundler
16
16
  map "--version" => :version
17
17
 
18
18
  desc "check [DIR]", "Checks the Gemfile.lock"
19
+ method_option :ignore, type: :array, aliases: "-i", default: []
20
+ method_option :follow_redirect, type: :boolean, aliases: "-r"
19
21
  method_option :gemfile_lock, type: :string, aliases: "-G",
20
22
  default: "Gemfile.lock"
21
- method_option :result, type: :string, aliases: "-r",
22
- default: "result.toml"
23
+ method_option :config, type: :string, aliases: "-c", default: ".bundler-alive.yml"
23
24
 
24
25
  def check(_dir = Dir.pwd)
25
26
  extend Reportable
26
27
  report = check_by_doctor
27
- report.save_as_file(options[:result])
28
28
  print_report(report)
29
29
 
30
30
  exit_status = report.result.all_alive? ? 0 : 1
@@ -39,13 +39,23 @@ module Bundler
39
39
  private
40
40
 
41
41
  def check_by_doctor
42
- doctor = begin
43
- Doctor.new(options[:gemfile_lock], options[:result])
44
- rescue Bundler::GemfileLockNotFound
42
+ doctor = initialize_doctor
43
+
44
+ begin
45
+ doctor.diagnose
46
+ rescue Bundler::Alive::Client::GitlabApi::AccessTokenNotFoundError => e
47
+ say "\n#{e.message}", :yellow
45
48
  exit 1
46
49
  end
50
+ end
47
51
 
48
- doctor.diagnose
52
+ def initialize_doctor
53
+ Doctor.new(lock_file: options[:gemfile_lock],
54
+ config_file: options[:config],
55
+ ignore_gems: options[:ignore],
56
+ follow_redirect: options[:follow_redirect])
57
+ rescue Bundler::GemfileLockNotFound
58
+ exit 1
49
59
  end
50
60
  end
51
61
  end
@@ -18,8 +18,21 @@ module Bundler
18
18
  class NotFound < StandardError
19
19
  end
20
20
 
21
- def initialize
21
+ #
22
+ # A new instance of `GemApiClient`
23
+ #
24
+ # @param [String] :config_path
25
+ # @param [Boolean] :follow_redirect
26
+ # Follow redirect URL in gems
27
+ #
28
+ # @return [GemApiClient]
29
+ #
30
+ def initialize(config_path: nil, follow_redirect: false)
22
31
  @error_messages = []
32
+ @config_gems = get_config_gems(config_path)
33
+ @follow_redirect = follow_redirect
34
+
35
+ freeze
23
36
  end
24
37
 
25
38
  #
@@ -29,8 +42,8 @@ module Bundler
29
42
  #
30
43
  # @return [Client::GemsApiResponse]
31
44
  #
32
- def gems_api_response(gem_names, &block)
33
- urls = service_with_urls(gem_names, &block)
45
+ def gems_api_response(gem_names)
46
+ urls = service_with_urls(gem_names)
34
47
  $stdout.puts <<~MESSAGE
35
48
 
36
49
  Get all source code repository URLs of gems are done!
@@ -43,7 +56,7 @@ module Bundler
43
56
 
44
57
  private
45
58
 
46
- attr_accessor :error_messages
59
+ attr_accessor :error_messages, :config_gems, :follow_redirect
47
60
 
48
61
  def api_url(gem_name)
49
62
  "https://rubygems.org/api/v1/gems/#{gem_name}.json"
@@ -55,8 +68,15 @@ module Bundler
55
68
  end
56
69
  end
57
70
 
58
- def service_with_urls(gem_names, &block)
59
- urls = get_repository_urls(gem_names, &block)
71
+ def get_config_gems(path)
72
+ return {} if path.nil? || !File.exist?(path)
73
+
74
+ config = YAML.load_file(path)
75
+ config["gems"]
76
+ end
77
+
78
+ def service_with_urls(gem_names)
79
+ urls = get_repository_urls(gem_names)
60
80
  urls.each_with_object({}) do |url, hash|
61
81
  service_name = url.service_name
62
82
  hash[service_name] = Array(hash[service_name]) << url
@@ -71,29 +91,51 @@ module Bundler
71
91
  # @return [SourceCodeRepositoryUrl]
72
92
  #
73
93
  def get_repository_url(gem_name)
94
+ url_from_config = get_repository_url_from_config(gem_name)
95
+ return url_from_config unless url_from_config.nil?
96
+
74
97
  url = api_url(gem_name)
75
98
  response = connection.get(url)
76
99
 
77
- raise NotFound, "Gem: #{gem_name} is not found in RubyGems.org." unless response.success?
100
+ raise NotFound, "[#{gem_name}] Not found in RubyGems.org." unless response.success?
78
101
 
79
102
  body = JSON.parse(response.body)
80
103
  raw_url = source_code_url(body: body, gem_name: gem_name)
81
104
  SourceCodeRepositoryUrl.new(raw_url, gem_name)
82
105
  end
83
106
 
107
+ def get_repository_url_from_config(gem_name)
108
+ return nil if config_gems.nil?
109
+ return nil unless config_gems.key?(gem_name)
110
+
111
+ gem = config_gems[gem_name]
112
+ SourceCodeRepositoryUrl.new(gem["url"], gem_name)
113
+ end
114
+
84
115
  def source_code_url(body:, gem_name:)
85
116
  url = body["source_code_uri"]
86
- return url if SourceCodeRepositoryUrl.support_url?(url)
117
+ return url_via_redirect(url) if SourceCodeRepositoryUrl.support_url?(url)
87
118
 
88
119
  url = body["homepage_uri"]
89
- return url if SourceCodeRepositoryUrl.support_url?(url)
120
+ return url_via_redirect(url) if SourceCodeRepositoryUrl.support_url?(url)
121
+
122
+ message = "[#{gem_name}] Source code repository is not found in RubyGems.org,"\
123
+ " or not supported. URL: https://rubygems.org/gems/#{gem_name}"
124
+ raise NotFound, message
125
+ end
126
+
127
+ def url_via_redirect(url)
128
+ return url unless follow_redirect
129
+
130
+ response = connection.head(url)
131
+ return response.headers["location"] if response.status == 301
90
132
 
91
- raise NotFound, "[#{gem_name}] source code repository is not found in RubyGems.org."
133
+ url
92
134
  end
93
135
 
94
136
  def get_repository_urls(gem_names)
95
137
  result = gem_names.map do |gem_name|
96
- yield if block_given?
138
+ $stdout.write "."
97
139
  get_repository_url(gem_name)
98
140
  rescue StandardError => e
99
141
  $stdout.write "W"
@@ -1,51 +1,45 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "octokit"
4
3
  require "json"
5
4
 
6
5
  module Bundler
7
6
  module Alive
8
7
  module Client
9
8
  #
10
- # API Client for GitHub API
9
+ # API Client for GitHub GraphQL API
11
10
  #
12
- # @see https://docs.github.com/en/rest/search#search-repositories
11
+ # @see https://docs.github.com/en/graphql
13
12
  #
14
- module GitHubApi
13
+ module GithubApi
15
14
  # Environment variable name of GitHub Access Token
16
15
  ACCESS_TOKEN_ENV_NAME = "BUNDLER_ALIVE_GITHUB_TOKEN"
17
16
 
18
17
  # Separator of query condition
19
18
  QUERY_CONDITION_SEPARATOR = " "
19
+ private_constant :QUERY_CONDITION_SEPARATOR
20
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
21
  # 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
-
22
+ QUERY_MAX_OPERATORS_AT_ONCE = 50
33
23
  private_constant :QUERY_MAX_OPERATORS_AT_ONCE
34
24
 
35
25
  def self.extended(base)
36
26
  base.instance_eval do
37
27
  @rate_limit_exceeded = false
38
28
  @retries_on_too_many_requests = 0
29
+ @name_with_archived = {}
39
30
  end
40
31
  end
41
32
 
42
33
  #
43
- # Creates a GitHub client
34
+ # Creates a GraphQL client
44
35
  #
45
- # @return [Octokit::Client]
36
+ # @return [GraphQL::Client]
46
37
  #
47
38
  def create_client
48
- Octokit::Client.new(access_token: ENV.fetch(ACCESS_TOKEN_ENV_NAME, nil))
39
+ require_relative "github_graphql"
40
+ extend GithubGraphql
41
+
42
+ GithubGraphql::CLIENT
49
43
  end
50
44
 
51
45
  #
@@ -55,15 +49,12 @@ module Bundler
55
49
  #
56
50
  # @return [StatusResult]
57
51
  #
58
- # rubocop:disable Metrics/MethodLength
59
52
  def query(urls:)
60
53
  collection = StatusCollection.new
61
- name_with_archived = get_name_with_statuses(urls)
54
+ @name_with_archived = get_name_with_statuses(urls)
62
55
  urls.each do |url|
63
- yield if block_given?
64
-
65
56
  gem_name = url.gem_name
66
- alive = !name_with_archived[gem_name]
57
+ alive = alive?(gem_name)
67
58
  status = Status.new(name: gem_name, repository_url: url, alive: alive, checked_at: Time.now)
68
59
  collection = collection.add(gem_name, status)
69
60
  end
@@ -71,10 +62,16 @@ module Bundler
71
62
  StatusResult.new(collection: collection, error_messages: @error_messages,
72
63
  rate_limit_exceeded: @rate_limit_exceeded)
73
64
  end
74
- # rubocop:enable Metrics/MethodLength
75
65
 
76
66
  private
77
67
 
68
+ def alive?(gem_name)
69
+ return false unless @name_with_archived.key?(gem_name)
70
+
71
+ value = @name_with_archived[gem_name]
72
+ value == Status::ALIVE_UNKNOWN ? Status::ALIVE_UNKNOWN : !value
73
+ end
74
+
78
75
  #
79
76
  # Search status of repositories
80
77
  #
@@ -85,34 +82,53 @@ module Bundler
85
82
  #
86
83
  # rubocop:disable Metrics/MethodLength
87
84
  def get_name_with_statuses(urls)
88
- raise ArgumentError unless urls.instance_of?(Array)
89
-
90
85
  name_with_status = {}
91
86
  urls.each_slice(QUERY_MAX_OPERATORS_AT_ONCE) do |sliced_urls|
87
+ $stdout.print "." * sliced_urls.size
88
+
92
89
  q = search_query(sliced_urls)
93
90
  repositories = search_repositories_with_retry(q)
94
91
  next if repositories.nil?
95
92
 
96
- repositories.each do |repository|
97
- name = repository["name"]
98
- name_with_status[name] = repository["archived"]
93
+ sliced_urls.each do |url|
94
+ repository = find_repository_from_repositories(url: url,
95
+ repositories: repositories)
96
+ alive_status = if repository.nil?
97
+ Status::ALIVE_UNKNOWN
98
+ else
99
+ repository["isArchived"]
100
+ end
101
+ name_with_status[url.gem_name] = alive_status
99
102
  end
100
103
  end
101
104
  name_with_status
102
105
  end
103
106
  # rubocop:enable Metrics/MethodLength
104
107
 
108
+ # @param [SourceCodeRepositoryUrl] :url
109
+ # @param [Array<Sawyer::Resource>] :repositories
110
+ #
111
+ # @return [Sawyer::Resource|nil]
112
+ def find_repository_from_repositories(url:, repositories:)
113
+ repositories.find do |repository|
114
+ # e.g.) tod's URL is https://github.com/JackC/tod
115
+ # but, the `nameWithOwner` is `jacks/tod`
116
+ slug(url.url).downcase == repository["nameWithOwner"].downcase
117
+ end
118
+ end
119
+
105
120
  #
106
- # Search query of repositories
121
+ # Search query of repositories (includes forked)
107
122
  #
108
123
  # @param [Array<RepositoryUrl>] urls
109
124
  #
110
125
  # @return [String]
111
126
  #
112
127
  def search_query(urls)
113
- urls.map do |url|
128
+ repository_query = urls.map do |url|
114
129
  "repo:#{slug(url.url)}"
115
130
  end.join(QUERY_CONDITION_SEPARATOR)
131
+ "#{repository_query} fork:true"
116
132
  end
117
133
 
118
134
  #
@@ -120,18 +136,12 @@ module Bundler
120
136
  #
121
137
  # @param [String] query
122
138
  #
123
- # @raise [Octokit::TooManyRequests]
124
- # when too many requested to GitHub.com
125
- # @raise [SourceCodeClient::SearchRepositoryError]
126
- # when Error without `Octokit::TooManyRequests`
127
- #
128
139
  # @return [Array<Sawyer::Resource>|nil]
129
140
  #
130
141
  def search_repositories(query)
131
- result = @client.search_repositories(query)
132
- result[:items]
133
- rescue Octokit::TooManyRequests => e
134
- raise e
142
+ result = @client.query(GithubGraphql::Query,
143
+ variables: { var_query: query, var_first: QUERY_MAX_OPERATORS_AT_ONCE })
144
+ result.data.search.nodes.map(&:to_h)
135
145
  rescue StandardError => e
136
146
  @error_messages << e.message
137
147
  []
@@ -139,21 +149,6 @@ module Bundler
139
149
 
140
150
  def search_repositories_with_retry(query)
141
151
  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
147
- end
148
-
149
- @rate_limit_exceeded = true
150
- []
151
- end
152
-
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})"
157
152
  end
158
153
 
159
154
  #
@@ -164,7 +159,13 @@ module Bundler
164
159
  # @return [String]
165
160
  #
166
161
  def slug(repository_url)
167
- Octokit::Repository.from_url(repository_url).slug
162
+ # from https://github.com/octokit/octokit.rb/blob/v4.22.0/lib/octokit/repository.rb#L12-L17
163
+ github_slug = URI.parse(repository_url).path[1..]
164
+ .gsub(%r{^repos/}, "")
165
+ .split("/", 3)[0..1]
166
+ .join("/")
167
+
168
+ github_slug.gsub(/\.git/, "")
168
169
  end
169
170
  end
170
171
  end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphql/client"
4
+ require "graphql/client/http"
5
+
6
+ module Bundler
7
+ module Alive
8
+ module Client
9
+ # GitHub GraphQL Module
10
+ module GithubGraphql
11
+ #
12
+ # Access token isn't set error
13
+ #
14
+ # @see https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token
15
+ #
16
+ class AccessTokenNotFoundError < StandardError
17
+ def initialize(_message = nil)
18
+ message = "Environment variable #{ACCESS_TOKEN_ENV_NAME} is not set."\
19
+ " Need to set GitHub Personal Access Token to be authenticated at GitHub GraphQL API."\
20
+ " See: https://docs.github.com/en/graphql/guides/forming-calls-with-graphql#the-graphql-endpoint"
21
+ super(message)
22
+ end
23
+ end
24
+
25
+ # Environment variable name of GitHub Access Token
26
+ ACCESS_TOKEN_ENV_NAME = "BUNDLER_ALIVE_GITHUB_TOKEN"
27
+
28
+ # GraphQL API endpoint
29
+ # @see https://docs.github.com/en/graphql/guides/forming-calls-with-graphql#the-graphql-endpoint
30
+ ENDPOINT = "https://api.github.com/graphql"
31
+ private_constant :ENDPOINT
32
+
33
+ QUERY = <<~QUERY
34
+ query($var_query: String!, $var_first: Int!) {
35
+ search(
36
+ query: $var_query
37
+ type: REPOSITORY
38
+ first: $var_first
39
+ ) {
40
+ repositoryCount
41
+ nodes {
42
+ ... on Repository {
43
+ isArchived
44
+ nameWithOwner
45
+ isMirror
46
+ }
47
+ }
48
+ }
49
+ }
50
+ QUERY
51
+ private_constant :QUERY
52
+
53
+ HTTP = GraphQL::Client::HTTP.new(ENDPOINT) do
54
+ def headers(_context)
55
+ token = ENV.fetch(ACCESS_TOKEN_ENV_NAME, nil)
56
+ raise AccessTokenNotFoundError if token.nil?
57
+
58
+ {
59
+ "Authorization" => "Bearer #{token}"
60
+ }
61
+ end
62
+ end
63
+ private_constant :HTTP
64
+
65
+ if File.exist?(SCHEMA_PATH)
66
+ SCHEMA = GraphQL::Client.load_schema(SCHEMA_PATH)
67
+ else
68
+ SCHEMA = GraphQL::Client.load_schema(HTTP)
69
+ Dir.mkdir(USER_PATH) unless Dir.exist?(USER_PATH)
70
+ GraphQL::Client.dump_schema(SCHEMA, SCHEMA_PATH)
71
+ end
72
+ private_constant :SCHEMA
73
+
74
+ # GraphQL client
75
+ CLIENT = GraphQL::Client.new(schema: SCHEMA, execute: HTTP)
76
+
77
+ # query
78
+ Query = CLIENT.parse(QUERY)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "gitlab"
4
+ require "json"
5
+
6
+ module Bundler
7
+ module Alive
8
+ module Client
9
+ #
10
+ # API Client for GitLab API
11
+ #
12
+ # @see https://docs.gitlab.com/ee/api/projects.html#get-single-project
13
+ #
14
+ module GitlabApi
15
+ #
16
+ # Access token isn't set error
17
+ #
18
+ # @see https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html
19
+ #
20
+ class AccessTokenNotFoundError < StandardError
21
+ def initialize(_message = nil)
22
+ message = "Environment variable #{ACCESS_TOKEN_ENV_NAME} is not set."\
23
+ " Need to set GitLab Personal Access Token to be authenticated at gitlab.com API."\
24
+ " See: https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html"
25
+ super(message)
26
+ end
27
+ end
28
+
29
+ # Environment variable name of GitLab Access Token
30
+ #
31
+ # @see https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html
32
+ #
33
+ ACCESS_TOKEN_ENV_NAME = "BUNDLER_ALIVE_GITLAB_TOKEN"
34
+
35
+ # Endpoint of GitLab API
36
+ ENDPOINT = "https://gitlab.com/api/v4"
37
+
38
+ def self.extended(base)
39
+ base.instance_eval do
40
+ @rate_limit_exceeded = false
41
+ @retries_on_too_many_requests = 0
42
+ end
43
+ end
44
+
45
+ #
46
+ # Creates a GitLab client
47
+ #
48
+ # @return [Gitlab::Client]
49
+ #
50
+ def create_client
51
+ access_token = ENV.fetch(ACCESS_TOKEN_ENV_NAME, nil)
52
+ raise AccessTokenNotFoundError if access_token.nil?
53
+
54
+ Gitlab.client(
55
+ endpoint: ENDPOINT,
56
+ private_token: access_token
57
+ )
58
+ end
59
+
60
+ #
61
+ # Query repository statuses
62
+ #
63
+ # @param [Array<RepositoryUrl>] :urls
64
+ #
65
+ # @return [StatusResult]
66
+ #
67
+ def query(urls:)
68
+ collection = StatusCollection.new
69
+ name_with_archived = get_name_with_statuses(urls)
70
+ urls.each do |url|
71
+ gem_name = url.gem_name
72
+ alive = name_with_archived.key?(gem_name) && !name_with_archived[gem_name]
73
+ status = Status.new(name: gem_name, repository_url: url, alive: alive, checked_at: Time.now)
74
+ collection = collection.add(gem_name, status)
75
+ end
76
+
77
+ StatusResult.new(collection: collection, error_messages: @error_messages,
78
+ rate_limit_exceeded: @rate_limit_exceeded)
79
+ end
80
+
81
+ private
82
+
83
+ #
84
+ # Search status of repositories
85
+ #
86
+ # @param [Array<RepositoryUrl>] urls
87
+ #
88
+ # @return [Hash<String, Boolean>]
89
+ # gem name with archived or not
90
+ #
91
+ def get_name_with_statuses(urls)
92
+ name_with_status = {}
93
+ urls.each do |url|
94
+ $stdout.write "."
95
+ project = search_repositories_with_retry(url)
96
+ next if project.nil? || project.empty?
97
+
98
+ name = url.gem_name
99
+ name_with_status[name] = project["archived"]
100
+ end
101
+ name_with_status
102
+ end
103
+
104
+ def project_path_from_url(url)
105
+ uri = URI.parse(url.url)
106
+ uri.path.split("/")[1..].join("/")
107
+ end
108
+
109
+ def search_repositories_with_retry(url)
110
+ project_path = project_path_from_url(url)
111
+
112
+ # Must be converted to hash due to warnings
113
+ @client.project(project_path).to_h
114
+ rescue StandardError => e
115
+ @error_messages << e.message
116
+ []
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -16,7 +16,8 @@ module Bundler
16
16
  end
17
17
 
18
18
  SERVICE_WITH_STRATEGIES = {
19
- SourceCodeRepository::Service::GITHUB => GitHubApi
19
+ SourceCodeRepository::Service::GITHUB => GithubApi,
20
+ SourceCodeRepository::Service::GITLAB => GitlabApi
20
21
  }.freeze
21
22
 
22
23
  private_constant :SERVICE_WITH_STRATEGIES
@@ -1,33 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler"
4
- require "octokit"
5
- require "toml-rb"
6
4
 
7
5
  module Bundler
8
6
  module Alive
9
7
  #
10
- # Diagnoses a `Gemfile.lock` with a TOML file
8
+ # Diagnoses a `Gemfile.lock`
11
9
  #
12
10
  class Doctor
13
11
  #
14
12
  # A new instance of Doctor
15
13
  #
16
- # @param [String] lock_file lock file of gem
17
- # @param [String] result_file file of result
14
+ # @param [String] :lock_file # lock file of gem
15
+ # @param [String] :config_file # config file
16
+ # @param [Array<String>] :ignore_gems ignore gems
17
+ # @param [Boolean] :follow_redirect Follow redirect URL in gems
18
18
  #
19
- def initialize(lock_file, result_file)
19
+ def initialize(lock_file:, config_file:, ignore_gems:, follow_redirect: false)
20
20
  @lock_file = lock_file
21
- @result_file = result_file
22
- @gem_client = Client::GemsApiClient.new
21
+ @gem_client = Client::GemsApiClient.new(config_path: config_file, follow_redirect: follow_redirect)
22
+ @ignore_gems = ignore_gems
23
23
  @result = nil
24
24
  @rate_limit_exceeded = false
25
- @announcer = Announcer.new
26
25
  @error_messages = []
27
26
  end
28
27
 
29
28
  #
30
- # Diagnoses gems in lock file of gem
29
+ # Diagnoses gems in Gemfile.lock
31
30
  #
32
31
  # @raise [Client::SourceCodeClient::RateLimitExceededError]
33
32
  # When exceeded access rate limit
@@ -38,41 +37,27 @@ module Bundler
38
37
  # @return [Report]
39
38
  #
40
39
  def diagnose
41
- $stdout.puts "#{collection_from_gemfile.total_size} gems are in Gemfile.lock"
40
+ message = "#{collection_from_gemfile.total_size + ignore_gems.size} gems are in Gemfile.lock"
41
+ message = "#{message} (#{ignore_gems.size} gems are ignored)" if ignore_gems.size.positive?
42
+ $stdout.puts message
43
+
42
44
  result = _diagnose
43
45
  Report.new(result)
44
46
  end
45
47
 
46
48
  private
47
49
 
48
- attr_reader :lock_file, :result_file, :gem_client, :announcer,
50
+ attr_reader :lock_file, :gem_client, :ignore_gems,
49
51
  :result, :error_messages, :rate_limit_exceeded
50
52
 
51
- #
52
- # @return [Array<String>]
53
- #
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
61
- end
62
- end
63
-
64
53
  def diagnose_by_service(service, urls)
65
54
  client = Client::SourceCodeClient.new(service_name: service)
66
- client.query(urls: urls) do
67
- announcer.announce
68
- end
55
+ client.query(urls: urls)
69
56
  end
70
57
 
58
+ # @return [StatusResult]
71
59
  def result_by_search(collection)
72
- gems_api_response = gem_client.gems_api_response(collection.names) do
73
- announcer.announce
74
- end
75
-
60
+ gems_api_response = gem_client.gems_api_response(collection.names)
76
61
  service_with_urls = gems_api_response.service_with_urls
77
62
  error_messages.concat(gems_api_response.error_messages)
78
63
 
@@ -83,19 +68,12 @@ module Bundler
83
68
  result
84
69
  end
85
70
 
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)
90
-
91
- collection = collection.add(name, status)
92
- end
93
- collection
94
- end
95
-
71
+ # @return [StatusCollection]
96
72
  def collection_from_gemfile
97
73
  gems_from_lockfile.each_with_object(StatusCollection.new) do |gem, collection|
98
74
  gem_name = gem.name
75
+ next if ignore_gems.include?(gem_name)
76
+
99
77
  status = Status.new(name: gem_name,
100
78
  repository_url: nil,
101
79
  alive: nil,
@@ -104,14 +82,13 @@ module Bundler
104
82
  end
105
83
  end
106
84
 
85
+ # @return [StatusResult]
107
86
  def _diagnose
108
- collection = fetch_target_collection(collection_from_gemfile, no_need_to_get_gems)
87
+ collection = collection_from_gemfile
109
88
  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
-
89
+ new_collection = collection.merge(result.collection)
114
90
  messages = error_messages.concat(result.error_messages)
91
+
115
92
  StatusResult.new(collection: new_collection,
116
93
  error_messages: messages,
117
94
  rate_limit_exceeded: result.rate_limit_exceeded)
@@ -20,16 +20,6 @@ module Bundler
20
20
 
21
21
  freeze
22
22
  end
23
-
24
- #
25
- # Save result to file
26
- #
27
- # @param [String] file_path
28
- #
29
- def save_as_file(file_path)
30
- body = TomlRB.dump(result.to_h)
31
- File.write(file_path, body)
32
- end
33
23
  end
34
24
  end
35
25
  end
@@ -21,11 +21,11 @@ module Bundler
21
21
  error_messages = report.error_messages
22
22
  print_error(error_messages)
23
23
 
24
- gems = result.need_to_report_gems
25
- $stdout.puts if gems.size.positive?
26
- gems.each do |_name, gem|
27
- $stdout.puts gem.report
28
- end
24
+ unknown_gems = result.unknown_gems
25
+ print_archived_gems(unknown_gems, header: "Unknown gems:") if unknown_gems.size.positive?
26
+
27
+ archived_gems = result.archived_gems
28
+ print_archived_gems(archived_gems, header: "Archived gems:") if archived_gems.size.positive?
29
29
 
30
30
  print_summary(result)
31
31
  print_message(result, report.rate_limit_exceeded)
@@ -33,18 +33,33 @@ module Bundler
33
33
 
34
34
  private
35
35
 
36
+ # soo messy
37
+ def print_archived_gems(gems, header:)
38
+ $stdout.puts
39
+ $stdout.puts header
40
+ gems_to_report = gems.map do |_name, gem|
41
+ gem.report.split("\n").each_with_object([]) do |line, gem_str|
42
+ gem_str << " #{line}"
43
+ end.join("\n")
44
+ end
45
+ $stdout.puts gems_to_report.join("\n\n")
46
+ end
47
+
36
48
  def print_error(error_messages)
37
- return if error_messages.nil?
49
+ return if error_messages.empty?
38
50
 
39
51
  $stdout.puts <<~ERROR
40
52
 
41
- #{error_messages.join("\n")}
53
+
54
+ Errors:
55
+ #{error_messages.join("\n ")}
42
56
  ERROR
43
57
  end
44
58
 
45
59
  def print_summary(result)
46
60
  $stdout.puts <<~RESULT
47
- Total: #{result.total_size} (Dead: #{result.dead_size}, Alive: #{result.alive_size}, Unknown: #{result.unknown_size})
61
+
62
+ Total: #{result.total_size} (Archived: #{result.archived_size}, Unknown: #{result.unknown_size}, Alive: #{result.alive_size})
48
63
  RESULT
49
64
  end
50
65
 
@@ -55,7 +70,7 @@ module Bundler
55
70
  end
56
71
 
57
72
  say "Too many requested! Retry later.", :yellow if rate_limit_exceeded
58
- if result.dead_size.positive?
73
+ if result.archived_size.positive?
59
74
  say "Not alive gems are found!", :red
60
75
  return
61
76
  end
@@ -6,6 +6,7 @@ module Bundler
6
6
  class SourceCodeRepository
7
7
  module Service
8
8
  GITHUB = :github
9
+ GITLAB = :gitlab
9
10
  end
10
11
 
11
12
  #
@@ -17,6 +18,8 @@ module Bundler
17
18
  raise ArgumentError, "Unknown url: #{url}" unless url.instance_of?(SourceCodeRepositoryUrl)
18
19
 
19
20
  @url = url
21
+
22
+ freeze
20
23
  end
21
24
 
22
25
  private
@@ -7,7 +7,8 @@ module Bundler
7
7
  # service domain with service
8
8
  DOMAIN_WITH_SERVICES = {
9
9
  "github.com" => SourceCodeRepository::Service::GITHUB,
10
- "www.github.com" => SourceCodeRepository::Service::GITHUB
10
+ "www.github.com" => SourceCodeRepository::Service::GITHUB,
11
+ "gitlab.com" => SourceCodeRepository::Service::GITLAB
11
12
  }.freeze
12
13
 
13
14
  private_constant :DOMAIN_WITH_SERVICES
@@ -44,6 +45,8 @@ module Bundler
44
45
  end
45
46
  message = "[#{name}] is not support URL: #{decorated_url}"
46
47
  super(message)
48
+
49
+ freeze
47
50
  end
48
51
  end
49
52
 
@@ -20,7 +20,7 @@ module Bundler
20
20
  # @param [String] :name
21
21
  # @param [SourceCodeRepositoryUrl] :repository_url
22
22
  # @param [Boolean] :alive
23
- # @param [] :checked_at
23
+ # @param [Time] :checked_at
24
24
  #
25
25
  # @return [Status]
26
26
  #
@@ -67,7 +67,6 @@ module Bundler
67
67
  <<~REPORT
68
68
  Name: #{name}
69
69
  URL: #{decorated_repository_url}
70
- Status: #{decorated_alive}
71
70
 
72
71
  REPORT
73
72
  end
@@ -10,30 +10,7 @@ module Bundler
10
10
  delegate each: :collection
11
11
  delegate values: :collection
12
12
 
13
- attr_reader :alive_size, :dead_size, :unknown_size
14
-
15
- #
16
- # Creates `StatusCollection` from TOML file
17
- #
18
- # @param [String] path
19
- #
20
- # @return [StatusCollection]
21
- #
22
- def self.new_from_toml_file(path)
23
- return new unless File.exist?(path)
24
-
25
- collection = new
26
- TomlRB.load_file(path).each do |name, v|
27
- next if v["alive"] == Status::ALIVE_UNKNOWN
28
-
29
- url = SourceCodeRepositoryUrl.new(v["repository_url"], name)
30
- status = Status.new(name: name, repository_url: url,
31
- alive: v["alive"], checked_at: v["checked_at"])
32
- collection = collection.add(name, status)
33
- end
34
-
35
- collection
36
- end
13
+ attr_reader :alive_size, :archived_size, :unknown_size
37
14
 
38
15
  #
39
16
  # Generates instance of `StatusCollection`
@@ -48,7 +25,8 @@ module Bundler
48
25
 
49
26
  @alive_size = _alive_size
50
27
  @unknown_size = _unknown_size
51
- @dead_size = _dead_size
28
+ @archived_size = _archived_size
29
+
52
30
  freeze
53
31
  end
54
32
 
@@ -107,10 +85,14 @@ module Bundler
107
85
  collection.transform_values(&:to_h)
108
86
  end
109
87
 
110
- def need_to_report_gems
88
+ def archived_gems
111
89
  collection.find_all { |_name, gem| !!!gem.alive }
112
90
  end
113
91
 
92
+ def unknown_gems
93
+ collection.find_all { |_name, gem| gem.unknown? }
94
+ end
95
+
114
96
  #
115
97
  # All of statuses are alive nor not
116
98
  #
@@ -137,7 +119,7 @@ module Bundler
137
119
  statuses_values.count { |gem| !!gem.alive && !gem.unknown? }
138
120
  end
139
121
 
140
- def _dead_size
122
+ def _archived_size
141
123
  statuses_values.count { |gem| !gem.alive && !gem.unknown? }
142
124
  end
143
125
 
@@ -17,10 +17,12 @@ module Bundler
17
17
  #
18
18
  # @return [StatusResult]
19
19
  #
20
- def initialize(collection: nil, error_messages: nil, rate_limit_exceeded: nil)
20
+ def initialize(collection: StatusCollection.new, error_messages: [], rate_limit_exceeded: false)
21
21
  @collection = collection
22
22
  @error_messages = error_messages
23
23
  @rate_limit_exceeded = rate_limit_exceeded
24
+
25
+ freeze
24
26
  end
25
27
 
26
28
  #
@@ -31,9 +33,12 @@ module Bundler
31
33
  # @return [StatusResult]
32
34
  #
33
35
  def merge(result)
34
- self.class.new(collection: result.collection,
35
- error_messages: result.error_messages,
36
- rate_limit_exceeded: result.rate_limit_exceeded)
36
+ merged_collection = collection.merge(result.collection)
37
+ merged_error_messages = error_messages + result.error_messages
38
+ merged_rate_limit_exceeded = rate_limit_exceeded || result.rate_limit_exceeded
39
+ self.class.new(collection: merged_collection,
40
+ error_messages: merged_error_messages,
41
+ rate_limit_exceeded: merged_rate_limit_exceeded)
37
42
  end
38
43
  end
39
44
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Bundler
4
4
  module Alive
5
- VERSION = "0.1.4"
5
+ VERSION = "0.1.7"
6
6
  end
7
7
  end
data/lib/bundler/alive.rb CHANGED
@@ -1,5 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ module Bundler
4
+ module Alive
5
+ USER_PATH = File.expand_path(File.join(Gem.user_home, ".local", "share", "bundler-alive"))
6
+ SCHEMA_PATH = File.join(USER_PATH, "schema.json")
7
+ end
8
+ end
9
+
3
10
  require_relative "alive/version"
4
11
  require_relative "alive/doctor"
5
12
  require_relative "alive/source_code_repository"
@@ -7,10 +14,10 @@ require_relative "alive/source_code_repository_url"
7
14
  require_relative "alive/status"
8
15
  require_relative "alive/status_result"
9
16
  require_relative "alive/status_collection"
10
- require_relative "alive/announcer"
11
17
  require_relative "alive/report"
12
18
  require_relative "alive/client/gems_api_client"
13
19
  require_relative "alive/client/gems_api_response"
14
- require_relative "alive/client/git_hub_api"
20
+ require_relative "alive/client/github_api"
21
+ require_relative "alive/client/gitlab_api"
15
22
  require_relative "alive/client/source_code_client"
16
23
  require_relative "alive/reportable"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundler-alive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katsuhiko YOSHIDA
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-16 00:00:00.000000000 Z
11
+ date: 2022-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: octokit
28
+ name: graphql-client
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: toml-rb
70
+ name: gitlab
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -192,8 +192,10 @@ executables:
192
192
  extensions: []
193
193
  extra_rdoc_files: []
194
194
  files:
195
+ - ".bundler-alive.default.yml"
195
196
  - ".rspec"
196
197
  - ".rubocop.yml"
198
+ - ".yardopts"
197
199
  - Gemfile
198
200
  - LICENSE
199
201
  - README.md
@@ -203,11 +205,12 @@ files:
203
205
  - bundler-alive.gemspec
204
206
  - gemspec.yml
205
207
  - lib/bundler/alive.rb
206
- - lib/bundler/alive/announcer.rb
207
208
  - lib/bundler/alive/cli.rb
208
209
  - lib/bundler/alive/client/gems_api_client.rb
209
210
  - lib/bundler/alive/client/gems_api_response.rb
210
- - lib/bundler/alive/client/git_hub_api.rb
211
+ - lib/bundler/alive/client/github_api.rb
212
+ - lib/bundler/alive/client/github_graphql.rb
213
+ - lib/bundler/alive/client/gitlab_api.rb
211
214
  - lib/bundler/alive/client/source_code_client.rb
212
215
  - lib/bundler/alive/doctor.rb
213
216
  - lib/bundler/alive/report.rb
@@ -223,7 +226,7 @@ licenses: []
223
226
  metadata:
224
227
  homepage_uri: https://github.com/kyoshidajp/bundler-alive
225
228
  source_code_uri: https://github.com/kyoshidajp/bundler-alive
226
- changelog_uri: https://github.com/kyoshidajp/bundler-alive
229
+ changelog_uri: https://github.com/kyoshidajp/bundler-alive/commits/main
227
230
  rubygems_mfa_required: 'true'
228
231
  post_install_message:
229
232
  rdoc_options: []
@@ -1,31 +0,0 @@
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