diffend-monitor 0.2.28 → 0.2.30

Sign up to get free protection for your applications and to get access to all the features.
@@ -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