artifactory-cleaner 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.idea/.gitignore +2 -0
  4. data/.idea/checkstyle-idea.xml +16 -0
  5. data/.idea/dictionaries/jgitlin.xml +7 -0
  6. data/.idea/misc.xml +6 -0
  7. data/.idea/modules.xml +8 -0
  8. data/.idea/vcs.xml +6 -0
  9. data/.rspec +3 -0
  10. data/.rspec_status +39 -0
  11. data/.travis.yml +6 -0
  12. data/CODE_OF_CONDUCT.md +74 -0
  13. data/Gemfile +12 -0
  14. data/README.md +73 -0
  15. data/Rakefile +20 -0
  16. data/artifactory-cleaner.gemspec +43 -0
  17. data/artifactory-cleaner.iml +9 -0
  18. data/bin/console +14 -0
  19. data/bin/setup +8 -0
  20. data/doc/rdoc/Artifactory.html +94 -0
  21. data/doc/rdoc/Artifactory/Cleaner.html +108 -0
  22. data/doc/rdoc/Artifactory/Cleaner/ArtifactBucket.html +504 -0
  23. data/doc/rdoc/Artifactory/Cleaner/ArtifactBucketCollection.html +570 -0
  24. data/doc/rdoc/Artifactory/Cleaner/ArtifactFilter.html +712 -0
  25. data/doc/rdoc/Artifactory/Cleaner/ArtifactFilterRule.html +519 -0
  26. data/doc/rdoc/Artifactory/Cleaner/CLI.html +625 -0
  27. data/doc/rdoc/Artifactory/Cleaner/Controller.html +1014 -0
  28. data/doc/rdoc/Artifactory/Cleaner/DiscoveredArtifact.html +400 -0
  29. data/doc/rdoc/Artifactory/Cleaner/DiscoveryWorker.html +466 -0
  30. data/doc/rdoc/Artifactory/Cleaner/Error.html +101 -0
  31. data/doc/rdoc/Artifactory/Cleaner/SpecHelpers.html +190 -0
  32. data/doc/rdoc/Artifactory/Cleaner/Util.html +157 -0
  33. data/doc/rdoc/CODE_OF_CONDUCT_md.html +228 -0
  34. data/doc/rdoc/Float.html +94 -0
  35. data/doc/rdoc/Gemfile.html +144 -0
  36. data/doc/rdoc/Gemfile_lock.html +217 -0
  37. data/doc/rdoc/Object.html +112 -0
  38. data/doc/rdoc/README_md.html +241 -0
  39. data/doc/rdoc/Rakefile.html +151 -0
  40. data/doc/rdoc/artifactory-cleaner_gemspec.html +173 -0
  41. data/doc/rdoc/artifactory-cleaner_iml.html +139 -0
  42. data/doc/rdoc/bin/setup.html +134 -0
  43. data/doc/rdoc/created.rid +219 -0
  44. data/doc/rdoc/css/fonts.css +167 -0
  45. data/doc/rdoc/css/rdoc.css +590 -0
  46. data/doc/rdoc/filterlist_yaml.html +149 -0
  47. data/doc/rdoc/filters/clean-amzn_yaml.html +133 -0
  48. data/doc/rdoc/filters/snapshots_yaml.html +137 -0
  49. data/doc/rdoc/filters/test-filter_yaml.html +137 -0
  50. data/doc/rdoc/filters/yum-test_yaml.html +141 -0
  51. data/doc/rdoc/fonts/Lato-Light.ttf +0 -0
  52. data/doc/rdoc/fonts/Lato-LightItalic.ttf +0 -0
  53. data/doc/rdoc/fonts/Lato-Regular.ttf +0 -0
  54. data/doc/rdoc/fonts/Lato-RegularItalic.ttf +0 -0
  55. data/doc/rdoc/fonts/SourceCodePro-Bold.ttf +0 -0
  56. data/doc/rdoc/fonts/SourceCodePro-Regular.ttf +0 -0
  57. data/doc/rdoc/images/add.png +0 -0
  58. data/doc/rdoc/images/arrow_up.png +0 -0
  59. data/doc/rdoc/images/brick.png +0 -0
  60. data/doc/rdoc/images/brick_link.png +0 -0
  61. data/doc/rdoc/images/bug.png +0 -0
  62. data/doc/rdoc/images/bullet_black.png +0 -0
  63. data/doc/rdoc/images/bullet_toggle_minus.png +0 -0
  64. data/doc/rdoc/images/bullet_toggle_plus.png +0 -0
  65. data/doc/rdoc/images/date.png +0 -0
  66. data/doc/rdoc/images/delete.png +0 -0
  67. data/doc/rdoc/images/find.png +0 -0
  68. data/doc/rdoc/images/loadingAnimation.gif +0 -0
  69. data/doc/rdoc/images/macFFBgHack.png +0 -0
  70. data/doc/rdoc/images/package.png +0 -0
  71. data/doc/rdoc/images/page_green.png +0 -0
  72. data/doc/rdoc/images/page_white_text.png +0 -0
  73. data/doc/rdoc/images/page_white_width.png +0 -0
  74. data/doc/rdoc/images/plugin.png +0 -0
  75. data/doc/rdoc/images/ruby.png +0 -0
  76. data/doc/rdoc/images/tag_blue.png +0 -0
  77. data/doc/rdoc/images/tag_green.png +0 -0
  78. data/doc/rdoc/images/transparent.png +0 -0
  79. data/doc/rdoc/images/wrench.png +0 -0
  80. data/doc/rdoc/images/wrench_orange.png +0 -0
  81. data/doc/rdoc/images/zoom.png +0 -0
  82. data/doc/rdoc/index.html +166 -0
  83. data/doc/rdoc/js/darkfish.js +161 -0
  84. data/doc/rdoc/js/jquery.js +4 -0
  85. data/doc/rdoc/js/navigation.js +141 -0
  86. data/doc/rdoc/js/navigation.js.gz +0 -0
  87. data/doc/rdoc/js/search.js +109 -0
  88. data/doc/rdoc/js/search_index.js +1 -0
  89. data/doc/rdoc/js/search_index.js.gz +0 -0
  90. data/doc/rdoc/js/searcher.js +229 -0
  91. data/doc/rdoc/js/searcher.js.gz +0 -0
  92. data/doc/rdoc/results/archive-test-4_log.html +762 -0
  93. data/doc/rdoc/results/buckets-2020-01-31_txt.html +233 -0
  94. data/doc/rdoc/results/clean-test-2_log.html +598 -0
  95. data/doc/rdoc/results/clean-test-3_log.html +128 -0
  96. data/doc/rdoc/results/clean-test-5_log.html +2721 -0
  97. data/doc/rdoc/results/clean-test-6_log.html +135 -0
  98. data/doc/rdoc/results/clean-test-7_log.html +137 -0
  99. data/doc/rdoc/results/clean-test-8-real_log.html +131 -0
  100. data/doc/rdoc/results/clean-test-9_log.html +131 -0
  101. data/doc/rdoc/results/clean-test1_log.html +1759 -0
  102. data/doc/rdoc/results/yum-test_2020-01-31_log.html +2854 -0
  103. data/doc/rdoc/results/yum-test_dry-run_log.html +1074 -0
  104. data/doc/rdoc/table_of_contents.html +581 -0
  105. data/exe/artifactory-cleaner +12 -0
  106. data/lib/artifactory/cleaner.rb +17 -0
  107. data/lib/artifactory/cleaner/artifact_bucket.rb +102 -0
  108. data/lib/artifactory/cleaner/artifact_bucket_collection.rb +118 -0
  109. data/lib/artifactory/cleaner/artifact_filter.rb +146 -0
  110. data/lib/artifactory/cleaner/artifact_filter_rule.rb +81 -0
  111. data/lib/artifactory/cleaner/cli.rb +415 -0
  112. data/lib/artifactory/cleaner/controller.rb +466 -0
  113. data/lib/artifactory/cleaner/discovered_artifact.rb +71 -0
  114. data/lib/artifactory/cleaner/discovery_worker.rb +126 -0
  115. data/lib/artifactory/cleaner/util.rb +21 -0
  116. data/lib/artifactory/cleaner/version.rb +7 -0
  117. 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