gitlab-triage 1.7.0 → 1.10.1

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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.gitlab-ci.yml +75 -40
  4. data/.gitlab/merge_request_templates/Release.md +35 -0
  5. data/.rubocop.yml +3 -0
  6. data/Gemfile +1 -1
  7. data/README.md +92 -3
  8. data/gitlab-triage.gemspec +1 -1
  9. data/lib/gitlab/triage/action/comment.rb +14 -1
  10. data/lib/gitlab/triage/api_query_builders/date_query_param_builder.rb +78 -0
  11. data/lib/gitlab/triage/command_builders/move_command_builder.rb +19 -0
  12. data/lib/gitlab/triage/command_builders/text_content_builder.rb +17 -1
  13. data/lib/gitlab/triage/engine.rb +41 -16
  14. data/lib/gitlab/triage/errors.rb +1 -1
  15. data/lib/gitlab/triage/filters/merge_request_date_conditions_filter.rb +51 -5
  16. data/lib/gitlab/triage/network.rb +2 -1
  17. data/lib/gitlab/triage/network_adapters/httparty_adapter.rb +13 -1
  18. data/lib/gitlab/triage/option_parser.rb +10 -0
  19. data/lib/gitlab/triage/options.rb +2 -0
  20. data/lib/gitlab/triage/policies/rule_policy.rb +1 -1
  21. data/lib/gitlab/triage/policies/summary_policy.rb +1 -1
  22. data/lib/gitlab/triage/policies_resources/rule_resources.rb +5 -6
  23. data/lib/gitlab/triage/policies_resources/summary_resources.rb +5 -6
  24. data/lib/gitlab/triage/resource/base.rb +5 -0
  25. data/lib/gitlab/triage/resource/merge_request.rb +9 -0
  26. data/lib/gitlab/triage/url_builders/url_builder.rb +10 -9
  27. data/lib/gitlab/triage/validators/limiter_validator.rb +3 -1
  28. data/lib/gitlab/triage/validators/params_validator.rb +5 -3
  29. data/lib/gitlab/triage/version.rb +3 -1
  30. data/support/.triage-policies.example.yml +2 -2
  31. metadata +6 -4
  32. data/lib/gitlab/triage/filters/issuable_date_conditions_filter.rb +0 -65
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 115d63f58149d8ed49f22fcafc929b06ebcf7ba180cb1047121f61a7c67d890b
4
- data.tar.gz: 13f5bfcfd2bcb8325a40566fe86e30e13f03eb2aa0e3935c0f91c53446867ae2
3
+ metadata.gz: 1242b945b940e1378f6bb8f66eb5f08f3adb387245c0425954aa7a6cc3e98f25
4
+ data.tar.gz: 925cbb79cd61b2c5f4eeb87eed0596c674902aa6fd44bfe679b9740d660aede6
5
5
  SHA512:
6
- metadata.gz: ac6caff9332e7cc266ea22abb7beed863c577e4d4d4a2fb4c63171cf1834b14b1d4f0abaf616408d7a3ae9261a1668ce5e54ff6de971e14fd5136f584d7e7cd3
7
- data.tar.gz: 24e71190417f5e36deca4d1b6b1ee1991eecdf7930b5af852c7b861b75dd8e50071e7c0c051c04a84f9247a9a89e4887ec016f7896823b333c5350cc1643b7ba
6
+ metadata.gz: 1caedc0d489bc0e45e66399aa9529cf3eaf9812a78cb99fddba5cc5a078b10ab748e763d2407e6911da9ba2cadbc56453040366c8bad1757a0ae07fbcbf25b1d
7
+ data.tar.gz: 12de27a31f013916beee14fe3da467e1ee316774e8fea019c0735c68c81f0e5694a79cdcb5d4bc94522b620a94c4c4989c4367f51fd28d400f19a1dca63d48dc
data/.gitignore CHANGED
@@ -6,7 +6,6 @@
6
6
  /Gemfile.lock
7
7
  /_yardoc/
8
8
  /coverage/
9
- /doc/
10
9
  /pkg/
11
10
  /spec/reports/
12
11
  /tmp/
@@ -1,49 +1,67 @@
1
- image: ruby:2.6
2
-
3
1
  stages:
4
2
  - prepare
5
3
  - test
6
4
  - triage
5
+ - release
7
6
 
8
- .default-only:
9
- only:
10
- refs:
11
- - master
12
- - merge_requests
13
- - tags
14
-
15
- .default-before_script:
7
+ default:
8
+ image: ruby:2.7
9
+ tags:
10
+ - gitlab-org
11
+ cache:
12
+ key:
13
+ files:
14
+ - Gemfile
15
+ - gitlab-triage.gemspec
16
+ paths:
17
+ - vendor/ruby
18
+ - Gemfile.lock
19
+ policy: pull
16
20
  before_script:
21
+ - ruby --version
17
22
  - gem install bundler --no-document --version 2.0.2
18
- - bundle install --jobs $(nproc) --retry 3 --quiet
23
+ - bundle --version
24
+ - bundle install --jobs $(nproc) --path=vendor --retry 3 --quiet
25
+ - bundle check
26
+
27
+ workflow:
28
+ rules:
29
+ # For merge requests, create a pipeline.
30
+ - if: '$CI_MERGE_REQUEST_IID'
31
+ # For `master` branch, create a pipeline (this includes on schedules, pushes, merges, etc.).
32
+ - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
33
+ # For tags, create a pipeline.
34
+ - if: '$CI_COMMIT_TAG'
35
+
36
+ .use-docker-in-docker:
37
+ image: docker:${DOCKER_VERSION}
38
+ services:
39
+ - docker:${DOCKER_VERSION}-dind
40
+ variables:
41
+ DOCKER_DRIVER: overlay2
42
+ DOCKER_HOST: tcp://docker:2375
43
+ DOCKER_TLS_CERTDIR: ""
44
+ tags:
45
+ # See https://gitlab.com/gitlab-com/www-gitlab-com/-/issues/7019 for tag descriptions
46
+ - gitlab-org-docker
19
47
 
20
48
  ###################
21
49
  ## Prepare stage ##
22
50
  ###################
23
51
  setup-test-env:
24
- extends: .default-only
25
52
  stage: prepare
