gitlab-triage 1.7.0 → 1.10.1

Sign up to get free protection for your applications and to get access to all the features.
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