Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									bbc06065aa
								
							
						
					
					
						commit
						179b33c546
					
				|  | @ -335,10 +335,13 @@ bundle-size-review: | |||
|   stage: test | ||||
|   needs: ["compile-production-assets"] | ||||
|   script: | ||||
|     - source scripts/utils.sh | ||||
|     - mkdir -p bundle-size-review | ||||
|     - cp webpack-report/index.html bundle-size-review/bundle-report.html | ||||
|     - yarn global add https://gitlab.com/gitlab-org/frontend/playground/webpack-memory-metrics.git | ||||
|     - danger --dangerfile=danger/bundle_size/Dangerfile --fail-on-errors=true --verbose --danger_id=bundle-size-review | ||||
|     - | | ||||
|       danger_id=$(echo -n ${DANGER_GITLAB_API_TOKEN} | md5sum | awk '{print $1}' | cut -c5-10) | ||||
|       run_timed_command "danger --dangerfile=danger/Dangerfile-bundle_size --fail-on-errors=true --verbose --danger_id=bundle-size-review-${danger_id}" | ||||
|   artifacts: | ||||
|     when: always | ||||
|     name: bundle-size-review | ||||
|  |  | |||
							
								
								
									
										14
									
								
								Dangerfile
								
								
								
								
							
							
						
						
									
										14
									
								
								Dangerfile
								
								
								
								
							|  | @ -11,16 +11,8 @@ project_name = ee? ? 'gitlab' : 'gitlab-foss' | |||
| 
 | ||||
| Gitlab::Dangerfiles.for_project(self, project_name) do |gitlab_dangerfiles| | ||||
|   gitlab_dangerfiles.import_plugins | ||||
|   gitlab_dangerfiles.config.ci_only_rules = ProjectHelper::CI_ONLY_RULES | ||||
|   gitlab_dangerfiles.config.files_to_category = ProjectHelper::CATEGORIES | ||||
| 
 | ||||
|   unless helper.release_automation? | ||||
|     danger.import_plugin('danger/plugins/*.rb') | ||||
|     gitlab_dangerfiles.import_dangerfiles(except: %w[simple_roulette]) | ||||
|     gitlab_dangerfiles.config.files_to_category = ProjectHelper::CATEGORIES | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| return if helper.release_automation? | ||||
| 
 | ||||
| project_helper.rule_names.each do |rule| | ||||
|   danger.import_dangerfile(path: File.join('danger', rule)) | ||||
|   gitlab_dangerfiles.import_dangerfiles(except: %w[simple_roulette]) | ||||
| end | ||||
|  |  | |||
							
								
								
									
										2
									
								
								Gemfile
								
								
								
								
							
							
						
						
									
										2
									
								
								Gemfile
								
								
								
								
							|  | @ -403,7 +403,7 @@ group :development, :test do | |||
| end | ||||
| 
 | ||||
| group :development, :test, :danger do | ||||
|   gem 'gitlab-dangerfiles', '~> 2.11.0', require: false | ||||
|   gem 'gitlab-dangerfiles', '~> 3.0', require: false | ||||
| end | ||||
| 
 | ||||
| group :development, :test, :coverage do | ||||
|  |  | |||
|  | @ -224,7 +224,7 @@ GEM | |||
|     css_parser (1.7.0) | ||||
|       addressable | ||||
|     daemons (1.3.1) | ||||
|     danger (8.4.5) | ||||
|     danger (8.5.0) | ||||
|       claide (~> 1.0) | ||||
|       claide-plugins (>= 0.9.2) | ||||
|       colored2 (~> 3.1) | ||||
|  | @ -463,9 +463,10 @@ GEM | |||
|       terminal-table (~> 1.5, >= 1.5.1) | ||||
|     gitlab-chronic (0.10.5) | ||||
|       numerizer (~> 0.2) | ||||
|     gitlab-dangerfiles (2.11.0) | ||||
|     gitlab-dangerfiles (3.0.0) | ||||
|       danger (>= 8.4.5) | ||||
|       danger-gitlab (>= 8.0.0) | ||||
|       rake | ||||
|     gitlab-experiment (0.7.0) | ||||
|       activesupport (>= 3.0) | ||||
|       request_store (>= 1.0) | ||||
|  | @ -1494,7 +1495,7 @@ DEPENDENCIES | |||
|   gitaly (~> 14.9.0.pre.rc4) | ||||
|   github-markup (~> 1.7.0) | ||||
|   gitlab-chronic (~> 0.10.5) | ||||
|   gitlab-dangerfiles (~> 2.11.0) | ||||
|   gitlab-dangerfiles (~> 3.0) | ||||
|   gitlab-experiment (~> 0.7.0) | ||||
|   gitlab-fog-azure-rm (~> 1.2.0) | ||||
|   gitlab-labkit (~> 0.22.0) | ||||
|  |  | |||
							
								
								
									
										3
									
								
								Rakefile
								
								
								
								
							
							
						
						
									
										3
									
								
								Rakefile
								
								
								
								
							|  | @ -16,3 +16,6 @@ require File.expand_path('config/initializers/01_active_record_database_tasks_co | |||
| Gitlab::Application.load_tasks | ||||
| 
 | ||||
| Knapsack.load_tasks if defined?(Knapsack) | ||||
| 
 | ||||
| require 'gitlab-dangerfiles' | ||||
| Gitlab::Dangerfiles.load_tasks | ||||
|  |  | |||
|  | @ -37,9 +37,6 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController | |||
| 
 | ||||
|   # limit scopes when signing in with GitLab | ||||
|   def downgrade_scopes! | ||||
|     return unless Feature.enabled?(:omniauth_login_minimal_scopes, current_user, | ||||
|                                    default_enabled: :yaml) | ||||
| 
 | ||||
|     auth_type = params.delete('gl_auth_type') | ||||
|     return unless auth_type == 'login' | ||||
| 
 | ||||
|  |  | |||
|  | @ -83,16 +83,8 @@ module WebpackHelper | |||
|   end | ||||
| 
 | ||||
|   def webpack_public_host | ||||
|     # We do not proxy the webpack output in the 'test' environment, | ||||
|     # so we must reference the webpack dev server directly. | ||||
|     if Rails.env.test? && Gitlab.config.webpack.dev_server.enabled | ||||
|       host = Gitlab.config.webpack.dev_server.host | ||||
|       port = Gitlab.config.webpack.dev_server.port | ||||
|       protocol = Gitlab.config.webpack.dev_server.https ? 'https' : 'http' | ||||
|       "#{protocol}://#{host}:#{port}" | ||||
|     else | ||||
|       ActionController::Base.asset_host.try(:chomp, '/') | ||||
|     end | ||||
|     # We proxy webpack output in 'test' and 'dev' environment, so we can just use asset_host | ||||
|     ActionController::Base.asset_host.try(:chomp, '/') | ||||
|   end | ||||
| 
 | ||||
|   def webpack_public_path | ||||
|  |  | |||
|  | @ -16,10 +16,14 @@ class Suggestion < ApplicationRecord | |||
|     note.latest_diff_file | ||||
|   end | ||||
| 
 | ||||
|   def project | ||||
|   def source_project | ||||
|     noteable.source_project | ||||
|   end | ||||
| 
 | ||||
|   def target_project | ||||
|     noteable.target_project | ||||
|   end | ||||
| 
 | ||||
|   def branch | ||||
|     noteable.source_branch | ||||
|   end | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class SuggestionPolicy < BasePolicy | ||||
|   delegate { @subject.project } | ||||
|   delegate { @subject.source_project } | ||||
| 
 | ||||
|   condition(:can_push_to_branch) do | ||||
|     Gitlab::UserAccess.new(@user, container: @subject.project).can_push_to_branch?(@subject.branch) | ||||
|     Gitlab::UserAccess.new(@user, container: @subject.source_project).can_push_to_branch?(@subject.branch) | ||||
|   end | ||||
| 
 | ||||
|   rule { can_push_to_branch }.enable :apply_suggestion | ||||
|  |  | |||
|  | @ -54,7 +54,7 @@ module Suggestions | |||
|         author_email: author&.email | ||||
|       } | ||||
| 
 | ||||