26
53
  script:
27
- - ruby --version
28
- - gem install bundler --no-document
29
- - bundle --version
30
- - bundle install --clean --jobs $(nproc) --path=vendor --retry=3 --quiet
31
- - bundle check
54
+ - echo "Setup done!"
32
55
  cache:
33
- key: "$CI_JOB_NAME-ruby-2.6"
34
- paths:
35
- - vendor/ruby
36
- - Gemfile.lock
56
+ policy: pull-push
37
57
  artifacts:
38
58
  paths:
39
- - vendor/ruby
40
59
  - Gemfile.lock
41
60
 
42
61
  ################
43
62
  ## Test stage ##
44
63
  ################
45
64
  rubocop:
46
- extends: [".default-only", ".default-before_script"]
47
65
  stage: test
48
66
  needs: ["setup-test-env"]
49
67
  dependencies: ["setup-test-env"]
@@ -55,20 +73,15 @@ rubocop:
55
73
  - .cache/rubocop_cache/
56
74
 
57
75
  # We need to copy this job's definition from the Code-Quality.gitlab-ci.yml
58
- # template because `only` is set without `refs`, so it takes precedence over `.default-only`.
76
+ # template because `only` is set without `refs`, so it takes precedence over default workflow rules.
59
77
  # See https://gitlab.com/gitlab-org/gitlab-ce/issues/66767.
60
78
  code_quality:
61
- extends: .default-only
62
- needs: ["setup-test-env"]
63
- dependencies: ["setup-test-env"]
79
+ extends: .use-docker-in-docker
64
80
  stage: test
65
- image: docker:stable
66
81
  allow_failure: true
67
- services:
68
- - docker:stable-dind
69
82
  variables:
70
- DOCKER_DRIVER: overlay2
71
83
  DOCKER_TLS_CERTDIR: ""
84
+ before_script: []
72
85
  script:
73
86
  - |
74
87
  if ! docker info &>/dev/null; then
@@ -85,14 +98,9 @@ code_quality:
85
98
  reports:
86
99
  codequality: gl-code-quality-report.json
87
100
  expire_in: 1 week
88
- except:
89
- variables:
90
- - $CODE_QUALITY_DISABLED
91
101
 
92
102
  specs:
93
- extends: [".default-only", ".default-before_script"]
94
103
  needs: ["setup-test-env"]
95
- dependencies: ["setup-test-env"]
96
104
  stage: test
97
105
  script:
98
106
  - bundle exec rake spec
@@ -101,12 +109,13 @@ specs:
101
109
  ## Triage stage ##
102
110
  ##################
103
111
  dry-run:gitlab-triage:
104
- extends: [".default-only", ".default-before_script"]
105
112
  needs: ["setup-test-env"]
106
- dependencies: ["setup-test-env"]
107
113
  stage: triage
108
114
  script:
