diffend 0.2.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.coditsu/ci.yml +3 -0
- data/.diffend.yml +3 -0
- data/.github/workflows/ci.yml +52 -0
- data/.gitignore +58 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +37 -0
- data/LICENSE +57 -0
- data/README.md +25 -0
- data/bin/bundle +114 -0
- data/bin/byebug +29 -0
- data/bin/htmldiff +29 -0
- data/bin/ldiff +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/certs/mensfeld.pem +25 -0
- data/certs/tomaszpajor.pem +25 -0
- data/diffend.gemspec +28 -0
- data/lib/diffend.rb +63 -0
- data/lib/diffend/commands.rb +11 -0
- data/lib/diffend/config/fetcher.rb +30 -0
- data/lib/diffend/config/file_finder.rb +38 -0
- data/lib/diffend/errors.rb +15 -0
- data/lib/diffend/voting.rb +73 -0
- data/lib/diffend/voting/request.rb +171 -0
- data/lib/diffend/voting/versions/local.rb +200 -0
- data/lib/diffend/voting/versions/remote.rb +174 -0
- data/plugins.rb +5 -0
- data/scripts/generate_payload_for_file.rb +15 -0
- metadata +127 -0
- metadata.gz.sig +0 -0
data/bin/ldiff
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'ldiff' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("diff-lcs", "ldiff")
|
data/bin/rake
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rake' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/certs/mensfeld.pem
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIEODCCAqCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDDBhtYWNp
|
3
|
+
ZWovREM9bWVuc2ZlbGQvREM9cGwwHhcNMTkwNzMwMTQ1NDU0WhcNMjAwNzI5MTQ1
|
4
|
+
NDU0WjAjMSEwHwYDVQQDDBhtYWNpZWovREM9bWVuc2ZlbGQvREM9cGwwggGiMA0G
|
5
|
+
CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC9fCwtaHZG2SyyNXiH8r0QbJQx/xxl
|
6
|
+
dkvwWz9QGJO+O8rEx20FB1Ab+MVkfOscwIv5jWpmk1U9whzDPl1uFtIbgu+sk+Zb
|
7
|
+
uQlZyK/DPN6c+/BbBL+RryTBRyvkPLoCVwm7uxc/JZ1n4AI6eF4cCZ2ieZ9QgQbU
|
8
|
+
MQs2QPqs9hT50Ez/40GnOdadVfiDDGz+NME2C4ms0BriXwZ1tcRTfJIHe2xjIbbb
|
9
|
+
y5qRGfsLKcgMzvLQR24olixyX1MR0s4+Wveq3QL/gBhL4veUcv+UABJA8IJR0kyB
|
10
|
+
seHHutusiwZ1v3SjjjW1xLLrc2ARV0mgCb0WaK2T4iA3oFTGLh6Ydz8LNl31KQFv
|
11
|
+
94nRd8IhmJxrhQ6dQ/WT9IXoa5S9lfT5lPJeINemH4/6QPABzf9W2IZlCdI9wCdB
|
12
|
+
TBaw57MKneGAYZiKjw6OALSy2ltQUCl3RqFl3VP7n8uFy1U987Q5VIIQ3O1UUsQD
|
13
|
+
Oe/h+r7GUU4RSPKgPlrwvW9bD/UQ+zF51v8CAwEAAaN3MHUwCQYDVR0TBAIwADAL
|
14
|
+
BgNVHQ8EBAMCBLAwHQYDVR0OBBYEFJNIBHdfEUD7TqHqIer2YhWaWhwcMB0GA1Ud
|
15
|
+
EQQWMBSBEm1hY2llakBtZW5zZmVsZC5wbDAdBgNVHRIEFjAUgRJtYWNpZWpAbWVu
|
16
|
+
c2ZlbGQucGwwDQYJKoZIhvcNAQELBQADggGBAKA4eqko6BTNhlysip6rfBkVTGri
|
17
|
+
ZXsL+kRb2hLvsQJS/kLyM21oMlu+LN0aPj3qEFR8mE/YeDD8rLAfruBRTltPNbR7
|
18
|
+
xA5eE1gkxY5LfExUtK3b2wPqfmo7mZgfcsMwfYg/tUXw1WpBCnrhAJodpGH6SXmp
|
19
|
+
A40qFUZst0vjiOoO+aTblIHPmMJXoZ3K42dTlNKlEiDKUWMRKSgpjjYGEYalFNWI
|
20
|
+
hHfCz2r8L2t+dYdMZg1JGbEkq4ADGsAA8ioZIpJd7V4hI17u5TCdi7X5wh/0gN0E
|
21
|
+
CgP+nLox3D+l2q0QuQEkayr+auFYkzTCkF+BmEk1D0Ru4mcf3F4CJvEmW4Pzbjqt
|
22
|
+
i1tsCWPtJ4E/UUKnKaWKqGbjrjHJ0MuShYzHkodox5IOiCXIQg+1+YSzfXUV6WEK
|
23
|
+
KJG/fhg1JV5vVDdVy6x+tv5SQ5ctU0feCsVfESi3rE3zRd+nvzE9HcZ5aXeL1UtJ
|
24
|
+
nT5Xrioegu2w1jPyVEgyZgTZC5rvD0nNS5sFNQ==
|
25
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,25 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIERDCCAqygAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBt0b21l
|
3
|
+
ay9EQz1wb2xpc2hnZWVrcy9EQz1jb20wHhcNMjAwNzA3MTY0NjU0WhcNMjEwNzA3
|
4
|
+
MTY0NjU0WjAmMSQwIgYDVQQDDBt0b21lay9EQz1wb2xpc2hnZWVrcy9EQz1jb20w
|
5
|
+
ggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDPRTvPuofdtL/wFEPOwPUr
|
6
|
+
vR0XHzM/ADb2GBuzu6fzgmoxaYXBe8A++0BbgFvK47T04i8bsbXnfkxrkz/nupQ5
|
7
|
+
SK2DPgS4HWnADuyBuyBY7LT4O1wwlytdlHtJgQV6NIcbprcOs/ZQKnimZpW9uByu
|
8
|
+
FoN3i94pAEQhuzK0S+wWPvSm22+6XGtCuOzyFGdnCJjGUOkCRno5Nx34MWz0NpJ3
|
9
|
+
9Ekkyy8g2cLvBcUdfeSrY7WsJ5cPCNrBs5cMuV426s1dDrhuvsW+sacwwY/4/LBw
|
10
|
+
JzEX4/zS+lsVIX+iOoIFGJdeGnpEWqKgWoaskxqseFi661td1n9UaMXxgoaYh/oX
|
11
|
+
3fJOy2jsZFboZ/eJ5rfciXLiCqSERGkEA+QcA2/jC/d77YJ1FfJW9uwJs3kptf4D
|
12
|
+
p6h8wuA3T6rN4QrxkGBYzOfUJ2zSQy1cFu0rTZiYdKo9X6BunnxhmUExNng7advu
|
13
|
+
qo8IDinyRlqA5+sOLXd4W3AS/RfF2nrayZNa3khTmmUCAwEAAaN9MHswCQYDVR0T
|
14
|
+
BAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFHRFOZPwpgOd2m8FIOodOii+OiID
|
15
|
+
MCAGA1UdEQQZMBeBFXRvbWVrQHBvbGlzaGdlZWtzLmNvbTAgBgNVHRIEGTAXgRV0
|
16
|
+
b21la0Bwb2xpc2hnZWVrcy5jb20wDQYJKoZIhvcNAQELBQADggGBAKWFwYTGZVoy
|
17
|
+
Bj3L9lvGOXpz8VWNoptFNHdncpaw1MMhS8UHcPQOUEiExX5ZH7MARy1fBjMXzIh9
|
18
|
+
41ZpCjR+S6uCEpzUcg5Z/kEWa/wOW6tqrX+zfyxFATDI20pYaQWOLepjbDxePFMZ
|
19
|
+
GAlIX5UNsze04A+wArXAttZB4oPt6loS1ao0GNdMb+syYMLzZUTW/sY2rm8zP4Mz
|
20
|
+
Kt+zjoqMxQ1Jf+EwH+0uq8Tj5BJcmG6mWYM+ljvRbxBwfimoUBUCQe6KIDouF0Og
|
21
|
+
uwLMY7X3jSERta4SxyY+iY7qNLsmG370GIGYbHuIiCwubFXt8jiPJZEdPE1xuzVF
|
22
|
+
CLsYItzC28UQEWrVe6sJ0Fuqv5VHM6t8jNClkXDwzf95efFlGSCFN4t+/dywVIK8
|
23
|
+
9MmF6uCQa1EjK2p8tYT0MnbHrFkoehxdX4VO9y99GAkhZyJNKPYPtyAUFV27sT2V
|
24
|
+
LfCJRk4ifKIN/FUCwDSn8Cz0m6oH265q0p6wdzI6qrWOjP8tGOMBTA==
|
25
|
+
-----END CERTIFICATE-----
|
data/diffend.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'diffend'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'diffend'
|
9
|
+
spec.version = Diffend::VERSION
|
10
|
+
spec.authors = ['Tomasz Pajor']
|
11
|
+
spec.email = ['contact@diffend.io']
|
12
|
+
|
13
|
+
spec.summary = 'OSS supply chain security and management platform'
|
14
|
+
spec.summary = 'OSS supply chain security and management platform.'
|
15
|
+
spec.homepage = Diffend::HOMEPAGE
|
16
|
+
spec.license = 'Prosperity Public License'
|
17
|
+
|
18
|
+
if $PROGRAM_NAME.end_with?('gem')
|
19
|
+
spec.signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
|
20
|
+
end
|
21
|
+
|
22
|
+
spec.cert_chain = %w[certs/tomaszpajor.pem]
|
23
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
|
24
|
+
spec.require_paths = %w[lib]
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler'
|
27
|
+
spec.add_development_dependency 'rake'
|
28
|
+
end
|
data/lib/diffend.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
%w[
|
4
|
+
bundler
|
5
|
+
].each(&method(:require))
|
6
|
+
|
7
|
+
%w[
|
8
|
+
config/fetcher
|
9
|
+
config/file_finder
|
10
|
+
commands
|
11
|
+
errors
|
12
|
+
voting
|
13
|
+
].each { |file| require "diffend/#{file}" }
|
14
|
+
|
15
|
+
%w[
|
16
|
+
request
|
17
|
+
versions/local
|
18
|
+
versions/remote
|
19
|
+
].each { |file| require "diffend/voting/#{file}" }
|
20
|
+
|
21
|
+
# Diffend main namespace
|
22
|
+
module Diffend
|
23
|
+
# Current plugin version
|
24
|
+
VERSION = '0.2.15'
|
25
|
+
# Diffend homepage
|
26
|
+
HOMEPAGE = 'https://diffend.io'
|
27
|
+
|
28
|
+
class << self
|
29
|
+
# Registers the plugin and add before install all hook
|
30
|
+
def register
|
31
|
+
Bundler::Plugin.add_hook('before-install-all') do |_|
|
32
|
+
Diffend::Voting.call(
|
33
|
+
command,
|
34
|
+
build_definition(command)
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Build clean instance of bundler definition, as we don't want to pollute the main one
|
40
|
+
#
|
41
|
+
# @param command [String] bundler command that we are executing
|
42
|
+
#
|
43
|
+
# @return [Bundler::Definition]
|
44
|
+
def build_definition(command)
|
45
|
+
unlock = command == 'update' ? true : nil
|
46
|
+
|
47
|
+
Bundler.configure
|
48
|
+
|
49
|
+
Bundler::Definition.build(
|
50
|
+
Bundler.default_gemfile,
|
51
|
+
Bundler.default_lockfile,
|
52
|
+
unlock
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Command that was run with bundle
|
57
|
+
#
|
58
|
+
# @return [String]
|
59
|
+
def command
|
60
|
+
ARGV.first || Diffend::Commands::INSTALL
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Diffend
|
6
|
+
# Module for all the components related to setting up the config
|
7
|
+
module Config
|
8
|
+
# Class responsible for fetching the config from .diffend.yml
|
9
|
+
module Fetcher
|
10
|
+
class << self
|
11
|
+
# @param build_path [String] path of the current build
|
12
|
+
# @return [OpenStruct] open struct with config details
|
13
|
+
# @example
|
14
|
+
# details = Fetcher.new.call('./')
|
15
|
+
# details.build_path #=> './'
|
16
|
+
def call(build_path)
|
17
|
+
OpenStruct.new(
|
18
|
+
YAML.safe_load(
|
19
|
+
ERB.new(
|
20
|
+
File.read(
|
21
|
+
FileFinder.call(build_path)
|
22
|
+
)
|
23
|
+
).result
|
24
|
+
).merge(build_path: build_path)
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Diffend
|
4
|
+
module Config
|
5
|
+
# Class used to figure out the file from which we should load the settings
|
6
|
+
module FileFinder
|
7
|
+
# Names of the files or paths where we will look for the settings
|
8
|
+
#
|
9
|
+
# @note We do the double dot trick, to look outside of the current dir because when
|
10
|
+
# executed from a docker container, we copy the local uncommitted settings into the
|
11
|
+
# dir above the app location not to pollute the reset state of the git repo
|
12
|
+
#
|
13
|
+
# @note Order is important, as for local env we should load from
|
14
|
+
# local file (if present first)
|
15
|
+
FILE_NAMES = %w[
|
16
|
+
.diffend.yml
|
17
|
+
].map { |name| ["../#{name}", name] }.tap(&:flatten!).freeze
|
18
|
+
|
19
|
+
private_constant :FILE_NAMES
|
20
|
+
|
21
|
+
class << self
|
22
|
+
# Looks for Diffend settings file for a given env
|
23
|
+
#
|
24
|
+
# @param build_path [String] path of the current build
|
25
|
+
#
|
26
|
+
# @return [String] path to the file from which we should load all the settings
|
27
|
+
def call(build_path)
|
28
|
+
FILE_NAMES
|
29
|
+
.map { |name| File.join(build_path, name) }
|
30
|
+
.map { |name| Dir[name] }
|
31
|
+
.find { |selection| !selection.empty? }
|
32
|
+
.tap { |path| path || raise(Errors::MissingConfigurationFile) }
|
33
|
+
.first
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Diffend
|
4
|
+
# Build runner app errors
|
5
|
+
module Errors
|
6
|
+
# Base error class from which all the errors should inherit
|
7
|
+
BaseError = Class.new(StandardError)
|
8
|
+
# Raised when we couldn't find a valid configuration file
|
9
|
+
MissingConfigurationFile = Class.new(BaseError)
|
10
|
+
# When unsupported response returned from the endpoint
|
11
|
+
UnsupportedResponse = Class.new(BaseError)
|
12
|
+
# When unsupported action returned from the endpoint
|
13
|
+
UnsupportedAction = Class.new(BaseError)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Diffend
|
4
|
+
# Verifies voting verdicts for gems
|
5
|
+
module Voting
|
6
|
+
class << self
|
7
|
+
# Build verdict
|
8
|
+
#
|
9
|
+
# @param command [String] either install or update
|
10
|
+
# @param definition [Bundler::Definition] definition for your source
|
11
|
+
def call(command, definition)
|
12
|
+
Versions::Remote
|
13
|
+
.call(command, definition)
|
14
|
+
.tap { |response| build_message(command, response) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_message(command, response)
|
18
|
+
if response.key?('error')
|
19
|
+
build_error(response)
|
20
|
+
elsif response.key?('action')
|
21
|
+
build_verdict(command, response)
|
22
|
+
else
|
23
|
+
raise UnsupportedResponse, response['action']
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_error(response)
|
28
|
+
build_error_message(response)
|
29
|
+
.tap(&Bundler.ui.method(:error))
|
30
|
+
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_verdict(command, response)
|
35
|
+
case response['action']
|
36
|
+
when 'allow'
|
37
|
+
build_allow_message(command, response)
|
38
|
+
.tap(&Bundler.ui.method(:confirm))
|
39
|
+
when 'deny'
|
40
|
+
build_deny_message(command, response)
|
41
|
+
.tap(&Bundler.ui.method(:error))
|
42
|
+
|
43
|
+
exit 1
|
44
|
+
else
|
45
|
+
raise UnsupportedAction, response['action']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_error_message(response)
|
50
|
+
<<~MSG
|
51
|
+
\nDiffend returned an error for your request.\n
|
52
|
+
#{response['error']}\n
|
53
|
+
MSG
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_allow_message(command, response)
|
57
|
+
<<~MSG
|
58
|
+
\nDiffend reported an allow verdict for #{command} command for this project.\n
|
59
|
+
All of our #{response['allows_count'] + response['denies_count']} checks succeeded.\n
|
60
|
+
#{response['review_url']}\n
|
61
|
+
MSG
|
62
|
+
end
|
63
|
+
|
64
|
+
def build_deny_message(command, response)
|
65
|
+
<<~MSG
|
66
|
+
\nDiffend reported a deny verdict for #{command} command for this project.\n
|
67
|
+
#{response['denies_count']} out of our #{response['allows_count'] + response['denies_count']} checks failed. Please go to the url below and review the issues.\n
|
68
|
+
#{response['review_url']}\n
|
69
|
+
MSG
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,171 @@
|
|
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
|
+
build_http(command, config.project_id) do |http, uri|
|
40
|
+
http.request(build_request(uri, config, payload))
|
41
|
+
end
|
42
|
+
rescue *CONNECTION_EXCEPTIONS => e
|
43
|
+
Bundler.ui.error('We experienced a connection issue, retrying...')
|
44
|
+
sleep(exponential_backoff(retry_count))
|
45
|
+
retry_count += 1
|
46
|
+
|
47
|
+
retry if retry_count < 3
|
48
|
+
|
49
|
+
puts prepare_report(payload, e)
|
50
|
+
Bundler.ui.error('^^^ Above is the dump of your request ^^^')
|
51
|
+
Bundler.ui.error(build_request_error_message)
|
52
|
+
|
53
|
+
exit 1
|
54
|
+
rescue *TIMEOUT_EXCEPTIONS => e
|
55
|
+
Bundler.ui.error('We experienced a timeout issue, retrying...')
|
56
|
+
sleep(exponential_backoff(retry_count))
|
57
|
+
retry_count += 1
|
58
|
+
|
59
|
+
retry if retry_count < 3
|
60
|
+
|
61
|
+
puts prepare_report(payload, e)
|
62
|
+
Bundler.ui.error('^^^ Above is the dump of your request ^^^')
|
63
|
+
Bundler.ui.error(build_request_error_message)
|
64
|
+
|
65
|
+
exit 1
|
66
|
+
rescue StandardError => e
|
67
|
+
puts prepare_report(payload, e)
|
68
|
+
Bundler.ui.error('^^^ Above is the dump of your request ^^^')
|
69
|
+
Bundler.ui.error(build_unhandled_exception_message)
|
70
|
+
|
71
|
+
exit 1
|
72
|
+
end
|
73
|
+
|
74
|
+
# Builds http connection object
|
75
|
+
#
|
76
|
+
# @param command [String] either install or update
|
77
|
+
# @param project_id [String] diffend project_id
|
78
|
+
def build_http(command, project_id)
|
79
|
+
uri = URI(endpoint_url(command, project_id))
|
80
|
+
|
81
|
+
Net::HTTP.start(
|
82
|
+
uri.host,
|
83
|
+
uri.port,
|
84
|
+
use_ssl: uri.scheme == 'https',
|
85
|
+
verify_mode: OpenSSL::SSL::VERIFY_NONE,
|
86
|
+
open_timeout: 5,
|
87
|
+
read_timeout: 5
|
88
|
+
) { |http| yield(http, uri) }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Build http post request and assigns headers and payload
|
92
|
+
#
|
93
|
+
# @param uri [URI::HTTPS]
|
94
|
+
# @param config [OpenStruct] Diffend config
|
95
|
+
# @param payload [Hash] with versions to check
|
96
|
+
#
|
97
|
+
# @return [Net::HTTP::Post]
|
98
|
+
def build_request(uri, config, payload)
|
99
|
+
Net::HTTP::Post
|
100
|
+
.new(uri.request_uri, HEADERS)
|
101
|
+
.tap { |request| assign_auth(request, config) }
|
102
|
+
.tap { |request| assign_payload(request, payload) }
|
103
|
+
end
|
104
|
+
|
105
|
+
# Assigns basic authorization if provided in the config
|
106
|
+
#
|
107
|
+
# @param request [Net::HTTP::Post] prepared http post
|
108
|
+
# @param config [OpenStruct] Diffend config
|
109
|
+
def assign_auth(request, config)
|
110
|
+
return unless config
|
111
|
+
return unless config.shareable_id
|
112
|
+
return unless config.shareable_key
|
113
|
+
|
114
|
+
request.basic_auth(config.shareable_id, config.shareable_key)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Assigns payload as json
|
118
|
+
#
|
119
|
+
# @param request [Net::HTTP::Post] prepared http post
|
120
|
+
# @param payload [Hash] with versions to check
|
121
|
+
def assign_payload(request, payload)
|
122
|
+
request.body = JSON.dump(payload: payload)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Provides diffend endpoint url
|
126
|
+
#
|
127
|
+
# @param command [String] either install or update
|
128
|
+
# @param project_id [String] diffend project_id
|
129
|
+
#
|
130
|
+
# @return [String] diffend endpoint
|
131
|
+
def endpoint_url(command, project_id)
|
132
|
+
return ENV['DIFFEND_URL'] if ENV.key?('DIFFEND_URL')
|
133
|
+
|
134
|
+
"https://my.diffend.io/api/projects/#{project_id}/bundle/#{command}"
|
135
|
+
end
|
136
|
+
|
137
|
+
def exponential_backoff(retry_count)
|
138
|
+
2**(retry_count + 1)
|
139
|
+
end
|
140
|
+
|
141
|
+
def build_request_error_message
|
142
|
+
<<~MSG
|
143
|
+
\nWe were unable to process your request at this time. We recorded this incident in our system and will review it.\n
|
144
|
+
If you think that this is a bug, don't hesitate.\n
|
145
|
+
Create an issue on https://github.com/diffend-io/diffend-ruby/issues\n
|
146
|
+
MSG
|
147
|
+
end
|
148
|
+
|
149
|
+
def build_unhandled_exception_message
|
150
|
+
<<~MSG
|
151
|
+
\nSomething went really wrong. We recorded this incident in our system and will review it.\n
|
152
|
+
This is a bug, don't hesitate.\n
|
153
|
+
Create an issue on https://github.com/diffend-io/diffend-ruby/issues\n
|
154
|
+
MSG
|
155
|
+
end
|
156
|
+
|
157
|
+
def prepare_report(payload, exception)
|
158
|
+
{
|
159
|
+
request_id: SecureRandom.uuid,
|
160
|
+
payload: payload,
|
161
|
+
exception: {
|
162
|
+
class: exception.class,
|
163
|
+
message: exception.message,
|
164
|
+
backtrace: exception.backtrace
|
165
|
+
}
|
166
|
+
}.to_json
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|