diffend 0.2.15
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.
- 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
|