diffend 0.2.19 → 0.2.27

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.
@@ -40,7 +40,7 @@ module Diffend
40
40
  instance = new(definition)
41
41
 
42
42
  case command
43
- when Commands::INSTALL then instance.build_install
43
+ when Commands::INSTALL, Commands::EXEC then instance.build_install
44
44
  when Commands::UPDATE then instance.build_update
45
45
  else
46
46
  raise ArgumentError, "invalid command: #{command}"
@@ -54,7 +54,6 @@ module Diffend
54
54
  def initialize(definition)
55
55
  @definition = definition
56
56
  @direct_dependencies = Hash[definition.dependencies.map { |val| [val.name, val] }]
57
- @main_source = definition.send(:sources).rubygems_sources.last
58
57
  # Support case without Gemfile.lock
59
58
  @locked_specs = @definition.locked_gems ? @definition.locked_gems.specs : []
60
59
  end
@@ -65,18 +64,16 @@ module Diffend
65
64
  def build_install
66
65
  hash = build_main
67
66
 
68
- @definition.requested_specs.each do |spec|
67
+ @definition.specs.each do |spec|
69
68
  next if skip?(spec.source)
70
69
 
71
70
  locked_spec = @locked_specs.find { |s| s.name == spec.name }
72
71
 
73
- spec2 = locked_spec || spec
74
-
75
- hash['dependencies'][spec2.name] = {
76
- 'platform' => build_spec_platform(spec2),
77
- 'source' => build_spec_source(spec2),
78
- 'type' => build_dependency_type(spec2.name),
79
- '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)
80
77
  }
81
78
  end
82
79
 
@@ -89,13 +86,13 @@ module Diffend
89
86
  def build_update
90
87
  hash = build_main
91
88
 
92
- @definition.requested_specs.each do |spec|
89
+ @definition.specs.each do |spec|
93
90
  next if skip?(spec.source)
94
91
 
95
92
  locked_spec = @locked_specs.find { |s| s.name == spec.name }
96
93
 
97
94
  hash['dependencies'][spec.name] = {
98
- 'platform' => build_spec_platform(spec),
95
+ 'platform' => build_spec_platform(spec, locked_spec),
99
96
  'source' => build_spec_source(spec),
100
97
  'type' => build_dependency_type(spec.name),
101
98
  'versions' => build_versions(spec, locked_spec)
@@ -126,7 +123,11 @@ module Diffend
126
123
  #
127
124
  # @return [Array<String>]
128
125
  def build_versions(spec, locked_spec = nil)
129
- locked_spec ? [locked_spec.version.to_s, spec.version.to_s] : [spec.version.to_s]
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
130
131
  end
131
132
 
132
133
  # @param specs [Array] specs that are direct dependencies
@@ -144,10 +145,25 @@ module Diffend
144
145
  # Build gem platform
145
146
  #
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]
147
160
  #
148
161
  # @return [String]
149
- def build_spec_platform(spec)
150
- spec.platform || spec.send(:generic_local_platform)
162
+ def parse_platform(platform)
163
+ case platform
164
+ when String then platform
165
+ when Gem::Platform then platform.os
166
+ end
151
167
  end
152
168
 
153
169
  # Build gem source type
@@ -159,7 +175,7 @@ module Diffend
159
175
  case source
160
176
  when Bundler::Source::Metadata
161
177
  GEM_SOURCES_TYPES[:local]
162
- when Bundler::Source::Rubygems
178
+ when Bundler::Source::Rubygems, Bundler::Source::Rubygems::Remote
163
179
  GEM_SOURCES_TYPES[:gemfile_source]
164
180
  when Bundler::Source::Git
165
181
  GEM_SOURCES_TYPES[:gemfile_git]
@@ -190,10 +206,20 @@ module Diffend
190
206
  #
191
207
  # @return [Bundler::Source] gem source type
192
208
  def source_for_spec(spec)
193
- if @direct_dependencies.key?(spec.name)
194
- @direct_dependencies[spec.name].source || @main_source
195
- else
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
196
220
  spec.source
221
+ else
222
+ raise ArgumentError, "unknown source #{spec.source.class}"
197
223
  end
198
224
  end
199
225
 
@@ -206,10 +232,10 @@ module Diffend
206
232
  case source
207
233
  when Bundler::Source::Metadata
208
234
  ''
209
- when Bundler::Source::Rubygems
210
- source_name(source.remotes.first)
235
+ when Bundler::Source::Rubygems::Remote
236
+ source_name(source.anonymized_uri)
211
237
  when Bundler::Source::Git
212
- source.uri
238
+ source.instance_variable_get(:@safe_uri)
213
239
  when Bundler::Source::Path
214
240
  source.path
215
241
  else
@@ -217,11 +243,11 @@ module Diffend
217
243
  end
218
244
  end
219
245
 
