From 14245e7755fb19d2429aa6a85273097a6b2eb43f Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 12 Aug 2020 21:09:54 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../blob/components/blob_edit_header.vue | 47 +++- .../components/incidents_settings_tabs.vue | 4 +- .../components/shared/delete_button.vue | 2 +- .../javascripts/snippets/components/edit.vue | 14 +- .../components/snippet_blob_actions_edit.vue | 25 ++ .../snippets/components/snippet_blob_edit.vue | 25 +- app/assets/javascripts/snippets/constants.js | 1 + app/assets/javascripts/snippets/utils/blob.js | 66 +++++ app/controllers/projects_controller.rb | 3 + app/helpers/groups_helper.rb | 2 +- app/helpers/projects_helper.rb | 2 +- app/models/pages_domain.rb | 10 +- app/models/project.rb | 14 + .../create_issue_service.rb | 74 ------ app/services/projects/destroy_service.rb | 2 +- .../service_desk_settings/update_service.rb | 2 + app/views/groups/projects.html.haml | 2 +- app/views/projects/_remove.html.haml | 6 +- .../projects/_service_desk_settings.html.haml | 3 +- app/views/projects/no_repo.html.haml | 2 +- .../process_alert_worker.rb | 17 +- ...100-refer-to-project-delete-not-remove.yml | 5 + .../unreleased/227838-h3-h4-class-fix.yml | 5 + .../gitlab_rails_cheat_sheet.md | 2 +- doc/api/projects.md | 4 +- doc/api/settings.md | 2 +- doc/development/feature_flags/process.md | 3 +- doc/development/i18n/externalization.md | 2 +- doc/operations/metrics/dashboards/default.md | 36 +++ doc/operations/metrics/dashboards/index.md | 7 +- doc/operations/metrics/index.md | 7 +- .../coverage_fuzzing/index.md | 2 - doc/user/permissions.md | 4 +- doc/user/project/index.md | 14 +- .../img/approvals_premium_mr_widget_v12_7.png | Bin 50214 -> 0 bytes .../img/approvals_premium_mr_widget_v13_3.png | Bin 0 -> 42034 bytes .../merge_requests/merge_request_approvals.md | 5 +- doc/user/project/settings/index.md | 10 +- lib/api/projects.rb | 2 +- lib/gitlab/service_desk_email.rb | 6 + locale/gitlab.pot | 44 ++-- .../settings/operations_settings_spec.rb | 2 +- spec/features/projects_spec.rb | 6 +- .../blob_edit_header_spec.js.snap | 21 +- .../blob/components/blob_edit_header_spec.js | 44 +++- .../incidents_settings_tabs_spec.js.snap | 6 +- .../project_delete_button_spec.js.snap | 2 +- .../__snapshots__/delete_button_spec.js.snap | 2 +- .../snippet_blob_edit_spec.js.snap | 29 +-- .../frontend/snippets/components/edit_spec.js | 4 +- .../snippet_blob_actions_edit_spec.js | 59 +++++ spec/frontend/snippets/utils/blob_spec.js | 134 ++++++++++ spec/lib/gitlab/service_desk_email_spec.rb | 22 ++ spec/models/pages_domain_spec.rb | 26 +- spec/models/project_spec.rb | 67 ++++- .../create_issue_service_spec.rb | 240 ------------------ .../update_service_spec.rb | 11 + .../process_alert_worker_spec.rb | 14 +- vendor/gitignore/C++.gitignore | 0 vendor/gitignore/Clojure.gitignore | 15 +- vendor/gitignore/Fortran.gitignore | 33 +-- vendor/gitignore/Java.gitignore | 0 vendor/gitignore/Kotlin.gitignore | 24 +- 63 files changed, 693 insertions(+), 551 deletions(-) create mode 100644 app/assets/javascripts/snippets/components/snippet_blob_actions_edit.vue create mode 100644 app/assets/javascripts/snippets/utils/blob.js delete mode 100644 app/services/incident_management/create_issue_service.rb create mode 100644 changelogs/unreleased/221100-refer-to-project-delete-not-remove.yml create mode 100644 changelogs/unreleased/227838-h3-h4-class-fix.yml create mode 100644 doc/operations/metrics/dashboards/default.md delete mode 100644 doc/user/project/merge_requests/img/approvals_premium_mr_widget_v12_7.png create mode 100644 doc/user/project/merge_requests/img/approvals_premium_mr_widget_v13_3.png create mode 100644 spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js create mode 100644 spec/frontend/snippets/utils/blob_spec.js delete mode 100644 spec/services/incident_management/create_issue_service_spec.rb mode change 100644 => 100755 vendor/gitignore/C++.gitignore mode change 100644 => 120000 vendor/gitignore/Clojure.gitignore mode change 100644 => 120000 vendor/gitignore/Fortran.gitignore mode change 100644 => 100755 vendor/gitignore/Java.gitignore mode change 100644 => 120000 vendor/gitignore/Kotlin.gitignore diff --git a/app/assets/javascripts/blob/components/blob_edit_header.vue b/app/assets/javascripts/blob/components/blob_edit_header.vue index e1e1d76f721..5d3a1f0ccdb 100644 --- a/app/assets/javascripts/blob/components/blob_edit_header.vue +++ b/app/assets/javascripts/blob/components/blob_edit_header.vue @@ -1,9 +1,10 @@ diff --git a/app/assets/javascripts/incidents_settings/components/incidents_settings_tabs.vue b/app/assets/javascripts/incidents_settings/components/incidents_settings_tabs.vue index 7411c0ffe0d..d6e963c6f4f 100644 --- a/app/assets/javascripts/incidents_settings/components/incidents_settings_tabs.vue +++ b/app/assets/javascripts/incidents_settings/components/incidents_settings_tabs.vue @@ -24,9 +24,9 @@ export default { class="settings no-animate qa-incident-management-settings" >
-

+

{{ $options.i18n.headerText }} -

+ {{ $options.i18n.expandBtnLabel }} diff --git a/app/assets/javascripts/projects/components/shared/delete_button.vue b/app/assets/javascripts/projects/components/shared/delete_button.vue index b00edc94abd..e3f4500d404 100644 --- a/app/assets/javascripts/projects/components/shared/delete_button.vue +++ b/app/assets/javascripts/projects/components/shared/delete_button.vue @@ -54,7 +54,7 @@ export default { }, }, strings: { - deleteProject: __('Remove project'), + deleteProject: __('Delete project'), title: __('Delete project. Are you ABSOLUTELY SURE?'), confirmText: __('Please type the following to confirm:'), }, diff --git a/app/assets/javascripts/snippets/components/edit.vue b/app/assets/javascripts/snippets/components/edit.vue index 896ab900fd7..81be39bda04 100644 --- a/app/assets/javascripts/snippets/components/edit.vue +++ b/app/assets/javascripts/snippets/components/edit.vue @@ -18,7 +18,7 @@ import { SNIPPET_BLOB_ACTION_UPDATE, SNIPPET_BLOB_ACTION_MOVE, } from '../constants'; -import SnippetBlobEdit from './snippet_blob_edit.vue'; +import SnippetBlobActionsEdit from './snippet_blob_actions_edit.vue'; import SnippetVisibilityEdit from './snippet_visibility_edit.vue'; import SnippetDescriptionEdit from './snippet_description_edit.vue'; import { SNIPPET_MARK_EDIT_APP_START } from '~/performance_constants'; @@ -27,7 +27,7 @@ export default { components: { SnippetDescriptionEdit, SnippetVisibilityEdit, - SnippetBlobEdit, + SnippetBlobActionsEdit, TitleField, FormFooterActions, GlButton, @@ -261,15 +261,7 @@ export default { :markdown-preview-path="markdownPreviewPath" :markdown-docs-path="markdownDocsPath" /> - - + +import SnippetBlobEdit from './snippet_blob_edit.vue'; + +export default { + components: { + SnippetBlobEdit, + }, + props: { + blobs: { + type: Array, + required: true, + }, + }, +}; + + + diff --git a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue index df293de103b..2fe7cae3ae4 100644 --- a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue +++ b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue @@ -91,17 +91,18 @@ export default { }; diff --git a/app/assets/javascripts/snippets/constants.js b/app/assets/javascripts/snippets/constants.js index 99ee698408d..a59d7aa84eb 100644 --- a/app/assets/javascripts/snippets/constants.js +++ b/app/assets/javascripts/snippets/constants.js @@ -30,3 +30,4 @@ export const SNIPPET_BLOB_CONTENT_FETCH_ERROR = __("Can't fetch content for the export const SNIPPET_BLOB_ACTION_CREATE = 'create'; export const SNIPPET_BLOB_ACTION_UPDATE = 'update'; export const SNIPPET_BLOB_ACTION_MOVE = 'move'; +export const SNIPPET_BLOB_ACTION_DELETE = 'delete'; diff --git a/app/assets/javascripts/snippets/utils/blob.js b/app/assets/javascripts/snippets/utils/blob.js new file mode 100644 index 00000000000..fd5ff9a3d2e --- /dev/null +++ b/app/assets/javascripts/snippets/utils/blob.js @@ -0,0 +1,66 @@ +import { uniqueId } from 'lodash'; +import { + SNIPPET_BLOB_ACTION_CREATE, + SNIPPET_BLOB_ACTION_UPDATE, + SNIPPET_BLOB_ACTION_MOVE, + SNIPPET_BLOB_ACTION_DELETE, +} from '../constants'; + +const createLocalId = () => uniqueId('blob_local_'); + +export const decorateBlob = blob => ({ + ...blob, + id: createLocalId(), + isLoaded: false, + content: '', +}); + +export const createBlob = () => ({ + id: createLocalId(), + content: '', + path: '', + isLoaded: true, +}); + +const diff = ({ content, path }, origBlob) => { + if (!origBlob) { + return { + action: SNIPPET_BLOB_ACTION_CREATE, + previousPath: path, + content, + filePath: path, + }; + } else if (origBlob.path !== path || origBlob.content !== content) { + return { + action: origBlob.path === path ? SNIPPET_BLOB_ACTION_UPDATE : SNIPPET_BLOB_ACTION_MOVE, + previousPath: origBlob.path, + content, + filePath: path, + }; + } + + return null; +}; + +/** + * This function returns an array of diff actions (to be sent to the BE) based on the current vs. original blobs + * + * @param {Object} blobs + * @param {Object} origBlobs + */ +export const diffAll = (blobs, origBlobs) => { + const deletedEntries = Object.values(origBlobs) + .filter(x => !blobs[x.id]) + .map(({ path, content }) => ({ + action: SNIPPET_BLOB_ACTION_DELETE, + previousPath: path, + filePath: path, + content, + })); + + const newEntries = Object.values(blobs) + .map(blob => diff(blob, origBlobs[blob.id])) + .filter(x => x); + + return [...deletedEntries, ...newEntries]; +}; diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 5146a44de83..10a23c0539e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -38,6 +38,9 @@ class ProjectsController < Projects::ApplicationController before_action only: [:new, :create] do frontend_experimentation_tracking_data(:new_create_project_ui, 'click_tab') push_frontend_feature_flag(:new_create_project_ui) if experiment_enabled?(:new_create_project_ui) + end + + before_action only: [:edit] do push_frontend_feature_flag(:service_desk_custom_address, @project) end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 5255dd27852..eb80acd869f 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -130,7 +130,7 @@ module GroupsHelper end def remove_group_message(group) - _("You are going to remove %{group_name}, this will also remove all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?") % + _("You are going to remove %{group_name}, this will also delete all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?") % { group_name: group.name } end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 089d187da61..3eae744e6ec 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -104,7 +104,7 @@ module ProjectsHelper end def remove_project_message(project) - _("You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?") % + _("You are going to delete %{project_full_name}. Deleted projects CANNOT be restored! Are you ABSOLUTELY sure?") % { project_full_name: project.full_name } end diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 856496f0941..d071d2d3c89 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -3,6 +3,7 @@ class PagesDomain < ApplicationRecord include Presentable include FromUnion + include AfterCommitQueue VERIFICATION_KEY = 'gitlab-pages-verification-code' VERIFICATION_THRESHOLD = 3.days.freeze @@ -222,6 +223,8 @@ class PagesDomain < ApplicationRecord private def pages_deployed? + return false unless project + # TODO: remove once `pages_metadatum` is migrated # https://gitlab.com/gitlab-org/gitlab/issues/33106 unless project.pages_metadatum @@ -244,8 +247,13 @@ class PagesDomain < ApplicationRecord # rubocop: disable CodeReuse/ServiceClass def update_daemon return if usage_serverless? + return unless pages_deployed? - ::Projects::UpdatePagesConfigurationService.new(project).execute + if Feature.enabled?(:async_update_pages_config, project) + run_after_commit { PagesUpdateConfigurationWorker.perform_async(project_id) } + else + Projects::UpdatePagesConfigurationService.new(project).execute + end end # rubocop: enable CodeReuse/ServiceClass diff --git a/app/models/project.rb b/app/models/project.rb index 87171b7debc..4ebd7a64275 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -2468,6 +2468,10 @@ class Project < ApplicationRecord alias_method :service_desk_enabled?, :service_desk_enabled def service_desk_address + service_desk_custom_address || service_desk_incoming_address + end + + def service_desk_incoming_address return unless service_desk_enabled? config = Gitlab.config.incoming_email @@ -2476,6 +2480,16 @@ class Project < ApplicationRecord config.address&.gsub(wildcard, "#{full_path_slug}-#{id}-issue-") end + def service_desk_custom_address + return unless ::Gitlab::ServiceDeskEmail.enabled? + return unless ::Feature.enabled?(:service_desk_custom_address, self) + + key = service_desk_setting&.project_key + return unless key.present? + + ::Gitlab::ServiceDeskEmail.address_for_key("#{full_path_slug}-#{key}") + end + def root_namespace if namespace.has_parent? namespace.root_ancestor diff --git a/app/services/incident_management/create_issue_service.rb b/app/services/incident_management/create_issue_service.rb deleted file mode 100644 index d1c5c6752d4..00000000000 --- a/app/services/incident_management/create_issue_service.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -module IncidentManagement - class CreateIssueService < BaseService - include Gitlab::Utils::StrongMemoize - include IncidentManagement::Settings - - attr_reader :alert - - def initialize(project, alert) - super(project, User.alert_bot) - @alert = alert - end - - def execute - return error('setting disabled') unless incident_management_setting.create_issue? - return error('invalid alert') unless alert_presenter.valid? - - result = create_incident - return error(result.message, result.payload[:issue]) unless result.success? - - result - end - - private - - def create_incident - ::IncidentManagement::Incidents::CreateService.new( - project, - current_user, - title: issue_title, - description: issue_description - ).execute - end - - def issue_title - alert_presenter.full_title - end - - def issue_description - horizontal_line = "\n\n---\n\n" - - [ - alert_summary, - alert_markdown, - issue_template_content - ].compact.join(horizontal_line) - end - - def alert_summary - alert_presenter.issue_summary_markdown - end - - def alert_markdown - alert_presenter.alert_markdown - end - - def alert_presenter - strong_memoize(:alert_presenter) do - Gitlab::Alerting::Alert.for_alert_management_alert(project: project, alert: alert).present - end - end - - def issue_template_content - incident_management_setting.issue_template_content - end - - def error(message, issue = nil) - log_error(%{Cannot create incident issue for "#{project.full_name}": #{message}}) - - ServiceResponse.error(payload: { issue: issue }, message: message) - end - end -end diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 2e949f2fc55..37487261f2c 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -31,7 +31,7 @@ module Projects attempt_destroy_transaction(project) system_hook_service.execute_hooks_for(project, :destroy) - log_info("Project \"#{project.full_path}\" was removed") + log_info("Project \"#{project.full_path}\" was deleted") current_user.invalidate_personal_projects_count diff --git a/app/services/service_desk_settings/update_service.rb b/app/services/service_desk_settings/update_service.rb index 08106b04d18..c837b75f439 100644 --- a/app/services/service_desk_settings/update_service.rb +++ b/app/services/service_desk_settings/update_service.rb @@ -9,6 +9,8 @@ module ServiceDeskSettings params.delete(:project_key) end + params[:project_key] = nil if params[:project_key].blank? + if settings.update(params) success else diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index bf9d89da24a..555c4004a3f 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -15,7 +15,7 @@ .controls = link_to _('Members'), project_project_members_path(project), id: "edit_#{dom_id(project)}", class: "btn" = link_to _('Edit'), edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn" - = link_to _('Remove'), project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-remove" + = link_to _('Delete'), project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-remove" .stats %span.badge.badge-pill diff --git a/app/views/projects/_remove.html.haml b/app/views/projects/_remove.html.haml index 30603416531..05eab3b3245 100644 --- a/app/views/projects/_remove.html.haml +++ b/app/views/projects/_remove.html.haml @@ -2,9 +2,9 @@ - confirm_phrase = s_('DeleteProject|Delete %{name}') % { name: project.full_name } .sub-section - %h4.danger-title= _('Remove project') + %h4.danger-title= _('Delete project') %p - %strong= _('Removing the project will delete its repository and all related resources including issues, merge requests etc.') + %strong= _('Deleting the project will delete its repository and all related resources including issues, merge requests etc.') %p - %strong= _('Removed projects cannot be restored!') + %strong= _('Deleted projects cannot be restored!') #js-project-delete-button{ data: { form_path: project_path(project), confirm_phrase: confirm_phrase } } diff --git a/app/views/projects/_service_desk_settings.html.haml b/app/views/projects/_service_desk_settings.html.haml index e6842bbb939..7c08955983a 100644 --- a/app/views/projects/_service_desk_settings.html.haml +++ b/app/views/projects/_service_desk_settings.html.haml @@ -10,7 +10,8 @@ - if ::Gitlab::ServiceDesk.supported? .js-service-desk-setting-root{ data: { endpoint: project_service_desk_path(@project), enabled: "#{@project.service_desk_enabled}", - incoming_email: (@project.service_desk_address if @project.service_desk_enabled), + incoming_email: (@project.service_desk_incoming_address if @project.service_desk_enabled), + custom_email: (@project.service_desk_custom_address if @project.service_desk_enabled), selected_template: "#{@project.service_desk_setting&.issue_template_key}", outgoing_name: "#{@project.service_desk_setting&.outgoing_name}", project_key: "#{@project.service_desk_setting&.project_key}", diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml index d5030a02cdd..0ab9d9c4005 100644 --- a/app/views/projects/no_repo.html.haml +++ b/app/views/projects/no_repo.html.haml @@ -22,4 +22,4 @@ - if can? current_user, :remove_project, @project .prepend-top-20 - = link_to _('Remove project'), project_path(@project), data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-inverted btn-remove float-right" + = link_to _('Delete project'), project_path(@project), data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-inverted btn-remove float-right" diff --git a/app/workers/incident_management/process_alert_worker.rb b/app/workers/incident_management/process_alert_worker.rb index a8c6c9aa121..59464b81d1b 100644 --- a/app/workers/incident_management/process_alert_worker.rb +++ b/app/workers/incident_management/process_alert_worker.rb @@ -17,10 +17,9 @@ module IncidentManagement return unless alert result = create_issue_for(alert) - return unless result.success? + return if result.success? - new_issue = result.payload[:issue] - link_issue_with_alert(alert, new_issue.id) + log_warning(alert, result) end private @@ -30,19 +29,19 @@ module IncidentManagement end def create_issue_for(alert) - IncidentManagement::CreateIssueService - .new(alert.project, alert) + AlertManagement::CreateAlertIssueService + .new(alert, User.alert_bot) .execute end - def link_issue_with_alert(alert, issue_id) - return if alert.update(issue_id: issue_id) + def log_warning(alert, result) + issue_id = result.payload[:issue]&.id Gitlab::AppLogger.warn( - message: 'Cannot link an Issue with Alert', + message: 'Cannot process an Incident', issue_id: issue_id, alert_id: alert.id, - alert_errors: alert.errors.messages + errors: result.message ) end end diff --git a/changelogs/unreleased/221100-refer-to-project-delete-not-remove.yml b/changelogs/unreleased/221100-refer-to-project-delete-not-remove.yml new file mode 100644 index 00000000000..73d6de12e03 --- /dev/null +++ b/changelogs/unreleased/221100-refer-to-project-delete-not-remove.yml @@ -0,0 +1,5 @@ +--- +title: Re-name project remove as project delete +merge_request: 38489 +author: +type: changed diff --git a/changelogs/unreleased/227838-h3-h4-class-fix.yml b/changelogs/unreleased/227838-h3-h4-class-fix.yml new file mode 100644 index 00000000000..a18f68804fb --- /dev/null +++ b/changelogs/unreleased/227838-h3-h4-class-fix.yml @@ -0,0 +1,5 @@ +--- +title: Replace mis-used CSS class in operations settings +merge_request: 39338 +author: +type: changed diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md index 8f15d47da32..22d699b424b 100644 --- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md +++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md @@ -167,7 +167,7 @@ user = User.find_by_username('root') # Find the project, update the xxx-changeme values from above project = Project.find_by_full_path('group-changeme/project-changeme') -# Delete the project +# Immediately delete the project ::Projects::DestroyService.new(project, user, {}).execute ``` diff --git a/doc/api/projects.md b/doc/api/projects.md index 06ddce7a871..3640008f16d 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -1832,11 +1832,11 @@ Example response: } ``` -## Remove project +## Delete project This endpoint: -- Removes a project including all associated resources (issues, merge requests etc). +- Deletes a project including all associated resources (issues, merge requests etc). - From [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) on [Premium or Silver](https://about.gitlab.com/pricing/) or higher tiers, group admins can [configure](../user/group/index.md#enabling-delayed-project-removal-premium) projects within a group to be deleted after a delayed period. diff --git a/doc/api/settings.md b/doc/api/settings.md index d548508c346..208ef63de07 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -221,7 +221,7 @@ are listed in the descriptions of the relevant settings. | `default_project_visibility` | string | no | What visibility level new projects receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. | | `default_projects_limit` | integer | no | Project limit per user. Default is `100000`. | | `default_snippet_visibility` | string | no | What visibility level new snippets receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. | -| `deletion_adjourned_period` | integer | no | **(PREMIUM ONLY)** The number of days to wait before removing a project or group that is marked for deletion. Value must be between 0 and 90. +| `deletion_adjourned_period` | integer | no | **(PREMIUM ONLY)** The number of days to wait before deleting a project or group that is marked for deletion. Value must be between 0 and 90. | `diff_max_patch_bytes` | integer | no | Maximum diff patch size (Bytes). | | `disabled_oauth_sign_in_sources` | array of strings | no | Disabled OAuth sign-in sources. | | `dns_rebinding_protection_enabled` | boolean | no | Enforce DNS rebinding attack protection. | diff --git a/doc/development/feature_flags/process.md b/doc/development/feature_flags/process.md index b053838964b..5dc3cf44a6e 100644 --- a/doc/development/feature_flags/process.md +++ b/doc/development/feature_flags/process.md @@ -24,7 +24,8 @@ should be leveraged: - When development of a feature will be spread across multiple merge requests, you can use the following workflow: - 1. Introduce a feature flag which is **off** by default, in the first merge request. + 1. [Create a new feature flag](development.md#create-a-new-feature-flag) + which is **off** by default, in the first merge request. 1. Submit incremental changes via one or more merge requests, ensuring that any new code added can only be reached if the feature flag is **on**. You can keep the feature flag enabled on your local GDK during development. diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md index 980691c6b4f..1374ca92256 100644 --- a/doc/development/i18n/externalization.md +++ b/doc/development/i18n/externalization.md @@ -636,7 +636,7 @@ Errors in `locale/zh_HK/gitlab.po`: Syntax error in msgstr Syntax error in message_line There should be only whitespace until the end of line after the double quote character of a message text. - Parsing result before error: '{:msgid=>["", "You are going to remove %{project_name_with_namespace}.\\n", "Removed project CANNOT be restored!\\n", "Are you ABSOLUTELY sure?"]}' + Parsing result before error: '{:msgid=>["", "You are going to delete %{project_name_with_namespace}.\\n", "Deleted projects CANNOT be restored!\\n", "Are you ABSOLUTELY sure?"]}' SimplePoParser filtered backtrace: SimplePoParser::ParserError Errors in `locale/zh_TW/gitlab.po`: 1 pipeline diff --git a/doc/operations/metrics/dashboards/default.md b/doc/operations/metrics/dashboards/default.md new file mode 100644 index 00000000000..78771861222 --- /dev/null +++ b/doc/operations/metrics/dashboards/default.md @@ -0,0 +1,36 @@ +--- +stage: Monitor +group: APM +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +--- + +# GitLab-defined metrics dashboards **(CORE)** + +GitLab provides some dashboards out-of-the-box for any project with +[Prometheus available](../../../user/project/integrations/prometheus.md). You can +[duplicate these GitLab-defined dashboards](index.md#duplicate-a-gitlab-defined-dashboard): + +- [Overview dashboard](#overview-dashboard). +- [Kubernetes pod health dashboard](#kubernetes-pod-health-dashboard). + +To learn about the components of a dashboard, read +[Metrics dashboard for your CI/CD environment](../index.md). + +## Overview dashboard + +This dashboard is the default metrics dashboard. It displays a large number of +metrics about the [deployed application](../index.md#configure-prometheus-to-gather-metrics). + +![Example of metrics dashboard](../img/example-dashboard_v13_3.png) + +## Kubernetes pod health dashboard + +NOTE: **Note:** +This dashboard requires Kubernetes v1.14 or higher, due to the +[change in metric labels](https://github.com/kubernetes/kubernetes/pull/69099) +in Kubernetes 1.14. + +This dashboard displays CPU, memory, network and disk metrics for the pods in your +[connected K8s cluster](../../../user/project/clusters/index.md). It provides a +[variable selector](templating_variables.md#metric_label_values-variable-type) +at the top of the dashboard to select which pod's metrics to display. diff --git a/doc/operations/metrics/dashboards/index.md b/doc/operations/metrics/dashboards/index.md index d9d244ac7de..73909e977c6 100644 --- a/doc/operations/metrics/dashboards/index.md +++ b/doc/operations/metrics/dashboards/index.md @@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/59974) in GitLab 12.1. -By default, all projects include a GitLab-defined Prometheus dashboard, which +By default, all projects include a [GitLab-defined Prometheus dashboard](default.md), which includes a few key metrics, but you can also define your own custom dashboards. You may create a [new dashboard from scratch](#add-a-new-dashboard-to-your-project) @@ -23,7 +23,8 @@ The metrics as defined below do not support alerts, unlike > UI option [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/228856) in GitLab 13.3. You can configure a custom dashboard by adding a new YAML file into your project's -`.gitlab/dashboards/` directory. For the dashboard to display on your project's **Operations > Metrics** page, the files must have a `.yml` +`.gitlab/dashboards/` directory. For the dashboard to display on your project's +**Operations > Metrics** page, the files must have a `.yml` extension and be present in your project's **default** branch. To create a new dashboard from the GitLab user interface: @@ -145,7 +146,7 @@ Your custom dashboard is available at `https://example.com/project/-/metrics/cus To manage the settings for your metrics dashboard: -1. Sign in as a user with project Maintainer or Admin +1. Sign in as a user with project Maintainer or Administrator [permissions](../../../user/permissions.md#project-members-permissions). 1. Navigate to your dashboard at **Operations > Metrics**. 1. In the top-right corner of your dashboard, click **Metrics Settings**: diff --git a/doc/operations/metrics/index.md b/doc/operations/metrics/index.md index 6c6986bd6a3..92c4a4986bc 100644 --- a/doc/operations/metrics/index.md +++ b/doc/operations/metrics/index.md @@ -72,16 +72,15 @@ and NGINX, and attempts to identify individual environments. To learn more about the supported metrics and scan processes, see the [Prometheus Metrics Library documentation](../../user/project/integrations/prometheus_library/index.md). -To view the metrics dashboard for an environment that has -To view the metrics dashboard for an environment that is +To view the [default metrics dashboard](dashboards/default.md) for an environment that is [configured to gather metrics](#configure-prometheus-to-gather-metrics): 1. *If the metrics dashboard is only visible to project members,* sign in to GitLab as a member of a project. Learn more about [metrics dashboard visibility](#metrics-dashboard-visibility). 1. In your project, navigate to **Operations > Metrics**. -GitLab displays the default metrics dashboard for the environment, like the -following example: +GitLab displays the [default metrics dashboard](dashboards/default.md) for the environment, +like the following example: ![Example of metrics dashboard](img/example-dashboard_v13_3.png) diff --git a/doc/user/application_security/coverage_fuzzing/index.md b/doc/user/application_security/coverage_fuzzing/index.md index 704423c6881..7fa0e34d90d 100644 --- a/doc/user/application_security/coverage_fuzzing/index.md +++ b/doc/user/application_security/coverage_fuzzing/index.md @@ -7,8 +7,6 @@ type: reference, howto # Coverage Guided Fuzz Testing **(ULTIMATE)** -> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3226) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.2 as an [Alpha feature](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha). - GitLab allows you to add coverage-guided fuzz testing to your pipelines. This helps you discover bugs and potential security issues that other QA processes may miss. Coverage-guided fuzzing sends random inputs to an instrumented version of your application in an effort to cause unexpected diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 9cd3d0ff9c6..39b61bd7bee 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -156,7 +156,7 @@ The following table depicts the various user permission levels in a project. | Transfer project to another namespace | | | | | ✓ | | Rename project | | | | | ✓ | | Remove fork relationship | | | | | ✓ | -| Remove project | | | | | ✓ | +| Delete project | | | | | ✓ | | Archive project | | | | | ✓ | | Delete issues | | | | | ✓ | | Delete pipelines | | | | | ✓ | @@ -416,7 +416,7 @@ instance and project. In addition, all admins can use the admin interface under | See commits and jobs | ✓ | ✓ | ✓ | ✓ | | Retry or cancel job | | ✓ | ✓ | ✓ | | Erase job artifacts and trace | | ✓ (*1*) | ✓ | ✓ | -| Remove project | | | ✓ | ✓ | +| Delete project | | | ✓ | ✓ | | Create project | | | ✓ | ✓ | | Change project configuration | | | ✓ | ✓ | | Add specific runners | | | ✓ | ✓ | diff --git a/doc/user/project/index.md b/doc/user/project/index.md index 154cc882397..4e5b924a1b7 100644 --- a/doc/user/project/index.md +++ b/doc/user/project/index.md @@ -180,22 +180,22 @@ Read through the documentation on [project settings](settings/index.md). - [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data) - [Importing and exporting projects between GitLab instances](settings/import_export.md) -## Remove a project +## Delete a project -To remove a project, first navigate to the home page for that project. +To delete a project, first navigate to the home page for that project. 1. Navigate to **Settings > General**. 1. Expand the **Advanced** section. -1. Scroll down to the **Remove project** section. -1. Click **Remove project** +1. Scroll down to the **Delete project** section. +1. Click **Delete project** 1. Confirm this action by typing in the expected text. -### Delayed removal **(PREMIUM)** +### Delayed deletion **(PREMIUM)** -By default, clicking to remove a project is followed by a seven day delay. Admins can restore the project during this period of time. +By default, clicking to delete a project is followed by a seven day delay. Admins can restore the project during this period of time. This delay [may be changed by an admin](../admin_area/settings/visibility_and_access_controls.md#default-deletion-delay-premium-only). -Admins can view all projects pending deletion. If you're an administrator, go to the top navigation bar, click **Projects > Your projects**, and then select the **Removed projects** tab. +Admins can view all projects pending deletion. If you're an administrator, go to the top navigation bar, click **Projects > Your projects**, and then select the **Deleted projects** tab. From this tab an admin can restore any project. ## CI/CD for external repositories **(PREMIUM)** diff --git a/doc/user/project/merge_requests/img/approvals_premium_mr_widget_v12_7.png b/doc/user/project/merge_requests/img/approvals_premium_mr_widget_v12_7.png deleted file mode 100644 index 164779a84502c3a135b83b8d8c1d30d5944d88c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50214 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
    uuBG|*@Z9dOcon)-MMNV(AWk%u( z7p;}rVX0!qM49iI(f(OKjxKRotCg9TpT5!tG}UbxT%=8xAh_gEzy6_;iJVo#-4E8v zj19*2(vOCTU82tBrja-IV}YN4p#;Y|eQ5ifm5om{@#Tc3 zS8i8%#D6)Ce5|!r3v(^nk%w&frAaRtutI@$ZRqVt)KG9p zzK{`#ruqra1wra~rwihx)juQpQ+a=?*XS&%3hP|l7_IB)7KW9$>|0R{!a;)ksL`1l z`H^!Rvf-F5~uLAX3enfT;0K2WbM&@TQoxc;NEj)0Cu@r|`&CH;a%8L++}{Pe&5 z$|8=TIU558>vZvH;6~|1txy;t$@k7I{FJw?tSa>PF*PF1{>8By-!gjOIAw)xF7FSUt(KF-vt#JR9@zVY|7wsl;&&-nhJ54D7QOxnTi1Z#hNyn>1s5u z{GYmn*T{hYlg83&cm&I_SoC&zVlP#+KO1RME;0NhBfJxY%>S_!mkLejDq()7n}Tl4E%G|_ zg6v^;xxiZ3F-rBCIMuR{BJwzTiNZ0JBd&dE0WIL-w%~T-g7x{z5j;yg3m zx~vmh*x)lt`yY{!%it=$TIGay=@#*60k!{6UuPZHWc2TG6oG-n6i^TlP(n(Oa7s%^ zqjblRknV06As~n}5(-QtMvatmLzr|)cgLvFx!vLK-q-!-zV6@8UgvzyH_kbGo^!r$ zWpZ$eBYSt4LYb(fgGdcb9M@5*r9cuQu)e>N=PerFHCodFH1YU?Dr6Atubs#WNNVAm zeO`*N`P=;#l;2-BcTY;VbY-&>32ZTu;Bzwb#tZwt(yyM)(Yy5OYm%@fa8!`)Red+t z){l5PtBL4BWX@!*V7l{^;DwTuF~<__0LLe-NU zUmuZo0C&{6A}CTOI!x;|@h^RPL|2hxLj#$f!f=>)xdEyupHQ04<81%!y$yiaLrU=F z*dC=-n`!u|!RytDf+UqfAGs9+_-Wr`v{8L71)1j)IQWuBIr#ou8l?49>i1In$tp(n zP2WM060@Va7>E90Xx|_n^*LXoLjrycHnI}SAk$M8fn;Z07{-f^SsM9nt`h)>Or9`yZk7|5utRMbj@+?AcAlgsJ85Q9%p=$#?Qgv5LuzA zF8832w*L&1mV6c785Z2+;HNO_PUF1xie+`;)LpGTOB`_AwuG5;BGdVfSTn6W+jkS6 zJ!s|Ei*2`P@R4ub)s0!jn7b;PHTKH zAeq%0sy(GEc*_}d018M3@7e?>FZ`!(3Qqy8vJwF9)I!tY?`VLFcy=IMJ-*j|O^{Z7 zCLdFuMq8p~WD4Bup^535e#L?Td^KyA*2H4qwNX3MeYKzro)u$TBc<6RKCeT?5sZ@7RM&FAw3w#{0U-RT8LiASoKM~V zEM$WdbgE-XTRhZ*5jj&-R7{+I8s{bEpC4q@P;T`-HupIkrY(hQeqEUK`Z{g6 zi_KTc-drw4Xo*kduWX{<1j3V?ds%Y=Z{vJ6Xqa#L&M~fK|2%!Od^iUNwKZ>9ORpmM zJu!`(WZ+8nNfdbp1JpYpfPSRCoIWf}^c_C37uIF5Z^jowp4P6$pLocDBZYRkk-#xWL4(x;lkRvMm zS;Qd!ZT9vo#OR5<+T{}mCXMP*+$GCEYZ{pt?Q|qKS@D85QcgB0`m=WWsp}Sod|df< zb7H-RgWzU5Gy!y)3b39;u$5=Myd(L~HD!5A45iMo_TEYeuGw-*;AT9tJ8eBh%m5B=lEJqI5*Z>=(HTPM_DD8u)*?c~$Ka|iq3Qy&~G>ikBo zS9tT@U(_7fY}+vJQIXcQu?mML9KTyUDza#rxM3XBi!3T)1_*?vpU$KKXYU_ZB0b!t zl81xxqy+Q+zv;(wk^Qr2+XeH^E=p>j@c@5KKTf)ay`mt@=yFG_ijhO?NA0Z+(FSmG zw^@M^U6Go}N)5Zf-*2{2$eFE!ZcZ%iUq%|svENb>u7Hw=!KS~d^QYr7DlV10i1w;m zW7`}yHJk(@X$wq32cRHa$qd%n>Q-jp*-%4u;un634}AKA1@k&weik!B1CmTfsKe;5 ztXklmZ^NMBtJ0^lo{JUhy6ywy`A26lt%dQF1Ph`wbwxYDXYPNOv966*QwicWf1Pm5 zkwue5tJhl-wJ(QXUZrz3`CTr#yKP&4%3+^be;a&}as-!2QV;aR6zpAUlYlh~Ohsnn zWLh;vV7W5f0T~bSE0@1vQ>FIa9;n#5VL?p6*!of?iGU=wZ~@6m?Y&|erb?%zg#oK6 zRx57f!-0*4wsW6^LS&a(44CFkUmeBf-@zL!(*R_88x_Gqu08?0#55(7AxYnxH>?-V zr(*knAHPjPJ^CZR`dVSNsoI@3bH%6x(-@`)jYn8EroyCB2me${hUzTbPClVcV5LG4 zhaQi)x(VJxEkdV5q;vA5&-SD1_4F@E3_ec4=6W?E ziut5Zn>QtB%a!@U8%=6PveRvhQip_1`#MuF0XHMh>ClF_3h!Ep4?G%-3eajb$4Q#w zN3_rC!HEDmRmiPpbcugBTEK8p{wwU=S|dN};ERy^YOsro zQbL}+G}nj?DNI=+{6iQNnz9-0iQRZQcs{-n^!@# z3?)nbtP6EqOZ{}k!J~h(sgnq6e78!9Up*NqQs~R(fb;O0d@M!>Sc3?}6OIbr=`c}{ zbc&8N0a1YaPdXdBVjE?TLHqL}b!bHf_K#rDJ~_mDJE*}+rbI9tQ7DwM-q=>d9qju6 zAVky+?aDpT(&o=iNN-k55U(yy>&vltX`9sU#zF-c`N<9~bZz>Kw zp8NEm=yD7n2Nd`?u)@At{rvvQ3^&W-{!R=QLA`qq!WVS%D3zsD^pb+gS?s~1zN*sK zT%-#KN9>*B@!+%9w7f-;eN+4Iu<$R-+vaie{7ylWpnpN&5|<-jC87OC*RV2tee!p! zA4x=Sf^J=cY0x5&n3x3ev$q6%lG8!Y;d1azOzscg$=-c`8J#`ZI+EKKgy3d*-Cr>? zx0xUfVJp2}yM)L1vj@t9r*EI=m;J?VymnW>00~O{ni(qJchw; zU{N^b&=Anem3xC+>_=C7QM>Eg%=MS?iwSFzD|U+b%67w>m28do%`v54z94vO$Bi$h zu^8Z4+T={#$Y$UfJ#J#2xE1q;U-I$_=i+s4=_LT1C0cM}o)F2ib63kefATPt;En5xVt(RdXH-jorra#MPIE7tvCe!fh0F$b@kAUBc z_ql@y8t>emJ-GOwY@uUwrxIF0j^_q;BiWX9ZFuCoxh!L}1q9 z`-z1{s;eh3GCpa4+8%;;;oj}gfNnUdr|+sGilxZFv< z`o9!60dl1CzI^g_Ra(2wJ@9$dFKJ+lO-*TtOGeH;LQ7I4Yy2e8>JZLzijG`n%s9vU zO(vbE9S6OP%Pe3n4@)ng!9(9m?TbS9e4Dvpww_3j%-sZRMkz|f2sOW?FkUjtD-6_i zP|zYSwB+ga@_5BEeU~*@{@$80Kz1LY_BhHn`P6J&9s3wK`)_&-7ceMW%&X&s@RrZZ z+cO|}f3gj#xZKkq2F7=<*l*gDJ+U(Uot!@blOuvOxWTF_H`JfBM=4Jz8xo;fCUN4l zR(@d7n&1$+0P)dL1PUdhx`0`-bZr`VZoTpuCj8N$J;Wu!ITrlTTaZQdb3bbiSyBhD zeHa5gKH)xkV-;VcEtM$Ar^GA7&H`=rzI0MbY&(sK9O5oJe#6oW)7%ne>OXFg;sT;r zkmT}LVxL~U=lH7Ll6xU*^eg@koBn8mo+-xQnU3-mEwmBzh!1wi49!>NQ7t?^0fu9k z3L@?ykC%L&m5p%3dhYNj<8`~sZ?x)?E`g-!GZiU&_Gnq_*j7K!kG37HzSE2QB$XRL zkTG|)&-41i=3l(HI01}8dhG9sF3;}KHcRlnswY$%c=pQi5%J_h) zl-H&CK%jaKsW&a~*k9tf(mUjSM5R9QbbyV!(l~n)RkY`)yfk&mPhI#b#6PyG&G5mQDWS|D$9Txff*~E-vvmi2MH)NhUThdVb5AL8(&9D9mVk2=qxzI z<t| z(CvC~6U1Z)cn2VH_tRfeK7nixQ8(PAgZm@!MF~Gex+EA6lh`o;Y|?GoC&*KemPQDH zmQWueh+~y5#nOt=Dgg=1VI>-s;TIwcizJ&tJ&-)qk@Z0+ujq^YV$F4c($BvI0G70G zjxjWo!9ENFDKocX-s!*m{Y*JYecEBCoIm>uQ@9aiVIp13ozw~ny!W?ql71uX5^A5Ue+ zX!J~i6Gi@v$JSb1`Sd>d1ybTNb>&dKbBW%5>YmJ+uAE-x=b^r&dT`Z@;$l22^PCz? z?^_zfU_^0GWTAM;R{B7|#S@z>iR$v4@#a{V5ICUBbI$QM?4=Qm`EGdIB#)oUK?9q` z%}$OA&Yc|%2*1M6LysfDPx$I3v2qK6aQw9O6ySbwXGnxg%fBU$L4Im-aF07^oil3~ z82REBnjWwleKw^^ta6Si7CXwz)w2t%&)*tG@1AxzlXWNhny3kncj1d1_ep6U8c{k6a!Ba8yyIX zWSx$Qn0{?FrNju5fv0XxtQUgQ?e+v~YnGC(R;_@N%^Eu=EI3*%sP;*KfJt{spOj*U z!L>!@mUsuwmUiAtguA{QwG8FIi@tQ;@6?^uw+8)W0oL*fe%cnT6#>>pS*FZ3NtXUK z$y-pQ3Y###D;mm%u+5fwDF7>`3%K>ske?nKm6mH-r|y1l1A7Vo zgkaU@u5E4hAxJ0ldN@;e)A$w3ElibxnG$x8yqOU(oIrq z&Sk4(0mv^;UN8%regA3sYZqhTP(ByrV_?czDxL)ch`o+8(sA_o;I`=kp;e)q`fJ51S*B5$tlujj<-NH_YEWh&Cxw~ zqR7=l#66sFKRoxl$mF<`ql>U`h5(?1BHy=I%n*~2NfV#HTu<-})N zMdeL8`?>;P2Pl;drfeaW`RajWOWvU)ZLH?$jw7kf{z6>}?deB{;3gT=7{z8x(C5c) zaVZ>~TRMQv0iBkQ@L@Z}^(G8$Z03n#5{uPx44FKG4!n=8@taM(_FiYcBue z)WL+gb^>r-4m-Z+@{4hH;5;^x;=L}JJ51UB%-i4lVDkChC|BbR!9Nz%c zRNqaSqD1J{7OUAJOsi=;ws1JJ(%1Y#n&R)eF&%Q~-L>Qva@%ky>aj}yZd;UD>=N3$ zQJ_-6z+!5>YW0a&t)!_QtHT4{*qV)E-Y$igi*s`h5o+O9yWQI-zTBtMbmi(FkX|}| zLiJ7+$x$)Rn`K>XOO(qHNfX;X%Kdg55*a#b-)xlUel106b6MBUn*>4~OH0$(iEAB2YQ%PNz{%U?cca0$(=U8KwgS(BBo}d1 zS!!h9M(!*6X<_{sS=tJno6HQ}0#N^}ehD!kGX0MTb4rl#aI%A*m8V0AD->gPC^1lr z7?Rr?PIEiv zzs)dx{359^&bbjpk@~%4A1oAfu zA#(Z2PdVC0Jq_2!(rQ?Ld=DW2k;3^-vS6I0CW*W2H;OMY+sm((XTZA5HihJm#?ak) zjuj4#+~MuE=(_HOVT5Q{1I@=+{b-8;osG`wXYk>d z|JwgLr>k3jsLkCnqS-A922VVRJMbEYmc9k@h7o zNRBHJeaVil%T4nmxx_AY;)3THmBkMmxdX4gz;WTNTLdO8d^a81JO8DzNa7@DXCf5X zA#A!{d6$qowUp%PtGP6*Ms5jud>BQHn@L?BVe1LMAV44)t*9oju@gvdNQQWQ0{&ddoS#*@`fK$80>+kh3rMj)`LQS!|&9l z43znzEHmBwWwjAKjkuuW{7`aIr91q|4H8KR! z%^+pVnHaZz+8K`SC~dT%-ztZB%#0J^K_F$LGKylMzp0VdPn8z@l)W=0)Te2h~|H5iX!K%O!5~cm}z8{D>}b5;yvq5mL@W+oj5aS$45q{hVAjsSG|w z44D|&ax%+lSK?I@$Zk&MPm!{<0H3=n?aLA}Hr1qf?IV(jUtGgh^a>%u)QaM1`Hc;1 z?cX#aMf4n@*#RsK{IPhX$>Hxug4BuuxukXOO3HdRZsMnE3fGT;!_n$^6pCK~@O?tIJzvcDRi1stmZwf1TB`m;eeh$uBQ>(FjksI4hPuq+MNfL78eZh_ zlggYg!TtcBwO%^U#TN9V z=YO4-`#sKT5Uf8_J+<~W$Fo8R>P>BvK6Oq$*PkrwlA&~)gk$Y%?(`YcPebg_^OEH^2;Hm0=wNd5pvFnq{x%8LXE{iq6CS z`g_-7qXZ-a2b*mxgiTVY7+AFh+?uOdr=b;b9WWBS7(L#y=WwgR=lZoKDF~UkmgLeg zg;3H3(TH$G7p_dadl{i1*duxGLRm%RgnB9g?nj~$$Vu|gQv1{1Q{|~KUy+-%^pWvKJX4w}DoHq%6S+Ru!`)s(R z?h@b`X4xyvKwY8sgj)n5-2#;i!ihb)cu0 General > Advanced**. -1. In the Remove project section, click the **Remove project** button. +1. In the "Delete project" section, click the **Delete project** button. 1. Confirm the action when asked to. This action: -- Removes a project including all associated resources (issues, merge requests etc). +- Deletes a project including all associated resources (issues, merge requests etc). - From [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) on [Premium or Silver](https://about.gitlab.com/pricing/) or higher tiers, group admins can [configure](../../group/index.md#enabling-delayed-project-removal-premium) projects within a group to be deleted after a delayed period. diff --git a/lib/api/projects.rb b/lib/api/projects.rb index d24dab63bd9..abbdb11a3f7 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -448,7 +448,7 @@ module API .execute.map { |lang| [lang.name, lang.share] }.to_h end - desc 'Remove a project' + desc 'Delete a project' delete ":id" do authorize! :remove_project, user_project diff --git a/lib/gitlab/service_desk_email.rb b/lib/gitlab/service_desk_email.rb index f8dba82cb40..52da10eff3e 100644 --- a/lib/gitlab/service_desk_email.rb +++ b/lib/gitlab/service_desk_email.rb @@ -17,6 +17,12 @@ module Gitlab def config Gitlab.config.service_desk_email end + + def address_for_key(key) + return if config.address.blank? + + config.address.sub(Gitlab::IncomingEmail::WILDCARD_PLACEHOLDER, key) + end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d47d472479f..3b582ad72e1 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -7884,15 +7884,27 @@ msgstr "" msgid "Deleted" msgstr "" +msgid "Deleted Projects" +msgstr "" + msgid "Deleted chat nickname: %{chat_name}!" msgstr "" msgid "Deleted in this version" msgstr "" +msgid "Deleted projects" +msgstr "" + +msgid "Deleted projects cannot be restored!" +msgstr "" + msgid "Deleting" msgstr "" +msgid "Deleting a project places it into a read-only state until %{date}, at which point the project will be permanently deleted. Are you ABSOLUTELY sure?" +msgstr "" + msgid "Deleting the license failed." msgstr "" @@ -7902,6 +7914,9 @@ msgstr "" msgid "Deleting the license failed. You are not permitted to perform this action." msgstr "" +msgid "Deleting the project will delete its repository and all related resources including issues, merge requests etc." +msgstr "" + msgid "Deletion pending. This project will be removed on %{date}. Repository and other project resources are read-only." msgstr "" @@ -20240,9 +20255,6 @@ msgstr "" msgid "Remove priority" msgstr "" -msgid "Remove project" -msgstr "" - msgid "Remove secondary node" msgstr "" @@ -20276,9 +20288,6 @@ msgstr "" msgid "Removed %{type} with id %{id}" msgstr "" -msgid "Removed Projects" -msgstr "" - msgid "Removed all labels." msgstr "" @@ -20291,12 +20300,6 @@ msgstr "" msgid "Removed parent epic %{epic_ref}." msgstr "" -msgid "Removed projects" -msgstr "" - -msgid "Removed projects cannot be restored!" -msgstr "" - msgid "Removed spent time." msgstr "" @@ -20345,15 +20348,9 @@ msgstr "" msgid "Removes time estimate." msgstr "" -msgid "Removing a project places it into a read-only state until %{date}, at which point the project will be permanently removed. Are you ABSOLUTELY sure?" -msgstr "" - msgid "Removing license…" msgstr "" -msgid "Removing the project will delete its repository and all related resources including issues, merge requests etc." -msgstr "" - msgid "Removing this group also removes all child projects, including archived projects, and their resources." msgstr "" @@ -22552,6 +22549,9 @@ msgstr "" msgid "SnippetsEmptyState|There are no snippets to show." msgstr "" +msgid "Snippets|Delete file" +msgstr "" + msgid "Snippets|Description (optional)" msgstr "" @@ -22744,10 +22744,10 @@ msgstr "" msgid "Something went wrong, unable to add projects to dashboard" msgstr "" -msgid "Something went wrong, unable to get projects" +msgid "Something went wrong, unable to delete project" msgstr "" -msgid "Something went wrong, unable to remove project" +msgid "Something went wrong, unable to get projects" msgstr "" msgid "Something went wrong, unable to search projects" @@ -27657,10 +27657,10 @@ msgstr "" msgid "You are connected to the Prometheus server, but there is currently no data to display." msgstr "" -msgid "You are going to remove %{group_name}, this will also remove all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?" +msgid "You are going to delete %{project_full_name}. Deleted projects CANNOT be restored! Are you ABSOLUTELY sure?" msgstr "" -msgid "You are going to remove %{project_full_name}. Removed project CANNOT be restored! Are you ABSOLUTELY sure?" +msgid "You are going to remove %{group_name}, this will also delete all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?" msgstr "" msgid "You are going to remove the fork relationship from %{project_full_name}. Are you ABSOLUTELY sure?" diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb index 878794bd897..c0a76a55e9a 100644 --- a/spec/features/projects/settings/operations_settings_spec.rb +++ b/spec/features/projects/settings/operations_settings_spec.rb @@ -35,7 +35,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do end it 'renders form for incident management' do - expect(page).to have_selector('h3', text: 'Incidents') + expect(page).to have_selector('h4', text: 'Incidents') end it 'sets correct default values' do diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index bf3f3eeb1a2..3577498c3b4 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -254,13 +254,13 @@ RSpec.describe 'Project' do end it 'focuses on the confirmation field' do - click_button 'Remove project' + click_button 'Delete project' expect(page).to have_selector '#confirm_name_input:focus' end - it 'removes a project', :sidekiq_might_not_need_inline do - expect { remove_with_confirm('Remove project', "Delete #{project.full_name}", 'Yes, delete project') }.to change { Project.count }.by(-1) + it 'deletes a project', :sidekiq_might_not_need_inline do + expect { remove_with_confirm('Delete project', "Delete #{project.full_name}", 'Yes, delete project') }.to change { Project.count }.by(-1) expect(page).to have_content "Project '#{project.full_name}' is in the process of being deleted." expect(Project.all.count).to be_zero expect(project.issues).to be_empty diff --git a/spec/frontend/blob/components/__snapshots__/blob_edit_header_spec.js.snap b/spec/frontend/blob/components/__snapshots__/blob_edit_header_spec.js.snap index 1e639f91797..a5690844053 100644 --- a/spec/frontend/blob/components/__snapshots__/blob_edit_header_spec.js.snap +++ b/spec/frontend/blob/components/__snapshots__/blob_edit_header_spec.js.snap @@ -4,13 +4,18 @@ exports[`Blob Header Editing rendering matches the snapshot 1`] = `
    - +
    + + + +
    `; diff --git a/spec/frontend/blob/components/blob_edit_header_spec.js b/spec/frontend/blob/components/blob_edit_header_spec.js index db7d7d7d48d..7c23e8dae59 100644 --- a/spec/frontend/blob/components/blob_edit_header_spec.js +++ b/spec/frontend/blob/components/blob_edit_header_spec.js @@ -1,18 +1,21 @@ import { shallowMount } from '@vue/test-utils'; import BlobEditHeader from '~/blob/components/blob_edit_header.vue'; -import { GlFormInput } from '@gitlab/ui'; +import { GlFormInput, GlButton } from '@gitlab/ui'; describe('Blob Header Editing', () => { let wrapper; const value = 'foo.md'; - function createComponent() { + const createComponent = (props = {}) => { wrapper = shallowMount(BlobEditHeader, { propsData: { value, + ...props, }, }); - } + }; + const findDeleteButton = () => + wrapper.findAll(GlButton).wrappers.find(x => x.text() === 'Delete file'); beforeEach(() => { createComponent(); @@ -30,6 +33,10 @@ describe('Blob Header Editing', () => { it('contains a form input field', () => { expect(wrapper.contains(GlFormInput)).toBe(true); }); + + it('does not show delete button', () => { + expect(findDeleteButton()).toBeUndefined(); + }); }); describe('functionality', () => { @@ -47,4 +54,35 @@ describe('Blob Header Editing', () => { }); }); }); + + describe.each` + props | expectedDisabled + ${{ showDelete: true }} | ${true} + ${{ showDelete: true, canDelete: true }} | ${false} + `('with $props', ({ props, expectedDisabled }) => { + beforeEach(() => { + createComponent(props); + }); + + it(`shows delete button (disabled=${expectedDisabled})`, () => { + const deleteButton = findDeleteButton(); + + expect(deleteButton.exists()).toBe(true); + expect(deleteButton.props('disabled')).toBe(expectedDisabled); + }); + }); + + describe('with delete button', () => { + beforeEach(() => { + createComponent({ showDelete: true, canDelete: true }); + }); + + it('emits delete when clicked', () => { + expect(wrapper.emitted().delete).toBeUndefined(); + + findDeleteButton().vm.$emit('click'); + + expect(wrapper.emitted().delete).toEqual([[]]); + }); + }); }); diff --git a/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap b/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap index 5f355ee8261..31978f8da64 100644 --- a/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap +++ b/spec/frontend/incidents_settings/components/__snapshots__/incidents_settings_tabs_spec.js.snap @@ -9,13 +9,13 @@ exports[`IncidentsSettingTabs should render the component 1`] = `
    -

    Incidents -

    + - Remove project + Delete project - Remove project + Delete project - + -
    - - - -
    +
    `; diff --git a/spec/frontend/snippets/components/edit_spec.js b/spec/frontend/snippets/components/edit_spec.js index 4eb624ebf34..8fdaaf3a39b 100644 --- a/spec/frontend/snippets/components/edit_spec.js +++ b/spec/frontend/snippets/components/edit_spec.js @@ -7,7 +7,7 @@ import { redirectTo } from '~/lib/utils/url_utility'; import SnippetEditApp from '~/snippets/components/edit.vue'; import SnippetDescriptionEdit from '~/snippets/components/snippet_description_edit.vue'; import SnippetVisibilityEdit from '~/snippets/components/snippet_visibility_edit.vue'; -import SnippetBlobEdit from '~/snippets/components/snippet_blob_edit.vue'; +import SnippetBlobActionsEdit from '~/snippets/components/snippet_blob_actions_edit.vue'; import TitleField from '~/vue_shared/components/form/title.vue'; import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue'; import { SNIPPET_CREATE_MUTATION_ERROR, SNIPPET_UPDATE_MUTATION_ERROR } from '~/snippets/constants'; @@ -141,7 +141,7 @@ describe('Snippet Edit app', () => { expect(wrapper.contains(TitleField)).toBe(true); expect(wrapper.contains(SnippetDescriptionEdit)).toBe(true); - expect(wrapper.contains(SnippetBlobEdit)).toBe(true); + expect(wrapper.contains(SnippetBlobActionsEdit)).toBe(true); expect(wrapper.contains(SnippetVisibilityEdit)).toBe(true); expect(wrapper.contains(FormFooterActions)).toBe(true); }); diff --git a/spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js b/spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js new file mode 100644 index 00000000000..a7b4f509e50 --- /dev/null +++ b/spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js @@ -0,0 +1,59 @@ +import { shallowMount } from '@vue/test-utils'; +import SnippetBlobActionsEdit from '~/snippets/components/snippet_blob_actions_edit.vue'; +import SnippetBlobEdit from '~/snippets/components/snippet_blob_edit.vue'; + +const TEST_BLOBS = [ + { name: 'foo', content: 'abc', rawPath: 'test/raw' }, + { name: 'bar', content: 'def', rawPath: 'test/raw' }, +]; +const TEST_EVENT = 'blob-update'; + +describe('snippets/components/snippet_blob_actions_edit', () => { + let onEvent; + let wrapper; + + const createComponent = (props = {}) => { + wrapper = shallowMount(SnippetBlobActionsEdit, { + propsData: { + blobs: [], + ...props, + }, + listeners: { + [TEST_EVENT]: onEvent, + }, + }); + }; + const findBlobEdit = () => wrapper.find(SnippetBlobEdit); + const findBlobEditData = () => wrapper.findAll(SnippetBlobEdit).wrappers.map(x => x.props()); + + beforeEach(() => { + onEvent = jest.fn(); + }); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe.each` + props | expectedData + ${{}} | ${[{ blob: null }]} + ${{ blobs: TEST_BLOBS }} | ${TEST_BLOBS.map(blob => ({ blob }))} + `('with $props', ({ props, expectedData }) => { + beforeEach(() => { + createComponent(props); + }); + + it('renders blob edit', () => { + expect(findBlobEditData()).toEqual(expectedData); + }); + + it('emits event', () => { + expect(onEvent).not.toHaveBeenCalled(); + + findBlobEdit().vm.$emit('blob-update', TEST_BLOBS[0]); + + expect(onEvent).toHaveBeenCalledWith(TEST_BLOBS[0]); + }); + }); +}); diff --git a/spec/frontend/snippets/utils/blob_spec.js b/spec/frontend/snippets/utils/blob_spec.js new file mode 100644 index 00000000000..ba39b9fec36 --- /dev/null +++ b/spec/frontend/snippets/utils/blob_spec.js @@ -0,0 +1,134 @@ +import { cloneDeep } from 'lodash'; +import { + SNIPPET_BLOB_ACTION_CREATE, + SNIPPET_BLOB_ACTION_UPDATE, + SNIPPET_BLOB_ACTION_MOVE, + SNIPPET_BLOB_ACTION_DELETE, +} from '~/snippets/constants'; +import { decorateBlob, createBlob, diffAll } from '~/snippets/utils/blob'; + +jest.mock('lodash/uniqueId', () => arg => `${arg}fakeUniqueId`); + +const TEST_RAW_BLOB = { + rawPath: '/test/blob/7/raw', +}; +const CONTENT_1 = 'Lorem ipsum dolar\nSit amit\n\nGoodbye!\n'; +const CONTENT_2 = 'Lorem ipsum dolar sit amit.\n\nGoodbye!\n'; + +describe('~/snippets/utils/blob', () => { + describe('decorateBlob', () => { + it('should decorate the given object with local blob properties', () => { + const orig = cloneDeep(TEST_RAW_BLOB); + + expect(decorateBlob(orig)).toEqual({ + ...TEST_RAW_BLOB, + id: 'blob_local_fakeUniqueId', + isLoaded: false, + content: '', + }); + }); + }); + + describe('createBlob', () => { + it('should create an empty local blob', () => { + expect(createBlob()).toEqual({ + id: 'blob_local_fakeUniqueId', + isLoaded: true, + content: '', + path: '', + }); + }); + }); + + describe('diffAll', () => { + // This object contains entries that contain an expected "diff" and the `id` + // or `origContent` that should be used to generate the expected diff. + const testEntries = { + created: { + id: 'blob_1', + diff: { + action: SNIPPET_BLOB_ACTION_CREATE, + filePath: '/new/file', + previousPath: '/new/file', + content: CONTENT_1, + }, + }, + deleted: { + id: 'blob_2', + diff: { + action: SNIPPET_BLOB_ACTION_DELETE, + filePath: '/src/delete/me', + previousPath: '/src/delete/me', + content: CONTENT_1, + }, + }, + updated: { + id: 'blob_3', + origContent: CONTENT_1, + diff: { + action: SNIPPET_BLOB_ACTION_UPDATE, + filePath: '/lorem.md', + previousPath: '/lorem.md', + content: CONTENT_2, + }, + }, + renamed: { + id: 'blob_4', + diff: { + action: SNIPPET_BLOB_ACTION_MOVE, + filePath: '/dolar.md', + previousPath: '/ipsum.md', + content: CONTENT_1, + }, + }, + renamedAndUpdated: { + id: 'blob_5', + origContent: CONTENT_1, + diff: { + action: SNIPPET_BLOB_ACTION_MOVE, + filePath: '/sit.md', + previousPath: '/sit/amit.md', + content: CONTENT_2, + }, + }, + }; + const createBlobsFromTestEntries = (entries, isOrig = false) => + entries.reduce( + (acc, { id, diff, origContent }) => + Object.assign(acc, { + [id]: { + id, + content: isOrig && origContent ? origContent : diff.content, + path: isOrig ? diff.previousPath : diff.filePath, + }, + }), + {}, + ); + + it('should create diff from original files', () => { + const origBlobs = createBlobsFromTestEntries( + [ + testEntries.deleted, + testEntries.updated, + testEntries.renamed, + testEntries.renamedAndUpdated, + ], + true, + ); + const blobs = createBlobsFromTestEntries([ + testEntries.created, + testEntries.updated, + testEntries.renamed, + testEntries.renamedAndUpdated, + ]); + + expect(diffAll(blobs, origBlobs)).toEqual([ + testEntries.deleted.diff, + testEntries.created.diff, + testEntries.updated.diff, + testEntries.renamed.diff, + testEntries.renamedAndUpdated.diff, + ]); + }); + }); +}); diff --git a/spec/lib/gitlab/service_desk_email_spec.rb b/spec/lib/gitlab/service_desk_email_spec.rb index 23e2b2ff3cf..67a1f07eec6 100644 --- a/spec/lib/gitlab/service_desk_email_spec.rb +++ b/spec/lib/gitlab/service_desk_email_spec.rb @@ -56,4 +56,26 @@ RSpec.describe Gitlab::ServiceDeskEmail do end end end + + describe '.address_for_key' do + context 'when service desk address is set' do + before do + stub_service_desk_email_setting(address: 'address+%{key}@example.com') + end + + it 'returns address' do + expect(described_class.address_for_key('foo')).to eq('address+foo@example.com') + end + end + + context 'when service desk address is not set' do + before do + stub_service_desk_email_setting(address: nil) + end + + it 'returns nil' do + expect(described_class.key_from_address('foo')).to be_nil + end + end + end end diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index 1fc4f098661..78980f8cdab 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -328,9 +328,11 @@ RSpec.describe PagesDomain do end describe '#update_daemon' do + let_it_be(:project) { create(:project).tap(&:mark_pages_as_deployed) } + context 'when usage is serverless' do it 'does not call the UpdatePagesConfigurationService' do - expect(Projects::UpdatePagesConfigurationService).not_to receive(:new) + expect(PagesUpdateConfigurationWorker).not_to receive(:perform_async) create(:pages_domain, usage: :serverless) end @@ -352,12 +354,30 @@ RSpec.describe PagesDomain do domain.destroy! end - it 'delegates to Projects::UpdatePagesConfigurationService' do + it 'delegates to Projects::UpdatePagesConfigurationService when not running async' do + stub_feature_flags(async_update_pages_config: false) + service = instance_double('Projects::UpdatePagesConfigurationService') expect(Projects::UpdatePagesConfigurationService).to receive(:new) { service } expect(service).to receive(:execute) - create(:pages_domain) + create(:pages_domain, project: project) + end + + it "schedules a PagesUpdateConfigurationWorker" do + expect(PagesUpdateConfigurationWorker).to receive(:perform_async).with(project.id) + + create(:pages_domain, project: project) + end + + context "when the pages aren't deployed" do + let_it_be(:project) { create(:project).tap(&:mark_pages_as_not_deployed) } + + it "does not schedule a PagesUpdateConfigurationWorker" do + expect(PagesUpdateConfigurationWorker).not_to receive(:perform_async).with(project.id) + + create(:pages_domain, project: project) + end end context 'configuration updates when attributes change' do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 0caaa5394fa..f967e76c709 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1450,16 +1450,69 @@ RSpec.describe Project do end describe '#service_desk_address' do - let_it_be(:project) { create(:project, service_desk_enabled: true) } + let_it_be(:project, reload: true) { create(:project, service_desk_enabled: true) } - before do - allow(Gitlab::ServiceDesk).to receive(:enabled?).and_return(true) - allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true) - allow(Gitlab.config.incoming_email).to receive(:address).and_return("test+%{key}@mail.com") + subject { project.service_desk_address } + + shared_examples 'with incoming email address' do + context 'when incoming email is enabled' do + before do + config = double(enabled: true, address: 'test+%{key}@mail.com') + allow(::Gitlab.config).to receive(:incoming_email).and_return(config) + end + + it 'uses project full path as service desk address key' do + expect(project.service_desk_address).to eq("test+#{project.full_path_slug}-#{project.project_id}-issue-@mail.com") + end + end + + context 'when incoming email is disabled' do + before do + config = double(enabled: false) + allow(::Gitlab.config).to receive(:incoming_email).and_return(config) + end + + it 'uses project full path as service desk address key' do + expect(project.service_desk_address).to be_nil + end + end end - it 'uses project full path as service desk address key' do - expect(project.service_desk_address).to eq("test+#{project.full_path_slug}-#{project.project_id}-issue-@mail.com") + context 'when service_desk_email is disabled' do + before do + allow(::Gitlab::ServiceDeskEmail).to receive(:enabled?).and_return(false) + end + + it_behaves_like 'with incoming email address' + end + + context 'when service_desk_email is enabled' do + before do + config = double(enabled: true, address: 'foo+%{key}@bar.com') + allow(::Gitlab::ServiceDeskEmail).to receive(:config).and_return(config) + end + + context 'when service_desk_custom_address flag is enabled' do + before do + stub_feature_flags(service_desk_custom_address: true) + end + + it 'returns custom address when project_key is set' do + create(:service_desk_setting, project: project, project_key: 'key1') + + expect(subject).to eq("foo+#{project.full_path_slug}-key1@bar.com") + end + + it_behaves_like 'with incoming email address' + end + + context 'when service_desk_custom_address flag is disabled' do + before do + stub_feature_flags(service_desk_custom_address: false) + end + + it_behaves_like 'with incoming email address' + end end end diff --git a/spec/services/incident_management/create_issue_service_spec.rb b/spec/services/incident_management/create_issue_service_spec.rb deleted file mode 100644 index 6c77779fd92..00000000000 --- a/spec/services/incident_management/create_issue_service_spec.rb +++ /dev/null @@ -1,240 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe IncidentManagement::CreateIssueService do - let(:project) { create(:project, :repository, :private) } - let_it_be(:user) { User.alert_bot } - let(:alert_starts_at) { Time.current } - let(:alert_title) { 'TITLE' } - let(:alert_annotations) { { title: alert_title } } - - let(:alert_payload) do - build_alert_payload( - annotations: alert_annotations, - starts_at: alert_starts_at - ) - end - - let(:alert) { create(:alert_management_alert, :prometheus, project: project, payload: alert_payload) } - let(:service) { described_class.new(project, alert) } - - let(:alert_presenter) do - Gitlab::Alerting::Alert.for_alert_management_alert(project: project, alert: alert).present - end - - let!(:setting) do - create(:project_incident_management_setting, project: project) - end - - subject(:execute) { service.execute } - - context 'when create_issue enabled' do - let(:issue) { execute.payload[:issue] } - - before do - setting.update!(create_issue: true) - end - - context 'without issue_template_content' do - it 'creates an issue with alert summary only' do - expect(execute).to be_success - - expect(issue.author).to eq(user) - expect(issue.title).to eq(alert_title) - expect(issue.description).to include(alert_presenter.issue_summary_markdown.strip) - expect(separator_count(issue.description)).to eq(0) - end - end - - context 'with erroneous issue service' do - let(:invalid_issue) do - build(:issue, project: project, title: nil).tap(&:valid?) - end - - let(:issue_error) { invalid_issue.errors.full_messages.to_sentence } - - it 'returns and logs the issue error' do - expect_next_instance_of(Issues::CreateService) do |issue_service| - expect(issue_service).to receive(:execute).and_return(invalid_issue) - end - - expect(service) - .to receive(:log_error) - .with(error_message(issue_error)) - - expect(execute).to be_error - expect(execute.message).to eq(issue_error) - end - end - - shared_examples 'GFM template' do - context 'plain content' do - let(:template_content) { 'some content' } - - it 'creates an issue appending issue template' do - expect(execute).to be_success - - expect(issue.description).to include(alert_presenter.issue_summary_markdown) - expect(separator_count(issue.description)).to eq(1) - expect(issue.description).to include(template_content) - end - end - - context 'quick actions' do - let(:user) { create(:user) } - let(:plain_text) { 'some content' } - - let(:template_content) do - <<~CONTENT - #{plain_text} - /due tomorrow - /assign @#{user.username} - CONTENT - end - - before do - project.add_maintainer(user) - end - - it 'creates an issue interpreting quick actions' do - expect(execute).to be_success - - expect(issue.description).to include(plain_text) - expect(issue.due_date).to be_present - expect(issue.assignees).to eq([user]) - end - end - end - - context 'with gitlab_incident_markdown' do - let(:alert_annotations) do - { title: alert_title, gitlab_incident_markdown: template_content } - end - - it_behaves_like 'GFM template' - end - - context 'with issue_template_content' do - before do - create_issue_template('bug', template_content) - setting.update!(issue_template_key: 'bug') - end - - it_behaves_like 'GFM template' - - context 'and gitlab_incident_markdown' do - let(:template_content) { 'plain text'} - let(:alt_template) { 'alternate text' } - let(:alert_annotations) do - { title: alert_title, gitlab_incident_markdown: alt_template } - end - - it 'includes both templates' do - expect(execute).to be_success - - expect(issue.description).to include(alert_presenter.issue_summary_markdown) - expect(issue.description).to include(template_content) - expect(issue.description).to include(alt_template) - expect(separator_count(issue.description)).to eq(2) - end - end - - private - - def create_issue_template(name, content) - project.repository.create_file( - project.creator, - ".gitlab/issue_templates/#{name}.md", - content, - message: 'message', - branch_name: 'master' - ) - end - end - - context 'with gitlab alert' do - let(:gitlab_alert) { create(:prometheus_alert, project: project) } - - before do - alert_payload['labels'] = { - 'gitlab_alert_id' => gitlab_alert.prometheus_metric_id.to_s - } - end - - it 'creates an issue' do - query_title = "#{gitlab_alert.title} #{gitlab_alert.computed_operator} #{gitlab_alert.threshold}" - - expect(execute).to be_success - - expect(issue.author).to eq(user) - expect(issue.title).to eq(alert_presenter.full_title) - expect(issue.title).to include(gitlab_alert.environment.name) - expect(issue.title).to include(query_title) - expect(issue.title).to include('for 5 minutes') - expect(issue.description).to include(alert_presenter.issue_summary_markdown.strip) - expect(separator_count(issue.description)).to eq(0) - end - end - - describe 'with invalid alert payload' do - shared_examples 'invalid alert' do - it 'does not create an issue' do - expect(service) - .to receive(:log_error) - .with(error_message('invalid alert')) - - expect(execute).to be_error - expect(execute.message).to eq('invalid alert') - end - end - - context 'without title' do - let(:alert_annotations) { {} } - - it_behaves_like 'invalid alert' - end - - context 'without startsAt' do - let(:alert_starts_at) { nil } - - it_behaves_like 'invalid alert' - end - end - end - - context 'when create_issue disabled' do - before do - setting.update!(create_issue: false) - end - - it 'returns an error' do - expect(service) - .to receive(:log_error) - .with(error_message('setting disabled')) - - expect(execute).to be_error - expect(execute.message).to eq('setting disabled') - end - end - - private - - def build_alert_payload(annotations: {}, starts_at: Time.current) - { - 'annotations' => annotations.stringify_keys - }.tap do |payload| - payload['startsAt'] = starts_at.rfc3339 if starts_at - end - end - - def error_message(message) - %{Cannot create incident issue for "#{project.full_name}": #{message}} - end - - def separator_count(text) - summary_separator = "\n\n---\n\n" - - text.scan(summary_separator).size - end -end diff --git a/spec/services/service_desk_settings/update_service_spec.rb b/spec/services/service_desk_settings/update_service_spec.rb index 8b920d536b4..fbef587365d 100644 --- a/spec/services/service_desk_settings/update_service_spec.rb +++ b/spec/services/service_desk_settings/update_service_spec.rb @@ -31,6 +31,17 @@ RSpec.describe ServiceDeskSettings::UpdateService do end end + context 'when project_key is an empty string' do + let(:params) { { project_key: '' } } + + it 'sets nil project_key' do + result = described_class.new(settings.project, user, params).execute + + expect(result[:status]).to eq :success + expect(settings.reload.project_key).to be_nil + end + end + context 'with invalid params' do let(:params) { { outgoing_name: 'x' * 256 } } diff --git a/spec/workers/incident_management/process_alert_worker_spec.rb b/spec/workers/incident_management/process_alert_worker_spec.rb index 9aac253c767..20ab283b49b 100644 --- a/spec/workers/incident_management/process_alert_worker_spec.rb +++ b/spec/workers/incident_management/process_alert_worker_spec.rb @@ -18,15 +18,15 @@ RSpec.describe IncidentManagement::ProcessAlertWorker do before do allow(Gitlab::AppLogger).to receive(:warn).and_call_original - allow(IncidentManagement::CreateIssueService) - .to receive(:new).with(alert.project, alert) + allow(AlertManagement::CreateAlertIssueService) + .to receive(:new).with(alert, User.alert_bot) .and_call_original end shared_examples 'creates issue successfully' do it 'creates an issue' do - expect(IncidentManagement::CreateIssueService) - .to receive(:new).with(alert.project, alert) + expect(AlertManagement::CreateAlertIssueService) + .to receive(:new).with(alert, User.alert_bot) expect { subject }.to change { Issue.count }.by(1) end @@ -58,10 +58,10 @@ RSpec.describe IncidentManagement::ProcessAlertWorker do subject expect(Gitlab::AppLogger).to have_received(:warn).with( - message: 'Cannot link an Issue with Alert', + message: 'Cannot process an Incident', issue_id: created_issue.id, alert_id: alert.id, - alert_errors: { hosts: ['hosts array is over 255 chars'] } + errors: 'Hosts hosts array is over 255 chars' ) end end @@ -80,7 +80,7 @@ RSpec.describe IncidentManagement::ProcessAlertWorker do subject { described_class.new.perform(nil, nil, invalid_alert_id) } it 'does not create issues' do - expect(IncidentManagement::CreateIssueService).not_to receive(:new) + expect(AlertManagement::CreateAlertIssueService).not_to receive(:new) expect { subject }.not_to change { Issue.count } end diff --git a/vendor/gitignore/C++.gitignore b/vendor/gitignore/C++.gitignore old mode 100644 new mode 100755 diff --git a/vendor/gitignore/Clojure.gitignore b/vendor/gitignore/Clojure.gitignore deleted file mode 100644 index a4cb69a32cc..00000000000 --- a/vendor/gitignore/Clojure.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -pom.xml -pom.xml.asc -*.jar -*.class -/lib/ -/classes/ -/target/ -/checkouts/ -.lein-deps-sum -.lein-repl-history -.lein-plugins/ -.lein-failures -.nrepl-port -.cpcache/ diff --git a/vendor/gitignore/Clojure.gitignore b/vendor/gitignore/Clojure.gitignore new file mode 120000 index 00000000000..7657a270c45 --- /dev/null +++ b/vendor/gitignore/Clojure.gitignore @@ -0,0 +1 @@ +Leiningen.gitignore \ No newline at end of file diff --git a/vendor/gitignore/Fortran.gitignore b/vendor/gitignore/Fortran.gitignore deleted file mode 100644 index 259148fa18f..00000000000 --- a/vendor/gitignore/Fortran.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app diff --git a/vendor/gitignore/Fortran.gitignore b/vendor/gitignore/Fortran.gitignore new file mode 120000 index 00000000000..5daba98a3e6 --- /dev/null +++ b/vendor/gitignore/Fortran.gitignore @@ -0,0 +1 @@ +C++.gitignore \ No newline at end of file diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore old mode 100644 new mode 100755 diff --git a/vendor/gitignore/Kotlin.gitignore b/vendor/gitignore/Kotlin.gitignore deleted file mode 100644 index a1c2a238a96..00000000000 --- a/vendor/gitignore/Kotlin.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# Compiled class file -*.class - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* diff --git a/vendor/gitignore/Kotlin.gitignore b/vendor/gitignore/Kotlin.gitignore new file mode 120000 index 00000000000..c48376eebcf --- /dev/null +++ b/vendor/gitignore/Kotlin.gitignore @@ -0,0 +1 @@ +Java.gitignore \ No newline at end of file