diffend 0.2.18 → 0.2.26
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.github/workflows/ci.yml +14 -2
- data/CHANGELOG.md +47 -1
- data/Gemfile +2 -1
- data/Gemfile.lock +12 -1
- data/{LICENSE → LICENSE.md} +0 -0
- data/lib/diffend.rb +86 -19
- data/lib/diffend/build_bundler_definition.rb +26 -0
- data/lib/diffend/errors.rb +2 -4
- data/lib/diffend/handle_errors/build_exception_payload.rb +30 -0
- data/lib/diffend/handle_errors/display_to_stdout.rb +17 -0
- data/lib/diffend/handle_errors/messages.rb +29 -0
- data/lib/diffend/handle_errors/report.rb +59 -0
- data/lib/diffend/request.rb +165 -0
- data/lib/diffend/voting.rb +71 -12
- data/lib/diffend/voting/versions/local.rb +156 -52
- data/lib/diffend/voting/versions/remote.rb +46 -14
- data/scripts/generate_payload_for_file.rb +1 -1
- metadata +9 -4
- metadata.gz.sig +0 -0
- data/lib/diffend/voting/request.rb +0 -191
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'openssl'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
7
|
+
module Diffend
|
|
8
|
+
# Module responsible for doing request to Diffend
|
|
9
|
+
module Request
|
|
10
|
+
# Message displayed when connection issue occured and we will retry
|
|
11
|
+
CONNECTION_MESSAGE = 'We experienced a connection issue, retrying...'
|
|
12
|
+
# List of connection exceptions
|
|
13
|
+
CONNECTION_EXCEPTIONS = [
|
|
14
|
+
Errno::ECONNRESET,
|
|
15
|
+
Errno::ENETUNREACH,
|
|
16
|
+
Errno::EHOSTUNREACH,
|
|
17
|
+
Errno::ECONNREFUSED
|
|
18
|
+
].freeze
|
|
19
|
+
# Message displayed when timeout occured and we will retry
|
|
20
|
+
TIMEOUT_MESSAGE = 'We experienced a connection issue, retrying...'
|
|
21
|
+
# List of timeout exceptions
|
|
22
|
+
TIMEOUT_EXCEPTIONS = [
|
|
23
|
+
Net::OpenTimeout,
|
|
24
|
+
Net::ReadTimeout
|
|
25
|
+
].freeze
|
|
26
|
+
# Message displayed when server issue occured and we will retry
|
|
27
|
+
SERVER_ERROR_MESSAGE = 'We experienced a server-side issue, retrying...'
|
|
28
|
+
# List of server issues
|
|
29
|
+
#
|
|
30
|
+
# 500 - Internal Server Error
|
|
31
|
+
# 502 - Bad Gateway
|
|
32
|
+
# 503 - Service Unavailable
|
|
33
|
+
# 504 - Gateway Timeout
|
|
34
|
+
SERVER_ERRORS = [500, 502, 503, 504].freeze
|
|
35
|
+
# Number of retries
|
|
36
|
+
RETRIES = 3
|
|
37
|
+
# Request headers
|
|
38
|
+
HEADERS = { 'Content-Type': 'application/json' }.freeze
|
|
39
|
+
|
|
40
|
+
private_constant :HEADERS
|
|
41
|
+
|
|
42
|
+
class << self
|
|
43
|
+
# Execute request
|
|
44
|
+
#
|
|
45
|
+
# @param config [OpenStruct] diffend config
|
|
46
|
+
# @param endpoint_url [String]
|
|
47
|
+
# @param payload [Hash]
|
|
48
|
+
#
|
|
49
|
+
# @return [Net::HTTPResponse] response from Diffend
|
|
50
|
+
def call(config, endpoint_url, payload)
|
|
51
|
+
retry_count ||= -1
|
|
52
|
+
|
|
53
|
+
build_http(endpoint_url) do |http, uri|
|
|
54
|
+
response = http.request(build_request(uri, config, payload))
|
|
55
|
+
|
|
56
|
+
if SERVER_ERRORS.include?(response.code.to_i)
|
|
57
|
+
raise Diffend::Errors::RequestServerError, response.code.to_i
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
response
|
|
61
|
+
end
|
|
62
|
+
rescue Diffend::Errors::RequestServerError => e
|
|
63
|
+
retry_count += 1
|
|
64
|
+
|
|
65
|
+
retry if handle_retry(SERVER_ERROR_MESSAGE, retry_count)
|
|
66
|
+
|
|
67
|
+
Diffend::HandleErrors::Report.call(
|
|
68
|
+
exception: e,
|
|
69
|
+
payload: payload,
|
|
70
|
+
config: config,
|
|
71
|
+
message: :request_error
|
|
72
|
+
)
|
|
73
|
+
rescue *CONNECTION_EXCEPTIONS => e
|
|
74
|
+
retry_count += 1
|
|
75
|
+
|
|
76
|
+
retry if handle_retry(CONNECTION_MESSAGE, retry_count)
|
|
77
|
+
|
|
78
|
+
Diffend::HandleErrors::Report.call(
|
|
79
|
+
exception: e,
|
|
80
|
+
payload: payload,
|
|
81
|
+
config: config,
|
|
82
|
+
message: :request_error
|
|
83
|
+
)
|
|
84
|
+
rescue *TIMEOUT_EXCEPTIONS => e
|
|
85
|
+
retry_count += 1
|
|
86
|
+
|
|
87
|
+
retry if handle_retry(TIMEOUT_MESSAGE, retry_count)
|
|
88
|
+
|
|
89
|
+
Diffend::HandleErrors::Report.call(
|
|
90
|
+
exception: e,
|
|
91
|
+
payload: payload,
|
|
92
|
+
config: config,
|
|
93
|
+
message: :request_error
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Handle retry
|
|
98
|
+
#
|
|
99
|
+
# @param message [String] message we want to display
|
|
100
|
+
# @param retry_count [Integer]
|
|
101
|
+
def handle_retry(message, retry_count)
|
|
102
|
+
return false if retry_count == RETRIES
|
|
103
|
+
|
|
104
|
+
Bundler.ui.error(message)
|
|
105
|
+
sleep(exponential_backoff(retry_count))
|
|
106
|
+
|
|
107
|
+
retry_count < RETRIES
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Builds http connection object
|
|
111
|
+
#
|
|
112
|
+
# @param url [String] command endpoint url
|
|
113
|
+
def build_http(url)
|
|
114
|
+
uri = URI(url)
|
|
115
|
+
|
|
116
|
+
Net::HTTP.start(
|
|
117
|
+
uri.host,
|
|
118
|
+
uri.port,
|
|
119
|
+
use_ssl: uri.scheme == 'https',
|
|
120
|
+
verify_mode: OpenSSL::SSL::VERIFY_NONE,
|
|
121
|
+
open_timeout: 5,
|
|
122
|
+
read_timeout: 5
|
|
123
|
+
) { |http| yield(http, uri) }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Build http post request and assigns headers and payload
|
|
127
|
+
#
|
|
128
|
+
# @param uri [URI::HTTPS]
|
|
129
|
+
# @param config [OpenStruct] Diffend config
|
|
130
|
+
# @param payload [Hash] with versions to check
|
|
131
|
+
#
|
|
132
|
+
# @return [Net::HTTP::Post]
|
|
133
|
+
def build_request(uri, config, payload)
|
|
134
|
+
Net::HTTP::Post
|
|
135
|
+
.new(uri.request_uri, HEADERS)
|
|
136
|
+
.tap { |request| assign_auth(request, config) }
|
|
137
|
+
.tap { |request| assign_payload(request, payload) }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Assigns basic authorization if provided in the config
|
|
141
|
+
#
|
|
142
|
+
# @param request [Net::HTTP::Post] prepared http post
|
|
143
|
+
# @param config [OpenStruct] Diffend config
|
|
144
|
+
def assign_auth(request, config)
|
|
145
|
+
return unless config
|
|
146
|
+
return unless config.shareable_id
|
|
147
|
+
return unless config.shareable_key
|
|
148
|
+
|
|
149
|
+
request.basic_auth(config.shareable_id, config.shareable_key)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Assigns payload as json
|
|
153
|
+
#
|
|
154
|
+
# @param request [Net::HTTP::Post] prepared http post
|
|
155
|
+
# @param payload [Hash] with versions to check
|
|
156
|
+
def assign_payload(request, payload)
|
|
157
|
+
request.body = JSON.dump(payload: payload)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def exponential_backoff(retry_count)
|
|
161
|
+
2**(retry_count + 1)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
data/lib/diffend/voting.rb
CHANGED
|
@@ -7,23 +7,33 @@ module Diffend
|
|
|
7
7
|
# Build verdict
|
|
8
8
|
#
|
|
9
9
|
# @param command [String] either install or update
|
|
10
|
+
# @param config [OpenStruct] diffend config
|
|
10
11
|
# @param definition [Bundler::Definition] definition for your source
|
|
11
|
-
def call(command, definition)
|
|
12
|
+
def call(command, config, definition)
|
|
12
13
|
Versions::Remote
|
|
13
|
-
.call(command, definition)
|
|
14
|
-
.tap { |response| build_message(command, response) }
|
|
14
|
+
.call(command, config, definition)
|
|
15
|
+
.tap { |response| build_message(command, config, response) }
|
|
15
16
|
end
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
# @param command [String] either install or update
|
|
19
|
+
# @param config [OpenStruct] diffend config
|
|
20
|
+
# @param response [Hash] response from diffend API
|
|
21
|
+
def build_message(command, config, response)
|
|
18
22
|
if response.key?('error')
|
|
19
23
|
build_error(response)
|
|
20
24
|
elsif response.key?('action')
|
|
21
|
-
build_verdict(command, response)
|
|
25
|
+
build_verdict(command, config, response)
|
|
22
26
|
else
|
|
23
|
-
|
|
27
|
+
Diffend::HandleErrors::Report.call(
|
|
28
|
+
config: config,
|
|
29
|
+
message: :unsupported_response,
|
|
30
|
+
payload: response,
|
|
31
|
+
report: true
|
|
32
|
+
)
|
|
24
33
|
end
|
|
25
34
|
end
|
|
26
35
|
|
|
36
|
+
# @param response [Hash] response from diffend API
|
|
27
37
|
def build_error(response)
|
|
28
38
|
build_error_message(response)
|
|
29
39
|
.tap(&Bundler.ui.method(:error))
|
|
@@ -31,21 +41,35 @@ module Diffend
|
|
|
31
41
|
exit 1
|
|
32
42
|
end
|
|
33
43
|
|
|
34
|
-
|
|
44
|
+
# @param command [String] either install or update
|
|
45
|
+
# @param config [OpenStruct] diffend config
|
|
46
|
+
# @param response [Hash] response from diffend API
|
|
47
|
+
def build_verdict(command, config, response)
|
|
35
48
|
case response['action']
|
|
36
49
|
when 'allow'
|
|
37
50
|
build_allow_message(command, response)
|
|
38
51
|
.tap(&Bundler.ui.method(:confirm))
|
|
52
|
+
when 'warn'
|
|
53
|
+
build_warn_message(command, response)
|
|
54
|
+
.tap(&Bundler.ui.method(:warn))
|
|
39
55
|
when 'deny'
|
|
40
56
|
build_deny_message(command, response)
|
|
41
57
|
.tap(&Bundler.ui.method(:error))
|
|
42
58
|
|
|
43
59
|
exit 1
|
|
44
60
|
else
|
|
45
|
-
|
|
61
|
+
Diffend::HandleErrors::Report.call(
|
|
62
|
+
config: config,
|
|
63
|
+
message: :unsupported_verdict,
|
|
64
|
+
payload: response,
|
|
65
|
+
report: true
|
|
66
|
+
)
|
|
46
67
|
end
|
|
47
68
|
end
|
|
48
69
|
|
|
70
|
+
# @param response [Hash] response from diffend API
|
|
71
|
+
#
|
|
72
|
+
# @return [String]
|
|
49
73
|
def build_error_message(response)
|
|
50
74
|
<<~MSG
|
|
51
75
|
\nDiffend returned an error for your request.\n
|
|
@@ -53,21 +77,56 @@ module Diffend
|
|
|
53
77
|
MSG
|
|
54
78
|
end
|
|
55
79
|
|
|
80
|
+
# @param command [String] either install or update
|
|
81
|
+
# @param response [Hash] response from diffend API
|
|
82
|
+
#
|
|
83
|
+
# @return [String]
|
|
56
84
|
def build_allow_message(command, response)
|
|
57
85
|
<<~MSG
|
|
58
|
-
|
|
59
|
-
|
|
86
|
+
#{build_message_header('an allow', command)}
|
|
87
|
+
#{build_message_info(response)}\n
|
|
60
88
|
#{response['review_url']}\n
|
|
61
89
|
MSG
|
|
62
90
|
end
|
|
63
91
|
|
|
92
|
+
# @param command [String] either install or update
|
|
93
|
+
# @param response [Hash] response from diffend API
|
|
94
|
+
#
|
|
95
|
+
# @return [String]
|
|
96
|
+
def build_warn_message(command, response)
|
|
97
|
+
<<~MSG
|
|
98
|
+
#{build_message_header('a warn', command)}
|
|
99
|
+
#{build_message_info(response)} Please go to the url below and review the issues.\n
|
|
100
|
+
#{response['review_url']}\n
|
|
101
|
+
MSG
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @param command [String] either install or update
|
|
105
|
+
# @param response [Hash] response from diffend API
|
|
106
|
+
#
|
|
107
|
+
# @return [String]
|
|
64
108
|
def build_deny_message(command, response)
|
|
65
109
|
<<~MSG
|
|
66
|
-
|
|
67
|
-
#{response
|
|
110
|
+
#{build_message_header('a deny', command)}
|
|
111
|
+
#{build_message_info(response)} Please go to the url below and review the issues.\n
|
|
68
112
|
#{response['review_url']}\n
|
|
69
113
|
MSG
|
|
70
114
|
end
|
|
115
|
+
|
|
116
|
+
# @param type [String] verdict type
|
|
117
|
+
# @param command [String] either install or update
|
|
118
|
+
#
|
|
119
|
+
# @return [String]
|
|
120
|
+
def build_message_header(type, command)
|
|
121
|
+
"\nDiffend reported #{type} verdict for #{command} command for this project."
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# @param response [Hash] response from diffend API
|
|
125
|
+
#
|
|
126
|
+
# @return [String]
|
|
127
|
+
def build_message_info(response)
|
|
128
|
+
"\nQuality score: #{response['quality_score']}, allows: #{response['allows_count']}, warnings: #{response['warns_count']}, denies: #{response['denies_count']}."
|
|
129
|
+
end
|
|
71
130
|
end
|
|
72
131
|
end
|
|
73
132
|
end
|
|
@@ -13,17 +13,22 @@ module Diffend
|
|
|
13
13
|
Bundler::Source::Gemspec,
|
|
14
14
|
Bundler::Source::Path
|
|
15
15
|
].freeze
|
|
16
|
-
|
|
16
|
+
# List of dependency types
|
|
17
|
+
DEPENDENCIES_TYPES = {
|
|
17
18
|
direct: 0,
|
|
18
|
-
|
|
19
|
+
dependency: 1
|
|
19
20
|
}.freeze
|
|
20
|
-
|
|
21
|
+
# List of sources types
|
|
22
|
+
SOURCES_TYPES = {
|
|
21
23
|
valid: 0,
|
|
22
24
|
multiple_primary: 1
|
|
23
25
|
}.freeze
|
|
24
|
-
|
|
26
|
+
# List of gem sources types
|
|
27
|
+
GEM_SOURCES_TYPES = {
|
|
25
28
|
local: 0,
|
|
26
|
-
|
|
29
|
+
gemfile_source: 1,
|
|
30
|
+
gemfile_git: 2,
|
|
31
|
+
gemfile_path: 3
|
|
27
32
|
}.freeze
|
|
28
33
|
|
|
29
34
|
class << self
|
|
@@ -49,43 +54,45 @@ module Diffend
|
|
|
49
54
|
def initialize(definition)
|
|
50
55
|
@definition = definition
|
|
51
56
|
@direct_dependencies = Hash[definition.dependencies.map { |val| [val.name, val] }]
|
|
52
|
-
@main_source = definition.send(:sources).rubygems_sources.last
|
|
53
57
|
# Support case without Gemfile.lock
|
|
54
58
|
@locked_specs = @definition.locked_gems ? @definition.locked_gems.specs : []
|
|
55
59
|
end
|
|
56
60
|
|
|
61
|
+
# Build install specification
|
|
62
|
+
#
|
|
63
|
+
# @return [Hash]
|
|
57
64
|
def build_install
|
|
58
65
|
hash = build_main
|
|
59
66
|
|
|
60
|
-
@definition.
|
|
67
|
+
@definition.specs.each do |spec|
|
|
61
68
|
next if skip?(spec.source)
|
|
62
69
|
|
|
63
70
|
locked_spec = @locked_specs.find { |s| s.name == spec.name }
|
|
64
71
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
'type' => build_dependency_type(spec2.name),
|
|
71
|
-
'versions' => build_versions(spec2)
|
|
72
|
+
hash['dependencies'][spec.name] = {
|
|
73
|
+
'platform' => build_spec_platform(spec, locked_spec),
|
|
74
|
+
'source' => build_spec_source(spec),
|
|
75
|
+
'type' => build_dependency_type(spec.name),
|
|
76
|
+
'versions' => build_versions(spec, locked_spec)
|
|
72
77
|
}
|
|
73
78
|
end
|
|
74
79
|
|
|
75
80
|
hash
|
|
76
81
|
end
|
|
77
82
|
|
|
78
|
-
#
|
|
83
|
+
# Build update specification
|
|
84
|
+
#
|
|
85
|
+
# @return [Hash]
|
|
79
86
|
def build_update
|
|
80
87
|
hash = build_main
|
|
81
88
|
|
|
82
|
-
@definition.
|
|
89
|
+
@definition.specs.each do |spec|
|
|
83
90
|
next if skip?(spec.source)
|
|
84
91
|
|
|
85
92
|
locked_spec = @locked_specs.find { |s| s.name == spec.name }
|
|
86
93
|
|
|
87
94
|
hash['dependencies'][spec.name] = {
|
|
88
|
-
'platform' => build_spec_platform(spec),
|
|
95
|
+
'platform' => build_spec_platform(spec, locked_spec),
|
|
89
96
|
'source' => build_spec_source(spec),
|
|
90
97
|
'type' => build_dependency_type(spec.name),
|
|
91
98
|
'versions' => build_versions(spec, locked_spec)
|
|
@@ -97,6 +104,9 @@ module Diffend
|
|
|
97
104
|
|
|
98
105
|
private
|
|
99
106
|
|
|
107
|
+
# Build default specification
|
|
108
|
+
#
|
|
109
|
+
# @return [Hash]
|
|
100
110
|
def build_main
|
|
101
111
|
{
|
|
102
112
|
'dependencies' => {},
|
|
@@ -106,8 +116,18 @@ module Diffend
|
|
|
106
116
|
}
|
|
107
117
|
end
|
|
108
118
|
|
|
119
|
+
# Build gem versions
|
|
120
|
+
#
|
|
121
|
+
# @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
|
|
122
|
+
# @param locked_spec [Bundler::LazySpecification, Gem::Specification, NilClass]
|
|
123
|
+
#
|
|
124
|
+
# @return [Array<String>]
|
|
109
125
|
def build_versions(spec, locked_spec = nil)
|
|
110
|
-
locked_spec
|
|
126
|
+
if locked_spec && locked_spec.version.to_s != spec.version.to_s
|
|
127
|
+
[locked_spec.version.to_s, spec.version.to_s]
|
|
128
|
+
else
|
|
129
|
+
[spec.version.to_s]
|
|
130
|
+
end
|
|
111
131
|
end
|
|
112
132
|
|
|
113
133
|
# @param specs [Array] specs that are direct dependencies
|
|
@@ -115,78 +135,162 @@ module Diffend
|
|
|
115
135
|
#
|
|
116
136
|
# @return [Boolean] dependency type
|
|
117
137
|
def build_dependency_type(name)
|
|
118
|
-
@direct_dependencies.key?(name)
|
|
138
|
+
if @direct_dependencies.key?(name)
|
|
139
|
+
DEPENDENCIES_TYPES[:direct]
|
|
140
|
+
else
|
|
141
|
+
DEPENDENCIES_TYPES[:dependency]
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Build gem platform
|
|
146
|
+
#
|
|
147
|
+
# @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
|
|
148
|
+
# @param locked_spec [Bundler::LazySpecification, Gem::Specification, NilClass]
|
|
149
|
+
#
|
|
150
|
+
# @return [String]
|
|
151
|
+
def build_spec_platform(spec, locked_spec)
|
|
152
|
+
parse_platform(
|
|
153
|
+
spec.platform || locked_spec&.platform || spec.send(:generic_local_platform)
|
|
154
|
+
)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Parse gem platform
|
|
158
|
+
#
|
|
159
|
+
# @param platform [String, Gem::Platform]
|
|
160
|
+
#
|
|
161
|
+
# @return [String]
|
|
162
|
+
def parse_platform(platform)
|
|
163
|
+
case platform
|
|
164
|
+
when String then platform
|
|
165
|
+
when Gem::Platform then platform.os
|
|
166
|
+
end
|
|
119
167
|
end
|
|
120
168
|
|
|
121
|
-
|
|
122
|
-
|
|
169
|
+
# Build gem source type
|
|
170
|
+
#
|
|
171
|
+
# @param source [Bundler::Source] gem source type
|
|
172
|
+
#
|
|
173
|
+
# @return [Integer] internal gem source type
|
|
174
|
+
def build_spec_gem_source_type(source)
|
|
175
|
+
case source
|
|
176
|
+
when Bundler::Source::Metadata
|
|
177
|
+
GEM_SOURCES_TYPES[:local]
|
|
178
|
+
when Bundler::Source::Rubygems, Bundler::Source::Rubygems::Remote
|
|
179
|
+
GEM_SOURCES_TYPES[:gemfile_source]
|
|
180
|
+
when Bundler::Source::Git
|
|
181
|
+
GEM_SOURCES_TYPES[:gemfile_git]
|
|
182
|
+
when Bundler::Source::Path
|
|
183
|
+
GEM_SOURCES_TYPES[:gemfile_path]
|
|
184
|
+
else
|
|
185
|
+
raise ArgumentError, "unknown source #{source.class}"
|
|
186
|
+
end
|
|
123
187
|
end
|
|
124
188
|
|
|
189
|
+
# Build gem source
|
|
190
|
+
#
|
|
191
|
+
# @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
|
|
192
|
+
#
|
|
193
|
+
# @return [Hash]
|
|
125
194
|
def build_spec_source(spec)
|
|
126
|
-
|
|
127
|
-
dep_spec = @direct_dependencies[spec.name]
|
|
195
|
+
source = source_for_spec(spec)
|
|
128
196
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
197
|
+
{
|
|
198
|
+
'type' => build_spec_gem_source_type(source),
|
|
199
|
+
'value' => source_name_from_source(source)
|
|
200
|
+
}
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Figure out source for gem
|
|
204
|
+
#
|
|
205
|
+
# @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
|
|
206
|
+
#
|
|
207
|
+
# @return [Bundler::Source] gem source type
|
|
208
|
+
def source_for_spec(spec)
|
|
209
|
+
return spec.remote if spec.remote
|
|
210
|
+
|
|
211
|
+
case spec.source
|
|
212
|
+
when Bundler::Source::Rubygems
|
|
213
|
+
spec
|
|
214
|
+
.source
|
|
215
|
+
.send(:remote_specs)
|
|
216
|
+
.search(Bundler::Dependency.new(spec.name, spec.version))
|
|
217
|
+
.last
|
|
218
|
+
.remote
|
|
219
|
+
when Bundler::Source::Metadata, Bundler::Source::Git, Bundler::Source::Path
|
|
220
|
+
spec.source
|
|
134
221
|
else
|
|
135
|
-
|
|
136
|
-
{ 'type' => GEM_SOURCES[:gemfile], 'url' => source_name_from_source(spec.source) }
|
|
137
|
-
else
|
|
138
|
-
{ 'type' => GEM_SOURCES[:local], 'url' => '' }
|
|
139
|
-
end
|
|
222
|
+
raise ArgumentError, "unknown source #{spec.source.class}"
|
|
140
223
|
end
|
|
141
224
|
end
|
|
142
225
|
|
|
226
|
+
# Build gem source name
|
|
227
|
+
#
|
|
228
|
+
# @param source [Bundler::Source] gem source type
|
|
229
|
+
#
|
|
230
|
+
# @return [String]
|
|
143
231
|
def source_name_from_source(source)
|
|
144
|
-
|
|
232
|
+
case source
|
|
233
|
+
when Bundler::Source::Metadata
|
|
234
|
+
''
|
|
235
|
+
when Bundler::Source::Rubygems::Remote
|
|
236
|
+
source_name(source.anonymized_uri)
|
|
237
|
+
when Bundler::Source::Git
|
|
238
|
+
source.instance_variable_get(:@safe_uri)
|
|
239
|
+
when Bundler::Source::Path
|
|
240
|
+
source.path
|
|
241
|
+
else
|
|
242
|
+
raise ArgumentError, "unknown source #{source.class}"
|
|
243
|
+
end
|
|
145
244
|
end
|
|
146
245
|
|
|
147
|
-
|
|
148
|
-
|
|
246
|
+
# @param uri [Bundler::URI]
|
|
247
|
+
#
|
|
248
|
+
# @return [String]
|
|
249
|
+
def source_name(uri)
|
|
250
|
+
uri.to_s[0...-1]
|
|
149
251
|
end
|
|
150
252
|
|
|
253
|
+
# Build sources used in the Gemfile
|
|
254
|
+
#
|
|
255
|
+
# @return [Array<Hash>]
|
|
151
256
|
def build_sources
|
|
152
257
|
sources = @definition.send(:sources).rubygems_sources
|
|
153
|
-
hash =
|
|
258
|
+
hash = {}
|
|
154
259
|
|
|
155
260
|
sources.each do |source|
|
|
156
|
-
type = source.remotes
|
|
261
|
+
type = build_source_type(source.remotes)
|
|
157
262
|
|
|
158
263
|
source.remotes.each do |src|
|
|
159
|
-
hash
|
|
264
|
+
hash[source_name(src)] = type
|
|
160
265
|
end
|
|
161
266
|
end
|
|
162
267
|
|
|
163
|
-
hash
|
|
268
|
+
hash.map { |name, type| { 'name' => name, 'type' => type } }
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Build gem source type
|
|
272
|
+
#
|
|
273
|
+
# @param remotes [Array<Bundler::URI>]
|
|
274
|
+
#
|
|
275
|
+
# @return [Integer] internal source type
|
|
276
|
+
def build_source_type(remotes)
|
|
277
|
+
remotes.count > 1 ? SOURCES_TYPES[:multiple_primary] : SOURCES_TYPES[:valid]
|
|
164
278
|
end
|
|
165
279
|
|
|
166
280
|
# Checks if we should skip a source
|
|
167
281
|
#
|
|
168
|
-
# @param source [Bundler::Source
|
|
282
|
+
# @param source [Bundler::Source] gem source type
|
|
169
283
|
#
|
|
170
284
|
# @return [Boolean] true if we should skip this source, false otherwise
|
|
171
285
|
def skip?(source)
|
|
172
|
-
return true if git?(source)
|
|
173
286
|
return true if me?(source)
|
|
174
287
|
|
|
175
288
|
false
|
|
176
289
|
end
|
|
177
290
|
|
|
178
|
-
# Checks if it's a git source
|
|
179
|
-
#
|
|
180
|
-
# @param source [Bundler::Source::Git, Bundler::Source::Rubygems]
|
|
181
|
-
#
|
|
182
|
-
# @return [Boolean] true if it's a git source, false otherwise
|
|
183
|
-
def git?(source)
|
|
184
|
-
source.instance_of?(Bundler::Source::Git)
|
|
185
|
-
end
|
|
186
|
-
|
|
187
291
|
# Checks if it's a self source, this happens for repositories that are a gem
|
|
188
292
|
#
|
|
189
|
-
# @param source [Bundler::Source
|
|
293
|
+
# @param source [Bundler::Source] gem source type
|
|
190
294
|
#
|
|
191
295
|
# @return [Boolean] true if it's a self source, false otherwise
|
|
192
296
|
def me?(source)
|