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 +4 -4
- data/.bundler-alive.default.yml +27 -1
- data/README.md +5 -1
- data/gemspec.yml +1 -0
- data/lib/bundler/alive/cli.rb +11 -4
- data/lib/bundler/alive/client/gems_api_client.rb +9 -2
- data/lib/bundler/alive/client/{git_hub_api.rb → github_api.rb} +33 -13
- data/lib/bundler/alive/client/gitlab_api.rb +121 -0
- data/lib/bundler/alive/client/source_code_client.rb +2 -1
- data/lib/bundler/alive/reportable.rb +10 -7
- data/lib/bundler/alive/source_code_repository.rb +1 -0
- data/lib/bundler/alive/source_code_repository_url.rb +2 -1
- data/lib/bundler/alive/status_collection.rb +4 -0
- data/lib/bundler/alive/status_result.rb +7 -4
- data/lib/bundler/alive/version.rb +1 -1
- data/lib/bundler/alive.rb +2 -1
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3e48b8f0f5a030f9b537e4150f6b10e5bdf3ed709cde30162d2030b3292cbc6
|
4
|
+
data.tar.gz: dff1d8579c996eeb24c599e2a40ba48b778d2939af8e80f3da94ed133a4ac0b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94d793718656beddd079aaa083fe501b2775a8b658d361b19d5c56906abba39551dd6f84628904c98235db7bc381028fb3ce261bb09178d00154ea3f682e2757
|
7
|
+
data.tar.gz: 4e4977e62794f2f9cd403187c4d60063788f8589ddef358beb2ba49827b65b7b3be6c64d563489b0c90938fe94f02b98af88fdf6bc93f29ea12bdc29ffc399a5
|
data/.bundler-alive.default.yml
CHANGED
@@ -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
|
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
data/lib/bundler/alive/cli.rb
CHANGED
@@ -38,13 +38,20 @@ module Bundler
|
|
38
38
|
private
|
39
39
|
|
40
40
|
def check_by_doctor
|
41
|
-
doctor =
|
42
|
-
|
43
|
-
|
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
|
-
|
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
|
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 =
|
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
|
-
|
105
|
-
|
106
|
-
|
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 =>
|
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
|
-
|
25
|
-
print_archived_gems(gems) if
|
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
|
37
|
-
|
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
|
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},
|
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
|
|
@@ -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
|
@@ -17,7 +17,7 @@ module Bundler
|
|
17
17
|
#
|
18
18
|
# @return [StatusResult]
|
19
19
|
#
|
20
|
-
def initialize(collection:
|
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
|
-
|
37
|
-
|
38
|
-
|
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
|
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/
|
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.
|
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-
|
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/
|
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
|