1# frozen_string_literal: true
2
3module Tooling
4  module Danger
5    module ProjectHelper
6      LOCAL_RULES ||= %w[
7        changelog
8        ci_config
9        database
10        documentation
11        duplicate_yarn_dependencies
12        eslint
13        gitaly
14        pajamas
15        pipeline
16        prettier
17        product_intelligence
18        utility_css
19        vue_shared_documentation
20      ].freeze
21
22      CI_ONLY_RULES ||= %w[
23        ce_ee_vue_templates
24        ci_templates
25        datateam
26        feature_flag
27        roulette
28        sidekiq_queues
29        specialization_labels
30        specs
31        z_metadata
32      ].freeze
33
34      MESSAGE_PREFIX = '==>'
35
36      # First-match win, so be sure to put more specific regex at the top...
37      CATEGORIES = {
38        [%r{usage_data\.rb}, %r{^(\+|-).*\s+(count|distinct_count|estimate_batch_distinct_count)\(.*\)(.*)$}] => [:database, :backend, :product_intelligence],
39
40        %r{\A((ee|jh)/)?config/feature_flags/} => :feature_flag,
41
42        %r{doc/api/usage_data.md} => [:product_intelligence],
43
44        %r{\Adoc/.*(\.(md|png|gif|jpg|yml))\z} => :docs,
45        %r{\A(CONTRIBUTING|LICENSE|MAINTENANCE|PHILOSOPHY|PROCESS|README)(\.md)?\z} => :docs,
46        %r{\Adata/whats_new/} => :docs,
47
48        %r{\A((ee|jh)/)?app/finders/(.+/)?integrations/} => [:integrations_be, :database, :backend],
49        [%r{\A((ee|jh)/)?db/(geo/)?(migrate|post_migrate)/}, %r{(:integrations|:\w+_tracker_data)\b}] => [:integrations_be, :database, :migration],
50        [%r{\A((ee|jh)/)?(app|lib)/.+\.rb}, %r{\b(Integrations::|\.execute_(integrations|hooks))\b}] => [:integrations_be, :backend],
51        %r{\A(
52          ((ee|jh)/)?app/((?!.*clusters)(?!.*alert_management)(?!.*views)(?!.*assets).+/)?integration.+ |
53          ((ee|jh)/)?app/((?!.*search).+/)?project_service.+ |
54          ((ee|jh)/)?app/(models|helpers|workers|services|controllers)/(.+/)?(jira_connect.+|.*hook.+) |
55          ((ee|jh)/)?app/controllers/(.+/)?oauth/jira/.+ |
56          ((ee|jh)/)?app/services/(.+/)?jira.+ |
57          ((ee|jh)/)?app/workers/(.+/)?(propagate_integration.+|irker_worker\.rb) |
58          ((ee|jh)/)?lib/(.+/)?(atlassian|data_builder|hook_data)/.+ |
59          ((ee|jh)/)?lib/(.+/)?.*integration.+ |
60          ((ee|jh)/)?lib/(.+/)?api/v3/github\.rb |
61          ((ee|jh)/)?lib/(.+/)?api/github/entities\.rb
62        )\z}x => [:integrations_be, :backend],
63
64        %r{\A(
65          ((ee|jh)/)?app/(views|assets)/((?!.*clusters)(?!.*alerts_settings).+/)?integration.+ |
66          ((ee|jh)/)?app/(views|assets)/(.+/)?jira_connect.+ |
67          ((ee|jh)/)?app/(views|assets)/((?!.*filtered_search).+/)?hooks?.+
68        )\z}x => [:integrations_fe, :frontend],
69
70        %r{\A(
71          app/assets/javascripts/tracking/.*\.js |
72          spec/frontend/tracking/.*\.js |
73          spec/frontend/tracking_spec\.js
74        )\z}x => [:frontend, :product_intelligence],
75        %r{\A((ee|jh)/)?app/(assets|views)/} => :frontend,
76        %r{\A((ee|jh)/)?public/} => :frontend,
77        %r{\A((ee|jh)/)?spec/(javascripts|frontend|frontend_integration)/} => :frontend,
78        %r{\A((ee|jh)/)?vendor/assets/} => :frontend,
79        %r{\A((ee|jh)/)?scripts/frontend/} => :frontend,
80        %r{(\A|/)(
81          \.babelrc |
82          \.browserslistrc |
83          \.eslintignore |
84          \.eslintrc(\.yml)? |
85          \.nvmrc |
86          \.prettierignore |
87          \.prettierrc |
88          \.stylelintrc |
89          \.haml-lint.yml |
90          \.haml-lint_todo.yml |
91          babel\.config\.js |
92          jest\.config\.js |
93          package\.json |
94          yarn\.lock |
95          config/.+\.js
96        )\z}x => :frontend,
97
98        %r{(\A|/)(
99          \.gitlab/ci/frontend\.gitlab-ci\.yml
100        )\z}x => %i[frontend tooling],
101
102        %r{\A((ee|jh)/)?db/(geo/)?(migrate|post_migrate)/} => [:database, :migration],
103        %r{\A((ee|jh)/)?db/(?!fixtures)[^/]+} => [:database],
104        %r{\A((ee|jh)/)?lib/gitlab/(database|background_migration|sql|github_import)(/|\.rb)} => [:database, :backend],
105        %r{\A(app/services/authorized_project_update/find_records_due_for_refresh_service)(/|\.rb)} => [:database, :backend],
106        %r{\A(app/models/project_authorization|app/services/users/refresh_authorized_projects_service)(/|\.rb)} => [:database, :backend],
107        %r{\A((ee|jh)/)?app/finders/} => [:database, :backend],
108        %r{\Arubocop/cop/migration(/|\.rb)} => :database,
109
110        %r{\A(\.ruby-version\z|\.nvmrc\z|\.tool-versions\z)} => :tooling,
111        %r{\A(\.gitlab-ci\.yml\z|\.gitlab\/ci)} => :tooling,
112        %r{\A\.codeclimate\.yml\z} => :tooling,
113        %r{\Alefthook.yml\z} => :tooling,
114        %r{\A\.editorconfig\z} => :tooling,
115        %r{Dangerfile\z} => :tooling,
116        %r{\A((ee|jh)/)?(danger/|tooling/danger/)} => :tooling,
117        %r{\A((ee|jh)/)?scripts/} => :tooling,
118        %r{\Atooling/} => :tooling,
119        %r{(CODEOWNERS)} => :tooling,
120        %r{(tests.yml)} => :tooling,
121
122        %r{\Alib/gitlab/ci/templates} => :ci_template,
123
124        %r{\A((ee|jh)/)?spec/features/} => :test,
125        %r{\A((ee|jh)/)?spec/support/shared_examples/features/} => :test,
126        %r{\A((ee|jh)/)?spec/support/shared_contexts/features/} => :test,
127        %r{\A((ee|jh)/)?spec/support/helpers/features/} => :test,
128
129        %r{\A((spec/)?lib/generators/gitlab/usage_metric_)} => [:product_intelligence],
130        %r{\A((ee|jh)/)?lib/gitlab/usage_data_counters/.*\.yml\z} => [:product_intelligence],
131        %r{\A((ee|jh)/)?config/(events|metrics)/((.*\.yml)|(schema\.json))\z} => [:product_intelligence],
132        %r{\A((ee|jh)/)?lib/gitlab/usage_data(_counters)?(/|\.rb)} => [:backend, :product_intelligence],
133        %r{\A(
134          lib/gitlab/tracking\.rb |
135          spec/lib/gitlab/tracking_spec\.rb |
136          app/helpers/tracking_helper\.rb |
137          spec/helpers/tracking_helper_spec\.rb |
138          (spec/)?lib/generators/gitlab/usage_metric_\S+ |
139          (spec/)?lib/generators/gitlab/usage_metric_definition/redis_hll_generator(_spec)?\.rb |
140          lib/generators/rails/usage_metric_definition_generator\.rb |
141          spec/lib/generators/usage_metric_definition_generator_spec\.rb |
142          generator_templates/usage_metric_definition/metric_definition\.yml)\z}x => [:backend, :product_intelligence],
143        %r{gitlab/usage_data(_spec)?\.rb} => [:product_intelligence],
144        [%r{\.haml\z}, %r{data: \{ track}] => [:product_intelligence],
145        [%r{\.(rb|haml)\z}, %r{Gitlab::Tracking\.(event|enabled\?|options)$}] => [:product_intelligence],
146        [%r{\.(vue|js)\z}, %r{(Tracking.event|/\btrack\(/|data-track-action)}] => [:product_intelligence],
147
148        %r{\A((ee|jh)/)?app/(?!assets|views)[^/]+} => :backend,
149        %r{\A((ee|jh)/)?(bin|config|generator_templates|lib|rubocop)/} => :backend,
150        %r{\A((ee|jh)/)?spec/migrations} => :database,
151        %r{\A((ee|jh)/)?spec/} => :backend,
152        %r{\A((ee|jh)/)?vendor/} => :backend,
153        %r{\A(Gemfile|Gemfile.lock|Rakefile)\z} => :backend,
154        %r{\A[A-Z_]+_VERSION\z} => :backend,
155        %r{\A\.rubocop(_todo)?\.yml\z} => :backend,
156        %r{\A\.rubocop_todo/.*\.yml\z} => :backend,
157        %r{\Afile_hooks/} => :backend,
158
159        %r{\A((ee|jh)/)?qa/} => :qa,
160
161        %r{\Aworkhorse/.*} => :workhorse,
162
163        # Files that don't fit into any category are marked with :none
164        %r{\A((ee|jh)/)?changelogs/} => :none,
165        %r{\Alocale/gitlab\.pot\z} => :none,
166
167        # GraphQL auto generated doc files and schema
168        %r{\Adoc/api/graphql/reference/} => :backend,
169
170        # Fallbacks in case the above patterns miss anything
171        %r{\.rb\z} => :backend,
172        %r{(
173          \.(md|txt)\z |
174          \.markdownlint\.json
175        )}x => :none, # To reinstate roulette for documentation, set to `:docs`.
176        %r{\.js\z} => :frontend
177      }.freeze
178
179      def local_warning_message
180        "#{MESSAGE_PREFIX} Only the following Danger rules can be run locally: #{LOCAL_RULES.join(', ')}"
181      end
182      module_function :local_warning_message # rubocop:disable Style/AccessModifierDeclarations
183
184      def success_message
185        "#{MESSAGE_PREFIX} No Danger rule violations!"
186      end
187      module_function :success_message # rubocop:disable Style/AccessModifierDeclarations
188
189      def rule_names
190        helper.ci? ? LOCAL_RULES | CI_ONLY_RULES : LOCAL_RULES
191      end
192
193      def all_ee_changes
194        helper.changes.files.grep(%r{\Aee/})
195      end
196
197      def file_lines(filename)
198        read_file(filename).lines(chomp: true)
199      end
200
201      def labels_to_add
202        @labels_to_add ||= []
203      end
204
205      private
206
207      def read_file(filename)
208        File.read(filename)
209      end
210    end
211  end
212end
213