diffend 0.2.29 → 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,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
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Diffend
6
+ # Module responsible for fetching diffend verdict on local context
7
+ module RequestVerdict
8
+ class << self
9
+ # @param command [String] either install or update
10
+ # @param definition [Bundler::Definition] definition for your source
11
+ # @param config [OpenStruct] diffend config
12
+ def call(command, config, definition)
13
+ payload = Diffend::LocalContext.call(command, config.project_id, definition)
14
+
15
+ response = Diffend::Request.call(
16
+ build_request_object(command, config, payload)
17
+ )
18
+
19
+ JSON.parse(response.body)
20
+ rescue Bundler::GemNotFound
21
+ raise ::Diffend::Errors::DependenciesResolveException
22
+ rescue StandardError => e
23
+ Diffend::HandleErrors::Report.call(
24
+ exception: e,
25
+ payload: payload || {},
26
+ config: config,
27
+ message: :unhandled_exception,
28
+ report: true
29
+ )
30
+ end
31
+
32
+ # @param command [String] either install or update
33
+ # @param config [OpenStruct] diffend config
34
+ # @param payload [Hash]
35
+ #
36
+ # @return [Diffend::RequestObject]
37
+ def build_request_object(command, config, payload)
38
+ Diffend::RequestObject.new(
39
+ config: config,
40
+ url: commands_url(command, config.project_id),
41
+ payload: payload,
42
+ request_method: :post
43
+ )
44
+ end
45
+
46
+ # Provides diffend command endpoint url
47
+ #
48
+ # @param command [String] either install or update
49
+ # @param project_id [String] diffend project_id
50
+ #
51
+ # @return [String] diffend endpoint
52
+ def commands_url(command, project_id)
53
+ return ENV['DIFFEND_COMMAND_URL'] if ENV.key?('DIFFEND_COMMAND_URL')
54
+
55
+ "https://my.diffend.io/api/projects/#{project_id}/bundle/#{command}"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -11,7 +11,7 @@ module Diffend
11
11
  # Initialize tracking
12
12
  def initialize
13
13
  @mutex = Mutex.new
14
- @config = fetch_config
14
+ @config = Diffend::Config.call
15
15
  end
16
16
 
17
17
  # Start tracking
@@ -50,15 +50,7 @@ module Diffend
50
50
 
51
51
  # Perform an exec request
52
52
  def exec_request
53
- Diffend::Voting.call(
54
- Diffend::Commands::EXEC,
55
- @config,
56
- Diffend::BuildBundlerDefinition.call(
57
- Diffend::Commands::EXEC,
58
- Bundler.default_gemfile,
59
- Bundler.default_lockfile
60
- )
61
- )
53
+ Diffend::Execute.call(Diffend::Commands::EXEC, @config)
62
54
  end
63
55
 
64
56
  # Perform a track request
@@ -82,17 +74,6 @@ module Diffend
82
74
  ).freeze
83
75
  end
84
76
 
85
- # Fetch diffend config file
86
- #
87
- # @return [OpenStruct, nil] configuration object
88
- #
89
- # @raise [Errors::MissingConfigurationFile] when no config file
90
- def fetch_config
91
- Config::Fetcher.call(
92
- File.expand_path('..', Bundler.bin_path)
93
- )
94
- end
95
-
96
77
  # @param project_id [String] diffend project_id
97
78
  # @param request_id [String]
98
79
  #
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Diffend
4
+ # Current version
5
+ VERSION = '0.2.30'
6
+ end
data/plugins.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'diffend'
3
+ require 'diffend/plugin'
4
4
 
5
- Diffend.register
5
+ Diffend::Plugin.register
@@ -3,7 +3,6 @@
3
3
  require 'byebug'
4
4
  require 'diffend'
5
5
 
6
-
7
6
  command = 'install'
8
7
  project_id = nil
9
8
 
@@ -12,4 +11,4 @@ lockfile = ARGV[1]
12
11
 