|       ::Files::MultiService.new(suggestion_set.project, current_user, params) | ||||
|       ::Files::MultiService.new(suggestion_set.source_project, current_user, params) | ||||
|     end | ||||
| 
 | ||||
|     def commit_message | ||||
|  |  | |||
|  | @ -1,8 +0,0 @@ | |||
| --- | ||||
| name: omniauth_login_minimal_scopes | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78556 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351331 | ||||
| milestone: '14.8' | ||||
| type: development | ||||
| group: 'group::authentication and authorization' | ||||
| default_enabled: false | ||||
|  | @ -21,7 +21,7 @@ if app.config.public_file_server.enabled | |||
| 
 | ||||
|   # If webpack-dev-server is configured, proxy webpack's public directory | ||||
|   # instead of looking for static assets | ||||
|   if Gitlab.config.webpack.dev_server.enabled && Rails.env.development? | ||||
|   if Gitlab.config.webpack.dev_server.enabled && Gitlab.dev_or_test_env? | ||||
|     app.config.middleware.insert_before( | ||||
|       Gitlab::Middleware::Static, | ||||
|       Gitlab::Webpack::DevServerMiddleware, | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| # frozen_string_literal: true | ||||
| # This file isn't named "Dangerfile" so that it's not imported by default since it's only meant to be run in the `bundle-size-review` job. | ||||
| 
 | ||||
| analysis_result = "./bundle-size-review/analysis.json" | ||||
| markdown_result = "./bundle-size-review/comparison.md" | ||||
|  | @ -19,7 +19,7 @@ return unless helper.ci? | |||
| 
 | ||||
| template_paths_to_review = helper.changes_by_category[:ci_template] | ||||
| 
 | ||||
| if gitlab.mr_labels.include?('ci::templates') || template_paths_to_review.any? | ||||
| if helper.mr_labels.include?('ci::templates') || template_paths_to_review.any? | ||||
|   message 'This merge request adds or changes files that require a ' \ | ||||
|     'review from the CI/CD Templates maintainers.' | ||||
| 
 | ||||
|  |  | |||
|  | @ -49,11 +49,11 @@ if geo_migration_created && !geo_db_schema_updated | |||
| end | ||||
| 
 | ||||
| return unless helper.ci? | ||||
| return if gitlab.mr_labels.include?(DATABASE_APPROVED_LABEL) | ||||
| return if helper.mr_labels.include?(DATABASE_APPROVED_LABEL) | ||||
| 
 | ||||
| db_paths_to_review = helper.changes_by_category[:database] | ||||
| 
 | ||||
| if gitlab.mr_labels.include?('database') || db_paths_to_review.any? | ||||
| if helper.mr_labels.include?('database') || db_paths_to_review.any? | ||||
|   message 'This merge request adds or changes files that require a ' \ | ||||
|     'review from the [Database team](https://gitlab.com/groups/gl-database/-/group_members).' | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,21 +22,21 @@ def check_feature_flag_yaml(feature_flag) | |||
|   end | ||||
| rescue Psych::Exception | ||||
|   # YAML could not be parsed, fail the build. | ||||
|   fail "#{gitlab.html_link(feature_flag.path)} isn't valid YAML! #{SEE_DOC}" | ||||
|   fail "#{helper.html_link(feature_flag.path)} isn't valid YAML! #{SEE_DOC}" | ||||
| rescue StandardError => e | ||||
|   warn "There was a problem trying to check the Feature Flag file. Exception: #{e.class.name} - #{e.message}" | ||||
| end | ||||
| 
 | ||||
| def message_for_feature_flag_missing_group!(feature_flag:, mr_group_label:) | ||||
|   if mr_group_label.nil? | ||||
|     warn "Consider setting `group` in #{gitlab.html_link(feature_flag.path)}. #{SEE_DOC}" | ||||
|     warn "Consider setting `group` in #{helper.html_link(feature_flag.path)}. #{SEE_DOC}" | ||||
|   else | ||||
|     mr_line = feature_flag.raw.lines.find_index("group:\n") | ||||
| 
 | ||||
|     if mr_line | ||||
|       markdown(format(SUGGEST_MR_COMMENT, group: mr_group_label), file: feature_flag.path, line: mr_line.succ) | ||||
|     else | ||||
|       warn %(Consider setting `group: "#{mr_group_label}"` in #{gitlab.html_link(feature_flag.path)}. #{SEE_DOC}) | ||||
|       warn %(Consider setting `group: "#{mr_group_label}"` in #{helper.html_link(feature_flag.path)}. #{SEE_DOC}) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -60,7 +60,7 @@ def message_for_feature_flag_with_group!(feature_flag:, mr_group_label:) | |||
|   if mr_group_label.nil? | ||||
|     helper.labels_to_add << feature_flag.group | ||||
|   else | ||||
|     fail %(`group` is set to ~"#{feature_flag.group}" in #{gitlab.html_link(feature_flag.path)}, which does not match ~"#{mr_group_label}" set on the MR!) | ||||
|     fail %(`group` is set to ~"#{feature_flag.group}" in #{helper.html_link(feature_flag.path)}, which does not match ~"#{mr_group_label}" set on the MR!) | ||||
|   end | ||||
| end | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,11 +16,11 @@ SPECIALIZATIONS = { | |||
| labels_to_add = helper.changes_by_category.each_with_object([]) do |(category, _changes), memo| | ||||
|   label = SPECIALIZATIONS[category] | ||||
|   next unless label | ||||
|   next if gitlab.mr_labels.include?(label) | ||||
|   next if helper.mr_labels.include?(label) | ||||
| 
 | ||||
|   # Don't override already-set scoped labels. | ||||
|   label_scope = label.split('::')[0...-1].join('::') | ||||
|   next if !label_scope.empty? && gitlab.mr_labels.any? { |mr_label| mr_label.start_with?(label_scope) } | ||||
|   next if !label_scope.empty? && helper.has_scoped_label_with_scope?(label_scope) | ||||
| 
 | ||||
|   memo << label | ||||
| end | ||||
|  |  | |||
|  | @ -37,7 +37,7 @@ has_ee_app_changes = all_changed_files.grep(%r{\Aee/(app|lib|db/(geo/)?(post_)?m | |||
| spec_changes = specs.changed_specs_files(ee: :exclude) | ||||
| has_spec_changes = spec_changes.any? | ||||
| has_ee_spec_changes = specs.changed_specs_files(ee: :only).any? | ||||
| new_specs_needed = (gitlab.mr_labels & NO_SPECS_LABELS).empty? | ||||
| new_specs_needed = (helper.mr_labels & NO_SPECS_LABELS).empty? | ||||
| 
 | ||||
| if (has_app_changes || has_ee_app_changes) && !(has_spec_changes || has_ee_spec_changes) && new_specs_needed | ||||
|   warn format(NO_NEW_SPEC_MESSAGE, labels: helper.labels_list(NO_SPECS_LABELS)), sticky: false | ||||
|  |  | |||
|  | @ -14,12 +14,12 @@ end | |||
| 
 | ||||
| has_milestone = !gitlab.mr_json["milestone"].nil? | ||||
| 
 | ||||
| unless has_milestone || (helper.security_mr? && gitlab.branch_for_base == DEFAULT_BRANCH) | ||||
| unless has_milestone || (helper.security_mr? && helper.mr_target_branch == DEFAULT_BRANCH) | ||||
|   warn "This merge request does not refer to an existing milestone.", sticky: false | ||||
| end | ||||
| 
 | ||||
| has_pick_into_stable_label = gitlab.mr_labels.find { |label| label.start_with?('Pick into') } | ||||
| has_pick_into_stable_label = helper.mr_labels.find { |label| label.start_with?('Pick into') } | ||||
| 
 | ||||
| if gitlab.branch_for_base != DEFAULT_BRANCH && !has_pick_into_stable_label && !helper.security_mr? | ||||
| if helper.mr_target_branch != DEFAULT_BRANCH && !has_pick_into_stable_label && !helper.security_mr? | ||||
|   warn "Most of the time, merge requests should target `#{DEFAULT_BRANCH}`. Otherwise, please set the relevant `Pick into X.Y` label." | ||||
| end | ||||
|  |  | |||
|  | @ -0,0 +1,19 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddTmpIndexToSupportLeakyRegexCleanup < Gitlab::Database::Migration[1.0] | ||||
|   INDEX_NAME = "tmp_index_merge_requests_draft_and_status_leaky_regex" | ||||
|   LEAKY_REGEXP_STR = "^\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP" | ||||
|   CORRECTED_REGEXP_STR = "^(\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP)" | ||||
| 
 | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   def up | ||||
|     add_concurrent_index :merge_requests, :id, | ||||
|       where: "draft = true AND state_id = 1 AND ((title)::text ~* '#{LEAKY_REGEXP_STR}'::text) AND ((title)::text !~* '#{CORRECTED_REGEXP_STR}'::text)", | ||||
|       name: INDEX_NAME | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     remove_concurrent_index_by_name :merge_requests, INDEX_NAME | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,42 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class CleanupDraftDataFromFaultyRegex < Gitlab::Database::Migration[1.0] | ||||
|   MIGRATION       = 'CleanupDraftDataFromFaultyRegex' | ||||
|   DELAY_INTERVAL  = 5.minutes | ||||
|   BATCH_SIZE      = 20 | ||||
| 
 | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   class MergeRequest < ActiveRecord::Base | ||||
|     LEAKY_REGEXP_STR     = "^\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP" | ||||
|     CORRECTED_REGEXP_STR = "^(\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP)" | ||||
| 
 | ||||
|     self.table_name = 'merge_requests' | ||||
| 
 | ||||
|     include ::EachBatch | ||||
| 
 | ||||
|     def self.eligible | ||||
|       where(state_id: 1) | ||||
|         .where(draft: true) | ||||
|         .where("title ~* ?", LEAKY_REGEXP_STR) | ||||
|         .where("title !~* ?", CORRECTED_REGEXP_STR) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def up | ||||
|     return unless Gitlab.com? | ||||
| 
 | ||||
|     queue_background_migration_jobs_by_range_at_intervals( | ||||
|       MergeRequest.eligible, | ||||
|       MIGRATION, | ||||
|       DELAY_INTERVAL, | ||||
|       batch_size: BATCH_SIZE, | ||||
|       track_jobs: true | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     # noop | ||||
|     # | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1 @@ | |||
| 4c329622299c76ca753381f1ccc0686714d07eeee8acfc834e576d5a5addaafc | ||||
|  | @ -0,0 +1 @@ | |||
| 7ca832e710026c0721ecdcd50b477073aeaf7cb795c50acd604897f85707b163 | ||||
|  | @ -29617,6 +29617,8 @@ CREATE INDEX tmp_index_issues_on_issue_type_and_id ON issues USING btree (issue_ | |||
| 
 | ||||
| CREATE INDEX tmp_index_members_on_state ON members USING btree (state) WHERE (state = 2); | ||||
| 
 | ||||
| CREATE INDEX tmp_index_merge_requests_draft_and_status_leaky_regex ON merge_requests USING btree (id) WHERE ((draft = true) AND (state_id = 1) AND ((title)::text ~* '^\[draft\]|\(draft\)|draft:|draft|\[WIP\]|WIP:|WIP'::text) AND ((title)::text !~* '^(\[draft\]|\(draft\)|draft:|draft|\[WIP\]|WIP:|WIP)'::text)); | ||||
| 
 | ||||
| CREATE INDEX tmp_index_namespaces_empty_traversal_ids_with_child_namespaces ON namespaces USING btree (id) WHERE ((parent_id IS NOT NULL) AND (traversal_ids = '{}'::integer[])); | ||||
| 
 | ||||
| CREATE INDEX tmp_index_namespaces_empty_traversal_ids_with_root_namespaces ON namespaces USING btree (id) WHERE ((parent_id IS NULL) AND (traversal_ids = '{}'::integer[])); | ||||
|  |  | |||
|  | @ -3236,6 +3236,10 @@ Input type: `iterationCreateInput` | |||
| 
 | ||||
| ### `Mutation.iterationDelete` | ||||
| 
 | ||||
| WARNING: | ||||
| **Deprecated** in 14.10. | ||||
| Manual iteration management is deprecated. Only automatic iteration cadences will be supported in the future. | ||||
| 
 | ||||
| Input type: `IterationDeleteInput` | ||||
| 
 | ||||
| #### Arguments | ||||
|  |  | |||
|  | @ -261,11 +261,17 @@ Do not use **Developer permissions**. A user who is assigned the Developer role | |||
| See [the Microsoft style guide](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/d/disable-disabled) for guidance on **disable**. | ||||
| Use **inactive** or **off** instead. ([Vale](../testing.md#vale) rule: [`InclusionAbleism.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/InclusionAbleism.yml)) | ||||
| 
 | ||||
| 
 | ||||
| ## disallow | ||||
| 
 | ||||
| Use **prevent** instead of **disallow**. ([Vale](../testing.md#vale) rule: [`Substitutions.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/Substitutions.yml)) | ||||
| 
 | ||||
| ## downgrade | ||||
| 
 | ||||
| To be more upbeat and precise, do not use **downgrade**. Focus instead on the action the user is taking. | ||||
| 
 | ||||
| - For changing to earlier GitLab versions, use [**roll back**](#roll-back). | ||||
| - For changing to lower GitLab tiers, use **change the subscription tier**. | ||||
| 
 | ||||
| ## dropdown list | ||||
| 
 | ||||
| Use **dropdown list** to refer to the UI element. Do not use **dropdown** without **list** after it. | ||||
|  | @ -748,6 +754,12 @@ Do not use **roles** and [**permissions**](#permissions) interchangeably. Each u | |||
| 
 | ||||
| Roles are not the same as [**access levels**](#access-level). | ||||
| 
 | ||||
| ## roll back | ||||
| 
 | ||||
| Use **roll back** for changing a GitLab version to an earlier one. | ||||
| 
 | ||||
| Do not use **roll back** for licensing or subscriptions. Use **change the subscription tier** instead. | ||||
| 
 | ||||
| ## runner, runners | ||||
| 
 | ||||
| Use lowercase for **runners**. These are the agents that run CI/CD jobs. See also [GitLab Runner](#gitlab-runner) and [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/233529). | ||||
|  | @ -934,6 +946,33 @@ Use [**2FA** and **two-factor authentication**](#2fa-two-factor-authentication) | |||
| 
 | ||||
| Do not use **type** if you can avoid it. Use **enter** instead. | ||||
| 
 | ||||
| ## update | ||||
| 
 | ||||
| Use **update** for installing a newer **patch** version of the software only. For example: | ||||
| 
 | ||||
| - Update GitLab from 14.9 to 14.9.1. | ||||
| 
 | ||||
| Do not use **update** for any other case. Instead, use **upgrade**. | ||||
| 
 | ||||
| ## upgrade | ||||
| 
 | ||||
| Use **upgrade** for: | ||||
| 
 | ||||
| - Choosing a higher subscription tier (Premium or Ultimate). | ||||
| - Installing a newer **major** (13.0, 14.0) or **minor** (13.8, 14.5) version of GitLab. | ||||
| 
 | ||||
| For example: | ||||
| 
 | ||||
| - Upgrade to GitLab Ultimate. | ||||
| - Upgrade GitLab from 14.0 to 14.1. | ||||
| - Upgrade GitLab from 14.0 to 15.0. | ||||
| 
 | ||||
| Use caution with the phrase **Upgrade GitLab** without any other text. | ||||
| Ensure the surrounding text clarifies whether | ||||
| you're talking about the product version or the subscription tier. | ||||
| 
 | ||||
| See also [downgrade](#downgrade) and [roll back](#roll-back). | ||||
| 
 | ||||
| ## useful | ||||
| 
 | ||||
| Do not use **useful**. If the user doesn't find the process to be useful, we lose their trust. ([Vale](../testing.md#vale) rule: [`Simplicity.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/Simplicity.yml)) | ||||
|  |  | |||
|  | @ -117,10 +117,9 @@ signed in. | |||
| 
 | ||||
| ## Reduce access privileges on sign in | ||||
| 
 | ||||
| > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/337663) in GitLab 14.8 [with a flag](../administration/feature_flags.md) named `omniauth_login_minimal_scopes`. Disabled by default. | ||||
| 
 | ||||
| FLAG: | ||||
| On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `omniauth_login_minimal_scopes`. On GitLab.com, this feature is not available. | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/337663) in GitLab 14.8 [with a flag](../administration/feature_flags.md) named `omniauth_login_minimal_scopes`. Disabled by default. | ||||
| > - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/351331) in GitLab 14.9. | ||||
| > - [Feature flag `omniauth_login_minimal_scopes`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83453) removed in GitLab 14.10 | ||||
| 
 | ||||
| If you use a GitLab instance for authentication, you can reduce access rights when an OAuth application is used for sign in. | ||||
| 
 | ||||
|  |  | |||
|  | @ -108,6 +108,8 @@ For example, to customize the commit message to output | |||
| **Addresses user_1's review**, set the custom text to | ||||
| `Addresses %{username}'s review`. | ||||
| 
 | ||||
| For merge requests created from forks, GitLab uses the template defined in target project. | ||||
| 
 | ||||
| NOTE: | ||||
| Custom commit messages for each applied suggestion is | ||||
| introduced by [#25381](https://gitlab.com/gitlab-org/gitlab/-/issues/25381). | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ pre-push: | |||
|   parallel: true | ||||
|   commands: | ||||
|     danger: | ||||
|       run: CI_PROJECT_DIR=. bundle exec danger dry_run | ||||
|       run: bundle exec rake danger_local | ||||
|     eslint: | ||||
|       tags: frontend style | ||||
|       files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD | ||||
|  |  | |||
|  | @ -0,0 +1,48 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module Gitlab | ||||
|   module BackgroundMigration | ||||
|     # Cleanup draft column data inserted by a faulty regex | ||||
|     # | ||||
|     class CleanupDraftDataFromFaultyRegex | ||||
|       # Migration only version of MergeRequest table | ||||
|       ## | ||||
|       class MergeRequest < ActiveRecord::Base | ||||
|         LEAKY_REGEXP_STR     = "^\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP" | ||||
|         CORRECTED_REGEXP_STR = "^(\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP)" | ||||
| 
 | ||||
|         include EachBatch | ||||
| 
 | ||||
|         self.table_name = 'merge_requests' | ||||
| 
 | ||||
|         def self.eligible | ||||
|           where(state_id: 1) | ||||
|             .where(draft: true) | ||||
|             .where("title ~* ?", LEAKY_REGEXP_STR) | ||||
|             .where("title !~* ?", CORRECTED_REGEXP_STR) | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       def perform(start_id, end_id) | ||||
|         eligible_mrs = MergeRequest.eligible.where(id: start_id..end_id).pluck(:id) | ||||
| 
 | ||||
|         return if eligible_mrs.empty? | ||||
| 
 | ||||
|         eligible_mrs.each_slice(10) do |slice| | ||||
|           MergeRequest.where(id: slice).update_all(draft: false) | ||||
|         end | ||||
| 
 | ||||
|         mark_job_as_succeeded(start_id, end_id) | ||||
|       end | ||||
| 
 | ||||
|       private | ||||
| 
 | ||||
|       def mark_job_as_succeeded(*arguments) | ||||
|         Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded( | ||||
|           'CleanupDraftDataFromFaultyRegex', | ||||
|           arguments | ||||
|         ) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -13,7 +13,7 @@ module Gitlab | |||
|       end | ||||
| 
 | ||||
|       def message | ||||
|         project = suggestion_set.project | ||||
|         project = suggestion_set.target_project | ||||
|         user_defined_message = @custom_message.presence || project.suggestion_commit_message.presence | ||||
|         message = user_defined_message || DEFAULT_SUGGESTION_COMMIT_MESSAGE | ||||
| 
 | ||||
|  | @ -37,8 +37,8 @@ module Gitlab | |||
|         'branch_name' => ->(user, suggestion_set) { suggestion_set.branch }, | ||||
|         'files_count' => ->(user, suggestion_set) { suggestion_set.file_paths.length }, | ||||
|         'file_paths' => ->(user, suggestion_set) { format_paths(suggestion_set.file_paths) }, | ||||
|         'project_name' => ->(user, suggestion_set) { suggestion_set.project.name }, | ||||
|         'project_path' => ->(user, suggestion_set) { suggestion_set.project.path }, | ||||
|         'project_name' => ->(user, suggestion_set) { suggestion_set.target_project.name }, | ||||
|         'project_path' => ->(user, suggestion_set) { suggestion_set.target_project.path }, | ||||
|         'user_full_name' => ->(user, suggestion_set) { user.name }, | ||||
|         'username' => ->(user, suggestion_set) { user.username }, | ||||
|         'suggestions_count' => ->(user, suggestion_set) { suggestion_set.suggestions.size } | ||||
|  |  | |||
|  | @ -9,8 +9,12 @@ module Gitlab | |||
|         @suggestions = suggestions | ||||
|       end | ||||
| 
 | ||||
|       def project | ||||
|         first_suggestion.project | ||||
|       def source_project | ||||
|         first_suggestion.source_project | ||||
|       end | ||||
| 
 | ||||
|       def target_project | ||||
|         first_suggestion.target_project | ||||
|       end | ||||
| 
 | ||||
|       def branch | ||||
|  |  | |||
|  | @ -1,19 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| desc 'Run local Danger rules' | ||||
| task :danger_local do | ||||
|   require_relative '../../tooling/danger/project_helper' | ||||
|   require 'gitlab/popen' | ||||
| 
 | ||||
|   puts("#{Tooling::Danger::ProjectHelper.local_warning_message}\n") | ||||
| 
 | ||||
|   # _status will _always_ be 0, regardless of failure or success :( | ||||
|   output, _status = Gitlab::Popen.popen(%w{danger dry_run}) | ||||
| 
 | ||||
|   if output.empty? | ||||
|     puts(Tooling::Danger::ProjectHelper.success_message) | ||||
|   else | ||||
|     puts(output) | ||||
|     exit(1) | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,54 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe Gitlab::BackgroundMigration::CleanupDraftDataFromFaultyRegex do | ||||
|   let(:namespaces)     { table(:namespaces) } | ||||
|   let(:projects)       { table(:projects) } | ||||
|   let(:merge_requests) { table(:merge_requests) } | ||||
| 
 | ||||
|   let(:group)   { namespaces.create!(name: 'gitlab', path: 'gitlab') } | ||||
|   let(:project) { projects.create!(namespace_id: group.id) } | ||||
| 
 | ||||
|   let(:draft_prefixes) { ["[Draft]", "(Draft)", "Draft:", "Draft", "[WIP]", "WIP:", "WIP"] } | ||||
| 
 | ||||
|   def create_merge_request(params) | ||||
|     common_params = { | ||||
|       target_project_id: project.id, | ||||
|       target_branch: 'feature1', | ||||
|       source_branch: 'master' | ||||
|     } | ||||
| 
 | ||||
|     merge_requests.create!(common_params.merge(params)) | ||||
|   end | ||||
| 
 | ||||
|   context "mr.draft == true, and title matches the leaky regex and not the corrected regex" do | ||||
|     let(:mr_ids) { merge_requests.all.collect(&:id) } | ||||
| 
 | ||||
|     before do | ||||
|       draft_prefixes.each do |prefix| | ||||
|         (1..4).each do |n| | ||||
|           create_merge_request( | ||||
|             title: "#{prefix} This is a title", | ||||
|             draft: true, | ||||
|             state_id: 1 | ||||
|           ) | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       create_merge_request(title: "This has draft in the title", draft: true, state_id: 1) | ||||
|     end | ||||
| 
 | ||||
|     it "updates all open draft merge request's draft field to true" do | ||||
|       expect { subject.perform(mr_ids.first, mr_ids.last) } | ||||
|         .to change { MergeRequest.where(draft: true).count } | ||||
|         .by(-1) | ||||
|     end | ||||
| 
 | ||||
|     it "marks successful slices as completed" do | ||||
|       expect(subject).to receive(:mark_job_as_succeeded).with(mr_ids.first, mr_ids.last) | ||||
| 
 | ||||
|       subject.perform(mr_ids.first, mr_ids.last) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -3,7 +3,10 @@ | |||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe Gitlab::Suggestions::CommitMessage do | ||||
|   def create_suggestion(file_path, new_line, to_content) | ||||
|   include ProjectForksHelper | ||||
|   using RSpec::Parameterized::TableSyntax | ||||
| 
 | ||||
|   def create_suggestion(merge_request, file_path, new_line, to_content) | ||||
|     position = Gitlab::Diff::Position.new(old_path: file_path, | ||||
|                                           new_path: file_path, | ||||
|                                           old_line: nil, | ||||
|  | @ -29,69 +32,111 @@ RSpec.describe Gitlab::Suggestions::CommitMessage do | |||
|     create(:project, :repository, path: 'project-1', name: 'Project_1') | ||||
|   end | ||||
| 
 | ||||
|   let_it_be(:merge_request) do | ||||
|   let_it_be(:forked_project) { fork_project(project, nil, repository: true) } | ||||
| 
 | ||||
|   let_it_be(:merge_request_same_project) do | ||||
|     create(:merge_request, source_project: project, target_project: project) | ||||
|   end | ||||
| 
 | ||||
|   let_it_be(:suggestion_set) do | ||||
|     suggestion1 = create_suggestion('files/ruby/popen.rb', 9, '*** SUGGESTION 1 ***') | ||||
|     suggestion2 = create_suggestion('files/ruby/popen.rb', 13, '*** SUGGESTION 2 ***') | ||||
|     suggestion3 = create_suggestion('files/ruby/regex.rb', 22, '*** SUGGESTION 3 ***') | ||||
|   let_it_be(:merge_request_from_fork) do | ||||
|     create(:merge_request, source_project: forked_project, target_project: project) | ||||
|   end | ||||
| 
 | ||||
|   let_it_be(:suggestion_set_same_project) do | ||||
|     suggestion1 = create_suggestion(merge_request_same_project, 'files/ruby/popen.rb', 9, '*** SUGGESTION 1 ***') | ||||
|     suggestion2 = create_suggestion(merge_request_same_project, 'files/ruby/popen.rb', 13, '*** SUGGESTION 2 ***') | ||||
|     suggestion3 = create_suggestion(merge_request_same_project, 'files/ruby/regex.rb', 22, '*** SUGGESTION 3 ***') | ||||
| 
 | ||||
|     Gitlab::Suggestions::SuggestionSet.new([suggestion1, suggestion2, suggestion3]) | ||||
|   end | ||||
| 
 | ||||
|   let_it_be(:suggestion_set_forked_project) do | ||||
|     suggestion1 = create_suggestion(merge_request_from_fork, 'files/ruby/popen.rb', 9, '*** SUGGESTION 1 ***') | ||||
|     suggestion2 = create_suggestion(merge_request_from_fork, 'files/ruby/popen.rb', 13, '*** SUGGESTION 2 ***') | ||||
|     suggestion3 = create_suggestion(merge_request_from_fork, 'files/ruby/regex.rb', 22, '*** SUGGESTION 3 ***') | ||||
| 
 | ||||
|     Gitlab::Suggestions::SuggestionSet.new([suggestion1, suggestion2, suggestion3]) | ||||
|   end | ||||
| 
 | ||||
|   describe '#message' do | ||||
|     before do | ||||
|       # Updating the suggestion_commit_message on a project shared across specs | ||||
|       # avoids recreating the repository for each spec. | ||||
|       project.update!(suggestion_commit_message: message) | ||||
|     end | ||||
|     where(:suggestion_set) { [ref(:suggestion_set_same_project), ref(:suggestion_set_forked_project)] } | ||||
| 
 | ||||
|     context 'when a custom commit message is not specified' do | ||||
|       let(:expected_message) { 'Apply 3 suggestion(s) to 2 file(s)' } | ||||
|     with_them do | ||||
|       before do | ||||
|         # Updating the suggestion_commit_message on a project shared across specs | ||||
|         # avoids recreating the repository for each spec. | ||||
|         project.update!(suggestion_commit_message: message) | ||||
|         forked_project.update!(suggestion_commit_message: fork_message) | ||||
|       end | ||||
| 
 | ||||
|       context 'and is nil' do | ||||
|         let(:message) { nil } | ||||
|       let(:fork_message) { nil } | ||||
| 
 | ||||
|         it 'uses the default commit message' do | ||||
|           expect(described_class | ||||
|                    .new(user, suggestion_set) | ||||
|                    .message).to eq(expected_message) | ||||
|       context 'when a custom commit message is not specified' do | ||||
|         let(:expected_message) { 'Apply 3 suggestion(s) to 2 file(s)' } | ||||
| 
 | ||||
|         context 'and is nil' do | ||||
|           let(:message) { nil } | ||||
| 
 | ||||
|           it 'uses the default commit message' do | ||||
|             expect(described_class | ||||
|                     .new(user, suggestion_set) | ||||
|                     .message).to eq(expected_message) | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         context 'and is an empty string' do | ||||
|           let(:message) { '' } | ||||
| 
 | ||||
|           it 'uses the default commit message' do | ||||
|             expect(described_class | ||||
|                     .new(user, suggestion_set) | ||||
|                     .message).to eq(expected_message) | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         context 'when a custom commit message is specified for forked project' do | ||||
|           let(:message) { nil } | ||||
|           let(:fork_message) { "I'm a sad message that will not be used :(" } | ||||
| 
 | ||||
|           it 'uses the default commit message' do | ||||
|             expect(described_class | ||||
|                      .new(user, suggestion_set) | ||||
|                      .message).to eq(expected_message) | ||||
|           end | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context 'and is an empty string' do | ||||
|         let(:message) { '' } | ||||
|       context 'when a custom commit message is specified' do | ||||
|         let(:message) { "i'm a project message. a user's custom message takes precedence over me :(" } | ||||
|         let(:custom_message) { "hello there! i'm a cool custom commit message." } | ||||
| 
 | ||||
|         it 'uses the default commit message' do | ||||
|           expect(described_class | ||||
|                    .new(user, suggestion_set) | ||||
|                    .message).to eq(expected_message) | ||||
|         it 'shows the custom commit message' do | ||||
|           expect(Gitlab::Suggestions::CommitMessage | ||||
|                   .new(user, suggestion_set, custom_message) | ||||
|                   .message).to eq(custom_message) | ||||
|         end | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when a custom commit message is specified' do | ||||
|       let(:message) { "i'm a project message. a user's custom message takes precedence over me :(" } | ||||
|       let(:custom_message) { "hello there! i'm a cool custom commit message." } | ||||
|       context 'is specified and includes all placeholders' do | ||||
|         let(:message) do | ||||
|           '*** %{branch_name} %{files_count} %{file_paths} %{project_name} %{project_path} %{user_full_name} %{username} %{suggestions_count} ***' | ||||
|         end | ||||
| 
 | ||||
|       it 'shows the custom commit message' do | ||||
|         expect(Gitlab::Suggestions::CommitMessage | ||||
|                  .new(user, suggestion_set, custom_message) | ||||
|                  .message).to eq(custom_message) | ||||
|       end | ||||
|     end | ||||
|         it 'generates a custom commit message' do | ||||
|           expect(Gitlab::Suggestions::CommitMessage | ||||
|                   .new(user, suggestion_set) | ||||
|                   .message).to eq('*** master 2 files/ruby/popen.rb, files/ruby/regex.rb Project_1 project-1 Test User test.user 3 ***') | ||||
|         end | ||||
| 
 | ||||
|     context 'is specified and includes all placeholders' do | ||||
|       let(:message) do | ||||
|         '*** %{branch_name} %{files_count} %{file_paths} %{project_name} %{project_path} %{user_full_name} %{username} %{suggestions_count} ***' | ||||
|       end | ||||
|         context 'when a custom commit message is specified for forked project' do | ||||
|           let(:fork_message) { "I'm a sad message that will not be used :(" } | ||||
| 
 | ||||
|       it 'generates a custom commit message' do | ||||
|         expect(Gitlab::Suggestions::CommitMessage | ||||
|                  .new(user, suggestion_set) | ||||
|                  .message).to eq('*** master 2 files/ruby/popen.rb, files/ruby/regex.rb Project_1 project-1 Test User test.user 3 ***') | ||||
|           it 'uses the target project commit message' do | ||||
|             expect(Gitlab::Suggestions::CommitMessage | ||||
|                     .new(user, suggestion_set) | ||||
|                     .message).to eq('*** master 2 files/ruby/popen.rb, files/ruby/regex.rb Project_1 project-1 Test User test.user 3 ***') | ||||
|           end | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -3,6 +3,9 @@ | |||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe Gitlab::Suggestions::SuggestionSet do | ||||
|   include ProjectForksHelper | ||||
|   using RSpec::Parameterized::TableSyntax | ||||
| 
 | ||||
|   def create_suggestion(file_path, new_line, to_content) | ||||
|     position = Gitlab::Diff::Position.new(old_path: file_path, | ||||
|                                           new_path: file_path, | ||||
|  | @ -24,86 +27,99 @@ RSpec.describe Gitlab::Suggestions::SuggestionSet do | |||
|   let_it_be(:user) { create(:user) } | ||||
| 
 | ||||
|   let_it_be(:project) { create(:project, :repository) } | ||||
|   let_it_be(:forked_project) { fork_project(project, nil, repository: true) } | ||||
| 
 | ||||
|   let_it_be(:merge_request) do | ||||
|   let_it_be(:merge_request_same_project) do | ||||
|     create(:merge_request, source_project: project, target_project: project) | ||||
|   end | ||||
| 
 | ||||
|   let_it_be(:suggestion) { create(:suggestion)} | ||||
| 
 | ||||
|   let_it_be(:suggestion2) do | ||||
|     create_suggestion('files/ruby/popen.rb', 13, "*** SUGGESTION 2 ***") | ||||
|   let_it_be(:merge_request_from_fork) do | ||||
|     create(:merge_request, source_project: forked_project, target_project: project) | ||||
|   end | ||||
| 
 | ||||
|   let_it_be(:suggestion3) do | ||||
|     create_suggestion('files/ruby/regex.rb', 22, "*** SUGGESTION 3 ***") | ||||
|   end | ||||
|   where(:merge_request) { [ref(:merge_request_same_project), ref(:merge_request_from_fork)] } | ||||
|   with_them do | ||||
|     let(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request) } | ||||
|     let(:suggestion) { create(:suggestion, note: note) } | ||||
| 
 | ||||
|   let_it_be(:unappliable_suggestion) { create(:suggestion, :unappliable) } | ||||
| 
 | ||||
|   let(:suggestion_set) { described_class.new([suggestion]) } | ||||
| 
 | ||||
|   describe '#project' do | ||||
|     it 'returns the project associated with the suggestions' do | ||||
|       expected_project = suggestion.project | ||||
| 
 | ||||
|       expect(suggestion_set.project).to be(expected_project) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#branch' do | ||||
|     it 'returns the branch associated with the suggestions' do | ||||
|       expected_branch = suggestion.branch | ||||
| 
 | ||||
|       expect(suggestion_set.branch).to be(expected_branch) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#valid?' do | ||||
|     it 'returns true if no errors are found' do | ||||
|       expect(suggestion_set.valid?).to be(true) | ||||
|     let(:suggestion2) do | ||||
|       create_suggestion('files/ruby/popen.rb', 13, "*** SUGGESTION 2 ***") | ||||
|     end | ||||
| 
 | ||||
|     it 'returns false if an error is found' do | ||||
|       suggestion_set = described_class.new([unappliable_suggestion]) | ||||
| 
 | ||||
|       expect(suggestion_set.valid?).to be(false) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#error_message' do | ||||
|     it 'returns an error message if an error is found' do | ||||
|       suggestion_set = described_class.new([unappliable_suggestion]) | ||||
| 
 | ||||
|       expect(suggestion_set.error_message).to be_a(String) | ||||
|     let(:suggestion3) do | ||||
|       create_suggestion('files/ruby/regex.rb', 22, "*** SUGGESTION 3 ***") | ||||
|     end | ||||
| 
 | ||||
|     it 'returns nil if no errors are found' do | ||||
|       expect(suggestion_set.error_message).to be(nil) | ||||
|     let(:unappliable_suggestion) { create(:suggestion, :unappliable) } | ||||
| 
 | ||||
|     let(:suggestion_set) { described_class.new([suggestion]) } | ||||
| 
 | ||||
|     describe '#source_project' do | ||||
|       it 'returns the source project associated with the suggestions' do | ||||
|         expect(suggestion_set.source_project).to be(merge_request.source_project) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#actions' do | ||||
|     it 'returns an array of hashes with proper key/value pairs' do | ||||
|       first_action = suggestion_set.actions.first | ||||
| 
 | ||||
|       file_suggestion = suggestion_set.send(:suggestions_per_file).first | ||||
| 
 | ||||
|       expect(first_action[:action]).to be('update') | ||||
|       expect(first_action[:file_path]).to eq(file_suggestion.file_path) | ||||
|       expect(first_action[:content]).to eq(file_suggestion.new_content) | ||||
|     describe '#target_project' do | ||||
|       it 'returns the target project associated with the suggestions' do | ||||
|         expect(suggestion_set.target_project).to be(project) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#file_paths' do | ||||
|     it 'returns an array of unique file paths associated with the suggestions' do | ||||
|       suggestion_set = described_class.new([suggestion, suggestion2, suggestion3]) | ||||
|     describe '#branch' do | ||||
|       it 'returns the branch associated with the suggestions' do | ||||
|         expected_branch = suggestion.branch | ||||
| 
 | ||||
|       expected_paths = %w(files/ruby/popen.rb files/ruby/regex.rb) | ||||
|         expect(suggestion_set.branch).to be(expected_branch) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|       actual_paths = suggestion_set.file_paths | ||||
|     describe '#valid?' do | ||||
|       it 'returns true if no errors are found' do | ||||
|         expect(suggestion_set.valid?).to be(true) | ||||
|       end | ||||
| 
 | ||||
|       expect(actual_paths.sort).to eq(expected_paths) | ||||
|       it 'returns false if an error is found' do | ||||
|         suggestion_set = described_class.new([unappliable_suggestion]) | ||||
| 
 | ||||
|         expect(suggestion_set.valid?).to be(false) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     describe '#error_message' do | ||||
|       it 'returns an error message if an error is found' do | ||||
|         suggestion_set = described_class.new([unappliable_suggestion]) | ||||
| 
 | ||||
|         expect(suggestion_set.error_message).to be_a(String) | ||||
|       end | ||||
| 
 | ||||
|       it 'returns nil if no errors are found' do | ||||
|         expect(suggestion_set.error_message).to be(nil) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     describe '#actions' do | ||||
|       it 'returns an array of hashes with proper key/value pairs' do | ||||
|         first_action = suggestion_set.actions.first | ||||
| 
 | ||||
|         file_suggestion = suggestion_set.send(:suggestions_per_file).first | ||||
| 
 | ||||
|         expect(first_action[:action]).to be('update') | ||||
|         expect(first_action[:file_path]).to eq(file_suggestion.file_path) | ||||
|         expect(first_action[:content]).to eq(file_suggestion.new_content) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     describe '#file_paths' do | ||||
|       it 'returns an array of unique file paths associated with the suggestions' do | ||||
|         suggestion_set = described_class.new([suggestion, suggestion2, suggestion3]) | ||||
| 
 | ||||
|         expected_paths = %w(files/ruby/popen.rb files/ruby/regex.rb) | ||||
| 
 | ||||
|         actual_paths = suggestion_set.file_paths | ||||
| 
 | ||||
|         expect(actual_paths.sort).to eq(expected_paths) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -0,0 +1,40 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| require_migration! | ||||
| 
 | ||||
| RSpec.describe CleanupDraftDataFromFaultyRegex do | ||||
|   let(:merge_requests) { table(:merge_requests) } | ||||
| 
 | ||||
|   let!(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') } | ||||
|   let!(:project) { table(:projects).create!(namespace_id: namespace.id) } | ||||
| 
 | ||||
|   let(:default_mr_values) do | ||||
|     { | ||||
|       target_project_id: project.id, | ||||
|       draft: true, | ||||
|       source_branch: 'master', | ||||
|       target_branch: 'feature' | ||||
|     } | ||||
|   end | ||||
| 
 | ||||
|   let!(:known_good_1) { merge_requests.create!(default_mr_values.merge(title: "Draft: Test Title")) } | ||||
|   let!(:known_good_2) { merge_requests.create!(default_mr_values.merge(title: "WIP: Test Title")) } | ||||
|   let!(:known_bad_1) { merge_requests.create!(default_mr_values.merge(title: "Known bad title drafts")) } | ||||
|   let!(:known_bad_2) { merge_requests.create!(default_mr_values.merge(title: "Known bad title wip")) } | ||||
| 
 | ||||
|   describe '#up' do | ||||
|     it 'schedules CleanupDraftDataFromFaultyRegex background jobs filtering for eligble MRs' do | ||||
|       stub_const("#{described_class}::BATCH_SIZE", 2) | ||||
|       allow(Gitlab).to receive(:com?).and_return(true) | ||||
| 
 | ||||
|       freeze_time do | ||||
|         migrate! | ||||
| 
 | ||||
|         expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, known_bad_1.id, known_bad_2.id) | ||||
| 
 | ||||
|         expect(BackgroundMigrationWorker.jobs.size).to eq(1) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -276,40 +276,6 @@ RSpec.describe Tooling::Danger::ProjectHelper do | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '.local_warning_message' do | ||||
|     it 'returns an informational message with rules that can run' do | ||||
|       expect(described_class.local_warning_message).to eq('==> Only the following Danger rules can be run locally: ci_config, database, documentation, duplicate_yarn_dependencies, eslint, gitaly, pajamas, pipeline, prettier, product_intelligence, utility_css, vue_shared_documentation, datateam') | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '.success_message' do | ||||
|     it 'returns an informational success message' do | ||||
|       expect(described_class.success_message).to eq('==> No Danger rule violations!') | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#rule_names' do | ||||
|     context 'when running locally' do | ||||
|       before do | ||||
|         expect(fake_helper).to receive(:ci?).and_return(false) | ||||
|       end | ||||
| 
 | ||||
|       it 'returns local only rules' do | ||||
|         expect(project_helper.rule_names).to match_array(described_class::LOCAL_RULES) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when running under CI' do | ||||
|       before do | ||||
|         expect(fake_helper).to receive(:ci?).and_return(true) | ||||
|       end | ||||
| 
 | ||||
|       it 'returns all rules' do | ||||
|         expect(project_helper.rule_names).to eq(described_class::LOCAL_RULES | described_class::CI_ONLY_RULES) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#file_lines' do | ||||
|     let(:filename) { 'spec/foo_spec.rb' } | ||||
|     let(:file_spy) { spy } | ||||
|  |  | |||
|  | @ -3,22 +3,6 @@ | |||
| module Tooling | ||||
|   module Danger | ||||
|     module ProjectHelper | ||||
|       LOCAL_RULES ||= %w[ | ||||
|         ci_config | ||||
|         database | ||||
|         documentation | ||||
|         duplicate_yarn_dependencies | ||||
|         eslint | ||||
|         gitaly | ||||
|         pajamas | ||||
|         pipeline | ||||
|         prettier | ||||
|         product_intelligence | ||||
|         utility_css | ||||
|         vue_shared_documentation | ||||
|         datateam | ||||
|       ].freeze | ||||
| 
 | ||||
|       CI_ONLY_RULES ||= %w[ | ||||
|         ce_ee_vue_templates | ||||
|         ci_templates | ||||
|  | @ -31,8 +15,6 @@ module Tooling | |||
|         z_metadata | ||||
|       ].freeze | ||||
| 
 | ||||
|       MESSAGE_PREFIX = '==>' | ||||
| 
 | ||||
|       # First-match win, so be sure to put more specific regex at the top... | ||||
|       CATEGORIES = { | ||||
|         [%r{usage_data\.rb}, %r{^(\+|-).*\s+(count|distinct_count|estimate_batch_distinct_count)\(.*\)(.*)$}] => [:database, :backend, :product_intelligence], | ||||
|  | @ -181,20 +163,6 @@ module Tooling | |||
|         %r{\.js\z} => :frontend | ||||
|       }.freeze | ||||
| 
 | ||||
|       def local_warning_message | ||||
|         "#{MESSAGE_PREFIX} Only the following Danger rules can be run locally: #{LOCAL_RULES.join(', ')}" | ||||
|       end | ||||
|       module_function :local_warning_message # rubocop:disable Style/AccessModifierDeclarations | ||||
| 
 | ||||
|       def success_message | ||||
|         "#{MESSAGE_PREFIX} No Danger rule violations!" | ||||
|       end | ||||
|       module_function :success_message # rubocop:disable Style/AccessModifierDeclarations | ||||
| 
 | ||||
|       def rule_names | ||||
|         helper.ci? ? LOCAL_RULES | CI_ONLY_RULES : LOCAL_RULES | ||||
|       end | ||||
| 
 | ||||
|       def file_lines(filename) | ||||
|         read_file(filename).lines(chomp: true) | ||||
|       end | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue