bundler-alive 0.1.5 → 0.1.6

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: dd88b794101cf7f456811a805f287ec314d9ecf202294f5ec13e5e339db5834d
4
- data.tar.gz: e0db2f2731c7dd50324900a5564365ff14abf82bc7169d30d9ad7444504dffec
3
+ metadata.gz: c3e48b8f0f5a030f9b537e4150f6b10e5bdf3ed709cde30162d2030b3292cbc6
4
+ data.tar.gz: dff1d8579c996eeb24c599e2a40ba48b778d2939af8e80f3da94ed133a4ac0b5
5
5
  SHA512:
6
- metadata.gz: 89fec08047da08d6178bae7e2760f22139005dc33cd38201a6c7782a589ab173a7b5b72ee934bb301216fe4a5e1e5814dd69b773a9c937ab7ca2f7c7b61f0d3a
7
- data.tar.gz: 0f1930be94cf0401d807ec1e63714dea3243c5bedbe226ff7cd4ff957e9d6d5f043d731f4903ca3dac35aa0403d1269cbc85e153a304894fad63848401ddec07
6
+ metadata.gz: 94d793718656beddd079aaa083fe501b2775a8b658d361b19d5c56906abba39551dd6f84628904c98235db7bc381028fb3ce261bb09178d00154ea3f682e2757
7
+ data.tar.gz: 4e4977e62794f2f9cd403187c4d60063788f8589ddef358beb2ba49827b65b7b3be6c64d563489b0c90938fe94f02b98af88fdf6bc93f29ea12bdc29ffc399a5
@@ -27,4 +27,30 @@ gems:
27
27
  fog-sakuracloud:
28
28
  url: https://github.com/fog/fog-sakuracloud
29
29
  ovirt-engine-sdk:
30
- url: https://github.com/oVirt/ovirt-engine-sdk-ruby
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
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  `bunder-alive` checks if gems in a RubyGem's `Gemfile.lock` are active.
9
9
 
10
- Currently only GitHub 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
 
@@ -41,6 +41,10 @@ Default `Gemfile.lock` location is in your current directory. You can specify it
41
41
  $ bundle-alive -G /path/to/Gemfile.lock
42
42
  ```
43
43
 
44
+ ## GitLab Access Token
45
+
46
+ When gems are in GitLab.com repository, you MUST set environment variable `BUNDLER_ALIVE_GITLAB_TOKEN`. See the document [Personal access tokens](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html).
47
+
44
48
  ## Exceeding rate limit
45
49
 
46
50
  In some cases, the following error may be output.
data/gemspec.yml CHANGED
@@ -19,6 +19,7 @@ dependencies:
19
19
  octokit:
20
20
  rake:
21
21
  thor:
22
+ gitlab:
22
23
 
23
24
  development_dependencies:
24
25
  rspec:
@@ -38,13 +38,20 @@ module Bundler
38
38
  private
39
39
 
40
40
  def check_by_doctor
41
- doctor = begin
42
- Doctor.new(options[:gemfile_lock], options[:config], options[:ignore])
43
- rescue Bundler::GemfileLockNotFound
41
+ doctor = initialize_doctor
42
+
43
+ begin
44
+ doctor.diagnose
45
+ rescue Bundler::Alive::Client::GitlabApi::AccessTokenNotFoundError => e
46
+ say "\n#{e.message}", :yellow
44
47
  exit 1
45
48
  end
49
+ end
46
50
 
47
- doctor.diagnose
51
+ def initialize_doctor
52
+ Doctor.new(options[:gemfile_lock], options[:config], options[:ignore])
53
+ rescue Bundler::GemfileLockNotFound
54
+ exit 1
48
55
  end
49
56
  end
50
57
  end
@@ -111,16 +111,23 @@ module Bundler
111
111
 
112
112
  def source_code_url(body:, gem_name:)
113
113
  url = body["source_code_uri"]
114
- return url if SourceCodeRepositoryUrl.support_url?(url)
114
+ return url_via_redirect(url) if SourceCodeRepositoryUrl.support_url?(url)
115
115
 
116
116
  url = body["homepage_uri"]
117
- return url if SourceCodeRepositoryUrl.support_url?(url)
117
+ return url_via_redirect(url) if SourceCodeRepositoryUrl.support_url?(url)
118
118
 
119
119
  message = "[#{gem_name}] Source code repository is not found in RubyGems.org,"\
120
120
  " or not supported. URL: https://rubygems.org/gems/#{gem_name}"
121
121
  raise NotFound, message
122
122
  end
123
123
 
124
+ def url_via_redirect(url)
125
+ response = connection.head(url)
126
+ return response.headers["location"] if response.status == 301
127
+
128
+ url
129
+ end
130
+
124
131
  def get_repository_urls(gem_names)
125
132
  result = gem_names.map do |gem_name|
126
133
  $stdout.write "."
@@ -11,7 +11,7 @@ module Bundler
11
11
  #
12
12
  # @see https://docs.github.com/en/rest/search#search-repositories
13
13
  #
14
- module GitHubApi
14
+ module GithubApi
15
15
  # Environment variable name of GitHub Access Token
16
16
  ACCESS_TOKEN_ENV_NAME = "BUNDLER_ALIVE_GITHUB_TOKEN"
17
17
 
@@ -44,6 +44,7 @@ module Bundler
44
44
  base.instance_eval do
45
45
  @rate_limit_exceeded = false
46
46
  @retries_on_too_many_requests = 0
47
+ @name_with_archived = {}
47
48
  end
48
49
  end
49
50
 
@@ -63,15 +64,12 @@ module Bundler
63
64
  #
64
65
  # @return [StatusResult]
65
66
  #
66
- # rubocop:disable Metrics/MethodLength
67
67
  def query(urls:)
68
68
  collection = StatusCollection.new
69
- name_with_archived = get_name_with_statuses(urls)
69
+ @name_with_archived = get_name_with_statuses(urls)
70
70
  urls.each do |url|
71
- $stdout.write "."
72
-
73
71
  gem_name = url.gem_name
74
- alive = !name_with_archived[gem_name]
72
+ alive = alive?(gem_name)
75
73
  status = Status.new(name: gem_name, repository_url: url, alive: alive, checked_at: Time.now)
76
74
  collection = collection.add(gem_name, status)
77
75
  end
@@ -79,10 +77,16 @@ module Bundler
79
77
  StatusResult.new(collection: collection, error_messages: @error_messages,
80
78
  rate_limit_exceeded: @rate_limit_exceeded)
81
79
  end
82
- # rubocop:enable Metrics/MethodLength
83
80
 
84
81
  private
85
82
 
83
+ def alive?(gem_name)
84
+ return false unless @name_with_archived.key?(gem_name)
85
+
86
+ value = @name_with_archived[gem_name]
87
+ value == Status::ALIVE_UNKNOWN ? Status::ALIVE_UNKNOWN : !value
88
+ end
89
+
86
90
  #
87
91
  # Search status of repositories
88
92
  #
@@ -93,23 +97,39 @@ module Bundler
93
97
  #
94
98
  # rubocop:disable Metrics/MethodLength
95
99
  def get_name_with_statuses(urls)
96
- raise ArgumentError unless urls.instance_of?(Array)
97
-
98
100
  name_with_status = {}
99
101
  urls.each_slice(QUERY_MAX_OPERATORS_AT_ONCE) do |sliced_urls|
102
+ $stdout.print "." * sliced_urls.size
103
+
100
104
  q = search_query(sliced_urls)
101
105
  repositories = search_repositories_with_retry(q)
102
106
  next if repositories.nil?
103
107
 
104
- repositories.each do |repository|
105
- name = repository["name"]
106
- name_with_status[name] = repository["archived"]
108
+ sliced_urls.each do |url|
109
+ repository = find_repository_from_repositories(url: url,
110
+ repositories: repositories)
111
+ alive_status = if repository.nil?
112
+ Status::ALIVE_UNKNOWN
113
+ else
114
+ repository["archived"]
115
+ end
116
+ name_with_status[url.gem_name] = alive_status
107
117
  end
108
118
  end
109
119
  name_with_status
110
120
  end
111
121
  # rubocop:enable Metrics/MethodLength
112
122
 
123
+ # @param [SourceCodeRepositoryUrl] :url
124
+ # @param [Array<Sawyer::Resource>] :repositories
125
+ #
126
+ # @return [Sawyer::Resource|nil]
127
+ def find_repository_from_repositories(url:, repositories:)
128
+ repositories.find do |repository|
129
+ slug(url.url) == repository["full_name"]
130
+ end
131
+ end
132
+
113
133
  #
114
134
  # Search query of repositories
115
135
  #
@@ -172,7 +192,7 @@ module Bundler
172
192
  # @return [String]
173
193
  #
174
194
  def slug(repository_url)
175
- Octokit::Repository.from_url(repository_url).slug
195
+ Octokit::Repository.from_url(repository_url).slug.gsub(/\.git/, "")
176
196
  end
177
197
  end
178
198
  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 [Octokit::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
@@ -21,8 +21,11 @@ module Bundler
21
21
  error_messages = report.error_messages
22
22
  print_error(error_messages)
23
23
 
24
- gems = result.archived_gems
25
- print_archived_gems(gems) if gems.size.positive?
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?
26
29
 
27
30
  print_summary(result)
28
31
  print_message(result, report.rate_limit_exceeded)
@@ -31,15 +34,15 @@ module Bundler
31
34
  private
32
35
 
33
36
  # soo messy
34
- def print_archived_gems(gems)
37
+ def print_archived_gems(gems, header:)
35
38
  $stdout.puts
36
- $stdout.puts "Archived gems:"
37
- archived_gems = gems.map do |_name, gem|
39
+ $stdout.puts header
40
+ gems_to_report = gems.map do |_name, gem|
38
41
  gem.report.split("\n").each_with_object([]) do |line, gem_str|
39
42
  gem_str << " #{line}"
40
43
  end.join("\n")
41
44
  end
42
- $stdout.puts archived_gems.join("\n\n")
45
+ $stdout.puts gems_to_report.join("\n\n")
43
46
  end
44
47
 
45
48
  def print_error(error_messages)
@@ -56,7 +59,7 @@ module Bundler
56
59
  def print_summary(result)
57
60
  $stdout.puts <<~RESULT
58
61
 
59
- Total: #{result.total_size} (Archived: #{result.archived_size}, Alive: #{result.alive_size}, Unknown: #{result.unknown_size})
62
+ Total: #{result.total_size} (Archived: #{result.archived_size}, Unknown: #{result.unknown_size}, Alive: #{result.alive_size})
60
63
  RESULT
61
64
  end
62
65
 
@@ -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
  #
@@ -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
@@ -89,6 +89,10 @@ module Bundler
89
89
  collection.find_all { |_name, gem| !!!gem.alive }
90
90
  end
91
91
 
92
+ def unknown_gems
93
+ collection.find_all { |_name, gem| gem.unknown? }
94
+ end
95
+
92
96
  #
93
97
  # All of statuses are alive nor not
94
98
  #
@@ -17,7 +17,7 @@ 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
@@ -33,9 +33,12 @@ module Bundler
33
33
  # @return [StatusResult]
34
34
  #
35
35
  def merge(result)
36
- self.class.new(collection: result.collection,
37
- error_messages: result.error_messages,
38
- 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)
39
42
  end
40
43
  end
41
44
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Bundler
4
4
  module Alive
5
- VERSION = "0.1.5"
5
+ VERSION = "0.1.6"
6
6
  end
7
7
  end
data/lib/bundler/alive.rb CHANGED
@@ -10,6 +10,7 @@ require_relative "alive/status_collection"
10
10
  require_relative "alive/report"
11
11
  require_relative "alive/client/gems_api_client"
12
12
  require_relative "alive/client/gems_api_response"
13
- require_relative "alive/client/git_hub_api"
13
+ require_relative "alive/client/github_api"
14
+ require_relative "alive/client/gitlab_api"
14
15
  require_relative "alive/client/source_code_client"
15
16
  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.5
4
+ version: 0.1.6
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-21 00:00:00.000000000 Z
11
+ date: 2022-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: gitlab
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rspec
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -194,7 +208,8 @@ files:
194
208
  - lib/bundler/alive/cli.rb
195
209
  - lib/bundler/alive/client/gems_api_client.rb
196
210
  - lib/bundler/alive/client/gems_api_response.rb
197
- - lib/bundler/alive/client/git_hub_api.rb
211
+ - lib/bundler/alive/client/github_api.rb
212
+ - lib/bundler/alive/client/gitlab_api.rb
198
213
  - lib/bundler/alive/client/source_code_client.rb
199
214
  - lib/bundler/alive/doctor.rb
200
215
  - lib/bundler/alive/report.rb