220
- # @param name [Bundler::URI]
246
+ # @param uri [Bundler::URI]
221
247
  #
222
- # @return [String] cleaned source name
223
- def source_name(name)
224
- name.to_s[0...-1]
248
+ # @return [String]
249
+ def source_name(uri)
250
+ uri.to_s[0...-1]
225
251
  end
226
252
 
227
253
  # Build sources used in the Gemfile
@@ -229,17 +255,17 @@ module Diffend
229
255
  # @return [Array<Hash>]
230
256
  def build_sources
231
257
  sources = @definition.send(:sources).rubygems_sources
232
- hash = []
258
+ hash = {}
233
259
 
234
260
  sources.each do |source|
235
261
  type = build_source_type(source.remotes)
236
262
 
237
263
  source.remotes.each do |src|
238
- hash << { 'name' => source_name(src), 'type' => type }
264
+ hash[source_name(src)] = type
239
265
  end
240
266
  end
241
267
 
242
- hash
268
+ hash.map { |name, type| { 'name' => name, 'type' => type } }
243
269
  end
244
270
 
245
271
  # Build gem source type
@@ -20,14 +20,37 @@ module Diffend
20
20
  class << self
21
21
  # @param command [String] either install or update
22
22
  # @param definition [Bundler::Definition] definition for your source
23
- def call(command, definition)
24
- config = fetch_config
23
+ # @param config [OpenStruct] diffend config
24
+ def call(command, config, definition)
25
+ payload = payload(command, config.project_id, definition)
25
26
 
26
- response = Request.call(
27
- command, payload(command, config&.project_id, definition), config
27
+ response = Diffend::Request.call(
28
+ build_request_object(command, config, payload)
28
29
  )
29
30
 
30
31
  JSON.parse(response.body)
32
+ rescue StandardError => e
33
+ Diffend::HandleErrors::Report.call(
34
+ exception: e,
35
+ payload: payload || {},
36
+ config: config,
37
+ message: :unhandled_exception,
38
+ report: true
39
+ )
40
+ end
41
+
42
+ # @param command [String] either install or update
43
+ # @param config [OpenStruct] diffend config
44
+ # @param payload [Hash]
45
+ #
46
+ # @return [Diffend::RequestObject]
47
+ def build_request_object(command, config, payload)
48
+ Diffend::RequestObject.new(
49
+ config: config,
50
+ url: commands_url(command, config.project_id),
51
+ payload: payload,
52
+ request_method: :post
53
+ )
31
54
  end
32
55
 
33
56
  # Build diffend, host, packages, and platform specific information
@@ -85,6 +108,9 @@ module Diffend
85
108
  }.freeze
86
109
  end
87
110
 
111
+ # Build platform ruby information
112
+ #
113
+ # @return [Hash]
88
114
  def build_platform_ruby
89
115
  if defined?(JRUBY_VERSION)
90
116
  revision = JRUBY_REVISION.to_s
@@ -117,7 +143,7 @@ module Diffend
117
143
  uname = Etc.uname
118
144
 
119
145
  {
120
- 'command' => { 'name' => '', 'options' => '' },
146
+ 'command' => build_host_command,
121
147
  'ips' => build_host_ips,
122
148
  'name' => uname[:nodename],
123
149
  'system' => {
@@ -127,10 +153,24 @@ module Diffend
127
153
  'version' => uname[:version]
128
154
  },
129
155
  'tags' => build_host_tags,
130
- 'user' => Etc.getpwuid(Process.uid).name
156
+ 'user' => Etc.getpwuid(Process.uid).name,
157
+ 'pid' => Process.pid
131
158
  }.freeze
132
159
  end
133
160
 
161
+ # Build host command information
162
+ #
163
+ # @return [Hash]
164
+ def build_host_command
165
+ {
166
+ 'name' => $PROGRAM_NAME.split('/').last.strip,
167
+ 'options' => ARGV.join(' ')
168
+ }
169
+ end
170
+
171
+ # Build host ips, except localhost and loopback
172
+ #
173
+ # @return [Array<String>]
134
174
  def build_host_ips
135
175
  Socket.ip_address_list.map do |ip|
136
176
  next if ip.ipv4_loopback? || ip.ipv6_loopback? || ip.ipv6_linklocal?
@@ -139,6 +179,9 @@ module Diffend
139
179
  end.compact
140
180
  end
141
181
 
182
+ # Build host tags
183
+ #
184
+ # @return [Array]
142
185
  def build_host_tags
143
186
  tags = []
144
187
 
@@ -155,15 +198,16 @@ module Diffend
155
198
  tags
156
199
  end
157
200
 
158
- # Fetch coditsu config file
201
+ # Provides diffend command endpoint url
159
202
  #
160
- # @return [OpenStruct, nil] configuration object
203
+ # @param command [String] either install or update
204
+ # @param project_id [String] diffend project_id
161
205
  #
