gem_enforcer 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +0 -2
- data/Gemfile.lock +1 -21
- data/README.md +50 -3
- data/examples/README.md +19 -0
- data/examples/git_tag.yml +26 -0
- data/examples/multiple_behavior.yml +40 -0
- data/examples/single_validation.yml +14 -0
- data/gem_enforcer.gemspec +0 -1
- data/gem_enforcer.yml +38 -38
- data/lib/gem_enforcer/errors.rb +1 -0
- data/lib/gem_enforcer/setup/behavior.rb +71 -0
- data/lib/gem_enforcer/setup/helper/on_failure.rb +56 -32
- data/lib/gem_enforcer/setup/helper/retrieval.rb +46 -37
- data/lib/gem_enforcer/setup/helper/version_enforcer.rb +184 -0
- data/lib/gem_enforcer/setup/validate.rb +57 -24
- data/lib/gem_enforcer/setup.rb +18 -5
- data/lib/gem_enforcer/version.rb +1 -1
- metadata +8 -17
- data/lib/gem_enforcer/setup/helper/version.rb +0 -151
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 743105e56589c3d27eab2fae8e761edde554f552922c43fc474a93be9ed0cfe8
|
4
|
+
data.tar.gz: 0e116051836cfe75a48b444483956e84b705c883a5552e8eda842d8415885f9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5162dfa82d5e76f7dd6045b2595fb15e913eec2b98e62e60fa4d14cd5db8be9fb565e0b669697485c0a5be84a1fad5cffcabe584f39873309720d9386d9d8965
|
7
|
+
data.tar.gz: fee03c5789429dd03e046b2b9d806e81a3503b1e97117e5ee5bfe559400cc6dbb0a1c436a334ffc5888f6c3acc6e0b2d4140fe42bbe633b32eeaa9b58d16606c
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
gem_enforcer (0.0
|
4
|
+
gem_enforcer (0.1.0)
|
5
5
|
class_composer (>= 1.0)
|
6
6
|
faraday
|
7
7
|
octokit
|
@@ -9,28 +9,14 @@ PATH
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
activesupport (7.1.4)
|
13
|
-
base64
|
14
|
-
bigdecimal
|
15
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
16
|
-
connection_pool (>= 2.2.5)
|
17
|
-
drb
|
18
|
-
i18n (>= 1.6, < 2)
|
19
|
-
minitest (>= 5.1)
|
20
|
-
mutex_m
|
21
|
-
tzinfo (~> 2.0)
|
22
12
|
addressable (2.8.7)
|
23
13
|
public_suffix (>= 2.0.2, < 7.0)
|
24
|
-
base64 (0.2.0)
|
25
|
-
bigdecimal (3.1.8)
|
26
14
|
byebug (11.1.3)
|
27
15
|
class_composer (1.0.2)
|
28
16
|
coderay (1.1.3)
|
29
17
|
concurrent-ruby (1.3.4)
|
30
|
-
connection_pool (2.4.1)
|
31
18
|
diff-lcs (1.5.1)
|
32
19
|
docile (1.4.1)
|
33
|
-
drb (2.2.1)
|
34
20
|
faker (3.4.2)
|
35
21
|
i18n (>= 1.8.11, < 2)
|
36
22
|
faraday (2.5.2)
|
@@ -40,8 +26,6 @@ GEM
|
|
40
26
|
i18n (1.14.5)
|
41
27
|
concurrent-ruby (~> 1.0)
|
42
28
|
method_source (1.1.0)
|
43
|
-
minitest (5.25.1)
|
44
|
-
mutex_m (0.2.0)
|
45
29
|
octokit (9.1.0)
|
46
30
|
faraday (>= 1, < 3)
|
47
31
|
sawyer (~> 0.9)
|
@@ -78,17 +62,13 @@ GEM
|
|
78
62
|
simplecov_json_formatter (~> 0.1)
|
79
63
|
simplecov-html (0.13.0)
|
80
64
|
simplecov_json_formatter (0.1.4)
|
81
|
-
tzinfo (2.0.6)
|
82
|
-
concurrent-ruby (~> 1.0)
|
83
65
|
|
84
66
|
PLATFORMS
|
85
67
|
aarch64-linux
|
86
68
|
ruby
|
87
69
|
|
88
70
|
DEPENDENCIES
|
89
|
-
activesupport
|
90
71
|
faker
|
91
|
-
faraday (< 2.6)
|
92
72
|
gem_enforcer!
|
93
73
|
pry
|
94
74
|
pry-byebug
|
data/README.md
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
# GemEnforcer
|
2
2
|
|
3
|
-
`GemEnforcer` is
|
3
|
+
`GemEnforcer` is intended to ensure that your ruby scripts services or gems are using an acceptable version of specific gems. In the event that the gem is out of compliance, GemEnforcer can exit or raise an error before continuing.
|
4
|
+
|
5
|
+
## Inspiration
|
6
|
+
|
7
|
+
I build a lot of scripts that live locally and on other developers laptops. It is hard to ensure that they know when a new version of a critical gem has been realeased. With GemEnforcer, after upgrading, I can enforce the developer is using:
|
8
|
+
- One of the 3 most recently released versions of Sidekiq
|
9
|
+
- Within 3 minor versions of the most recently released Major version
|
10
|
+
- The most recent patch vesion of a current minor version
|
11
|
+
- And so many more combinations
|
12
|
+
|
4
13
|
|
5
14
|
## Installation
|
6
15
|
|
@@ -18,16 +27,54 @@ Or install it yourself as:
|
|
18
27
|
|
19
28
|
$ gem install gem_enforcer
|
20
29
|
|
30
|
+
### Create a Configuration File
|
31
|
+
|
32
|
+
[Check out Configuration examples](examples)
|
33
|
+
|
34
|
+
### Configure the Gem
|
35
|
+
|
36
|
+
Prior to running the validation, you can set custom configurations to tailor the GemEnforcement experience
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
GemEnforcer.configuration do |config|
|
40
|
+
# TO use Git Tags, the access token must be provided
|
41
|
+
# As a default it will check ENV["GITHUB_TOKEN"] or ENV["BUNDLE_GITHUB__COM"] if it is there
|
42
|
+
# If not, it must be provided to use git tag
|
43
|
+
config.github_access_token = "my_access_token"
|
44
|
+
|
45
|
+
config.yml_config_path = "The path to your config file"
|
46
|
+
config.logger = MyLoggerClass #TTY::Logger or Logger classes allowed
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
### Run Validation
|
51
|
+
|
52
|
+
Validations can get run anywhere in your code. Suggested for it to run early on during boot process
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
# Run configuration validations
|
56
|
+
unless GemEnforcer::Setup.validate_yml!
|
57
|
+
# There are errors in the configuration
|
58
|
+
puts GemEnforcer::Setup.errors
|
59
|
+
end
|
60
|
+
|
61
|
+
# Run the validations based on the config file
|
62
|
+
# If validate_yml! failed, this will raise an error!
|
63
|
+
GemEnforcer::Setup.run_validations!
|
64
|
+
```
|
21
65
|
|
22
66
|
## Development
|
23
67
|
|
24
|
-
This
|
68
|
+
This Gem is very close to 100% test coverage. To understand the inner workings, I would start with the test cases
|
69
|
+
|
70
|
+
You can run this gem using docker with make commands (`make bash`) or just on your local machine running at least Ruby 3.2.
|
71
|
+
|
25
72
|
|
26
73
|
## Contributing
|
27
74
|
|
28
75
|
This gem welcomes contribution.
|
29
76
|
|
30
77
|
Bug reports and pull requests are welcome on GitHub at
|
31
|
-
https://github.com/matt-taylor/
|
78
|
+
https://github.com/matt-taylor/gem_enforcer.
|
32
79
|
|
33
80
|
|
data/examples/README.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Examples
|
2
|
+
|
3
|
+
## [Single Validation Example](git_tag.yml)
|
4
|
+
|
5
|
+
This is the most basic example. GemEnforcment provides some sane defaults for you to get going quickly. With minimal information, you can start enforcing gem versioning in your script, gem, or application!
|
6
|
+
|
7
|
+
## [Enforcement with Multiple Behaviors Example](multiple_behavior.yml)
|
8
|
+
|
9
|
+
Multiple behaviors can be super helpful when you want to send a warning before the nuclear option of exiting or raising an error. Using multiple behaviors you can stack the experience and layer multiple warnings on when the Enforcment becomes more forceful
|
10
|
+
|
11
|
+
(Also shows a basic example of how a single config can support multiple gem validations)
|
12
|
+
|
13
|
+
## [Git Tag with SemVer Example](git_tag.yml)
|
14
|
+
|
15
|
+
### Git Tag
|
16
|
+
Git Tags allow you to query the version list from git tags. This can help the gem you care about is not in a gem source but rather just in git
|
17
|
+
|
18
|
+
### SemVer Version Enforcement
|
19
|
+
SemVer versioning enforcment allows you to tailor the Gem Enforcement. Based on the Major, Minor, and/or Patch version of the current gem, you can set individual requirements to meet your needs
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# This config file will ensure rails_base gem is up to date based on SemVer specifics
|
2
|
+
# NOTE: Usage of git is a bit more expensive and will consume Github API get operations that are counted towards rate limit
|
3
|
+
# Major:
|
4
|
+
# Major version is set to 0. This will enforce that the gem is up to date with the latest major.
|
5
|
+
# If the current major version does not equal what is released, failure behavior is triggered
|
6
|
+
#
|
7
|
+
# Patch:
|
8
|
+
# Given the current versions major.minor, the patch version is expected to be within the most 2 recent releases
|
9
|
+
# If not, it will trigger a failure
|
10
|
+
#
|
11
|
+
# Notice that minor is missing. This means it does not matter what minor version the current gem is on only that it is
|
12
|
+
# on the most recent major version and within 2 of the most recent patches released for the given minor version
|
13
|
+
#
|
14
|
+
# On Failure:
|
15
|
+
# Using log level Error, output the default message and then raise an error
|
16
|
+
---
|
17
|
+
gems:
|
18
|
+
rails_base: # Enforce the gem rails_base
|
19
|
+
git: matt-taylor # Using git tags where the owner is matt-taylor and the gem name is rails_base
|
20
|
+
behaviors: # Array of different behavior -- Order matters here
|
21
|
+
- version_enforce:
|
22
|
+
major: 0 # Must be on at least the most recent major version
|
23
|
+
patch: 2 # For the given current major.minor version, must be within at least 2 patches
|
24
|
+
on_failure:
|
25
|
+
log_level: error # Log Level to output (This can be a custom log level based on your logger)
|
26
|
+
behavior: raise
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# This config file will ensure Rails gem is up to date with the most recent release
|
2
|
+
# It uses the default rubygems.org ruby server to query versions against
|
3
|
+
# It has two behaviors
|
4
|
+
# Behavior 1:
|
5
|
+
# => Must be within the last 3 releases
|
6
|
+
# => When not within last 3 releases, it will log message and exit immediately
|
7
|
+
# Behavior 2:
|
8
|
+
# => Must be within the last 2 releases
|
9
|
+
# => When not within last 2 releases, it will log message and continue
|
10
|
+
# => Behavior is seen as a warning
|
11
|
+
#
|
12
|
+
# With multiple behaviors, order is important. If order was switched, if more than 3 releases behind,
|
13
|
+
# both messages would be shown to user instead of just one
|
14
|
+
# => Log Message via ERROR
|
15
|
+
# => Do nothing
|
16
|
+
|
17
|
+
# Config can support multiple gems!!
|
18
|
+
---
|
19
|
+
gems:
|
20
|
+
rails: # Enforce the gem Rails
|
21
|
+
server: true # Use the default gem server (https://rubygems.org)
|
22
|
+
behaviors: # Array of different behavior -- Order matters here
|
23
|
+
- version_enforce:
|
24
|
+
releases: 3 # Behavior 1 will trigger when the current version is greater than the 3rd most recent and beyond
|
25
|
+
on_failure:
|
26
|
+
log_level: error # Log Level to output (This can be a custom log level based on your logger)
|
27
|
+
# message: Custom Message to output # There is a default message and this is optional but takes precedence
|
28
|
+
behavior: exit # When behavior is triggered, exit the application immediately
|
29
|
+
- version_enforce:
|
30
|
+
releases: 2 # Behavior 2 will trigger when the current version is 2nd most recent and beyond
|
31
|
+
on_failure:
|
32
|
+
log_level: warn # Log Level to output (This can be a custom log level based on your logger)
|
33
|
+
# Message can have interpolated strings: `version` => current version, `versions_behind` => versions behind, `max_version` => most recent version
|
34
|
+
message: "You are running rails v%{version} which is %{versions_behind} versions behind. Please consider updating"
|
35
|
+
|
36
|
+
sidekiq:
|
37
|
+
server: https://rubygems.org # Input your custom server here
|
38
|
+
behaviors:
|
39
|
+
- version_enforce:
|
40
|
+
insync: true # Behavior 1 triggered as soon as version is not the most recent
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This config file will ensure Sidekiq gem is up to date with the most recent release
|
2
|
+
# When this is not met, it will use the default failure behavior
|
3
|
+
# => Log Message via ERROR
|
4
|
+
# => Do nothing
|
5
|
+
---
|
6
|
+
gems:
|
7
|
+
sidekiq:
|
8
|
+
server: https://rubygems.org # Input your custom server here
|
9
|
+
behaviors:
|
10
|
+
- version_enforce:
|
11
|
+
insync: true # Behavior 1 triggered as soon as version is not the most recent
|
12
|
+
# on_failure: This is the default on_failure behavior when none is provided
|
13
|
+
# log_level: error
|
14
|
+
# behavior: # none Logs message and returns
|
data/gem_enforcer.gemspec
CHANGED
data/gem_enforcer.yml
CHANGED
@@ -4,56 +4,56 @@ invalid_config:
|
|
4
4
|
behavior: exit
|
5
5
|
gems:
|
6
6
|
rails:
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
behaviors:
|
8
|
+
- on_failure:
|
9
|
+
log_level: warn # on failure, log a warn message
|
10
|
+
behavior: exit
|
11
|
+
version_enforce:
|
12
|
+
minor: 3 # within the last 3 minor releases
|
12
13
|
server: true # when set to true, defaults to https://rubygems.org
|
13
14
|
|
14
15
|
shoryuken:
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
behaviors:
|
17
|
+
- on_failure:
|
18
|
+
log_level: info # on failure, log a info message
|
19
|
+
version_enforce:
|
20
|
+
# Must be within the most current version released
|
21
|
+
# If above is true, must be within the last 2 minor releases
|
22
|
+
# If above is true, must be within the last 3 patches released
|
23
|
+
major: 0 # within the current major version
|
24
|
+
minor: 2 # within the last 3 minor releases
|
25
|
+
patch: 3 # within the last 3 patches released
|
25
26
|
server: true # when set to true, defaults to https://rubygems.org
|
26
27
|
|
27
28
|
redis:
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
behaviors:
|
30
|
+
- on_failure:
|
31
|
+
log_level: error
|
32
|
+
behavior: raise # on failure, log error and exit(1)
|
33
|
+
version_enforce:
|
34
|
+
insync: true # Ensure gem is up to date
|
35
|
+
server: https://rubygems.org
|
34
36
|
|
35
37
|
faraday:
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
# major: 0
|
44
|
-
# minor: 6
|
45
|
-
# patch: 0
|
46
|
-
server:
|
47
|
-
source: https://rubygems.org
|
38
|
+
behaviors:
|
39
|
+
- on_failure:
|
40
|
+
log_level: error
|
41
|
+
behavior: exit # on failure, log error and exit(1)
|
42
|
+
version_enforce:
|
43
|
+
insync: true # Ensure gem is up to date
|
44
|
+
server: https://rubygems.org
|
48
45
|
|
49
46
|
custom_gem:
|
50
|
-
|
47
|
+
behaviors:
|
48
|
+
- version_enforce:
|
49
|
+
insync: true
|
51
50
|
server: true
|
51
|
+
|
52
52
|
sidekiq:
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
behaviors:
|
54
|
+
- version_enforce:
|
55
|
+
insync: truee
|
56
|
+
git: sidekiq # Github root page for the gem
|
57
57
|
|
58
58
|
|
59
59
|
|
data/lib/gem_enforcer/errors.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
require "gem_enforcer/setup/helper/on_failure"
|
5
|
+
require "gem_enforcer/setup/helper/version_enforcer"
|
6
|
+
|
7
|
+
module GemEnforcer
|
8
|
+
module Setup
|
9
|
+
class Behavior
|
10
|
+
attr_reader :gem_name, :params, :index
|
11
|
+
|
12
|
+
def initialize(gem_name:, index:, **params)
|
13
|
+
@gem_name = gem_name
|
14
|
+
@index = index
|
15
|
+
@params = params.transform_keys(&:to_sym)
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid_config?
|
19
|
+
@valid_config ||= validate_config
|
20
|
+
end
|
21
|
+
|
22
|
+
def run_behavior!(version_list:, version:)
|
23
|
+
unless valid_config?
|
24
|
+
raise ConfigError, "Attempted to run validations with invalid Version Configurations"
|
25
|
+
end
|
26
|
+
|
27
|
+
return true if version.nil?
|
28
|
+
return true if version_enforcer.valid_gem_versions?(version_list:, version:)
|
29
|
+
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def error_status
|
34
|
+
return nil if errors.empty?
|
35
|
+
|
36
|
+
errors.map { "behaviors[#{index}].#{_1}" }
|
37
|
+
end
|
38
|
+
|
39
|
+
def version_enforcer
|
40
|
+
@version_enforcer ||= Helper::VersionEnforcer.new(gem_name:, version_enforce: params[:version_enforce])
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_failure
|
44
|
+
@on_failure ||= Helper::OnFailure.new(gem_name:, on_failure: params[:on_failure])
|
45
|
+
end
|
46
|
+
|
47
|
+
def run_failure!(message:, version:, version_list:)
|
48
|
+
params = {
|
49
|
+
version:,
|
50
|
+
c: version_list.max,
|
51
|
+
versions_behind: version_enforcer.versions_behind(version_list:, version:),
|
52
|
+
}
|
53
|
+
on_failure.run_on_failure!(message:, **params)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def errors
|
59
|
+
@errors ||= []
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate_config
|
63
|
+
boolean = version_enforcer.valid_config?
|
64
|
+
boolean &= on_failure.valid_config?
|
65
|
+
|
66
|
+
@errors = Array(version_enforcer.errors).compact + Array(on_failure.errors).compact
|
67
|
+
@errors.length == 0
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -3,54 +3,78 @@
|
|
3
3
|
module GemEnforcer
|
4
4
|
module Setup
|
5
5
|
module Helper
|
6
|
-
|
7
|
-
|
6
|
+
class OnFailure
|
7
|
+
attr_reader :gem_name, :on_failure
|
8
|
+
|
9
|
+
ALLOWED_KEYS = [LOG_LEVEL = :log_level, FAILURE_BEHAVIOR = :behavior, MESSAGE = :message]
|
10
|
+
ALLOWED_FAILURE_BEHAVIOR = [:raise, :exit, DEFAULT_BEHAVIOR = :none]
|
8
11
|
DEFAULT_LOG_LEVEL = :error
|
9
12
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
def initialize(gem_name:, on_failure:)
|
14
|
+
@gem_name = gem_name
|
15
|
+
@on_failure = on_failure.transform_keys(&:to_sym) rescue on_failure
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid_config?
|
19
|
+
@valid_config ||= validate_config
|
20
|
+
end
|
21
|
+
|
22
|
+
def errors
|
23
|
+
@errors ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_on_failure!(message:, **params)
|
27
|
+
unless valid_config?
|
28
|
+
raise ConfigError, "Attempted to run on_failure with an invalid config."
|
16
29
|
end
|
17
30
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
return false
|
27
|
-
end
|
31
|
+
send_message = (provided_message || message) % params
|
32
|
+
GemEnforcer.logger.public_send(on_failure_log_level, send_message)
|
33
|
+
|
34
|
+
case on_failure_behavior.to_sym
|
35
|
+
when :raise
|
36
|
+
raise ValidationError, send_message
|
37
|
+
when :exit
|
38
|
+
Kernel.exit(1)
|
28
39
|
end
|
29
40
|
|
30
|
-
|
31
|
-
false
|
41
|
+
true
|
32
42
|
end
|
33
43
|
|
34
|
-
|
35
|
-
message = params.dig("on_failure", "message") rescue nil
|
36
|
-
return message if message
|
44
|
+
private
|
37
45
|
|
38
|
-
|
46
|
+
def on_failure_behavior
|
47
|
+
(on_failure[FAILURE_BEHAVIOR] || DEFAULT_BEHAVIOR) rescue DEFAULT_BEHAVIOR
|
39
48
|
end
|
40
49
|
|
41
|
-
def
|
50
|
+
def on_failure_log_level
|
51
|
+
(on_failure[LOG_LEVEL] || DEFAULT_LOG_LEVEL) rescue DEFAULT_LOG_LEVEL
|
52
|
+
end
|
42
53
|
|
54
|
+
def provided_message
|
55
|
+
on_failure[MESSAGE] rescue nil
|
43
56
|
end
|
44
57
|
|
45
|
-
def
|
46
|
-
|
58
|
+
def validate_config
|
59
|
+
return true if on_failure.nil?
|
47
60
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
when :exit
|
52
|
-
Kernel.exit(1)
|
61
|
+
unless Hash === on_failure
|
62
|
+
errors << "on_failure: Expected to contain a Hash. Contained a [#{on_failure.class}]"
|
63
|
+
return false
|
53
64
|
end
|
65
|
+
|
66
|
+
disallowed_keys = on_failure.keys - ALLOWED_KEYS
|
67
|
+
if disallowed_keys.length > 0
|
68
|
+
errors << "on_failure: Contained unexpected keys. Only #{ALLOWED_KEYS} are allowed. Found #{disallowed_keys}"
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
|
72
|
+
if on_failure[FAILURE_BEHAVIOR] && ALLOWED_FAILURE_BEHAVIOR.none? { _1 == on_failure[FAILURE_BEHAVIOR].to_sym }
|
73
|
+
errors << "on_failure.#{FAILURE_BEHAVIOR}: Value must be one of #{ALLOWED_FAILURE_BEHAVIOR}. Provided [#{on_failure[FAILURE_BEHAVIOR]}] "
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
|
77
|
+
true
|
54
78
|
end
|
55
79
|
end
|
56
80
|
end
|
@@ -3,56 +3,65 @@
|
|
3
3
|
module GemEnforcer
|
4
4
|
module Setup
|
5
5
|
module Helper
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
errors << "retrieval: Missing retrieval type. Expected `server` or `git`"
|
22
|
-
false
|
23
|
-
end
|
6
|
+
class Retrieval
|
7
|
+
attr_reader :gem_name, :server, :git, :retrieval_factory
|
8
|
+
|
9
|
+
def initialize(gem_name:, server:, git:)
|
10
|
+
@gem_name = gem_name
|
11
|
+
@server = server
|
12
|
+
@git = git
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid_config?
|
16
|
+
@valid_config ||= validate_config
|
17
|
+
end
|
18
|
+
|
19
|
+
def errors
|
20
|
+
@errors ||= []
|
24
21
|
end
|
25
22
|
|
26
23
|
def retrieve_version_list
|
27
|
-
|
24
|
+
unless valid_config?
|
25
|
+
raise ConfigError, "Attempted to run validations with invalid Version Configurations"
|
26
|
+
end
|
27
|
+
|
28
|
+
retrieval_factory.gem_versions(name: gem_name).sort
|
28
29
|
end
|
29
30
|
|
30
|
-
|
31
|
-
server = params.dig("server")
|
31
|
+
private
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
def validate_config
|
34
|
+
if server && git
|
35
|
+
errors << "retrieval: `server` and `git` keys present. Must only choose 1"
|
36
|
+
return false
|
36
37
|
end
|
37
38
|
|
38
|
-
if
|
39
|
-
|
39
|
+
if server
|
40
|
+
if server == true
|
41
|
+
@source = GemEnforcer::DEFAULT_SERVER_SOURCE
|
42
|
+
elsif String === server
|
43
|
+
@source = server
|
44
|
+
else
|
45
|
+
errors << "server: Server retrieval provided. Expected `true` or a string of the rubygem source endpoint"
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
@retrieval_factory = Retrieve.server_retrieval_by_source(source: @source)
|
40
49
|
return true
|
41
50
|
end
|
42
51
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
if git
|
53
|
+
if String === git
|
54
|
+
@owner = git
|
55
|
+
@retrieval_factory = Retrieve.github_retrieval_by_owner(owner: git)
|
56
|
+
return true
|
57
|
+
else
|
58
|
+
errors << "git: Git retrieval provided. Expected string of the owner/organization of the gem"
|
59
|
+
return false
|
60
|
+
end
|
52
61
|
end
|
53
62
|
|
54
|
-
errors << "retrieval
|
55
|
-
false
|
63
|
+
errors << "retrieval: `server` and `git` keys are missing. Must provide 1 retrieval method"
|
64
|
+
return false
|
56
65
|
end
|
57
66
|
end
|
58
67
|
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GemEnforcer
|
4
|
+
module Setup
|
5
|
+
module Helper
|
6
|
+
class VersionEnforcer
|
7
|
+
attr_reader :gem_name, :version_enforce, :error_validation_message
|
8
|
+
|
9
|
+
ALLOWED_VERSION_INSYNC = :insync
|
10
|
+
ALLOWED_VERSION_REALEASE = :releases
|
11
|
+
ALLOWED_VERSION_SEMVER = [MAJOR = :major, MINOR = :minor, PATCH = :patch]
|
12
|
+
|
13
|
+
def initialize(gem_name:, version_enforce:)
|
14
|
+
@gem_name = gem_name
|
15
|
+
@version_enforce = version_enforce.transform_keys(&:to_sym) rescue nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid_config?
|
19
|
+
@valid_config ||= validate_config
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid_gem_versions?(version_list:, version:)
|
23
|
+
unless valid_config?
|
24
|
+
raise ConfigError, "Attempted to run validations with invalid Version Configurations"
|
25
|
+
end
|
26
|
+
|
27
|
+
return true if version.nil?
|
28
|
+
|
29
|
+
min_to_max_version_sorted_list = version_list.sort
|
30
|
+
case @version_type
|
31
|
+
when :insync
|
32
|
+
__validation_insync(version_list: min_to_max_version_sorted_list, version:)
|
33
|
+
when :releases
|
34
|
+
__validation_releases(version_list: min_to_max_version_sorted_list, version:)
|
35
|
+
when :semver
|
36
|
+
__validation_semver(version_list: min_to_max_version_sorted_list, version:)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def errors
|
41
|
+
@errors ||= []
|
42
|
+
end
|
43
|
+
|
44
|
+
def versions_behind(version_list:, version:)
|
45
|
+
version_list.sort.reverse.find_index(version)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def __validation_insync(version_list:, version:)
|
51
|
+
max_version = version_list.max
|
52
|
+
return true if version >= max_version
|
53
|
+
|
54
|
+
@error_validation_message = "[#{gem_name}] Enforcer expects the most recent version. Version #{version}. Most Recent version #{max_version}"
|
55
|
+
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
def __validation_releases(version_list:, version:)
|
60
|
+
releases_behind = version_enforce[ALLOWED_VERSION_REALEASE]
|
61
|
+
min_version_allowed = version_list[-releases_behind]
|
62
|
+
return true if version >= min_version_allowed
|
63
|
+
|
64
|
+
if index = version_list.sort.reverse.find_index(version)
|
65
|
+
version_text = "Version [#{version}] is the #{index} oldest version."
|
66
|
+
else
|
67
|
+
version_text = "Version [#{version}] is the was not found in the provided list."
|
68
|
+
end
|
69
|
+
|
70
|
+
@error_validation_message = "[#{gem_name}] Enforcer expects the version to be within the most recent #{releases_behind} versions. #{version_text}"
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
def __validation_semver(version_list:, version:)
|
75
|
+
if max_major_versions_behind = version_enforce[:major]
|
76
|
+
if error = __threshold_semver_distance(type: :major, number: version.segments[0], list: version_list.map { _1.segments[0] }, threshold: max_major_versions_behind, version: version)
|
77
|
+
@error_validation_message = error
|
78
|
+
return false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
if max_minor_versions_behind = version_enforce[:minor]
|
83
|
+
# Select only the minor versions that match the major version
|
84
|
+
current_major_version = version.segments[0]
|
85
|
+
minor_version_check_list = version_list.select { _1.segments[0] == current_major_version }.map { _1.segments[1] }
|
86
|
+
if error = __threshold_semver_distance(type: :minor, number: version.segments[1], list: minor_version_check_list, threshold: max_minor_versions_behind, version: version)
|
87
|
+
@error_validation_message = error
|
88
|
+
return false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
if max_patch_versions_behind = version_enforce[:patch]
|
93
|
+
# Select only the patch versions that match the major.minor version
|
94
|
+
current_major_minor_version = version.segments[0..1]
|
95
|
+
patch_version_check_list = version_list.select { _1.segments[0..1] == current_major_minor_version }.map { _1.segments[2] }
|
96
|
+
if error = __threshold_semver_distance(type: :patch, number: version.segments[2], list: patch_version_check_list, threshold: max_patch_versions_behind, version: version)
|
97
|
+
@error_validation_message = error
|
98
|
+
return false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
true
|
103
|
+
end
|
104
|
+
|
105
|
+
def __threshold_semver_distance(type:, number:, list:, threshold:, version:)
|
106
|
+
# remove duplicates ans sort in highest to lowest number
|
107
|
+
uniq_list = list.uniq.sort.reverse
|
108
|
+
|
109
|
+
# get the position in the sorted array
|
110
|
+
position_in_sorted_array = uniq_list.find_index(number)
|
111
|
+
|
112
|
+
# if position is less than or equal to the threshold, we are good
|
113
|
+
# otherwise, it is out of compliance
|
114
|
+
return nil if position_in_sorted_array <= threshold
|
115
|
+
|
116
|
+
"[#{gem_name}] Enforcer expects the version to be within #{threshold} #{type} versions of the most recent version. Version is #{version}"
|
117
|
+
end
|
118
|
+
|
119
|
+
def validate_config
|
120
|
+
unless Hash === version_enforce
|
121
|
+
errors << "version_enforce: Must be a hash containing [#{ALLOWED_VERSION_INSYNC}] or [#{ALLOWED_VERSION_REALEASE}] or any of [#{ALLOWED_VERSION_SEMVER}]"
|
122
|
+
return false
|
123
|
+
end
|
124
|
+
|
125
|
+
if version_enforce.keys.include?(ALLOWED_VERSION_INSYNC)
|
126
|
+
@version_type = :insync
|
127
|
+
__validate_config_insync
|
128
|
+
elsif version_enforce.keys.include?(ALLOWED_VERSION_REALEASE)
|
129
|
+
@version_type = :releases
|
130
|
+
__validate_config_releases
|
131
|
+
elsif version_enforce.keys.any? { ALLOWED_VERSION_SEMVER.include?(_1) }
|
132
|
+
@version_type = :semver
|
133
|
+
__validate_config_semver
|
134
|
+
else
|
135
|
+
errors << "version_enforce: Invalid config. Hash must contain [#{ALLOWED_VERSION_INSYNC}] or [#{ALLOWED_VERSION_REALEASE}] or any of [#{ALLOWED_VERSION_SEMVER}]"
|
136
|
+
false
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def __validate_config_insync
|
141
|
+
return false unless validate_expected_keys(ALLOWED_VERSION_INSYNC, "insync")
|
142
|
+
|
143
|
+
return true if version_enforce[ALLOWED_VERSION_INSYNC]
|
144
|
+
|
145
|
+
errors << "version_enforce.#{ALLOWED_VERSION_INSYNC}: When key is present, value must be true. Received [#{version_enforce[ALLOWED_VERSION_INSYNC]}]"
|
146
|
+
false
|
147
|
+
end
|
148
|
+
|
149
|
+
def __validate_config_releases
|
150
|
+
return false unless validate_expected_keys(ALLOWED_VERSION_REALEASE, ALLOWED_VERSION_REALEASE)
|
151
|
+
|
152
|
+
validate_integer(version_enforce[ALLOWED_VERSION_REALEASE], ALLOWED_VERSION_REALEASE)
|
153
|
+
end
|
154
|
+
|
155
|
+
def __validate_config_semver
|
156
|
+
return false unless validate_expected_keys(ALLOWED_VERSION_SEMVER, "SemVer")
|
157
|
+
|
158
|
+
boolean = true
|
159
|
+
boolean &= validate_integer(version_enforce[MAJOR], "major") if version_enforce[MAJOR]
|
160
|
+
boolean &= validate_integer(version_enforce[MINOR], "minor") if version_enforce[MINOR]
|
161
|
+
boolean &= validate_integer(version_enforce[PATCH], "patch") if version_enforce[PATCH]
|
162
|
+
|
163
|
+
boolean
|
164
|
+
end
|
165
|
+
|
166
|
+
def validate_expected_keys(allowed, type)
|
167
|
+
disallowed_keys = version_enforce.keys - Array(allowed)
|
168
|
+
|
169
|
+
return true if disallowed_keys.length == 0
|
170
|
+
|
171
|
+
errors << "version_enforce: Unexpected keys present for `#{type}`. Allowed keys [#{allowed}] but received #{version_enforce.keys}. [#{disallowed_keys}] is not allowed"
|
172
|
+
false
|
173
|
+
end
|
174
|
+
|
175
|
+
def validate_integer(val, dig)
|
176
|
+
return true if Integer === val
|
177
|
+
|
178
|
+
errors << "version_enforce.#{dig}: Expected value to be an Integer. Recieved type #{val.class} [#{val}]"
|
179
|
+
false
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -1,38 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
4
|
-
|
3
|
+
require "gem_enforcer/setup/behavior"
|
5
4
|
require "gem_enforcer/setup/helper/retrieval"
|
6
|
-
require "gem_enforcer/setup/helper/on_failure"
|
7
|
-
require "gem_enforcer/setup/helper/version"
|
8
5
|
|
9
6
|
module GemEnforcer
|
10
7
|
module Setup
|
11
8
|
class Validate
|
12
|
-
attr_reader :gem_name, :params, :
|
13
|
-
|
14
|
-
include Helper::Retrieval
|
15
|
-
include Helper::OnFailure
|
16
|
-
include Helper::Version
|
9
|
+
attr_reader :gem_name, :params, :behaviors, :retrieval
|
17
10
|
|
18
11
|
def initialize(name:, **params)
|
19
12
|
@params = params
|
20
13
|
@gem_name = name
|
21
|
-
|
22
|
-
@
|
14
|
+
@behaviors = []
|
15
|
+
@errors = []
|
23
16
|
end
|
24
17
|
|
25
|
-
|
26
|
-
|
27
|
-
unless validation_status
|
18
|
+
def run_validation!
|
19
|
+
unless valid_config?
|
28
20
|
raise Error, "Unable to run validation with invalid config."
|
29
21
|
end
|
30
|
-
|
31
22
|
return true if current_version.nil?
|
32
23
|
|
33
|
-
|
24
|
+
version_list = retrieval.retrieve_version_list
|
25
|
+
passed_behavior, failed_behavior = behaviors.partition { _1.run_behavior!(version_list:, version: current_version) }
|
26
|
+
|
27
|
+
return true if failed_behavior.empty?
|
28
|
+
|
29
|
+
|
30
|
+
failed_behavior.each do |failed|
|
31
|
+
default_message = failed.version_enforcer.error_validation_message
|
32
|
+
failed.run_failure!(message: default_message, version: current_version, version_list: version_list)
|
33
|
+
end
|
34
34
|
|
35
|
-
execute_on_failure!(behavior: behavior)
|
36
35
|
false
|
37
36
|
end
|
38
37
|
|
@@ -40,6 +39,10 @@ module GemEnforcer
|
|
40
39
|
Gem.loaded_specs[gem_name]&.version
|
41
40
|
end
|
42
41
|
|
42
|
+
def valid_config?
|
43
|
+
@valid_config ||= validate_config
|
44
|
+
end
|
45
|
+
|
43
46
|
def error_status
|
44
47
|
return nil if errors.empty?
|
45
48
|
|
@@ -48,17 +51,47 @@ module GemEnforcer
|
|
48
51
|
|
49
52
|
private
|
50
53
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
+
def validate_config
|
55
|
+
generate_behaviors!
|
56
|
+
generate_retreival!
|
57
|
+
|
58
|
+
boolean = true
|
59
|
+
if behaviors.length == 0
|
60
|
+
@errors << "behaviors: At least 1 behavior is expected per gem validation"
|
61
|
+
boolean = false
|
62
|
+
else
|
63
|
+
unless behaviors.all? { _1.valid_config? }
|
64
|
+
# at least 1 beavior failed validation
|
65
|
+
behaviors.each do |b|
|
66
|
+
@errors += b.error_status if b.error_status
|
67
|
+
end
|
68
|
+
errors.flatten!
|
69
|
+
boolean = false
|
70
|
+
end
|
71
|
+
end
|
54
72
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
73
|
+
unless retrieval.valid_config?
|
74
|
+
@errors += retrieval.errors
|
75
|
+
boolean = false
|
76
|
+
end
|
59
77
|
|
60
78
|
boolean
|
61
79
|
end
|
80
|
+
|
81
|
+
def generate_behaviors!
|
82
|
+
raw_behaviors = Array(params["behaviors"] || params[:behaviors] || [])
|
83
|
+
raw_behaviors.each_with_index do |behavior, index|
|
84
|
+
@behaviors << Behavior.new(index:, gem_name:, **behavior)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def generate_retreival!
|
89
|
+
@retrieval = Helper::Retrieval.new(gem_name:, server: (params[:server] || params["server"]), git: (params[:git] || params["git"]))
|
90
|
+
end
|
91
|
+
|
92
|
+
def errors
|
93
|
+
@errors ||= []
|
94
|
+
end
|
62
95
|
end
|
63
96
|
end
|
64
97
|
end
|
data/lib/gem_enforcer/setup.rb
CHANGED
@@ -8,24 +8,37 @@ module GemEnforcer
|
|
8
8
|
module_function
|
9
9
|
|
10
10
|
def validate_yml!
|
11
|
-
errors = config_yml["gems"].map do |name, metadata|
|
11
|
+
@errors = config_yml["gems"].map do |name, metadata|
|
12
12
|
validator = Validate.new(name: name, **metadata)
|
13
13
|
validations << validator
|
14
14
|
|
15
|
-
validator.error_status
|
16
|
-
end.compact
|
15
|
+
validator.error_status unless validator.valid_config?
|
16
|
+
end.flatten.compact
|
17
17
|
return true if errors.empty?
|
18
18
|
|
19
19
|
log_level = config_yml.dig("invalid_config", "log_level") || "error"
|
20
20
|
behavior = config_yml.dig("invalid_config", "behavior") || "exit"
|
21
|
+
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def errors
|
26
|
+
@errors ||= []
|
21
27
|
end
|
22
28
|
|
23
29
|
def validations
|
24
30
|
@validations ||= []
|
25
31
|
end
|
26
32
|
|
27
|
-
def run_validations!
|
28
|
-
validations.each { _1.run_validation!
|
33
|
+
def run_validations!
|
34
|
+
validations.each { _1.run_validation! }
|
35
|
+
end
|
36
|
+
|
37
|
+
def execute!
|
38
|
+
if validate_yml!
|
39
|
+
|
40
|
+
else
|
41
|
+
end
|
29
42
|
end
|
30
43
|
|
31
44
|
def config_yml
|
data/lib/gem_enforcer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gem_enforcer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Taylor
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-09-
|
11
|
+
date: 2024-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: class_composer
|
@@ -94,20 +94,6 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '3.0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: simplecov
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - "~>"
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: 0.17.0
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - "~>"
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: 0.17.0
|
111
97
|
description: Provide the ability to validate targetted gems are up to date before
|
112
98
|
executing commands
|
113
99
|
email:
|
@@ -129,6 +115,10 @@ files:
|
|
129
115
|
- README.md
|
130
116
|
- bin/console
|
131
117
|
- docker-compose.yml
|
118
|
+
- examples/README.md
|
119
|
+
- examples/git_tag.yml
|
120
|
+
- examples/multiple_behavior.yml
|
121
|
+
- examples/single_validation.yml
|
132
122
|
- gem_enforcer.gemspec
|
133
123
|
- gem_enforcer.yml
|
134
124
|
- lib/gem_enforcer.rb
|
@@ -138,9 +128,10 @@ files:
|
|
138
128
|
- lib/gem_enforcer/retrieve/gem_server.rb
|
139
129
|
- lib/gem_enforcer/retrieve/git_tag.rb
|
140
130
|
- lib/gem_enforcer/setup.rb
|
131
|
+
- lib/gem_enforcer/setup/behavior.rb
|
141
132
|
- lib/gem_enforcer/setup/helper/on_failure.rb
|
142
133
|
- lib/gem_enforcer/setup/helper/retrieval.rb
|
143
|
-
- lib/gem_enforcer/setup/helper/
|
134
|
+
- lib/gem_enforcer/setup/helper/version_enforcer.rb
|
144
135
|
- lib/gem_enforcer/setup/validate.rb
|
145
136
|
- lib/gem_enforcer/version.rb
|
146
137
|
homepage: https://github.com/matt-taylor/gem_enforcer
|
@@ -1,151 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GemEnforcer
|
4
|
-
module Setup
|
5
|
-
module Helper
|
6
|
-
module Version
|
7
|
-
ALLOWED_VERSION_REALEASE = :releases
|
8
|
-
ALLOWED_VERSION_SEMVER = [:major, :minor, :patch]
|
9
|
-
ALLOWED_VERSION_THRESHOLD_KEYS = [ALLOWED_VERSION_REALEASE, *ALLOWED_VERSION_SEMVER]
|
10
|
-
|
11
|
-
def version_default_message
|
12
|
-
@default_version_message
|
13
|
-
end
|
14
|
-
|
15
|
-
def validate_version
|
16
|
-
if params.keys.include?("enforce_insync") && params.keys.include?("version_threshold")
|
17
|
-
errors << "version: Must only contain `enforce_insync` or `version_threshold`"
|
18
|
-
return false
|
19
|
-
end
|
20
|
-
|
21
|
-
if params["enforce_insync"]
|
22
|
-
@version_type = :enforce_insync
|
23
|
-
@default_version_message = "Version must be the most recently released version."
|
24
|
-
return true
|
25
|
-
end
|
26
|
-
|
27
|
-
@version_type = :version_threshold
|
28
|
-
version_threshold_keys = params["version_threshold"]&.keys || []
|
29
|
-
if version_threshold_keys.sort == [ALLOWED_VERSION_REALEASE.to_s].sort
|
30
|
-
@version_threshold = :releases
|
31
|
-
@default_version_message = "Version must be within #{params["version_threshold"]["releases"]} of the most recently released versions"
|
32
|
-
_releases_validate
|
33
|
-
elsif ALLOWED_VERSION_THRESHOLD_KEYS.any? { version_threshold_keys.include?(_1.to_s) }
|
34
|
-
@version_threshold = :semver
|
35
|
-
message = []
|
36
|
-
message << "#{params["version_threshold"]["major"]} major versions" if params["version_threshold"]["major"]
|
37
|
-
message << "#{params["version_threshold"]["minor"]} minor versions" if params["version_threshold"]["minor"]
|
38
|
-
message << "#{params["version_threshold"]["patch"]} patch versions" if params["version_threshold"]["patch"]
|
39
|
-
@default_version_message = "Version must be within #{message.join(" and ")} of the most recent release."
|
40
|
-
_semver_validate
|
41
|
-
else
|
42
|
-
errors << "version.version_threshold: Expected keys to contain [#{ALLOWED_VERSION_REALEASE}] or #{ALLOWED_VERSION_SEMVER}"
|
43
|
-
false
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def _semver_validate
|
48
|
-
boolean = true
|
49
|
-
if major = params["version_threshold"]["major"]
|
50
|
-
unless Integer === major
|
51
|
-
boolean = false
|
52
|
-
errors << "version.version_threshold.major: Expected value to be an Integer"
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
if minor = params["version_threshold"]["minor"]
|
57
|
-
unless Integer === minor
|
58
|
-
boolean = false
|
59
|
-
errors << "version.version_threshold.minor: Expected value to be an Integer"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
if patch = params["version_threshold"]["patch"]
|
64
|
-
unless Integer === patch
|
65
|
-
boolean = false
|
66
|
-
errors << "version.version_threshold.patch: Expected value to be an Integer"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
boolean
|
71
|
-
end
|
72
|
-
|
73
|
-
def _releases_validate
|
74
|
-
release_count = params["version_threshold"]["releases"]
|
75
|
-
unless Integer === release_count
|
76
|
-
errors << "version.version_threshold.releases: Expected value to be an Integer"
|
77
|
-
return false
|
78
|
-
end
|
79
|
-
|
80
|
-
true
|
81
|
-
end
|
82
|
-
|
83
|
-
def version_execute?(version_list:)
|
84
|
-
if @version_type == :enforce_insync
|
85
|
-
__validate_enforce_insync?(version_list:)
|
86
|
-
else
|
87
|
-
if @version_threshold == :releases
|
88
|
-
__validate_version_threshold_releases?(version_list:)
|
89
|
-
else
|
90
|
-
__validate_version_threshold_semver?(version_list:)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def __validate_version_threshold_semver?(version_list:)
|
96
|
-
if max_major_versions_behind = params.dig("version_threshold", "major")
|
97
|
-
return false unless __threshold_semver_distance(type: :major, number: current_version.segments[0], list: version_list.map { _1.segments[0] }, threshold: max_major_versions_behind)
|
98
|
-
end
|
99
|
-
|
100
|
-
if max_minor_versions_behind = params.dig("version_threshold", "minor")
|
101
|
-
# Select only the minor versions that match the major version
|
102
|
-
current_major_version = current_version.segments[0]
|
103
|
-
minor_version_check_list = version_list.select { _1.segments[0] == current_major_version }.map { _1.segments[1] }
|
104
|
-
return false unless __threshold_semver_distance(type: :minor, number: current_version.segments[1], list: minor_version_check_list, threshold: max_minor_versions_behind)
|
105
|
-
end
|
106
|
-
|
107
|
-
if max_patch_versions_behind = params.dig("version_threshold", "patch")
|
108
|
-
# Select only the patch versions that match the major version
|
109
|
-
current_major_minor_version = current_version.segments[0..1]
|
110
|
-
patch_version_check_list = version_list.select { _1.segments[0..1] == current_major_minor_version }.map { _1.segments[2] }
|
111
|
-
return false unless __threshold_semver_distance(type: :patch, number: current_version.segments[2], list: patch_version_check_list, threshold: max_patch_versions_behind)
|
112
|
-
end
|
113
|
-
|
114
|
-
true
|
115
|
-
end
|
116
|
-
|
117
|
-
def __threshold_semver_distance(type:, number:, list:, threshold:)
|
118
|
-
# remove duplicates ans sort in highest to lowest number
|
119
|
-
uniq_list = list.uniq.sort.reverse
|
120
|
-
|
121
|
-
# get the position in the sorted array
|
122
|
-
position_in_sorted_array = uniq_list.find_index(number)
|
123
|
-
|
124
|
-
# if position is less than or equal to the threshold, we are good
|
125
|
-
# otherwise, it is out of compliance
|
126
|
-
return true if position_in_sorted_array <= threshold
|
127
|
-
|
128
|
-
@default_version_message += " Failed to match #{type} version threshold"
|
129
|
-
|
130
|
-
false
|
131
|
-
end
|
132
|
-
|
133
|
-
def __validate_version_threshold_releases?(version_list:)
|
134
|
-
releases_behind = params.dig("version_threshold", "releases").to_i
|
135
|
-
|
136
|
-
min_version_allowed = version_list[-releases_behind]
|
137
|
-
current_version >= min_version_allowed
|
138
|
-
end
|
139
|
-
|
140
|
-
def __validate_enforce_insync?(version_list:)
|
141
|
-
max_version = version_list.max
|
142
|
-
return true if current_version >= max_version
|
143
|
-
|
144
|
-
@default_version_message += " Please upgrade to at least v#{max_version}"
|
145
|
-
|
146
|
-
false
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|