13
12
  definition = Diffend::BuildBundlerDefinition.call(command, gemfile lockfile)
14
13
 
15
- pp Diffend::Voting::Versions::Remote.payload(command, project_id, definition)
14
+ pp Diffend::LocalContext.call(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.29
4
+ version: 0.2.30
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomasz Pajor
@@ -94,21 +94,28 @@ files:
94
94
  - lib/diffend.rb
95
95
  - lib/diffend/build_bundler_definition.rb
96
96
  - lib/diffend/commands.rb
97
+ - lib/diffend/config.rb
97
98
  - lib/diffend/config/fetcher.rb
98
99
  - lib/diffend/config/file_finder.rb
99
100
  - lib/diffend/config/validator.rb
100
101
  - lib/diffend/errors.rb
102
+ - lib/diffend/execute.rb
101
103
  - lib/diffend/handle_errors/build_exception_payload.rb
102
104
  - lib/diffend/handle_errors/display_to_stdout.rb
103
105
  - lib/diffend/handle_errors/messages.rb
104
106
  - lib/diffend/handle_errors/report.rb
107
+ - lib/diffend/local_context.rb
108
+ - lib/diffend/local_context/diffend.rb
109
+ - lib/diffend/local_context/host.rb
110
+ - lib/diffend/local_context/packages.rb
111
+ - lib/diffend/local_context/platform.rb
105
112
  - lib/diffend/monitor.rb
113
+ - lib/diffend/plugin.rb
106
114
  - lib/diffend/request.rb
107
115
  - lib/diffend/request_object.rb
116
+ - lib/diffend/request_verdict.rb
108
117
  - lib/diffend/track.rb
109
- - lib/diffend/voting.rb
110
- - lib/diffend/voting/versions/local.rb
111
- - lib/diffend/voting/versions/remote.rb
118
+ - lib/diffend/version.rb
112
119
  - plugins.rb
113
120
  - scripts/generate_payload_for_file.rb
114
121
  homepage: https://diffend.io
@@ -133,5 +140,5 @@ requirements: []
133
140
  rubygems_version: 3.1.2
134
141
  signing_key:
135
142
  specification_version: 4
136
- summary: OSS supply chain security and management platform.
143
+ summary: OSS supply chain security and management platform
137
144
  test_files: []
metadata.gz.sig CHANGED
Binary file
@@ -1,304 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Diffend
4
- module Voting
5
- # Module responsible for handling both local and remote gem versions
6
- module Versions
7
- # Module responsible for preparing current or current/new versions of gems
8
- class Local
9
- # Definition of a local path, if it matches it means that we are the source
10
- ME_PATH = '.'
11
- # Sources that we expect to match ourselves too
12
- ME_SOURCES = [
13
- Bundler::Source::Gemspec,
14
- Bundler::Source::Path
15
- ].freeze
16
- # List of dependency types
17
- DEPENDENCIES_TYPES = {
18
- direct: 0,
19
- dependency: 1
20
- }.freeze
21
- # List of sources types
22
- SOURCES_TYPES = {
23
- valid: 0,
24
- multiple_primary: 1
25
- }.freeze
26
- # List of gem sources types
27
- GEM_SOURCES_TYPES = {
28
- local: 0,
29
- gemfile_source: 1,
30
- gemfile_git: 2,
31
- gemfile_path: 3
32
- }.freeze
33
-
34
- class << self
35
- # @param command [String] either install or update
36
- # @param definition [Bundler::Definition] definition for your source
37
- def call(command, definition)
38
- Bundler.ui.silence { definition.resolve_remotely! }
39
-
40
- instance = new(definition)
41
-
42
- case command
43
- when Commands::INSTALL, Commands::EXEC then instance.build_install
44
- when Commands::UPDATE then instance.build_update
45
- else
46
- raise ArgumentError, "invalid command: #{command}"
47
- end
48
- end
49
- end
50
-
51
- # @param definition [Bundler::Definition] definition for your source
52
- #
53
- # @return [Hash] local dependencies
54
- def initialize(definition)
55
- @definition = definition
56
- @direct_dependencies = Hash[definition.dependencies.map { |val| [val.name, val] }]
57
- # Support case without Gemfile.lock
58
- @locked_specs = @definition.locked_gems ? @definition.locked_gems.specs : []
59
- end
60
-
61
- # Build install specification
62
- #
63
- # @return [Hash]
64
- def build_install
65
- hash = build_main
66
-
67
- @definition.specs.each do |spec|
68
- next if skip?(spec.source)
69
-
70
- locked_spec = @locked_specs.find { |s| s.name == spec.name }
71
-
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)
77
- }
78
- end
79
-
80
- hash
81
- end
82
-
83
- # Build update specification
84
- #
85
- # @return [Hash]
86
- def build_update
87
- hash = build_main
88
-
89
- @definition.specs.each do |spec|
90
- next if skip?(spec.source)
91
-
92
- locked_spec = @locked_specs.find { |s| s.name == spec.name }
93
-
94
- hash['dependencies'][spec.name] = {
95
- 'platform' => build_spec_platform(spec, locked_spec),
96
- 'source' => build_spec_source(spec),
97
- 'type' => build_dependency_type(spec.name),
98
- 'versions' => build_versions(spec, locked_spec)
99
- }
100
- end
101
-
102
- hash
103
- end
104
-
105
- private
106
-
107
- # Build default specification
108
- #
109
- # @return [Hash]
110
- def build_main
111
- {
112
- 'dependencies' => {},
113
- 'sources' => build_sources,
114
- 'plugins' => {},
115
- 'platforms' => @definition.platforms.map(&:to_s)
116
- }
117
- end
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>]
125
- def build_versions(spec, locked_spec = nil)
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
131
- end
132
-
133
- # @param specs [Array] specs that are direct dependencies
134
- # @param name [String] spec name
135
- #
136
- # @return [Boolean] dependency type
137
- def build_dependency_type(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
167
- end
168
-
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
187
- end
188
-
189
- # Build gem source
190
- #
191
- # @param spec [Bundler::StubSpecification, Bundler::LazySpecification, Gem::Specification]
192
- #
193
- # @return [Hash]
194
- def build_spec_source(spec)
195
- source = source_for_spec(spec)
196
-
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
221
- else
222
- raise ArgumentError, "unknown source #{spec.source.class}"
223
- end
224
- end
225
-
226
- # Build gem source name
227
- #
228
- # @param source [Bundler::Source] gem source type
229
- #
230
- # @return [String]
231
- def source_name_from_source(source)
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
244
- end
245
-
246
- # @param uri [Bundler::URI]
247
- #
248
- # @return [String]
249
- def source_name(uri)
250
- uri.to_s[0...-1]
251
- end
252
-
253
- # Build sources used in the Gemfile
254
- #
255
- # @return [Array<Hash>]
256
- def build_sources
257
- sources = @definition.send(:sources).rubygems_sources
258
- hash = {}
259
-
260
- sources.each do |source|
261
- type = build_source_type(source.remotes)
262
-
263
- source.remotes.each do |src|
264
- hash[source_name(src)] = type
265
- end
266
- end
267
-
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]
278
- end
279
-
280
- # Checks if we should skip a source
281
- #
282
- # @param source [Bundler::Source] gem source type
283
- #
284
- # @return [Boolean] true if we should skip this source, false otherwise
285
- def skip?(source)
286
- return true if me?(source)
287
-
288
- false
289
- end
290
-
291
- # Checks if it's a self source, this happens for repositories that are a gem
292
- #
293
- # @param source [Bundler::Source] gem source type
294
- #
295
- # @return [Boolean] true if it's a self source, false otherwise
296
- def me?(source)
297
- return false unless ME_SOURCES.include?(source.class)
298
-
299
- source.path.to_s == ME_PATH
300
- end
301
- end
302
- end
303
- end
304
- end