162
- # @raise [Errors::MissingConfigurationFile] when no config file
163
- def fetch_config
164
- Config::Fetcher.call(
165
- File.expand_path('..', Bundler.bin_path)
166
- )
206
+ # @return [String] diffend endpoint
207
+ def commands_url(command, project_id)
208
+ return ENV['DIFFEND_COMMAND_URL'] if ENV.key?('DIFFEND_COMMAND_URL')
209
+
210
+ "https://my.diffend.io/api/projects/#{project_id}/bundle/#{command}"
167
211
  end
168
212
  end
169
213
  end
@@ -10,6 +10,6 @@ project_id = nil
10
10
  gemfile = ARGV[0]
11
11
  lockfile = ARGV[1]
12
12
 
13
- definition = Bundler::Definition.build(gemfile, lockfile, true)
13
+ definition = Diffend::BuildBundlerDefinition.call(command, gemfile lockfile)
14
14
 
15
15
  pp Diffend::Voting::Versions::Remote.payload(command, project_id, definition)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diffend
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.19
4
+ version: 0.2.27
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomasz Pajor
@@ -34,7 +34,7 @@ cert_chain:
34
34
  9MmF6uCQa1EjK2p8tYT0MnbHrFkoehxdX4VO9y99GAkhZyJNKPYPtyAUFV27sT2V
35
35
  LfCJRk4ifKIN/FUCwDSn8Cz0m6oH265q0p6wdzI6qrWOjP8tGOMBTA==
36
36
  -----END CERTIFICATE-----
37
- date: 2020-08-18 00:00:00.000000000 Z
37
+ date: 2020-09-16 00:00:00.000000000 Z
38
38
  dependencies:
39
39
  - !ruby/object:Gem::Dependency
40
40
  name: bundler
@@ -92,13 +92,21 @@ files:
92
92
  - certs/tomaszpajor.pem
93
93
  - diffend.gemspec
94
94
  - lib/diffend.rb
95
+ - lib/diffend/build_bundler_definition.rb
95
96
  - lib/diffend/commands.rb
96
97
  - lib/diffend/config/fetcher.rb
97
98
  - lib/diffend/config/file_finder.rb
98
99
  - lib/diffend/config/validator.rb
99
100
  - lib/diffend/errors.rb
101
+ - lib/diffend/handle_errors/build_exception_payload.rb
102
+ - lib/diffend/handle_errors/display_to_stdout.rb
103
+ - lib/diffend/handle_errors/messages.rb
104
+ - lib/diffend/handle_errors/report.rb
105
+ - lib/diffend/monitor.rb
106
+ - lib/diffend/request.rb
107
+ - lib/diffend/request_object.rb
108
+ - lib/diffend/track.rb
100
109
  - lib/diffend/voting.rb
101
- - lib/diffend/voting/request.rb
102
110
  - lib/diffend/voting/versions/local.rb
103
111
  - lib/diffend/voting/versions/remote.rb
104
112
  - plugins.rb
