artifactory-cleaner 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.idea/.gitignore +2 -0
- data/.idea/checkstyle-idea.xml +16 -0
- data/.idea/dictionaries/jgitlin.xml +7 -0
- data/.idea/misc.xml +6 -0
- data/.idea/modules.xml +8 -0
- data/.idea/vcs.xml +6 -0
- data/.rspec +3 -0
- data/.rspec_status +39 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +12 -0
- data/README.md +73 -0
- data/Rakefile +20 -0
- data/artifactory-cleaner.gemspec +43 -0
- data/artifactory-cleaner.iml +9 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/doc/rdoc/Artifactory.html +94 -0
- data/doc/rdoc/Artifactory/Cleaner.html +108 -0
- data/doc/rdoc/Artifactory/Cleaner/ArtifactBucket.html +504 -0
- data/doc/rdoc/Artifactory/Cleaner/ArtifactBucketCollection.html +570 -0
- data/doc/rdoc/Artifactory/Cleaner/ArtifactFilter.html +712 -0
- data/doc/rdoc/Artifactory/Cleaner/ArtifactFilterRule.html +519 -0
- data/doc/rdoc/Artifactory/Cleaner/CLI.html +625 -0
- data/doc/rdoc/Artifactory/Cleaner/Controller.html +1014 -0
- data/doc/rdoc/Artifactory/Cleaner/DiscoveredArtifact.html +400 -0
- data/doc/rdoc/Artifactory/Cleaner/DiscoveryWorker.html +466 -0
- data/doc/rdoc/Artifactory/Cleaner/Error.html +101 -0
- data/doc/rdoc/Artifactory/Cleaner/SpecHelpers.html +190 -0
- data/doc/rdoc/Artifactory/Cleaner/Util.html +157 -0
- data/doc/rdoc/CODE_OF_CONDUCT_md.html +228 -0
- data/doc/rdoc/Float.html +94 -0
- data/doc/rdoc/Gemfile.html +144 -0
- data/doc/rdoc/Gemfile_lock.html +217 -0
- data/doc/rdoc/Object.html +112 -0
- data/doc/rdoc/README_md.html +241 -0
- data/doc/rdoc/Rakefile.html +151 -0
- data/doc/rdoc/artifactory-cleaner_gemspec.html +173 -0
- data/doc/rdoc/artifactory-cleaner_iml.html +139 -0
- data/doc/rdoc/bin/setup.html +134 -0
- data/doc/rdoc/created.rid +219 -0
- data/doc/rdoc/css/fonts.css +167 -0
- data/doc/rdoc/css/rdoc.css +590 -0
- data/doc/rdoc/filterlist_yaml.html +149 -0
- data/doc/rdoc/filters/clean-amzn_yaml.html +133 -0
- data/doc/rdoc/filters/snapshots_yaml.html +137 -0
- data/doc/rdoc/filters/test-filter_yaml.html +137 -0
- data/doc/rdoc/filters/yum-test_yaml.html +141 -0
- data/doc/rdoc/fonts/Lato-Light.ttf +0 -0
- data/doc/rdoc/fonts/Lato-LightItalic.ttf +0 -0
- data/doc/rdoc/fonts/Lato-Regular.ttf +0 -0
- data/doc/rdoc/fonts/Lato-RegularItalic.ttf +0 -0
- data/doc/rdoc/fonts/SourceCodePro-Bold.ttf +0 -0
- data/doc/rdoc/fonts/SourceCodePro-Regular.ttf +0 -0
- data/doc/rdoc/images/add.png +0 -0
- data/doc/rdoc/images/arrow_up.png +0 -0
- data/doc/rdoc/images/brick.png +0 -0
- data/doc/rdoc/images/brick_link.png +0 -0
- data/doc/rdoc/images/bug.png +0 -0
- data/doc/rdoc/images/bullet_black.png +0 -0
- data/doc/rdoc/images/bullet_toggle_minus.png +0 -0
- data/doc/rdoc/images/bullet_toggle_plus.png +0 -0
- data/doc/rdoc/images/date.png +0 -0
- data/doc/rdoc/images/delete.png +0 -0
- data/doc/rdoc/images/find.png +0 -0
- data/doc/rdoc/images/loadingAnimation.gif +0 -0
- data/doc/rdoc/images/macFFBgHack.png +0 -0
- data/doc/rdoc/images/package.png +0 -0
- data/doc/rdoc/images/page_green.png +0 -0
- data/doc/rdoc/images/page_white_text.png +0 -0
- data/doc/rdoc/images/page_white_width.png +0 -0
- data/doc/rdoc/images/plugin.png +0 -0
- data/doc/rdoc/images/ruby.png +0 -0
- data/doc/rdoc/images/tag_blue.png +0 -0
- data/doc/rdoc/images/tag_green.png +0 -0
- data/doc/rdoc/images/transparent.png +0 -0
- data/doc/rdoc/images/wrench.png +0 -0
- data/doc/rdoc/images/wrench_orange.png +0 -0
- data/doc/rdoc/images/zoom.png +0 -0
- data/doc/rdoc/index.html +166 -0
- data/doc/rdoc/js/darkfish.js +161 -0
- data/doc/rdoc/js/jquery.js +4 -0
- data/doc/rdoc/js/navigation.js +141 -0
- data/doc/rdoc/js/navigation.js.gz +0 -0
- data/doc/rdoc/js/search.js +109 -0
- data/doc/rdoc/js/search_index.js +1 -0
- data/doc/rdoc/js/search_index.js.gz +0 -0
- data/doc/rdoc/js/searcher.js +229 -0
- data/doc/rdoc/js/searcher.js.gz +0 -0
- data/doc/rdoc/results/archive-test-4_log.html +762 -0
- data/doc/rdoc/results/buckets-2020-01-31_txt.html +233 -0
- data/doc/rdoc/results/clean-test-2_log.html +598 -0
- data/doc/rdoc/results/clean-test-3_log.html +128 -0
- data/doc/rdoc/results/clean-test-5_log.html +2721 -0
- data/doc/rdoc/results/clean-test-6_log.html +135 -0
- data/doc/rdoc/results/clean-test-7_log.html +137 -0
- data/doc/rdoc/results/clean-test-8-real_log.html +131 -0
- data/doc/rdoc/results/clean-test-9_log.html +131 -0
- data/doc/rdoc/results/clean-test1_log.html +1759 -0
- data/doc/rdoc/results/yum-test_2020-01-31_log.html +2854 -0
- data/doc/rdoc/results/yum-test_dry-run_log.html +1074 -0
- data/doc/rdoc/table_of_contents.html +581 -0
- data/exe/artifactory-cleaner +12 -0
- data/lib/artifactory/cleaner.rb +17 -0
- data/lib/artifactory/cleaner/artifact_bucket.rb +102 -0
- data/lib/artifactory/cleaner/artifact_bucket_collection.rb +118 -0
- data/lib/artifactory/cleaner/artifact_filter.rb +146 -0
- data/lib/artifactory/cleaner/artifact_filter_rule.rb +81 -0
- data/lib/artifactory/cleaner/cli.rb +415 -0
- data/lib/artifactory/cleaner/controller.rb +466 -0
- data/lib/artifactory/cleaner/discovered_artifact.rb +71 -0
- data/lib/artifactory/cleaner/discovery_worker.rb +126 -0
- data/lib/artifactory/cleaner/util.rb +21 -0
- data/lib/artifactory/cleaner/version.rb +7 -0
- metadata +252 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
##
|
4
|
+
# Artifactory Cleaner CLI
|
5
|
+
# This file provides the command-line interface to Artifactory Cleaner. It is just an entrypoint to the
|
6
|
+
# Artifactory::Cleaner::CLI class; all the work is done there
|
7
|
+
# @see Artifactory::Cleaner::CLI
|
8
|
+
|
9
|
+
require 'bundler/setup'
|
10
|
+
require 'artifactory/cleaner'
|
11
|
+
|
12
|
+
Artifactory::Cleaner::CLI.start(ARGV)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "artifactory/cleaner/version"
|
2
|
+
require "artifactory/cleaner/util"
|
3
|
+
require "artifactory/cleaner/discovered_artifact"
|
4
|
+
require "artifactory/cleaner/artifact_bucket"
|
5
|
+
require "artifactory/cleaner/artifact_bucket_collection"
|
6
|
+
require "artifactory/cleaner/artifact_filter"
|
7
|
+
require "artifactory/cleaner/artifact_filter_rule"
|
8
|
+
require "artifactory/cleaner/discovery_worker"
|
9
|
+
require "artifactory/cleaner/controller"
|
10
|
+
require "artifactory/cleaner/cli"
|
11
|
+
|
12
|
+
module Artifactory
|
13
|
+
module Cleaner
|
14
|
+
class Error < StandardError; end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'artifactory'
|
3
|
+
|
4
|
+
module Artifactory
|
5
|
+
module Cleaner
|
6
|
+
|
7
|
+
##
|
8
|
+
# A collection of Artifacts within a date range
|
9
|
+
#
|
10
|
+
# An Artifactory::Cleaner::ArtifactBucket represents an "age bucket" when analyzing Artifact usage; Artifacts are
|
11
|
+
# grouped into buckets of time to aid in developing an archive strategy.
|
12
|
+
#
|
13
|
+
# Artifactory::Cleaner::ArtifactBucket is largely just an Array of Artifactory::Resource::Artifact instances, with
|
14
|
+
# logic to maintain a filesize count and properties fr the age of the artifacts within.
|
15
|
+
#
|
16
|
+
# This class works with the Artifactory::Cleaner::ArtifactBucketCollection class, which maintains a collection of
|
17
|
+
# Artifactory::Cleaner::ArtifactBucket instances and handles selecting the proper one for a given Artifact
|
18
|
+
class ArtifactBucket
|
19
|
+
extend Forwardable
|
20
|
+
include Enumerable
|
21
|
+
|
22
|
+
attr_reader :min
|
23
|
+
attr_reader :max
|
24
|
+
attr_reader :filesize
|
25
|
+
|
26
|
+
##
|
27
|
+
# ArtifactBucket constructor
|
28
|
+
#
|
29
|
+
# Params:
|
30
|
+
# +min+:: Lower bound (in days) for the age of artifacts this bucket should contain
|
31
|
+
# +max+:: Upper bound (in days) for the age of artifacts this bucket should contain, defaults to none (infinity)
|
32
|
+
def initialize(min,max=nil)
|
33
|
+
@min = min
|
34
|
+
@max = max.nil? ? Float::INFINITY : max
|
35
|
+
@filesize = 0
|
36
|
+
@collection = []
|
37
|
+
end
|
38
|
+
|
39
|
+
delegate [:[], :slice, :clear, :first, :last, :delete, :shift, :length, :empty?, :each] => :@collection
|
40
|
+
|
41
|
+
##
|
42
|
+
# Given an age (in days) return true if this bucket covers that age (if it's within the min and max of this bucket)
|
43
|
+
def covers?(age)
|
44
|
+
age >= @min && age < @max
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Update an artifact in the bucket
|
49
|
+
#
|
50
|
+
# TODO: This method does not validate if the artifact still belongs in this bucket by age
|
51
|
+
def []=(key, artifact)
|
52
|
+
raise TypeError, "expected Artifactory::Resource::Artifact, got #{artifact.class.name}" unless artifact.is_a? Artifactory::Resource::Artifact
|
53
|
+
@filesize -= @collection[key].size if @collection[key].is_a? Artifactory::Resource::Artifact
|
54
|
+
@filesize += artifact.size
|
55
|
+
@collection[key] = artifact
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Add an artifact to the end of this bucket
|
60
|
+
#
|
61
|
+
# Calls push on the Array which backs this bucket
|
62
|
+
#
|
63
|
+
# Aliased as method `<<`
|
64
|
+
#
|
65
|
+
# TODO: This method does not validate if the artifact belongs in this bucket by age
|
66
|
+
#
|
67
|
+
# @see: Array#push
|
68
|
+
def push(artifact)
|
69
|
+
raise TypeError, "expected Artifactory::Resource::Artifact, got #{artifact.class.name}" unless artifact.is_a? Artifactory::Resource::Artifact
|
70
|
+
@collection.push artifact
|
71
|
+
@filesize += artifact.size
|
72
|
+
self
|
73
|
+
end
|
74
|
+
alias_method :<<, :push
|
75
|
+
|
76
|
+
##
|
77
|
+
# Add an artifact to the beginning of this bucket
|
78
|
+
#
|
79
|
+
# Calls unshift on the Array which backs this bucket
|
80
|
+
#
|
81
|
+
# TODO: This method does not validate if the artifact belongs in this bucket by age
|
82
|
+
#
|
83
|
+
# @see Array#unshift
|
84
|
+
def unshift(artifact)
|
85
|
+
raise TypeError, "expected Artifactory::Resource::Artifact, got #{artifact.class.name}" unless artifact.is_a? Artifactory::Resource::Artifact
|
86
|
+
@collection.unshift artifact
|
87
|
+
@filesize += artifact.size
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Recalculate the file size of this bucket by adding up the size of all artifacts it contains
|
93
|
+
#
|
94
|
+
# This method forces recalculation of the total fize size of all artifacts within this bucket; the filesize is
|
95
|
+
# tracked automatically as artifacts are added, so thi method should be unnecessary. It is av available in case
|
96
|
+
# the tracking built in to `push`/`unshift`/`[]=` hsa a bug, or in case artifact sizes somehow change
|
97
|
+
def recalculate_filesize
|
98
|
+
@filesize = @collection.reduce(0) {|sum,asset| sum + asset.size}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'artifactory'
|
3
|
+
|
4
|
+
module Artifactory
|
5
|
+
module Cleaner
|
6
|
+
|
7
|
+
##
|
8
|
+
# Organize Artifacts by age bucket for analysis
|
9
|
+
#
|
10
|
+
# An Artifactory::Cleaner::ArtifactBucketCollection represents "age buckets" used for analyzing Artifact usage.
|
11
|
+
# Artifacts are grouped into buckets of time to aid in developing an archive strategy. This class maintains a
|
12
|
+
# list of buckets and handles the logic for sorting Artifacts into those buckets.
|
13
|
+
#
|
14
|
+
# Artifactory::Cleaner::ArtifactBucketCollection is largely just an Array of Artifactory::Cleaner::ArtifactBucket
|
15
|
+
# instances, with logic to sort and select them and logic to distribute Artifactory::Resource::Artifact instances
|
16
|
+
# into the proper Artifactory::Cleaner::ArtifactBucket
|
17
|
+
class ArtifactBucketCollection
|
18
|
+
extend Forwardable
|
19
|
+
include Enumerable
|
20
|
+
|
21
|
+
def initialize(buckets = [30,60,90,180,365,730,1095,nil])
|
22
|
+
@buckets = []
|
23
|
+
define_buckets(buckets)
|
24
|
+
end
|
25
|
+
|
26
|
+
delegate [:length, :each, :first, :last, :each] => :@buckets
|
27
|
+
|
28
|
+
##
|
29
|
+
# Remove all Artifacts from this collection
|
30
|
+
#
|
31
|
+
# Calls `clear` on every bucket within this collection
|
32
|
+
def clear
|
33
|
+
@buckets.each &:clear
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Adjust the bucket sizes within this collection
|
38
|
+
#
|
39
|
+
# Given an Enumerable of ages (as integer values of days) define buckets representing those periods within this
|
40
|
+
# collection. This method is similar to the constructor: provide an Enumerable where each value represents a
|
41
|
+
# bucket size and new buckets will be added to this collection representing the ages (in days) contained within
|
42
|
+
# `bucket_list`
|
43
|
+
#
|
44
|
+
# TODO: This will not update older buckets or move artifacts around, so if buckets were already defined then this
|
45
|
+
# method may result in an invalid configuration, E.G. overlapping buckets or artifacts which are no longer in
|
46
|
+
# the desired buckets. For best results, call this method on an ArtifactBucketCollection for which you already
|
47
|
+
# know the bucket sizes and to which no artifacts have yet been added
|
48
|
+
def define_buckets(bucket_list)
|
49
|
+
last_size = 0
|
50
|
+
bucket_list.each do |size|
|
51
|
+
@buckets << Artifactory::Cleaner::ArtifactBucket.new(last_size,size)
|
52
|
+
last_size = size
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Return an Array containing the bucket sizes of this collection.
|
58
|
+
#
|
59
|
+
# Returns the `max` property from every bucket within this collection, thus representing the bucket sizes this
|
60
|
+
# collection contains (as a properly configured ArtifactBucketCollection has the min of each bucket set to the max
|
61
|
+
# of the previous bucket, thus covering an entire time range)
|
62
|
+
def bucket_sizes
|
63
|
+
@buckets.map &:max
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Total number of Artifacts within this collection
|
68
|
+
#
|
69
|
+
# Returns the sum of the length of all buckets within this collection
|
70
|
+
def artifact_count
|
71
|
+
@buckets.reduce(0) { |sum, bkt| sum + bkt.length }
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Add a new artifact to this collection
|
76
|
+
#
|
77
|
+
# Given an Artifactory::Resource::Artifact `artifact`, find the proper ArtifactBucket within this ArtifactBucketCollection
|
78
|
+
# and add the artifact ton that bucket
|
79
|
+
#
|
80
|
+
# Aliased as `<<`
|
81
|
+
def add(artifact)
|
82
|
+
age = (Time.now - Artifactory::Cleaner::DiscoveredArtifact.latest_date_from(artifact))/(3600*24)
|
83
|
+
|
84
|
+
if (bucket = @buckets.find {|b| b.covers? age})
|
85
|
+
bucket << artifact
|
86
|
+
else
|
87
|
+
raise RangeError, "No bucket available for an artifact of age #{age.floor} days"
|
88
|
+
end
|
89
|
+
self
|
90
|
+
end
|
91
|
+
alias_method :<<, :add
|
92
|
+
|
93
|
+
##
|
94
|
+
# Accessor for a bucket of a given age
|
95
|
+
#
|
96
|
+
# Returns the bucket which covers the period `age` (represented as an artifact age, in days)
|
97
|
+
#
|
98
|
+
# Aliased as `[]`
|
99
|
+
def bucket(age)
|
100
|
+
@buckets.find {|b| b.covers? age}
|
101
|
+
end
|
102
|
+
alias_method :[], :bucket
|
103
|
+
|
104
|
+
##
|
105
|
+
# Human-readable summary of this collection
|
106
|
+
#
|
107
|
+
# Returns a string summarizing each bucket within this collection: how many packages and what filesize each bucket
|
108
|
+
# contains. Used when analyzing artifact searches: artifacts discovered from a search are placed into an
|
109
|
+
# ArtifactBucketCollection and then this report can be produced to describe how old the artifacts are and where
|
110
|
+
# opportunities for cleaning exist.
|
111
|
+
def report
|
112
|
+
buckets.map {|bucket|
|
113
|
+
"#{bucket.length} packages between #{bucket.min} and #{bucket.max} days, totaling #{Artifactory::Cleaner::Util::filesize bucket.filesize}"
|
114
|
+
}.join("\n")
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module Artifactory
|
2
|
+
module Cleaner
|
3
|
+
##
|
4
|
+
# Filter a list of artifacts based on a series of include/exclude rules
|
5
|
+
#
|
6
|
+
# Artifactory::Cleaner::ArtifactFilter is used to filter a list of artifacts based on rules. It is both a whitelist
|
7
|
+
# and a blacklist: it maintains a list of rules in sorted priority order, and the first rule which matches a given
|
8
|
+
# artifact determines the action for that artifact (include or exclude)
|
9
|
+
#
|
10
|
+
# Rules are stored in ascending priority order with lower numbers being greater priority. (Think "Priority 1" or
|
11
|
+
# process queue scheduling via `nice` value under Linux.
|
12
|
+
class ArtifactFilter
|
13
|
+
extend Forwardable
|
14
|
+
include Enumerable
|
15
|
+
|
16
|
+
##
|
17
|
+
# ArtifactFilter constructor
|
18
|
+
def initialize()
|
19
|
+
@rules = []
|
20
|
+
@sorted = false
|
21
|
+
@default_action = :include
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_accessor :default_action
|
25
|
+
|
26
|
+
delegate [:length, :clear, :empty?] => :@rules
|
27
|
+
|
28
|
+
##
|
29
|
+
# Access a rule by index
|
30
|
+
def [](*args)
|
31
|
+
sort_if_needed
|
32
|
+
@rules[*args]
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Update a given rule
|
37
|
+
def []=(key, rule)
|
38
|
+
raise TypeError, "expected Artifactory::Cleaner::ArtifactFilterRule, got #{rule.class.name}" unless rule.is_a? Artifactory::Cleaner::ArtifactFilterRule
|
39
|
+
@rules[key] = rule
|
40
|
+
@sorted = false
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Slice the filter rules, see Array#slice
|
45
|
+
def slice(*args, &block)
|
46
|
+
sort_if_needed
|
47
|
+
@rules.slice(*args, &block)
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Search for a rule
|
52
|
+
def bsearch(*args, &block)
|
53
|
+
sort_if_needed
|
54
|
+
@rules.bsearch(*args, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Get the first (numerically first priority) rule
|
59
|
+
def first
|
60
|
+
sort_if_needed
|
61
|
+
@rules.first
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Get the last (numerically last priority) rule
|
66
|
+
def last
|
67
|
+
sort_if_needed
|
68
|
+
@rules.last
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Iterate over all rules (See Enumerable#each)
|
73
|
+
def each(&block)
|
74
|
+
sort_if_needed
|
75
|
+
@rules.each(&block)
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# Add rules to this filter
|
80
|
+
#
|
81
|
+
# Like Array#push this method adds a rule to the end of the array, however the array will be sorted in priority
|
82
|
+
# order before usage so addition at the end or the beginning is somewhat meaningless
|
83
|
+
def push(rule)
|
84
|
+
raise TypeError, "expected Artifactory::Cleaner::ArtifactFilterRule, got #{rule.class.name}" unless rule.is_a? Artifactory::Cleaner::ArtifactFilterRule
|
85
|
+
@rules.push rule
|
86
|
+
@sorted = false
|
87
|
+
self
|
88
|
+
end
|
89
|
+
alias_method :<<, :push
|
90
|
+
|
91
|
+
##
|
92
|
+
# Add a rule to this filter
|
93
|
+
#
|
94
|
+
# Like Array#unshift this method adds a rule to the beginning of the array, however the array will be sorted in
|
95
|
+
# priority order before usage so addition at the end or the beginning is somewhat meaningless
|
96
|
+
def unshift(rule)
|
97
|
+
raise TypeError, "expected Artifactory::Cleaner::ArtifactFilterRule, got #{rule.class.name}" unless rule.is_a? Artifactory::Cleaner::ArtifactFilterRule
|
98
|
+
@rules.unshift rule
|
99
|
+
@sorted = false
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
# Ensure the filterset is sorted properly. Should not need to be called manually
|
105
|
+
def sort!
|
106
|
+
sort_if_needed
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Filter a given Artifactory::Resource::Artifact and return the action which should be taken
|
112
|
+
#
|
113
|
+
# Returns a symbol from the rule which matches this artifact, or the default action (:include) if no rules matched
|
114
|
+
def action_for(artifact)
|
115
|
+
sort_if_needed
|
116
|
+
@rules.each do |rule|
|
117
|
+
action = rule.action_for artifact
|
118
|
+
return action if action
|
119
|
+
end
|
120
|
+
@default_action
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
##
|
125
|
+
# Filter a collection of Artifactory::Resource::Artifact instances, returning the ones for which the action matches
|
126
|
+
#
|
127
|
+
# Takes an Enumerable filled with Artifactory::Resource::Artifact instances and returns a filtered enumerable for
|
128
|
+
# which all artifacts matched the desired action after applying the filter rules to each item. `action` defaults
|
129
|
+
# to :include but can be changed if desired
|
130
|
+
#
|
131
|
+
# Unlike Array#filter +this method does not take a block+
|
132
|
+
def filter(artifacts, action = :include)
|
133
|
+
artifacts.filter {|artifact| action_for(artifact) == action}
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
##
|
139
|
+
# Sort the array but avoid needless sorting
|
140
|
+
def sort_if_needed
|
141
|
+
@rules.sort! unless @sorted
|
142
|
+
@sorted = true
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Artifactory
|
2
|
+
module Cleaner
|
3
|
+
##
|
4
|
+
# Filter a collection of artifacts based on include/deny rules
|
5
|
+
#
|
6
|
+
# The Artifactory::Cleaner::ArtifactFilterRile class represents a whitelist or blacklist entry. It matches a package
|
7
|
+
# and then targets that package for inclusion or exclusion.
|
8
|
+
class ArtifactFilterRule
|
9
|
+
include Comparable
|
10
|
+
|
11
|
+
def initialize(action: :include, priority: 0, property: :uri, regex: //)
|
12
|
+
@regex = regex if regex.is_a? Regexp
|
13
|
+
@action = action
|
14
|
+
@priority = priority.to_i
|
15
|
+
@property = property.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :regex
|
19
|
+
|
20
|
+
##
|
21
|
+
# Change the regex of this rule
|
22
|
+
def regex=(re)
|
23
|
+
raise TypeError, 'Expected a Regexp' unless re.is_a? Regexp
|
24
|
+
@regex = re
|
25
|
+
end
|
26
|
+
alias_method :regexp, :regex
|
27
|
+
alias_method :regexp=, :regex=
|
28
|
+
|
29
|
+
# TODO: Allow changing priority. Right now this would cause problems because if the rule is in a filer, the filter won't be sorted properly after this change.
|
30
|
+
attr_reader :priority
|
31
|
+
attr_accessor :property
|
32
|
+
attr_accessor :action
|
33
|
+
|
34
|
+
##
|
35
|
+
# Does this rule trigger an action on a given artifact?
|
36
|
+
#
|
37
|
+
# This method returns true if the given artifact matches the criteria of this rule, and the rule is of type :include
|
38
|
+
def action_for(artifact)
|
39
|
+
if matches?(artifact)
|
40
|
+
@action
|
41
|
+
else
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Does this rule determine that an artifact should be included?
|
48
|
+
#
|
49
|
+
# This method returns true if the given artifact matches the criteria of this rule, and the rule is of type :include
|
50
|
+
def includes?(artifact)
|
51
|
+
@type == :include && matches?(artifact)
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Does this rule determine that an artifact should be excluded?
|
56
|
+
#
|
57
|
+
# This method returns true if the given artifact matches the criteria of this rule, and the rule is of type :exclude
|
58
|
+
def excludes?(artifact)
|
59
|
+
@type == :exclude && matches?(artifact)
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Does this rule match a given package?
|
64
|
+
#
|
65
|
+
# Returns true if the `property` of a given artifact matches the `regex`
|
66
|
+
def matches?(artifact)
|
67
|
+
@regex.is_a? Regexp and @regex.match?(artifact.send(@property).to_s)
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Compare priority with another rule
|
72
|
+
def <=>(other_rule)
|
73
|
+
if other_rule.is_a? ArtifactFilterRule
|
74
|
+
@priority <=> other_rule.priority
|
75
|
+
else
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|