diffend-monitor 0.2.28 → 0.2.30

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.
@@ -0,0 +1,302 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Diffend
4
+ # Module responsible for building local context
5
+ module LocalContext
6
+ # Module responsible for building packages information from local context
7
+ class Packages
8
+ # Definition of a local path, if it matches it means that we are the source
9
+ ME_PATH = '.'
10
+ # Sources that we expect to match ourselves too
11
+ ME_SOURCES = [
12
+ Bundler::Source::Gemspec,
13
+ Bundler::Source::Path
14
+ ].freeze
15
+ # List of dependency types
16
+ DEPENDENCIES_TYPES = {
17
+ direct: 0,
18
+ dependency: 1
19
+ }.freeze
20
+ # List of sources types
21
+ SOURCES_TYPES = {
22
+ valid: 0,
23
+ multiple_primary: 1
24
+ }.freeze
25
+ # List of gem sources types
26
+ GEM_SOURCES_TYPES = {
27
+ local: 0,
28
+ gemfile_source: 1,
29
+ gemfile_git: 2,
30
+ gemfile_path: 3
31
+ }.freeze
32
+
33
+ class << self
34
+ # @param command [String] either install or update
35
+ # @param definition [Bundler::Definition] definition for your source
36
+ def call(command, definition)
37
+ Bundler.ui.silence { definition.resolve_remotely! }
38
+
39
+ instance = new(definition)
40
+
41
+ case command
42
+ when Commands::INSTALL, Commands::EXEC then instance.build_install
43
+ when Commands::UPDATE then instance.build_update
44
+ else
45
+ raise ArgumentError, "invalid command: #{command}"
46
+ end
47
+ end
48
+ end
49
+
50
+ # @param definition [Bundler::Definition] definition for your source
51
+ #
52
+ # @return [Hash] local dependencies
53
+ def initialize(definition)
54
+ @definition = definition
55
+ @direct_dependencies = Hash[definition.dependencies.map { |val| [val.name, val] }]
56
+ # Support case without Gemfile.lock
57
+ @locked_specs = @definition.locked_gems ? @definition.locked_gems.specs : []
58
+ end
59
+
60
+ # Build install specification
61
+ #
62
+ # @return [Hash]
63
+ def build_install
64
+ hash = build_main
65
+
66
+ @definition.specs.each do |spec|
67
+ next if skip?(spec.source)
68
+
69
+ locked_spec = @locked_specs.find { |s| s.name == spec.name }
70
+
71
+ hash['dependencies'][spec.name] = {
72
+ 'platform' => build_spec_platform(spec, locked_spec),
73
+ 'source' => build_spec_source(spec),
74
+ 'type' => build_dependency_type(spec.name),
75
+ 'versions' => build_versions(spec, locked_spec)
76
+ }
77
+ end
78
+
79
+ hash
80
+ end
81
+
82
+ # Build update specification
83
+ #
84
+ # @return [Hash]
85
+ def build_update
86
+ hash = build_main
87
+
88
+ @definition.specs.each do |spec|
89
+ next if skip?(spec.source)
90
+
91
+ locked_spec = @locked_specs.find { |s| s.name == spec.name }
92
+
93
+ hash['dependencies'][spec.name] = {
94
+ 'platform' => build_spec_platform(spec, locked_spec),
95
+ 'source' => build_spec_source(spec),
96
+ 'type' => build_dependency_type(spec.name),
97
+ 'versions' => build_versions(spec, locked_spec)
98
+ }
99
+ end
100
+
101
+ hash
102
+ end
103
+
104
+ private
105
+
106
+ # Build default specification
107
+ #
108
+ # @return [Hash]
109
+ def build_main
110
+ {
111
+ 'dependencies' => {},
112
+ 'sources' => build_sources,
113
+ 'plugins' => {},
114
+ 'platforms' => @definition.platforms.map(&:to_s)
115
+ }
116
+ end
117
+
118
+ # Build gem versions
119
+ #
120
+ # @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
121
+ # @param locked_spec [Bundler::LazySpecification, Gem::Specification, NilClass]
122
+ #
123
+ # @return [Array<String>]
124
+ def build_versions(spec, locked_spec = nil)
125
+ if locked_spec && locked_spec.version.to_s != spec.version.to_s
126
+ [locked_spec.version.to_s, spec.version.to_s]
127
+ else
128
+ [spec.version.to_s]
129
+ end
130
+ end
131
+
132
+ # @param specs [Array] specs that are direct dependencies
133
+ # @param name [String] spec name
134
+ #
135
+ # @return [Boolean] dependency type
136
+ def build_dependency_type(name)
137
+ if @direct_dependencies.key?(name)
138
+ DEPENDENCIES_TYPES[:direct]
139
+ else
140
+ DEPENDENCIES_TYPES[:dependency]
141
+ end
142
+ end
143
+
144
+ # Build gem platform
145
+ #
146
+ # @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
147
+ # @param locked_spec [Bundler::LazySpecification, Gem::Specification, NilClass]
148
+ #
149
+ # @return [String]
150
+ def build_spec_platform(spec, locked_spec)
151
+ parse_platform(
152
+ spec.platform || locked_spec&.platform || spec.send(:generic_local_platform)
153
+ )
154
+ end
155
+
156
+ # Parse gem platform
157
+ #
158
+ # @param platform [String, Gem::Platform]
159
+ #
160
+ # @return [String]
161
+ def parse_platform(platform)
162
+ case platform
163
+ when String then platform
164
+ when Gem::Platform then platform.os
165
+ end
166
+ end
167
+
168
+ # Build gem source type
169
+ #
170
+ # @param source [Bundler::Source] gem source type
171
+ #
172
+ # @return [Integer] internal gem source type
173
+ def build_spec_gem_source_type(source)
174
+ case source
175
+ when Bundler::Source::Metadata
176
+ GEM_SOURCES_TYPES[:local]
177
+ when Bundler::Source::Rubygems, Bundler::Source::Rubygems::Remote
178
+ GEM_SOURCES_TYPES[:gemfile_source]
179
+ when Bundler::Source::Git
180
+ GEM_SOURCES_TYPES[:gemfile_git]
181
+ when Bundler::Source::Path
182
+ GEM_SOURCES_TYPES[:gemfile_path]
183
+ else
184
+ raise ArgumentError, "unknown source #{source.class}"
185
+ end
186
+ end
187
+
188
+ # Build gem source
189
+ #
190
+ # @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
191
+ #
192
+ # @return [Hash]
193
+ def build_spec_source(spec)
194
+ source = source_for_spec(spec)
195
+
196
+ {
197
+ 'type' => build_spec_gem_source_type(source),
198
+ 'value' => source_name_from_source(source)
199
+ }
200
+ end
201
+
202
+ # Figure out source for gem
203
+ #
204
+ # @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
205
+ #
206
+ # @return [Bundler::Source] gem source type
207
+ def source_for_spec(spec)
208
+ return spec.remote if spec.remote
209
+
210
+ case spec.source
211
+ when Bundler::Source::Rubygems
212
+ spec
213
+ .source
214
+ .send(:remote_specs)
215
+ .search(Bundler::Dependency.new(spec.name, spec.version))
216
+ .last
217
+ .remote
218
+ when Bundler::Source::Metadata, Bundler::Source::Git, Bundler::Source::Path
219
+ spec.source
220
+ else
221
+ raise ArgumentError, "unknown source #{spec.source.class}"
222
+ end
223
+ end
224
+
225
+ # Build gem source name
226
+ #
227
+ # @param source [Bundler::Source] gem source type
228
+ #
229
+ # @return [String]
230
+ def source_name_from_source(source)
231
+ case source
232
+ when Bundler::Source::Metadata
233
+ ''
234
+ when Bundler::Source::Rubygems::Remote
235
+ source_name(source.anonymized_uri)
236
+ when Bundler::Source::Git
237
+ source.instance_variable_get(:@safe_uri)
238
+ when Bundler::Source::Path
239
+ source.path
240
+ else
241
+ raise ArgumentError, "unknown source #{source.class}"
242
+ end
243
+ end
244
+
245
+ # @param uri [Bundler::URI]
246
+ #
247
+ # @return [String]
248
+ def source_name(uri)
249
+ uri.to_s[0...-1]
250
+ end
251
+
252
+ # Build sources used in the Gemfile
253
+ #
254
+ # @return [Array<Hash>]
255
+ def build_sources
256
+ sources = @definition.send(:sources).rubygems_sources
257
+ hash = {}
258
+
259
+ sources.each do |source|
260
+ type = build_source_type(source.remotes)
261
+
262
+ source.remotes.each do |src|
263
+ hash[source_name(src)] = type
264
+ end
265
+ end
266
+
267
+ hash.map { |name, type| { 'name' => name, 'type' => type } }
268
+ end
269
+
270
+ # Build gem source type
271
+ #
272
+ # @param remotes [Array<Bundler::URI>]
273
+ #
274
+ # @return [Integer] internal source type
275
+ def build_source_type(remotes)
276
+ remotes.count > 1 ? SOURCES_TYPES[:multiple_primary] : SOURCES_TYPES[:valid]
277
+ end
278
+
279
+ # Checks if we should skip a source
280
+ #
281
+ # @param source [Bundler::Source] gem source type
282
+ #
283
+ # @return [Boolean] true if we should skip this source, false otherwise
284
+ def skip?(source)
285
+ return true if me?(source)
286
+
287
+ false
288
+ end
289
+
290
+ # Checks if it's a self source, this happens for repositories that are a gem
291
+ #
292
+ # @param source [Bundler::Source] gem source type
293
+ #
294
+ # @return [Boolean] true if it's a self source, false otherwise
295
+ def me?(source)
296
+ return false unless ME_SOURCES.include?(source.class)
297
+
298
+ source.path.to_s == ME_PATH
299
+ end
300
+ end
301
+ end
302
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Diffend
4
+ # Module responsible for building local context
5
+ module LocalContext
6
+ # Module responsible for building platform information from local context
7
+ module Platform
8
+ class << self
9
+ # Build platform information
10
+ #
11
+ # @return [Hash]
12
+ def call
13
+ {
14
+ 'bundler' => {
15
+ 'version' => Bundler::VERSION
16
+ },
17
+ 'environment' => environment,
18
+ 'ruby' => ruby_information,
19
+ 'rubygems' => {
20
+ 'specification_version' => Gem::Specification::CURRENT_SPECIFICATION_VERSION,
21
+ 'version' => Gem::VERSION
22
+ }
23
+ }.freeze
24
+ end
25
+
26
+ private
27
+
28
+ # Build platform ruby information
29
+ #
30
+ # @return [Hash]
31
+ def ruby_information
32
+ if defined?(JRUBY_VERSION)
33
+ revision = JRUBY_REVISION.to_s
34
+ version = JRUBY_VERSION
35
+ else
36
+ revision = RUBY_REVISION.to_s
37
+ version = RUBY_ENGINE_VERSION
38
+ end
39
+
40
+ {
41
+ 'engine' => RUBY_ENGINE,
42
+ 'patchlevel' => RUBY_PATCHLEVEL,
43
+ 'release_date' => RUBY_RELEASE_DATE,
44
+ 'revision' => revision,
45
+ 'version' => version
46
+ }
47
+ end
48
+
49
+ # Build platform environment information
50
+ #
51
+ # @return [String]
52
+ def environment
53
+ ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,30 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ENV['DIFFEND_ENV'] ||= 'development'
4
+
3
5
  %w[
4
- build_bundler_definition
6
+ version
5
7
  errors
8
+ build_bundler_definition
9
+ commands
10
+ config
6
11
  config/fetcher
7
12
  config/file_finder
8
13
  config/validator
9
- commands
10
14
  handle_errors/messages
11
15
  handle_errors/build_exception_payload
12
16
  handle_errors/display_to_stdout
13
17
  handle_errors/report
14
18
  request_object
15
19
  request
16
- voting
20
+ local_context/diffend
21
+ local_context/host
22
+ local_context/packages
23
+ local_context/platform
24
+ local_context
25
+ request_verdict
26
+ execute
17
27
  track
18
28
  ].each { |file| require "diffend/#{file}" }