metadata.gz.sig CHANGED
Binary file
@@ -1,191 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'net/http'
4
- require 'openssl'
5
- require 'json'
6
- require 'securerandom'
7
-
8
- module Diffend
9
- module Voting
10
- # Module responsible for doing request to Diffend
11
- module Request
12
- # List of connection exceptions
13
- CONNECTION_EXCEPTIONS = [
14
- Errno::ECONNRESET,
15
- Errno::ENETUNREACH,
16
- Errno::EHOSTUNREACH,
17
- Errno::ECONNREFUSED
18
- ].freeze
19
- # List of timeout exceptions
20
- TIMEOUT_EXCEPTIONS = [
21
- Net::OpenTimeout,
22
- Net::ReadTimeout
23
- ].freeze
24
- # Request headers
25
- HEADERS = { 'Content-Type': 'application/json' }.freeze
26
-
27
- private_constant :HEADERS
28
-
29
- class << self
30
- # Execute request to Diffend
31
- #
32
- # @param command [String] either install or update
33
- # @param payload [Hash] with versions to check
34
- # @param config [OpenStruct] Diffend config
35
- #
36
- # @return [Net::HTTPResponse] response from Diffend
37
- def call(command, payload, config)
38
- retry_count ||= 0
39
-
40
- build_http(commands_url(command, config.project_id)) do |http, uri|
41
- http.request(build_request(uri, config, payload))
42
- end
43
- rescue *CONNECTION_EXCEPTIONS => e
44
- Bundler.ui.error('We experienced a connection issue, retrying...')
45
- sleep(exponential_backoff(retry_count))
46
- retry_count += 1
47
-
48
- retry if retry_count < 3
49
-
50
- output_report(build_report(payload, e))
51
- Bundler.ui.error('^^^ Above is the dump of your request ^^^')
52
- Bundler.ui.error(build_request_error_message)
53
-
54
- exit 1
55
- rescue *TIMEOUT_EXCEPTIONS => e
56
- Bundler.ui.error('We experienced a timeout issue, retrying...')
57
- sleep(exponential_backoff(retry_count))
58
- retry_count += 1
59
-
60
- retry if retry_count < 3
61
-
62
- output_report(build_report(payload, e))
63
- Bundler.ui.error('^^^ Above is the dump of your request ^^^')
64
- Bundler.ui.error(build_request_error_message)
65
-
66
- exit 1
67
- rescue StandardError => e
68
- exception_payload = build_report(payload, e)
69
- output_report(exception_payload)
70
- Bundler.ui.error('^^^ Above is the dump of your request ^^^')
71
- Bundler.ui.error(build_unhandled_exception_message)
72
-
73
- build_http(errors_url(config.project_id)) do |http, uri|
74
- http.request(build_request(uri, config, exception_payload))
75
- end
76
-
77
- exit 1
78
- end
79
-
80
- # Builds http connection object
81
- #
82
- # @param url [String] command endpoint url
83
- def build_http(url)
84
- uri = URI(url)
85
-
86
- Net::HTTP.start(
87
- uri.host,
88
- uri.port,
89
- use_ssl: uri.scheme == 'https',
90
- verify_mode: OpenSSL::SSL::VERIFY_NONE,
91
- open_timeout: 5,
92
- read_timeout: 5
93
- ) { |http| yield(http, uri) }
94
- end
95
-
96
- # Build http post request and assigns headers and payload
97
- #
98
- # @param uri [URI::HTTPS]
99
- # @param config [OpenStruct] Diffend config
100
- # @param payload [Hash] with versions to check
101
- #
102
- # @return [Net::HTTP::Post]
103
- def build_request(uri, config, payload)
104
- Net::HTTP::Post
105
- .new(uri.request_uri, HEADERS)
106
- .tap { |request| assign_auth(request, config) }
107
- .tap { |request| assign_payload(request, payload) }
108
- end
109
-
110
- # Assigns basic authorization if provided in the config
111
- #
112
- # @param request [Net::HTTP::Post] prepared http post
113
- # @param config [OpenStruct] Diffend config
114
- def assign_auth(request, config)
115
- return unless config
116
- return unless config.shareable_id
117
- return unless config.shareable_key
118
-
119
- request.basic_auth(config.shareable_id, config.shareable_key)
120
- end
121
-
122
- # Assigns payload as json
123
- #
124
- # @param request [Net::HTTP::Post] prepared http post
125
- # @param payload [Hash] with versions to check
126
- def assign_payload(request, payload)
127
- request.body = JSON.dump(payload: payload)
128
- end
129
-
130
- # Provides diffend command endpoint url
131
- #
132
- # @param command [String] either install or update
133
- # @param project_id [String] diffend project_id
134
- #
135
- # @return [String] diffend endpoint
136
- def commands_url(command, project_id)
137
- return ENV['DIFFEND_COMMAND_URL'] if ENV.key?('DIFFEND_COMMAND_URL')
138
-
139
- "https://my.diffend.io/api/projects/#{project_id}/bundle/#{command}"
140
- end
141
-
142
- # Provides diffend errors endpoint url
143
- #
144
- # @param project_id [String] diffend project_id
145
- #
146
- # @return [String] diffend endpoint
147
- def errors_url(project_id)
148
- return ENV['DIFFEND_ERROR_URL'] if ENV.key?('DIFFEND_ERROR_URL')
149
-
150
- "https://my.diffend.io/api/projects/#{project_id}/errors"
151
- end
152
-
153
- def exponential_backoff(retry_count)
154
- 2**(retry_count + 1)
155
- end
156
-
157
- def build_request_error_message
158
- <<~MSG
159
- \nWe were unable to process your request at this time. We recorded this incident in our system and will review it.\n
160
- If you think that this is a bug, don't hesitate.\n
161
- Create an issue on https://github.com/diffend-io/diffend-ruby/issues\n
162
- MSG
163
- end
164
-
165
- def build_unhandled_exception_message
166
- <<~MSG
167
- \nSomething went really wrong. We recorded this incident in our system and will review it.\n
168
- This is a bug, don't hesitate.\n
169
- Create an issue on https://github.com/diffend-io/diffend-ruby/issues\n
170
- MSG
171
- end
172
-
173
- def build_report(payload, exception)
174
- {
175
- request_id: SecureRandom.uuid,
176
- payload: payload,
177
- exception: {
178
- class: exception.class,
179
- message: exception.message,
180
- backtrace: exception.backtrace
181
- }
182
- }
183
- end
184
-
185
- def output_report(report)
186
- puts report.to_json
187
- end
188
- end
189
- end
190
- end
191
- end