109
- - bundle exec rake install:local
115
+ - bundle exec rake build
116
+ - gem install pkg/*.gem
117
+ - which gitlab-triage
118
+ - gitlab-triage --version
110
119
  - gitlab-triage --help
111
120
  - gitlab-triage --init
112
121
  - gitlab-triage --dry-run --debug --token $API_TOKEN --source-id $CI_PROJECT_PATH
@@ -114,4 +123,30 @@ dry-run:gitlab-triage:
114
123
  # This job requires allows to override the `CI_PROJECT_PATH` variable when triggered.
115
124
  dry-run:custom:
116
125
  extends: dry-run:gitlab-triage
117
- when: manual
126
+ rules:
127
+ - when: manual
128
+ allow_failure: true
129
+
130
+ ###################
131
+ ## Release stage ##
132
+ ###################
133
+ release:
134
+ stage: release
135
+ rules:
136
+ - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "push"'
137
+ changes: ["lib/gitlab/triage/version.rb"]
138
+ - if: '$CI_MERGE_REQUEST_TITLE =~ /RELEASE/'
139
+ when: manual
140
+ before_script: []
141
+ script:
142
+ - version=$(ruby -r ./lib/gitlab/triage/version -e 'puts Gitlab::Triage::VERSION' | tr -d "\n")
143
+ - tag="v${version}"
144
+ - message="Version ${version}."
145
+ # TODO: Add release notes from the Release MR?
146
+ - 'curl --request POST --header "PRIVATE-TOKEN: ${API_TOKEN}" -d "tag_name=${tag}" -d "ref=${CI_COMMIT_SHA}" -d "message=${message}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/repository/tags"'
147
+ - gem build gitlab-triage.gemspec
148
+ - gem push "gitlab-triage-${version}.gem"
149
+ artifacts:
150
+ paths:
151
+ - gitlab-triage*.gem
152
+ expire_in: 30 days
@@ -0,0 +1,35 @@
1
+ <!-- Replace `v4.5.0` with the previous release here, and `e18d76b309e42888759c1effe96767f13e34ae55`
2
+ with the latest commit from https://gitlab.com/gitlab-org/gitlab-triage/commits/master that will be included in the release. -->
3
+ - Diff: https://gitlab.com/gitlab-org/gitlab-triage/compare/v4.5.0...e18d76b309e42888759c1effe96767f13e34ae55
4
+
5
+ - Release notes:
6
+
7
+ <!-- Keep the sections order but remove the empty sections -->
8
+
9
+ ```markdown
10
+ ### New features and features updates
11
+
12
+ - !aaa <Title of the aaa MR>.
13
+
14
+ ### Fixes
15
+
16
+ - !bbb <Title of the bbb MR>.
17
+
18
+ ### Doc changes
19
+
20
+ - !ccc <Title of the ccc MR>.
21
+
22
+ ### Other changes (tooling, technical debt)
23
+
24
+ - !ddd <Title of the ddd MR>.
25
+ ```
26
+
27
+ - Checklist before merging:
28
+ - [ ] Diff link is up-to-date.
29
+ - [ ] Based on the diff, `lib/gitlab/triage/version.rb` is updated, according to [SemVer](https://semver.org).
30
+ - [ ] Release notes are accurate.
31
+
32
+ - Checklist after merging:
33
+ - [ ] [Update the release notes for the newly created tag](docs/release_process.md#how-to).
34
+
35
+ /label ~"Engineering Productivity" ~"ep::triage" ~"tooling::workflow"
@@ -11,3 +11,6 @@ AllCops:
11
11
 
12
12
  Rails/Output:
13
13
  Enabled: false
14
+
15
+ Metrics/LineLength:
16
+ Max: 152
data/Gemfile CHANGED
@@ -4,5 +4,5 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :test do
7
- gem 'gitlab-styles', '~> 3.0'
7
+ gem 'gitlab-styles', '~> 3.3.0'
8
8
  end
data/README.md CHANGED
@@ -58,7 +58,7 @@ resource_rules:
58
58
  interval: 5
59
59
  state: opened
60
60
  labels:
61
- - No Label
61
+ - None
62
62
  limits:
63
63
  most_recent: 50
64
64
  actions:
@@ -66,6 +66,7 @@ resource_rules:
66
66
  - needs attention
67
67
  mention:
68
68
  - markglenfletcher
69
+ move: gitlab-org/backlog
69
70
  comment: |
70
71
  {{author}} This issue is unlabelled after 5 days. It needs attention. Please take care of this before the end of #{2.days.from_now.strftime('%Y-%m-%d')}
71
72
  summarize:
@@ -85,9 +86,27 @@ resource_rules:
85
86
  /label ~"needs attention"
86
87
  merge_requests:
87
88
  rules:
88
- []
89
+ - name: My policy
90
+ conditions:
91
+ state: opened
92
+ labels:
93
+ - None
94
+ limits:
95
+ most_recent: 50
96
+ actions:
97
+ labels:
98
+ - needs attention
99
+ comment_type: thread
100
+ comment: |
101
+ {{author}} This issue is unlabelled. Please add one or more labels.
89
102
  ```
90
103
 
104
+ ### Real world example
105
+
106
+ We're enforcing multiple polices with pipeline schedules at [triage-ops](
107
+ https://gitlab.com/gitlab-org/quality/triage-ops), where we're also
108
+ extensively utilizing the [plugins system](#can-i-customize).
109
+
91
110
  ### Fields
92
111
 
93
112
  A policy consists of the following fields:
@@ -210,6 +229,22 @@ conditions:
210
229
  - feature proposal
211
230
  ```
212
231
 
232
+ ###### Predefined special label names
233
+
234
+ Basing on the [issues API](https://docs.gitlab.com/ee/api/issues.html), there
235
+ are two special predefined label names we can use here:
236
+
237
+ * `None`: This indicates that no labels were present
238
+ * `Any`: This indicates that any labels were presented
239
+
240
+ Example:
241
+
242
+ ```yml
243
+ conditions:
244
+ labels:
245
+ - None
246
+ ```
247
+
213
248
  ###### Labels brace expansion
214
249
 
215
250
  We could expand the labels by using brace expansion, which is a pattern
@@ -506,7 +541,9 @@ Available action types:
506
541
  - [`remove_labels` action](#remove-labels-action)
507
542
  - [`status` action](#status-action)
508
543
  - [`mention` action](#mention-action)
544
+ - [`move` action](#move-action)
509
545
  - [`comment` action](#comment-action)
546
+ - [`comment_type` action option](#comment-type-action-option)
510
547
  - [`summarize` action](#summarize-action)
511
548
 
512
549
  ##### Labels action
@@ -572,6 +609,19 @@ actions:
572
609
  - markglenfletcher
573
610
  ```
574
611
 
612
+ ##### Move action
613
+
614
+ Moves an issue (merge request is not supported yet) to the specified project.
615
+
616
+ Accepts a string containing the target project path.
617
+
618
+ Example:
619
+
620
+ ```yml
621
+ actions:
622
+ move: target/project_path
623
+ ```
624
+
575
625
  ##### Comment action
576
626
 
577
627
  Adds a comment to the resource.
@@ -619,6 +669,26 @@ actions:
619
669
  {{author}} Are you still interested in finishing this merge request?
620
670
  ```
621
671
 
672
+ ##### Comment type action option
673
+
674
+ Determines the type of comment to be added to the resource.
675
+
676
+ The following comment types are supported:
677
+
678
+ - `comment` (default): creates a regular comment on the resource
679
+ - `thread`: starts a resolvable thread (discussion) on the resource
680
+
681
+ For merge requests, if `comment_type` is set to `thread`, we can also configure that [all threads should be resolved before merging](https://docs.gitlab.com/ee/user/discussions/#only-allow-merge-requests-to-be-merged-if-all-threads-are-resolved), therefore this comment can prevent it from merging.
682
+
683
+ Example:
684
+
685
+ ```yml
686
+ actions:
687
+ comment_type: thread
688
+ comment: |
689
+ {{author}} Are you still interested in finishing this merge request?
690
+ ```
691
+
622
692
  ###### Harnessing Quick Actions
623
693
 
624
694
  [GitLab's quick actions feature](https://docs.gitlab.com/ce/user/project/quick_actions.html) is available in Core.
@@ -839,7 +909,7 @@ Which could generate an issue like:
839
909
 
840
910
  Here's a list of currently available Ruby expression API:
841
911
 
842
- ##### API
912
+ ##### Methods for `Issue` and `MergeRequest` (the context)
843
913
 
844
914
  | Name | Return type | Description |
845
915
  | ---- | ---- | ---- |
@@ -854,6 +924,12 @@ Here's a list of currently available Ruby expression API:
854
924
  | project_path | String | The path with namespace to the issues or merge requests project |
855
925
  | full_resource_reference | String | A full reference incuding project path to the issue or merge request |
856
926
 
927
+ ##### Methods for `MergeRequest` (merge request context)
928
+
929
+ | Method | Return type | Description |
930
+ | ---- | ---- | ---- |
931
+ | first_contribution? | Boolean | `true` if it's the author's first contribution to the project; `false` otherwise. This API requires an additional API request for the merge request, thus would be slower. |
932
+
857
933
  ##### Methods for `Milestone`
858
934
 
859
935
  | Method | Return type | Description |
@@ -930,6 +1006,7 @@ Usage: gitlab-triage [options]
930
1006
  -r, --require [string] Require a file before performing
931
1007
  -d, --debug Print debug information
932
1008
  -h, --help Print help message
1009
+ --all-projects Process all projects visible to `--token`
933
1010
  --init Initialize the project with a policy file
934
1011
  --init-ci Initialize the project with a .gitlab-ci.yml file
935
1012
  ```
@@ -948,6 +1025,14 @@ Triaging against a whole group:
948
1025
  gitlab-triage --dry-run --token $API_TOKEN --source-id gitlab-org --source groups
949
1026
  ```
950
1027
 
1028
+ Triaging against an entire instance:
1029
+
1030
+ ```
1031
+ gitlab-triage --dry-run --token $API_TOKEN --all-projects
1032
+ ```
1033
+
1034
+ > **Note:** The `--all-projects` option will process all resources for all projects visible to the specified `$API_TOKEN`
1035
+
951
1036
  #### Running on GitLab CI pipeline
952
1037
 
953
1038
  You can enforce policies using a scheduled pipeline:
@@ -1030,3 +1115,7 @@ resource_rules:
1030
1115
  ### Contributing
1031
1116
 
1032
1117
  Please refer to the [Contributing Guide](CONTRIBUTING.md).
1118
+
1119
+ ## Release Process
1120
+
1121
+ Please refer to the [Release Process](docs/release_process.md).
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.license = 'MIT'
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
- f.match(%r{^(test|spec|features)/})
16
+ f.match(%r{^(docs|test|spec|features)/})
17
17
  end
18
18
  spec.bindir = 'bin'
19
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
@@ -7,6 +7,7 @@ require_relative '../command_builders/label_command_builder'
7
7
  require_relative '../command_builders/remove_label_command_builder'
8
8
  require_relative '../command_builders/cc_command_builder'
9
9
  require_relative '../command_builders/status_command_builder'
10
+ require_relative '../command_builders/move_command_builder'
10
11
 
11
12
  module Gitlab
12
13
  module Triage
@@ -43,6 +44,7 @@ module Gitlab
43
44
  CommandBuilders::LabelCommandBuilder.new(policy.actions[:labels]).build_command,
44
45
  CommandBuilders::RemoveLabelCommandBuilder.new(policy.actions[:remove_labels]).build_command,
45
46
  CommandBuilders::CcCommandBuilder.new(policy.actions[:mention]).build_command,
47
+ CommandBuilders::MoveCommandBuilder.new(policy.actions[:move]).build_command,
46
48
  CommandBuilders::StatusCommandBuilder.new(policy.actions[:status]).build_command
47
49
  ]
48
50
  ).build_command
@@ -61,13 +63,24 @@ module Gitlab
61
63
  source_id: resource[:project_id],
62
64
  resource_type: policy.type,
63
65
  resource_id: resource['iid'],
64
- sub_resource_type: 'notes'
66
+ sub_resource_type: sub_resource_type
65
67
  ).build
66
68
 
67
69
  puts Gitlab::Triage::UI.debug "post_url: #{post_url}" if network.options.debug
68
70
 
69
71
  post_url
70
72
  end
73
+
74
+ def sub_resource_type
75
+ case type = policy.actions[:comment_type]
76
+ when 'comment', nil # nil is default
77
+ 'notes'
78
+ when 'thread'
79
+ 'discussions'
80
+ else
81
+ raise ArgumentError, "Unknown comment type: #{type}"
82
+ end
83
+ end
71
84
  end
72
85
  end
73
86
  end
@@ -0,0 +1,78 @@
1
+ require_relative '../validators/params_validator'
2
+
3
+ module Gitlab
4
+ module Triage
5
+ module APIQueryBuilders
6
+ class DateQueryParamBuilder
7
+ ATTRIBUTES = %w[updated_at created_at].freeze
8
+ CONDITIONS = %w[older_than newer_than].freeze
9
+ INTERVAL_TYPES = %w[days weeks months years].freeze
10
+
11
+ def self.filter_parameters
12
+ [
13
+ {
14
+ name: :attribute,
15
+ type: String,
16
+ values: ATTRIBUTES
17
+ },
18
+ {
19
+ name: :condition,
20
+ type: String,
21
+ values: CONDITIONS
22
+ },
23
+ {
24
+ name: :interval_type,
25
+ type: String,
26
+ values: INTERVAL_TYPES
27
+ },
28
+ {
29
+ name: :interval,
30
+ type: Numeric
31
+ }
32
+ ]
33
+ end
34
+
35
+ def self.applicable?(condition)
36
+ ATTRIBUTES.include?(condition[:attribute].to_s)
37
+ end
38
+
39
+ def initialize(condition_hash)
40
+ @attribute = condition_hash[:attribute].to_s
41
+ @interval_condition = condition_hash[:condition].to_sym
42
+ @interval_type = condition_hash[:interval_type]
43
+ @interval = condition_hash[:interval]
44
+ validate_condition(condition_hash)
45
+ end
46
+
47
+ def validate_condition(condition)
48
+ ParamsValidator.new(self.class.filter_parameters, condition).validate!
49
+ end
50
+
51
+ def param_name
52
+ prefix = attribute.sub(/_at\z/, '')
53
+ suffix =
54
+ case interval_condition
55
+ when :older_than
56
+ 'before'
57
+ when :newer_than
58
+ 'after'
59
+ end
60
+
61
+ "#{prefix}_#{suffix}"
62
+ end
63
+
64
+ def param_content
65
+ interval.public_send(interval_type).ago.to_date # rubocop:disable GitlabSecurity/PublicSend
66
+ end
67
+
68
+ def build_param
69
+ "&#{param_name}=#{param_content.strip}"
70
+ end
71
+
72
+ private
73
+
74
+ attr_reader :condition_hash, :attribute, :interval_condition, :interval_type, :interval
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'base_command_builder'
2
+
3
+ module Gitlab
4
+ module Triage
5
+ module CommandBuilders
6
+ class MoveCommandBuilder < BaseCommandBuilder
7
+ private
8
+
9
+ def slash_command_string
10
+ "/move"
11
+ end
12
+
13
+ def format_item(item)
14
+ item
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_support/core_ext/array/wrap'
4
+ require 'cgi'
4
5
 
5
6
  require_relative 'base_command_builder'
6
7
  require_relative '../resource/context'
@@ -76,7 +77,22 @@ module Gitlab
76
77
  template.sub(PLACEHOLDER_REGEX, attribute.to_s)
77
78
  end.join(', ')
78
79
 
79
- comment.gsub("{{#{placeholder}}}", formatted_text)
80
+ escaped_text =
81
+ case placeholder
82
+ when :items
83
+ # We don't need to escape it because it's recursive,
84
+ # which the contents should all be escaped already.
85
+ # Or put it another way, items isn't an attribute
86
+ # retrieved externally. It's a generated value which
87
+ # should be safe to begin with. At some point we
88
+ # may want to make this more distinguishable,
89
+ # separating values from API and values generated.
90
+ formatted_text
91
+ else
92
+ CGI.escape_html(formatted_text)
93
+ end
94
+
95
+ comment.gsub("{{#{placeholder}}}", escaped_text)
80
96
  end
81
97
  end
82
98
 
@@ -2,7 +2,6 @@ require 'active_support/all'
2
2
  require 'active_support/inflector'
3
3
 
4
4
  require_relative 'expand_condition'
5
- require_relative 'filters/issuable_date_conditions_filter'
6
5
  require_relative 'filters/merge_request_date_conditions_filter'
7
6
  require_relative 'filters/votes_conditions_filter'
8
7
  require_relative 'filters/forbidden_labels_conditions_filter'
@@ -16,6 +15,7 @@ require_relative 'policies/rule_policy'
16
15
  require_relative 'policies/summary_policy'
17
16
  require_relative 'policies_resources/rule_resources'
18
17
  require_relative 'policies_resources/summary_resources'
18
+ require_relative 'api_query_builders/date_query_param_builder'
19
19
  require_relative 'api_query_builders/single_query_param_builder'
20
20
  require_relative 'api_query_builders/multi_query_param_builder'
21
21
  require_relative 'url_builders/url_builder'
@@ -38,6 +38,7 @@ module Gitlab
38
38
  @options = options
39
39
  @network_adapter_class = network_adapter_class
40
40
 
41
+ assert_all!
41
42
  assert_project_id!
42
43
  assert_token!
43
44
  require_ruby_files
@@ -66,6 +67,7 @@ module Gitlab
66
67
 
67
68
  def assert_project_id!
68
69
  return if options.source_id
70
+ return if options.all
69
71
 
70
72
  raise ArgumentError, 'A project_id is needed (pass it with the `--source-id` option)!'
71
73
  end
@@ -76,6 +78,11 @@ module Gitlab
76
78
  raise ArgumentError, 'A token is needed (pass it with the `--token` option)!'
77
79
  end
78
80
 
81
+ def assert_all!
82
+ raise ArgumentError, '--all-projects option cannot be used in conjunction with --source and --source-id option!' if
83
+ options.all && (options.source || options.source_id)
84
+ end
85
+
79
86
  def require_ruby_files
80
87
  options.require_files.each(&method(:require))
81
88
  end
@@ -146,7 +153,7 @@ module Gitlab
146
153
  end
147
154
 
148
155
  def resources_for_rule(resource_type, rule)
149
- puts Gitlab::Triage::UI.header("Processing rule: **#{rule[:name]}**", char: '-')
156
+ puts Gitlab::Triage::UI.header("Gathering resources for rule: **#{rule[:name]}**", char: '-')
150
157
 
151
158
  ExpandCondition.perform(rule_conditions(rule)) do |conditions|
152
159
  # retrieving the resources for every rule is inefficient
@@ -186,22 +193,35 @@ module Gitlab
186
193
  resources.select do |resource|
187
194
  results = []
188
195
 
196
+ # rubocop:disable Style/IfUnlessModifier
189
197
  if conditions[:date]
190
- results << case resource[:type]
191
- when 'issues'
192
- Filters::IssuableDateConditionsFilter.new(resource, conditions[:date]).calculate
193
- when 'merge_requests'
194
- Filters::MergeRequestDateConditionsFilter.new(resource, conditions[:date]).calculate
195
- else
196
- raise "Unknown resource type: #{resource[:type]}"
197
- end
198
+ results << Filters::MergeRequestDateConditionsFilter.new(resource, conditions[:date]).calculate
199
+ end
200
+
201
+ if conditions[:upvotes]
202
+ results << Filters::VotesConditionsFilter.new(resource, conditions[:upvotes]).calculate
203
+ end
204
+
205
+ if conditions[:forbidden_labels]
206
+ results << Filters::ForbiddenLabelsConditionsFilter.new(resource, conditions[:forbidden_labels]).calculate
207
+ end
208
+
209
+ if conditions[:no_additional_labels]
210
+ results << Filters::NoAdditionalLabelsConditionsFilter.new(resource, conditions.fetch(:labels) { [] }).calculate
198
211
  end
199
- results << Filters::VotesConditionsFilter.new(resource, conditions[:upvotes]).calculate if conditions[:upvotes]
200
- results << Filters::ForbiddenLabelsConditionsFilter.new(resource, conditions[:forbidden_labels]).calculate if conditions[:forbidden_labels]
201
- results << Filters::NoAdditionalLabelsConditionsFilter.new(resource, conditions.fetch(:labels) { [] }).calculate if conditions[:no_additional_labels]
202
- results << Filters::AuthorMemberConditionsFilter.new(resource, conditions[:author_member], network).calculate if conditions[:author_member]
203
- results << Filters::AssigneeMemberConditionsFilter.new(resource, conditions[:assignee_member], network).calculate if conditions[:assignee_member]
204
- results << Filters::RubyConditionsFilter.new(resource, conditions, network).calculate if conditions[:ruby]
212
+
213
+ if conditions[:author_member]
214
+ results << Filters::AuthorMemberConditionsFilter.new(resource, conditions[:author_member], network).calculate
215
+ end
216
+
217
+ if conditions[:assignee_member]
218
+ results << Filters::AssigneeMemberConditionsFilter.new(resource, conditions[:assignee_member], network).calculate
219
+ end
220
+
221
+ if conditions[:ruby]
222
+ results << Filters::RubyConditionsFilter.new(resource, conditions, network).calculate
223
+ end
224
+ # rubocop:enable Style/IfUnlessModifier
205
225
 
206
226
  results.all?
207
227
  end
@@ -229,12 +249,17 @@ module Gitlab
229
249
  condition_builders << APIQueryBuilders::SingleQueryParamBuilder.new('source_branch', conditions[:source_branch]) if conditions[:source_branch]
230
250
  condition_builders << APIQueryBuilders::SingleQueryParamBuilder.new('target_branch', conditions[:target_branch]) if conditions[:target_branch]
231
251
 
252
+ if conditions[:date] && APIQueryBuilders::DateQueryParamBuilder.applicable?(conditions[:date])
253
+ condition_builders << APIQueryBuilders::DateQueryParamBuilder.new(conditions.delete(:date))
254
+ end
255
+
232
256
  condition_builders.each do |condition_builder|
233
257
  params[condition_builder.param_name] = condition_builder.param_content
234
258
  end
235
259
 
236
260
  UrlBuilders::UrlBuilder.new(
237
261
  network_options: options,
262
+ all: options.all,
238
263
  source: options.source,
239
264
  source_id: options.source_id,
240
265
  resource_type: resource_type,
@@ -1 +1 @@
1
- require 'gitlab/triage/errors/network'
1
+ require_relative 'errors/network'
@@ -1,21 +1,67 @@
1
- require_relative 'issuable_date_conditions_filter'
1
+ require_relative 'base_conditions_filter'
2
2
 
3
3
  module Gitlab
4
4
  module Triage
5
5
  module Filters
6
- class MergeRequestDateConditionsFilter < IssuableDateConditionsFilter
7
- ATTRIBUTES = %w[updated_at created_at merged_at].freeze
6
+ class MergeRequestDateConditionsFilter < BaseConditionsFilter
7
+ ATTRIBUTES = %w[merged_at].freeze
8
+ CONDITIONS = %w[older_than newer_than].freeze
9
+ INTERVAL_TYPES = %w[days weeks months years].freeze
10
+
11
+ def self.allowed_attributes
12
+ self::ATTRIBUTES
13
+ end
14
+
15
+ def self.filter_parameters
16
+ [
17
+ {
18
+ name: :attribute,
19
+ type: String,
20
+ values: allowed_attributes
21
+ },
22
+ {
23
+ name: :condition,
24
+ type: String,
25
+ values: CONDITIONS
26
+ },
27
+ {
28
+ name: :interval_type,
29
+ type: String,
30
+ values: INTERVAL_TYPES
31
+ },
32
+ {
33
+ name: :interval,
34
+ type: Numeric
35
+ }
36
+ ]
37
+ end
38
+
39
+ def initialize_variables(condition)
40
+ @attribute = condition[:attribute].to_sym
41
+ @condition = condition[:condition].to_sym
42
+ @interval_type = condition[:interval_type].to_sym
43
+ @interval = condition[:interval]
44
+ end
8
45
 
9
46
  # Guard against merge requests with no merged_at values
10
47
  def resource_value
11
- super if @resource[@attribute]
48
+ @resource[@attribute]&.to_date
49
+ end
50
+
51
+ def condition_value
52
+ @interval.public_send(@interval_type).ago.to_date # rubocop:disable GitlabSecurity/PublicSend
12
53
  end
13
54
 
14
55
  # Guard against merge requests with no merged_at values
15
56
  def calculate
16
57
  return false unless resource_value
17
58
 
18
- super
59
+ case @condition
60
+ when :older_than
61
+ resource_value < condition_value
62
+ when :newer_than
63
+ resource_value > condition_value
64
+ end
19
65
  end
20
66
  end
21
67
  end
@@ -31,11 +31,12 @@ module Gitlab
31
31
 
32
32
  begin
33
33
  print '.'
34
+ url = response.fetch(:next_page_url) { url }
34
35
 
35
36
  response = execute_with_retry([Net::ReadTimeout, Errors::Network::InternalServerError]) do
36
37
  puts Gitlab::Triage::UI.debug "query_api: #{url}" if options.debug
37
38
 
38
- @adapter.get(token, response.fetch(:next_page_url) { url })
39
+ @adapter.get(token, url)
39
40
  end
40
41
 
41
42
  results = response.delete(:results)
@@ -22,7 +22,7 @@ module Gitlab
22
22
 
23
23
  {
24
24
  more_pages: (response.headers["x-next-page"].to_s != ""),
25
- next_page_url: url + "&page=#{response.headers['x-next-page']}",
25
+ next_page_url: next_page_url(url, response),
26
26
  results: response.parsed_response,
27
27
  ratelimit_remaining: response.headers["ratelimit-remaining"].to_i,
28
28
  ratelimit_reset_at: Time.at(response.headers["ratelimit-reset"].to_i)
@@ -66,6 +66,18 @@ module Gitlab
66
66
 
67
67
  raise Errors::Network::InternalServerError, 'Internal server error encountered!'
68
68
  end
69
+
70
+ def next_page_url(url, response)
71
+ return unless response.headers['x-next-page'].present?
72
+
73
+ next_page = "&page=#{response.headers['x-next-page']}"
74
+
75
+ if url.include?('&page')
76
+ url.gsub(/&page=\d+/, next_page)
77
+ else
78
+ url + next_page
79
+ end
80
+ end
69
81
  end
70
82
  end
71
83
  end
@@ -23,6 +23,10 @@ module Gitlab
23
23
  options.policies_files << value
24
24
  end
25
25
 
26
+ opts.on('--all-projects', 'Process all projects the token has access to') do |value|
27
+ options.all = value
28
+ end
29
+
26
30
  opts.on('-s', '--source [type]', [:projects, :groups], 'The source type between [ projects or groups ], default value: projects') do |value|
27
31
  options.source = value
28
32
  end
@@ -59,6 +63,12 @@ module Gitlab
59
63
  exit # rubocop:disable Rails/Exit
60
64
  end
61
65
 
66
+ opts.on('-v', '--version', 'Print version') do
67
+ require_relative 'version'
68
+ $stdout.puts Gitlab::Triage::VERSION
69
+ exit # rubocop:disable Rails/Exit
70
+ end
71
+
62
72
  opts.on('--init', 'Initialize the project with a policy file') do
63
73
  example_path =
64
74
  File.expand_path('../../../support/.triage-policies.example.yml', __dir__)
@@ -3,6 +3,7 @@ module Gitlab
3
3
  Options = Struct.new(
4
4
  :dry_run,
5
5
  :policies_files,
6
+ :all,
6
7
  :source,
7
8
  :source_id,
8
9
  :token,
@@ -17,6 +18,7 @@ module Gitlab
17
18
  # Defaults
18
19
  self.host_url ||= 'https://gitlab.com'
19
20
  self.api_version ||= 'v4'
21
+ self.all ||= false
20
22
  self.source ||= 'projects'
21
23
  self.require_files ||= []
22
24
  self.policies_files ||= Set.new
@@ -15,7 +15,7 @@ module Gitlab
15
15
  type: type,
16
16
  policy_spec: policy_spec,
17
17
  action: action,
18
- resources: resources.resources,
18
+ resources: resources,
19
19
  network: network)
20
20
  end
21
21
  end
@@ -10,7 +10,7 @@ module Gitlab
10
10
  # Build an issue from several rules policies
11
11
  def build_issue
12
12
  action = actions[:summarize]
13
- issues = resources.build_issues do |inner_policy_spec, inner_resources|
13
+ issues = resources.map do |inner_policy_spec, inner_resources|
14
14
  Policies::RulePolicy.new(
15
15
  type, inner_policy_spec, inner_resources, network)
16
16
  .build_issue
@@ -1,20 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'forwardable'
4
+
3
5
  module Gitlab
4
6
  module Triage
5
7
  module PoliciesResources
6
8
  class RuleResources
7
- attr_reader :resources
9
+ include Enumerable
10
+ extend Forwardable
8
11
 
9
12
  def initialize(new_resources)
10
13
  @resources = new_resources
11
14
  end
12
15
 
13
- def each
14
- resources.each do |resource|
15
- yield(resource)
16
- end
17
- end
16
+ def_delegator :@resources, :each
18
17
  end
19
18
  end
20
19
  end
@@ -1,20 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'forwardable'
4
+
3
5
  module Gitlab
4
6
  module Triage
5
7
  module PoliciesResources
6
8
  class SummaryResources
7
- attr_reader :rule_to_resources
9
+ include Enumerable
10
+ extend Forwardable
8
11
 
9
12
  def initialize(new_rule_to_resources)
10
13
  @rule_to_resources = new_rule_to_resources
11
14
  end
12
15
 
13
- def build_issues
14
- rule_to_resources.map do |inner_policy_spec, inner_resources|
15
- yield(inner_policy_spec, inner_resources)
16
- end
17
- end
16
+ def_delegator :@rule_to_resources, :each
18
17
  end
19
18
  end
20
19
  end
@@ -41,6 +41,11 @@ module Gitlab
41
41
 
42
42
  private
43
43
 
44
+ def expand_resource!(params: {})
45
+ resource.merge!(
46
+ network.query_api_cached(resource_url(params: params)).first)
47
+ end
48
+
44
49
  def source_resource
45
50
  @source_resource ||= network.query_api_cached(source_url).first
46
51
  end
@@ -12,6 +12,15 @@ module Gitlab
12
12
  def reference
13
13
  '!'
14
14
  end
15
+
16
+ def first_contribution?
17
+ if resource.key?(:first_contribution)
18
+ resource[:first_contribution]
19
+ else
20
+ expanded = expand_resource!
21
+ expanded[:first_contribution]
22
+ end
23
+ end
15
24
  end
16
25
  end
17
26
  end
@@ -6,6 +6,7 @@ module Gitlab
6
6
  @network_options = options.fetch(:network_options)
7
7
  @host_url = @network_options.host_url
8
8
  @api_version = @network_options.api_version
9
+ @all = options.fetch(:all, false)
9
10
  @source = options.fetch(:source, 'projects')
10
11
  @source_id = options.fetch(:source_id)
11
12
  @resource_type = options.fetch(:resource_type, nil)
@@ -15,11 +16,11 @@ module Gitlab
15
16
  end
16
17
 
17
18
  def build
18
- base_url.tap do |url|
19
- url << "/#{@resource_id}" if @resource_id
20
- url << "/#{@sub_resource_type}" if @sub_resource_type
21
- url << params_string if @params
22
- end
19
+ url = base_url
20
+ url << "/#{@resource_id}" if @resource_id
21
+ url << "/#{@sub_resource_type}" if @sub_resource_type
22
+ url << params_string if @params
23
+ url
23
24
  end
24
25
 
25
26
  private
@@ -29,10 +30,10 @@ module Gitlab
29
30
  end
30
31
 
31
32
  def base_url
32
- "#{host_with_api_url}/#{@source}/#{CGI.escape(@source_id.to_s)}"
33
- .tap do |url|
34
- url << "/#{@resource_type}" if @resource_type
35
- end
33
+ url = host_with_api_url
34
+ url << "/#{@source}/#{CGI.escape(@source_id.to_s)}" unless @all
35
+ url << "/#{@resource_type}" if @resource_type
36
+ url
36
37
  end
37
38
 
38
39
  def params_string
@@ -12,7 +12,9 @@ module Gitlab
12
12
  end
13
13
 
14
14
  def validate_required_parameters(value)
15
- raise ArgumentError, "For the limits field, please specify one of: `#{params_limiter_names.join('`, `')}`" unless value.keys.map(&:to_sym).one? { |key| params_limiter_names.include?(key) }
15
+ return if value.keys.one? { |key| params_limiter_names.include?(key.to_sym) }
16
+
17
+ raise ArgumentError, "For the limits field, please specify one of: `#{params_limiter_names.join('`, `')}`"
16
18
  end
17
19
  end
18
20
  end
@@ -1,6 +1,8 @@
1
1
  module Gitlab
2
2
  module Triage
3
3
  class ParamsValidator
4
+ InvalidParameter = Class.new(ArgumentError)
5
+
4
6
  def initialize(parameter_definitions, value)
5
7
  @parameter_definitions = parameter_definitions
6
8
  @value = value
@@ -16,7 +18,7 @@ module Gitlab
16
18
 
17
19
  def validate_required_parameters(value)
18
20
  @parameter_definitions.each do |param|
19
- raise ArgumentError, "#{param[:name]} is a required parameter" unless value[param[:name]]
21
+ raise InvalidParameter, "#{param[:name]} is a required parameter" unless value[param[:name]]
20
22
  end
21
23
  end
22
24
 
@@ -24,7 +26,7 @@ module Gitlab
24
26
  @parameter_definitions.each do |param|
25
27
  if value.has_key?(param[:name])
26
28
  param_types = Array(param[:type]).flatten
27
- raise ArgumentError, "#{param[:name]} must be of type #{param[:type]}" unless param_types.any? { |type| value[param[:name]].is_a?(type) }
29
+ raise InvalidParameter, "#{param[:name]} must be of type #{param[:type]}" unless param_types.any? { |type| value[param[:name]].is_a?(type) }
28
30
  end
29
31
  end
30
32
  end
@@ -32,7 +34,7 @@ module Gitlab
32
34
  def validate_parameter_content(value)
33
35
  @parameter_definitions.each do |param|
34
36
  if param[:values]
35
- raise ArgumentError, "#{param[:name]} must be of one of #{param[:values].join(',')}" unless param[:values].include?(value[param[:name]])
37
+ raise InvalidParameter, "#{param[:name]} must be of one of #{param[:values].join(',')}" unless param[:values].include?(value[param[:name]])
36
38
  end
37
39
  end
38
40
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gitlab
2
4
  module Triage
3
- VERSION = '1.7.0'.freeze
5
+ VERSION = '1.10.1'
4
6
  end
5
7
  end
@@ -6,10 +6,10 @@ resource_rules:
6
6
  date:
7
7
  attribute: created_at
8
8
  condition: older_than
9
- interval_type: week
9
+ interval_type: weeks
10
10
  interval: 1
11
11
  labels:
12
- - No label
12
+ - None
13
13
  state: opened
14
14
  actions:
15
15
  comment: |
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-triage
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitLab
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-04 00:00:00.000000000 Z
11
+ date: 2020-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -120,6 +120,7 @@ files:
120
120
  - ".gitignore"
121
121
  - ".gitlab-ci.yml"
122
122
  - ".gitlab/CODEOWNERS"
123
+ - ".gitlab/merge_request_templates/Release.md"
123
124
  - ".rubocop.yml"
124
125
  - CONTRIBUTING.md
125
126
  - Gemfile
@@ -135,12 +136,14 @@ files:
135
136
  - lib/gitlab/triage/action/comment.rb
136
137
  - lib/gitlab/triage/action/summarize.rb
137
138
  - lib/gitlab/triage/api_query_builders/base_query_param_builder.rb
139
+ - lib/gitlab/triage/api_query_builders/date_query_param_builder.rb
138
140
  - lib/gitlab/triage/api_query_builders/multi_query_param_builder.rb
139
141
  - lib/gitlab/triage/api_query_builders/single_query_param_builder.rb
140
142
  - lib/gitlab/triage/command_builders/base_command_builder.rb
141
143
  - lib/gitlab/triage/command_builders/cc_command_builder.rb
142
144
  - lib/gitlab/triage/command_builders/comment_command_builder.rb
143
145
  - lib/gitlab/triage/command_builders/label_command_builder.rb
146
+ - lib/gitlab/triage/command_builders/move_command_builder.rb
144
147
  - lib/gitlab/triage/command_builders/remove_label_command_builder.rb
145
148
  - lib/gitlab/triage/command_builders/status_command_builder.rb
146
149
  - lib/gitlab/triage/command_builders/text_content_builder.rb
@@ -156,7 +159,6 @@ files:
156
159
  - lib/gitlab/triage/filters/author_member_conditions_filter.rb
157
160
  - lib/gitlab/triage/filters/base_conditions_filter.rb
158
161
  - lib/gitlab/triage/filters/forbidden_labels_conditions_filter.rb
159
- - lib/gitlab/triage/filters/issuable_date_conditions_filter.rb
160
162
  - lib/gitlab/triage/filters/member_conditions_filter.rb
161
163
  - lib/gitlab/triage/filters/merge_request_date_conditions_filter.rb
162
164
  - lib/gitlab/triage/filters/name_conditions_filter.rb
@@ -212,7 +214,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
212
214
  - !ruby/object:Gem::Version
213
215
  version: '0'
214
216
  requirements: []
215
- rubygems_version: 3.0.3
217
+ rubygems_version: 3.1.2
216
218
  signing_key:
217
219
  specification_version: 4
218
220
  summary: GitLab triage automation project.
@@ -1,65 +0,0 @@
1
- require_relative 'base_conditions_filter'
2
-
3
- module Gitlab
4
- module Triage
5
- module Filters
6
- class IssuableDateConditionsFilter < BaseConditionsFilter
7
- ATTRIBUTES = %w[updated_at created_at].freeze
8
- CONDITIONS = %w[older_than newer_than].freeze
9
- INTERVAL_TYPES = %w[days weeks months years].freeze
10
-
11
- def self.allowed_attributes
12
- self::ATTRIBUTES
13
- end
14
-
15
- def self.filter_parameters
16
- [
17
- {
18
- name: :attribute,
19
- type: String,
20
- values: allowed_attributes
21
- },
22
- {
23
- name: :condition,
24
- type: String,
25
- values: CONDITIONS
26
- },
27
- {
28
- name: :interval_type,
29
- type: String,
30
- values: INTERVAL_TYPES
31
- },
32
- {
33
- name: :interval,
34
- type: Numeric
35
- }
36
- ]
37
- end
38
-
39
- def initialize_variables(condition)
40
- @attribute = condition[:attribute].to_sym
41
- @condition = condition[:condition].to_sym
42
- @interval_type = condition[:interval_type].to_sym
43
- @interval = condition[:interval]
44
- end
45
-
46
- def resource_value
47
- @resource[@attribute].to_date
48
- end
49
-
50
- def condition_value
51
- @interval.public_send(@interval_type).ago.to_date # rubocop:disable GitlabSecurity/PublicSend
52
- end
53
-
54
- def calculate
55
- case @condition
56
- when :older_than
57
- resource_value < condition_value
58
- when :newer_than
59
- resource_value > condition_value
60
- end
61
- end
62
- end
63
- end
64
- end
65
- end