19
29
 
20
- %w[
21
- versions/local
22
- versions/remote
23
- ].each { |file| require "diffend/voting/#{file}" }
30
+ return if ENV['DIFFEND_ENV'].strip.empty?
31
+ return if %w[development test].include?(ENV['DIFFEND_ENV'])
24
32
 
25
- unless %w[development test].include?(ENV['DIFFEND_ENV'])
26
- Thread.new do
27
- track = Diffend::Track.new
28
- track.start
29
- end
33
+ Thread.new do
34
+ track = Diffend::Track.new
35
+ track.start
30
36
  end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV['DIFFEND_ENV'] ||= 'development'
4
+
5
+ %w[
6
+ bundler
7
+ ].each(&method(:require))
8
+
9
+ %w[
10
+ version
11
+ errors
12
+ build_bundler_definition
13
+ commands
14
+ config
15
+ config/fetcher
16
+ config/file_finder
17
+ config/validator
18
+ handle_errors/messages
19
+ handle_errors/build_exception_payload
20
+ handle_errors/display_to_stdout
21
+ handle_errors/report
22
+ request_object
23
+ request
24
+ local_context/diffend
25
+ local_context/host
26
+ local_context/packages
27
+ local_context/platform
28
+ local_context
29
+ request_verdict
30
+ execute
31
+ track
32
+ ].each { |file| require "diffend/#{file}" }
33
+
34
+ module Diffend
35
+ module Plugin
36
+ class << self
37
+ # Registers the plugin and add before install all hook
38
+ def register
39
+ ::Bundler::Plugin.add_hook('before-install-all') do |_|
40
+ execute
41
+ end
42
+ end
43
+
44
+ # Execute diffend plugin
45
+ def execute
46
+ return unless enabled?
47
+
48
+ verify_version
49
+
50
+ config = Diffend::Config.call
51
+
52
+ Diffend::Execute.call(command, config)
53
+ rescue Diffend::Errors::HandledException
54
+ return if ENV['DIFFEND_IGNORE_ERRORS'] == 'true'
55
+
56
+ exit 255
57
+ rescue StandardError => e
58
+ Diffend::HandleErrors::Report.call(
59
+ exception: e,
60
+ config: config,
61
+ message: :unhandled_exception,
62
+ report: true,
63
+ raise_exception: false
64
+ )
65
+
66
+ return if ENV['DIFFEND_IGNORE_ERRORS'] == 'true'
67
+
68
+ exit 255
69
+ end
70
+
71
+ def verify_version
72
+ return if ENV['DIFFEND_DEVELOPMENT'] == 'true'
73
+ return if installed_version == Diffend::VERSION
74
+
75
+ build_outdated_version_message(installed_version)
76
+ .tap(&::Bundler.ui.method(:error))
77
+
78
+ exit 2
79
+ end
80
+
81
+ # @return [String] installed plugin version
82
+ def installed_version
83
+ ::Bundler::Plugin
84
+ .index
85
+ .plugin_path('diffend')
86
+ .basename
87
+ .to_s
88
+ .split('-')
89
+ .last
90
+ end
91
+
92
+ # Checks if plugin is enabled
93
+ #
94
+ # @return [Boolean] true if enabled, false otherwise
95
+ def enabled?
96
+ ::Bundler
97
+ .default_gemfile
98
+ .read
99
+ .split("\n")
100
+ .reject(&:empty?)
101
+ .map(&:strip)
102
+ .select { |line| line.start_with?('plugin') }
103
+ .any? { |line| line.include?('diffend') }
104
+ end
105
+
106
+ # @param version [Hash] installed version
107
+ #
108
+ # @return [String]
109
+ def build_outdated_version_message(version)
110
+ <<~MSG
111
+ \nYou are running an outdated version (#{version}) of the plugin, which will lead to issues.
112
+ \nPlease upgrade to the latest one (#{VERSION}) by executing "rm -rf .bundle/plugin".\n
113
+ MSG
114
+ end
115
+
116
+ # Command that was run with bundle
117
+ #
118
+ # @return [String]
119
+ def command
120
+ ARGV.first || ::Bundler.feature_flag.default_cli_command.to_s
121
+ end
122
+ end
123
+ end
124
+ end