Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/CODEOWNERS1
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock6
-rw-r--r--app/assets/javascripts/monitoring/stores/actions.js2
-rw-r--r--app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue15
-rw-r--r--app/controllers/concerns/snippets_actions.rb1
-rw-r--r--app/controllers/concerns/snippets_sort.rb9
-rw-r--r--app/controllers/dashboard/snippets_controller.rb3
-rw-r--r--app/controllers/projects/snippets_controller.rb2
-rw-r--r--app/controllers/snippets_controller.rb2
-rw-r--r--app/finders/snippets_finder.rb10
-rw-r--r--app/policies/merge_request_policy.rb4
-rw-r--r--app/services/members/destroy_service.rb4
-rw-r--r--app/services/merge_requests/approval_service.rb10
-rw-r--r--app/services/merge_requests/remove_approval_service.rb8
-rw-r--r--app/views/notify/push_to_merge_request_email.text.haml4
-rw-r--r--changelogs/unreleased/217362-move-plan-stage-usage-activity-to-ce.yml5
-rw-r--r--changelogs/unreleased/223159-fix-dahsboard-warning-logic.yml5
-rw-r--r--changelogs/unreleased/fj-default-order-snippet-lists.yml5
-rw-r--r--changelogs/unreleased/propagate-ds-java-version-in-dependency-scanning.yml5
-rw-r--r--changelogs/unreleased/tc-fix-plain-text-commit-mails.yml5
-rw-r--r--doc/README.md2
-rw-r--r--doc/administration/feature_flags.md18
-rw-r--r--doc/administration/packages/container_registry.md2
-rw-r--r--doc/development/fe_guide/tooling.md38
-rw-r--r--doc/development/telemetry/usage_ping.md17
-rw-r--r--doc/operations/index.md1
-rw-r--r--doc/operations/metrics/dashboards/panel_types.md261
-rw-r--r--doc/operations/metrics/dashboards/yaml.md2
-rw-r--r--doc/user/permissions.md1
-rw-r--r--doc/user/project/integrations/prometheus.md256
-rw-r--r--lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml1
-rw-r--r--lib/gitlab/usage_data.rb9
-rw-r--r--package.json4
-rw-r--r--qa/qa.rb4
-rw-r--r--qa/qa/page/modal/delete_wiki.rb17
-rw-r--r--qa/qa/page/project/wiki/edit.rb9
-rw-r--r--qa/qa/page/project/wiki/show.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/jira_issue_import_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/wiki/project_based_page_deletion_spec.rb26
-rwxr-xr-xscripts/lint-doc.sh3
-rw-r--r--spec/controllers/concerns/renders_commits_spec.rb2
-rw-r--r--spec/controllers/dashboard/snippets_controller_spec.rb4
-rw-r--r--spec/controllers/projects/snippets_controller_spec.rb28
-rw-r--r--spec/controllers/snippets_controller_spec.rb6
-rw-r--r--spec/finders/snippets_finder_spec.rb16
-rw-r--r--spec/frontend/monitoring/store/actions_spec.js19
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb25
-rw-r--r--spec/policies/merge_request_policy_spec.rb1
-rw-r--r--spec/services/merge_requests/approval_service_spec.rb14
-rw-r--r--spec/services/merge_requests/remove_approval_service_spec.rb9
-rw-r--r--spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb41
-rw-r--r--spec/support/shared_examples/views/plain_text_email.rb9
-rw-r--r--spec/views/notify/push_to_merge_request_email.text.haml_spec.rb19
-rw-r--r--yarn.lock8
55 files changed, 688 insertions, 300 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 2c5169dc921..79e4d4925f1 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -13,6 +13,7 @@
/doc/development/ @marcia @mjang1
/doc/development/documentation/ @mikelewis
/doc/ci @marcel.amirault @sselhorn
+/doc/operations @aqualls @eread
/doc/user/clusters @aqualls
/doc/user/infrastructure @aqualls
/doc/user/project/clusters @aqualls
diff --git a/Gemfile b/Gemfile
index 62d6d3767ed..c1f93c78932 100644
--- a/Gemfile
+++ b/Gemfile
@@ -244,7 +244,9 @@ gem 'slack-messenger', '~> 2.3.3'
gem 'hangouts-chat', '~> 0.0.5'
# Asana integration
-gem 'asana', '~> 0.9'
+# asana 0.10.1 needs faraday 1.0
+# https://gitlab.com/gitlab-org/gitlab/-/issues/224296
+gem 'asana', '0.10.0'
# FogBugz integration
gem 'ruby-fogbugz', '~> 0.2.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index f68fd455352..03c213333df 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -76,7 +76,7 @@ GEM
apollo_upload_server (2.0.0.beta.3)
graphql (>= 1.8)
rails (>= 4.2)
- asana (0.9.3)
+ asana (0.10.0)
faraday (~> 0.9)
faraday_middleware (~> 0.9)
faraday_middleware-multi_json (~> 0.0)
@@ -304,7 +304,7 @@ GEM
multipart-post (>= 1.2, < 3)
faraday-http-cache (2.0.0)
faraday (~> 0.8)
- faraday_middleware (0.12.2)
+ faraday_middleware (0.14.0)
faraday (>= 0.7.4, < 1.0)
faraday_middleware-aws-signers-v4 (0.1.7)
aws-sdk-resources (~> 2)
@@ -1175,7 +1175,7 @@ DEPENDENCIES
addressable (~> 2.7)
akismet (~> 3.0)
apollo_upload_server (~> 2.0.0.beta3)
- asana (~> 0.9)
+ asana (= 0.10.0)
asciidoctor (~> 2.0.10)
asciidoctor-include-ext (~> 0.3.1)
asciidoctor-plantuml (~> 0.0.12)
diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js
index d74e0887c9b..cac04faae98 100644
--- a/app/assets/javascripts/monitoring/stores/actions.js
+++ b/app/assets/javascripts/monitoring/stores/actions.js
@@ -375,7 +375,7 @@ export const fetchDashboardValidationWarnings = ({ state, dispatch }) => {
})
.then(resp => resp.data?.project?.environments?.nodes?.[0]?.metricsDashboard)
.then(({ schemaValidationWarnings } = {}) => {
- const hasWarnings = schemaValidationWarnings?.length !== 0;
+ const hasWarnings = schemaValidationWarnings && schemaValidationWarnings.length !== 0;
/**
* The payload of the dispatch is a boolean, because at the moment a standard
* warning message is shown instead of the warnings the BE returns
diff --git a/app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue b/app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue
index 580cca49b5e..a7b7d597fb7 100644
--- a/app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue
+++ b/app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue
@@ -55,13 +55,22 @@ export default {
<template>
<div class="d-inline-block">
- <button v-gl-modal="modalId" type="button" class="btn btn-danger">{{ __('Delete') }}</button>
+ <button
+ v-gl-modal="modalId"
+ type="button"
+ class="btn btn-danger"
+ data-qa-selector="delete_button"
+ >
+ {{ __('Delete') }}
+ </button>
<gl-modal
:title="title"
- :ok-title="s__('WikiPageConfirmDelete|Delete page')"
+ :action-primary="{
+ text: s__('WikiPageConfirmDelete|Delete page'),
+ attributes: { variant: 'danger', 'data-qa-selector': 'confirm_deletion_button' },
+ }"
:modal-id="modalId"
title-tag="h4"
- ok-variant="danger"
@ok="onSubmit"
>
{{ message }}
diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb
index 3b8f9722913..048b18c5c61 100644
--- a/app/controllers/concerns/snippets_actions.rb
+++ b/app/controllers/concerns/snippets_actions.rb
@@ -8,6 +8,7 @@ module SnippetsActions
include PaginatedCollection
include Gitlab::NoteableMetadata
include Snippets::SendBlob
+ include SnippetsSort
included do
skip_before_action :verify_authenticity_token,
diff --git a/app/controllers/concerns/snippets_sort.rb b/app/controllers/concerns/snippets_sort.rb
new file mode 100644
index 00000000000..f122c843af7
--- /dev/null
+++ b/app/controllers/concerns/snippets_sort.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module SnippetsSort
+ extend ActiveSupport::Concern
+
+ def sort_param
+ params[:sort].presence || 'updated_desc'
+ end
+end
diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb
index aa09fcdbe61..a8ca3dbd0e7 100644
--- a/app/controllers/dashboard/snippets_controller.rb
+++ b/app/controllers/dashboard/snippets_controller.rb
@@ -3,6 +3,7 @@
class Dashboard::SnippetsController < Dashboard::ApplicationController
include PaginatedCollection
include Gitlab::NoteableMetadata
+ include SnippetsSort
skip_cross_project_access_check :index
@@ -11,7 +12,7 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController
.new(current_user, author: current_user)
.execute
- @snippets = SnippetsFinder.new(current_user, author: current_user, scope: params[:scope])
+ @snippets = SnippetsFinder.new(current_user, author: current_user, scope: params[:scope], sort: sort_param)
.execute
.page(params[:page])
.inc_author
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 1bc63fce5fc..49840e847f2 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -19,7 +19,7 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController
.new(current_user, project: @project)
.execute
- @snippets = SnippetsFinder.new(current_user, project: @project, scope: params[:scope])
+ @snippets = SnippetsFinder.new(current_user, project: @project, scope: params[:scope], sort: sort_param)
.execute
.page(params[:page])
.inc_author
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index 87d87390e57..e68b821459d 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -21,7 +21,7 @@ class SnippetsController < Snippets::ApplicationController
if params[:username].present?
@user = UserFinder.new(params[:username]).find_by_username!
- @snippets = SnippetsFinder.new(current_user, author: @user, scope: params[:scope])
+ @snippets = SnippetsFinder.new(current_user, author: @user, scope: params[:scope], sort: sort_param)
.execute
.page(params[:page])
.inc_author
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index 4f63810423b..ecde2c9f475 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -43,7 +43,7 @@ class SnippetsFinder < UnionFinder
include Gitlab::Utils::StrongMemoize
attr_accessor :current_user, :params
- delegate :explore, :only_personal, :only_project, :scope, to: :params
+ delegate :explore, :only_personal, :only_project, :scope, :sort, to: :params
def initialize(current_user = nil, params = {})
@current_user = current_user
@@ -69,7 +69,9 @@ class SnippetsFinder < UnionFinder
items = init_collection
items = by_ids(items)
- items.with_optional_visibility(visibility_from_scope).fresh
+ items = items.with_optional_visibility(visibility_from_scope)
+
+ items.order_by(sort_param)
end
private
@@ -202,6 +204,10 @@ class SnippetsFinder < UnionFinder
params[:project].is_a?(Project) ? params[:project] : Project.find_by_id(params[:project])
end
end
+
+ def sort_param
+ sort.presence || 'id_desc'
+ end
end
SnippetsFinder.prepend_if_ee('EE::SnippetsFinder')
diff --git a/app/policies/merge_request_policy.rb b/app/policies/merge_request_policy.rb
index e2aca2a37d5..e5ac228b0ee 100644
--- a/app/policies/merge_request_policy.rb
+++ b/app/policies/merge_request_policy.rb
@@ -10,6 +10,10 @@ class MergeRequestPolicy < IssuablePolicy
# it would not be safe to prevent :create_note there, since
# note permissions are shared, and this would apply too broadly.
rule { ~can?(:read_merge_request) }.prevent :create_note
+
+ rule { can?(:update_merge_request) }.policy do
+ enable :approve_merge_request
+ end
end
MergeRequestPolicy.prepend_if_ee('EE::MergeRequestPolicy')
diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb
index 6e89cadb82d..d0e81da5aab 100644
--- a/app/services/members/destroy_service.rb
+++ b/app/services/members/destroy_service.rb
@@ -2,8 +2,6 @@
module Members
class DestroyService < Members::BaseService
- WAIT_FOR_DELETE = 1.hour
-
def execute(member, skip_authorization: false, skip_subresources: false, unassign_issuables: false)
raise Gitlab::Access::AccessDeniedError unless skip_authorization || can_destroy_member?(member)
@@ -72,7 +70,7 @@ module Members
source_type = member.is_a?(GroupMember) ? 'Group' : 'Project'
member.run_after_commit do
- MembersDestroyer::UnassignIssuablesWorker.perform_in(WAIT_FOR_DELETE, member.user_id, member.source_id, source_type)
+ MembersDestroyer::UnassignIssuablesWorker.perform_async(member.user_id, member.source_id, source_type)
end
end
end
diff --git a/app/services/merge_requests/approval_service.rb b/app/services/merge_requests/approval_service.rb
index 0fe165303f2..150ec85fca9 100644
--- a/app/services/merge_requests/approval_service.rb
+++ b/app/services/merge_requests/approval_service.rb
@@ -3,19 +3,27 @@
module MergeRequests
class ApprovalService < MergeRequests::BaseService
def execute(merge_request)
+ return unless can_be_approved?(merge_request)
+
approval = merge_request.approvals.new(user: current_user)
- return unless save_approval(approval)
+ return success unless save_approval(approval)
reset_approvals_cache(merge_request)
create_event(merge_request)
create_approval_note(merge_request)
mark_pending_todos_as_done(merge_request)
execute_approval_hooks(merge_request, current_user)
+
+ success
end
private
+ def can_be_approved?(merge_request)
+ current_user.can?(:approve_merge_request, merge_request)
+ end
+
def reset_approvals_cache(merge_request)
merge_request.approvals.reset
end
diff --git a/app/services/merge_requests/remove_approval_service.rb b/app/services/merge_requests/remove_approval_service.rb
index e2675f64ee4..aeaaebdbff6 100644
--- a/app/services/merge_requests/remove_approval_service.rb
+++ b/app/services/merge_requests/remove_approval_service.rb
@@ -4,6 +4,8 @@ module MergeRequests
class RemoveApprovalService < MergeRequests::BaseService
# rubocop: disable CodeReuse/ActiveRecord
def execute(merge_request)
+ return unless approved_by_user?(merge_request)
+
# paranoid protection against running wrong deletes
return unless merge_request.id && current_user.id
@@ -15,11 +17,17 @@ module MergeRequests
reset_approvals_cache(merge_request)
create_note(merge_request)
end
+
+ success
end
# rubocop: enable CodeReuse/ActiveRecord
private
+ def approved_by_user?(merge_request)
+ merge_request.approved_by_users.include?(current_user)
+ end
+
def reset_approvals_cache(merge_request)
merge_request.approvals.reset
end
diff --git a/app/views/notify/push_to_merge_request_email.text.haml b/app/views/notify/push_to_merge_request_email.text.haml
index 55cbd62b7e8..5c2005a47e5 100644
--- a/app/views/notify/push_to_merge_request_email.text.haml
+++ b/app/views/notify/push_to_merge_request_email.text.haml
@@ -1,4 +1,6 @@
-#{sanitize_name(@updated_by_user.name)} pushed new commits to merge request #{merge_request_reference_link(@merge_request)}
+#{sanitize_name(@updated_by_user.name)} pushed new commits to merge request #{@merge_request.to_reference}
+
+Merge Request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
\
- if @existing_commits.any?
- count = @existing_commits.size
diff --git a/changelogs/unreleased/217362-move-plan-stage-usage-activity-to-ce.yml b/changelogs/unreleased/217362-move-plan-stage-usage-activity-to-ce.yml
new file mode 100644
index 00000000000..6bfffbc19a3
--- /dev/null
+++ b/changelogs/unreleased/217362-move-plan-stage-usage-activity-to-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Move plan stage usage activity to CE
+merge_request: 36087
+author:
+type: changed
diff --git a/changelogs/unreleased/223159-fix-dahsboard-warning-logic.yml b/changelogs/unreleased/223159-fix-dahsboard-warning-logic.yml
new file mode 100644
index 00000000000..ba69c9ac07d
--- /dev/null
+++ b/changelogs/unreleased/223159-fix-dahsboard-warning-logic.yml
@@ -0,0 +1,5 @@
+---
+title: Fix dashboard schema validation issue
+merge_request: 36577
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-default-order-snippet-lists.yml b/changelogs/unreleased/fj-default-order-snippet-lists.yml
new file mode 100644
index 00000000000..e902628ee0f
--- /dev/null
+++ b/changelogs/unreleased/fj-default-order-snippet-lists.yml
@@ -0,0 +1,5 @@
+---
+title: Reorder snippets in lists using `updated_at` column
+merge_request: 34393
+author: Dibyadarshi Dash @ddash2
+type: changed
diff --git a/changelogs/unreleased/propagate-ds-java-version-in-dependency-scanning.yml b/changelogs/unreleased/propagate-ds-java-version-in-dependency-scanning.yml
new file mode 100644
index 00000000000..85677505b0b
--- /dev/null
+++ b/changelogs/unreleased/propagate-ds-java-version-in-dependency-scanning.yml
@@ -0,0 +1,5 @@
+---
+title: Propagate DS_JAVA_VERSION for dependency scanning
+merge_request: 36448
+author:
+type: fixed
diff --git a/changelogs/unreleased/tc-fix-plain-text-commit-mails.yml b/changelogs/unreleased/tc-fix-plain-text-commit-mails.yml
new file mode 100644
index 00000000000..a3475d4529f
--- /dev/null
+++ b/changelogs/unreleased/tc-fix-plain-text-commit-mails.yml
@@ -0,0 +1,5 @@
+---
+title: Remove HTML link from plain text mail
+merge_request: 36301
+author:
+type: fixed
diff --git a/doc/README.md b/doc/README.md
index d40741e0174..8e7934172c0 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -138,7 +138,7 @@ The following documentation relates to the DevOps **Plan** stage:
Consolidate source code into a single [distributed version control system](https://en.wikipedia.org/wiki/Distributed_version_control)
that’s easily managed and controlled without disrupting your workflow.
-GitLab’s Git repositories come complete with branching tools and access
+GitLab repositories come complete with branching tools and access
controls, providing a scalable, single source of truth for collaborating
on projects and code.
diff --git a/doc/administration/feature_flags.md b/doc/administration/feature_flags.md
index 5aadbf75c5d..678ab6c5d7b 100644
--- a/doc/administration/feature_flags.md
+++ b/doc/administration/feature_flags.md
@@ -109,6 +109,24 @@ For example, to enable the [`:junit_pipeline_view`](../ci/junit_test_reports.md#
Feature.enable(:junit_pipeline_view, Project.find(1234))
```
+`Feature.enable` and `Feature.disable` always return `nil`, this is not an indication that the command failed:
+
+```ruby
+irb(main):001:0> Feature.enable(:release_evidence_collection)
+=> nil
+```
+
+To check if a flag is enabled or disabled you can use `Feature.enabled?` or `Feature.disabled?`:
+
+```ruby
+Feature.enable(:release_evidence_collection)
+=> nil
+Feature.enabled?(:release_evidence_collection)
+=> true
+Feature.disabled?(:release_evidence_collection)
+=> false
+```
+
When the feature is ready, GitLab will remove the feature flag, the option for
enabling and disabling it will no longer exist, and the feature will become
available in all instances.
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 8f55345a9a8..169d02fe13d 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -927,7 +927,7 @@ larger images, or images that take longer than 5 minutes to push, users may
encounter this error.
Administrators can increase the token duration in **Admin area > Settings >
-Container Registry > Authorization token duration (minutes)**.
+CI/CD > Container Registry > Authorization token duration (minutes)**.
### AWS S3 with the GitLab registry error when pushing large images
diff --git a/doc/development/fe_guide/tooling.md b/doc/development/fe_guide/tooling.md
index 585cd969c96..c693df36e6e 100644
--- a/doc/development/fe_guide/tooling.md
+++ b/doc/development/fe_guide/tooling.md
@@ -4,6 +4,44 @@
We use ESLint to encapsulate and enforce frontend code standards. Our configuration may be found in the [`gitlab-eslint-config`](https://gitlab.com/gitlab-org/gitlab-eslint-config) project.
+### Yarn Script
+
+This section describes yarn scripts that are available to validate and apply automatic fixes to files using ESLint.
+
+To check all currently staged files (based on `git diff`) with ESLint, run the following script:
+
+```shell
+yarn eslint-staged
+```
+
+_A list of problems found will be logged to the console._
+
+To apply automatic ESLint fixes to all currently staged files (based on `git diff`), run the following script:
+
+```shell
+yarn eslint-staged-fix
+```
+
+_If manual changes are required, a list of changes will be sent to the console._
+
+To check **all** files in the repository with ESLint, run the following script:
+
+```shell
+yarn eslint
+```
+
+_A list of problems found will be logged to the console._
+
+To apply automatic ESLint fixes to **all** files in the repository, run the following script:
+
+```shell
+yarn eslint-fix
+```
+
+_If manual changes are required, a list of changes will be sent to the console._
+
+**Caution:** Limit use to global rule updates. Otherwise, the changes can lead to huge Merge Requests.
+
### Disabling ESLint in new files
Do not disable ESLint when creating new files. Existing files may have existing rules
diff --git a/doc/development/telemetry/usage_ping.md b/doc/development/telemetry/usage_ping.md
index 0ca1018352c..ee3bf644a23 100644
--- a/doc/development/telemetry/usage_ping.md
+++ b/doc/development/telemetry/usage_ping.md
@@ -681,10 +681,19 @@ appear to be associated to any of the services running, since they all appear to
| `ldap_group_sync_enabled` | `usage_activity_by_stage` | `manage` | | EE | |
| `ldap_admin_sync_enabled` | `usage_activity_by_stage` | `manage` | | EE | |
| `group_saml_enabled` | `usage_activity_by_stage` | `manage` | | EE | |
-| `projects_jira_dvcs_server_active` | `usage_activity_by_stage` | `plan` | | | |
-| `service_desk_enabled_projects` | `usage_activity_by_stage` | `plan` | | | |
-| `service_desk_issues` | `usage_activity_by_stage` | `plan` | | | |
-| `todos: 0` | `usage_activity_by_stage` | `plan` | | | |
+| `issues` | `usage_activity_by_stage` | `plan` | | CE+EE | |
+| `notes` | `usage_activity_by_stage` | `plan` | | CE+EE | |
+| `projects` | `usage_activity_by_stage` | `plan` | | CE+EE | |
+| `todos` | `usage_activity_by_stage` | `plan` | | CE+EE | |
+| `assignee_lists` | `usage_activity_by_stage` | `plan` | | EE | |
+| `epics` | `usage_activity_by_stage` | `plan` | | EE | |
+| `label_lists` | `usage_activity_by_stage` | `plan` | | EE | |
+| `milestone_lists` | `usage_activity_by_stage` | `plan` | | EE | |
+| `projects_jira_active` | `usage_activity_by_stage` | `plan` | | EE | |
+| `projects_jira_dvcs_server_active` | `usage_activity_by_stage` | `plan` | | EE | |
+| `projects_jira_dvcs_server_active` | `usage_activity_by_stage` | `plan` | | EE | |
+| `service_desk_enabled_projects` | `usage_activity_by_stage` | `plan` | | EE | |
+| `service_desk_issues` | `usage_activity_by_stage` | `plan` | | EE | |
| `deployments` | `usage_activity_by_stage` | `release` | | CE+EE | Total deployments |
| `failed_deployments` | `usage_activity_by_stage` | `release` | | CE+EE | Total failed deployments |
| `projects_mirrored_with_pipelines_enabled` | `usage_activity_by_stage` | `release` | | EE | Projects with repository mirroring enabled |
diff --git a/doc/operations/index.md b/doc/operations/index.md
index f39d664a21b..314a1b231ba 100644
--- a/doc/operations/index.md
+++ b/doc/operations/index.md
@@ -11,6 +11,7 @@ your applications:
- Collect [Prometheus metrics](../user/project/integrations/prometheus_library/index.md).
- Deploy to different [environments](../ci/environments/index.md).
+- Manage your [Alerts](../user/project/operations/alert_management.md) and [Incidents](../user/incident_management/index.md).
- Connect your project to a [Kubernetes cluster](../user/project/clusters/index.md).
- Manage your infrastructure with [Infrastructure as Code](../user/infrastructure/index.md) approaches.
- Discover and view errors generated by your applications with [Error Tracking](../user/project/operations/error_tracking.md).
diff --git a/doc/operations/metrics/dashboards/panel_types.md b/doc/operations/metrics/dashboards/panel_types.md
new file mode 100644
index 00000000000..2b4f65d0f67
--- /dev/null
+++ b/doc/operations/metrics/dashboards/panel_types.md
@@ -0,0 +1,261 @@
+---
+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
+---
+
+# Panel types for dashboards
+
+The below panel types are supported in monitoring dashboards.
+
+## Area or Line Chart
+
+To add an area chart panel type to a dashboard, look at the following sample dashboard file:
+
+```yaml
+dashboard: 'Dashboard Title'
+panel_groups:
+ - group: 'Group Title'
+ panels:
+ - type: area-chart # or line-chart
+ title: 'Area Chart Title'
+ y_label: "Y-Axis"
+ y_axis:
+ format: number
+ precision: 0
+ metrics:
+ - id: area_http_requests_total
+ query_range: 'http_requests_total'
+ label: "Instance: {{instance}}, Method: {{method}}"
+ unit: "count"
+```
+
+Note the following properties:
+
+| Property | Type | Required | Description |
+| ------ | ------ | ------ | ------ |
+| type | string | no | Type of panel to be rendered. Optional for area panel types |
+| query_range | string | required | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
+
+![area panel chart](../../../user/project/integrations/img/prometheus_dashboard_area_panel_type_v12_8.png)
+
+Starting in [version 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/202696), the y-axis values will automatically scale according to the data. Previously, it always started from 0.
+
+## Anomaly chart
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16530) in GitLab 12.5.
+
+To add an anomaly chart panel type to a dashboard, add a panel with *exactly* 3 metrics.
+
+The first metric represents the current state, and the second and third metrics represent the upper and lower limit respectively:
+
+```yaml
+dashboard: 'Dashboard Title'
+panel_groups:
+ - group: 'Group Title'
+ panels:
+ - type: anomaly-chart
+ title: "Chart Title"
+ y_label: "Y-Axis"
+ metrics:
+ - id: anomaly_requests_normal
+ query_range: 'http_requests_total'
+ label: "# of Requests"
+ unit: "count"
+ metrics:
+ - id: anomaly_requests_upper_limit
+ query_range: 10000
+ label: "Max # of requests"
+ unit: "count"
+ metrics:
+ - id: anomaly_requests_lower_limit
+ query_range: 2000
+ label: "Min # of requests"
+ unit: "count"
+```
+
+Note the following properties:
+
+| Property | Type | Required | Description |
+| ------ | ------ | ------ | ------ |
+| type | string | required | Must be `anomaly-chart` for anomaly panel types |
+| query_range | yes | required | For anomaly panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) in every metric. |
+
+![anomaly panel type](../../../user/project/integrations/img/prometheus_dashboard_anomaly_panel_type.png)
+
+## Bar chart
+
+To add a bar chart to a dashboard, look at the following sample dashboard file:
+
+```yaml
+dashboard: 'Dashboard Title'
+panel_groups:
+ - group: 'Group title'
+ panels:
+ - type: bar
+ title: "Http Handlers"
+ x_label: 'Response Size'
+ y_axis:
+ name: "Handlers"
+ metrics:
+ - id: prometheus_http_response_size_bytes_bucket
+ query_range: "sum(increase(prometheus_http_response_size_bytes_bucket[1d])) by (handler)"
+ unit: 'Bytes'
+```
+
+Note the following properties:
+
+| Property | Type | Required | Description |
+| ------ | ------ | ------ | ------ |
+| `type` | string | yes | Type of panel to be rendered. For bar chart types, set to `bar` |
+| `query_range` | yes | yes | For bar chart, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries)
+
+![bar chart panel type](../../../user/project/integrations/img/prometheus_dashboard_bar_chart_panel_type_v12.10.png)
+
+## Column chart
+
+To add a column panel type to a dashboard, look at the following sample dashboard file:
+
+```yaml
+dashboard: 'Dashboard Title'
+panel_groups:
+ - group: 'Group title'
+ panels:
+ - title: "Column"
+ type: "column"
+ metrics:
+ - id: 1024_memory
+ query: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024'
+ unit: MB
+ label: "Memory Usage"
+```
+
+Note the following properties:
+
+| Property | Type | Required | Description |
+| ------ | ------ | ------ | ------ |
+| type | string | yes | Type of panel to be rendered. For column panel types, set to `column` |
+| query_range | yes | yes | For column panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
+
+![anomaly panel type](../../../user/project/integrations/img/prometheus_dashboard_column_panel_type.png)
+
+## Stacked column
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30583) in GitLab 12.8.
+
+To add a stacked column panel type to a dashboard, look at the following sample dashboard file:
+
+```yaml
+dashboard: 'Dashboard title'
+priority: 1
+panel_groups:
+ - group: 'Group Title'
+ priority: 5
+ panels:
+ - type: 'stacked-column'
+ title: "Stacked column"
+ y_label: "y label"
+ x_label: 'x label'
+ metrics:
+ - id: memory_1
+ query_range: 'memory_query'
+ label: "memory query 1"
+ unit: "count"
+ series_name: 'group 1'
+ - id: memory_2
+ query_range: 'memory_query_2'
+ label: "memory query 2"
+ unit: "count"
+ series_name: 'group 2'
+```
+
+![stacked column panel type](../../../user/project/integrations/img/prometheus_dashboard_stacked_column_panel_type_v12_8.png)
+
+| Property | Type | Required | Description |
+| ------ | ------ | ------ | ------ |
+| `type` | string | yes | Type of panel to be rendered. For stacked column panel types, set to `stacked-column` |
+| `query_range` | yes | yes | For stacked column panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
+
+## Single Stat
+
+To add a single stat panel type to a dashboard, look at the following sample dashboard file:
+
+```yaml
+dashboard: 'Dashboard Title'
+panel_groups:
+ - group: 'Group Title'
+ panels:
+ - title: "Single Stat"
+ type: "single-stat"
+ metrics:
+ - id: 10
+ query: 'max(go_memstats_alloc_bytes{job="prometheus"})'
+ unit: MB
+ label: "Total"
+```
+
+Note the following properties:
+
+| Property | Type | Required | Description |
+| ------ | ------ | ------ | ------ |
+| type | string | yes | Type of panel to be rendered. For single stat panel types, set to `single-stat` |
+| query | string | yes | For single stat panel types, you must use an [instant query](https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries) |
+
+![single stat panel type](../../../user/project/integrations/img/prometheus_dashboard_single_stat_panel_type.png)
+
+## Percentile based results
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/201946) in GitLab 12.8.
+
+Query results sometimes need to be represented as a percentage value out of 100. You can use the `max_value` property at the root of the panel definition:
+
+```yaml
+dashboard: 'Dashboard Title'
+panel_groups:
+ - group: 'Group Title'
+ panels:
+ - title: "Single Stat"
+ type: "single-stat"
+ max_value: 100
+ metrics:
+ - id: 10
+ query: 'max(go_memstats_alloc_bytes{job="prometheus"})'
+ unit: '%'
+ label: "Total"
+```
+
+For example, if you have a query value of `53.6`, adding `%` as the unit results in a single stat value of `53.6%`, but if the maximum expected value of the query is `120`, the value would be `44.6%`. Adding the `max_value` causes the correct percentage value to display.
+
+## Heatmaps
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30581) in GitLab 12.5.
+
+To add a heatmap panel type to a dashboard, look at the following sample dashboard file:
+
+```yaml
+dashboard: 'Dashboard Title'
+panel_groups:
+ - group: 'Group Title'
+ panels:
+ - title: "Heatmap"
+ type: "heatmap"
+ metrics:
+ - id: 10
+ query: 'sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[60m])) by (status_code)'
+ unit: req/sec
+ label: "Status code"
+```
+
+Note the following properties:
+
+| Property | Type | Required | Description |
+| ------ | ------ | ------ | ------ |
+| type | string | yes | Type of panel to be rendered. For heatmap panel types, set to `heatmap` |
+| query_range | yes | yes | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
+
+![heatmap panel type](../../../user/project/integrations/img/heatmap_panel_type.png)
+
+CAUTION: **Warning:**
+When a query returns too many data points, the heatmap data bucket dimensions tend downwards to 0, making the chart's data invisible, as shown in the image below. To fix this problem, limit the amount of data returned by changing the time range filter on the metrics dashboard UI, or adding the **step** property to your dashboard's YAML file.
+
+![heatmap chart_too_much_data](../../../user/project/integrations/img/heatmap_chart_too_much_data_v_13_2.png)
diff --git a/doc/operations/metrics/dashboards/yaml.md b/doc/operations/metrics/dashboards/yaml.md
index ce834fabca7..501bfd29eb5 100644
--- a/doc/operations/metrics/dashboards/yaml.md
+++ b/doc/operations/metrics/dashboards/yaml.md
@@ -59,7 +59,7 @@ Panels in a panel group are laid out in rows consisting of two panels per row. A
| `title` | string | yes | Heading for the panel. |
| `y_label` | string | no, but highly encouraged | Y-Axis label for the panel. |
| `y_axis` | string | no | Y-Axis configuration for the panel. |
-| `max_value` | number | no | Denominator value used for calculating [percentile based results](../../../user/project/integrations/prometheus.md#percentile-based-results) |
+| `max_value` | number | no | Denominator value used for calculating [percentile based results](panel_types.md#percentile-based-results) |
| `weight` | number | no, defaults to order in file | Order to appear within the grouping. Lower number means higher priority, which will be higher on the page. Numbers do not need to be consecutive. |
| `metrics` | array | yes | The metrics which should be displayed in the panel. Any number of metrics can be displayed when `type` is `area-chart` or `line-chart`, whereas only 3 can be displayed when `type` is `anomaly-chart`. |
| `links` | array | no | Add links to display on the chart's [context menu](index.md#chart-context-menu). |
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 321487a1c6d..f08a144db90 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -120,6 +120,7 @@ The following table depicts the various user permission levels in a project.
| Rewrite/remove Git tags | | | ✓ | ✓ | ✓ |
| Manage Feature Flags **(PREMIUM)** | | | ✓ | ✓ | ✓ |
| Create/edit/delete metrics dashboard annotations | | | ✓ | ✓ | ✓ |
+| Run CI/CD pipeline against a protected branch | | | ✓ (*5*) | ✓ | ✓ |
| Use environment terminals | | | | ✓ | ✓ |
| Run Web IDE's Interactive Web Terminals **(ULTIMATE ONLY)** | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ |
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 1e09c7f1a0b..e3ed5e3e764 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -353,262 +353,6 @@ When **Metrics Dashboard YAML definition is invalid** at least one of the follow
Metrics Dashboard YAML definition validation information is also available as a [GraphQL API field](../../../api/graphql/reference/index.md#metricsdashboard)
-#### Panel types for dashboards
-
-The below panel types are supported in monitoring dashboards.
-
-##### Area or Line Chart
-
-To add an area chart panel type to a dashboard, look at the following sample dashboard file:
-
-```yaml
-dashboard: 'Dashboard Title'
-panel_groups:
- - group: 'Group Title'
- panels:
- - type: area-chart # or line-chart
- title: 'Area Chart Title'
- y_label: "Y-Axis"
- y_axis:
- format: number
- precision: 0
- metrics:
- - id: area_http_requests_total
- query_range: 'http_requests_total'
- label: "Instance: {{instance}}, Method: {{method}}"
- unit: "count"
-```
-
-Note the following properties:
-
-| Property | Type | Required | Description |
-| ------ | ------ | ------ | ------ |
-| type | string | no | Type of panel to be rendered. Optional for area panel types |
-| query_range | string | required | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
-
-![area panel chart](img/prometheus_dashboard_area_panel_type_v12_8.png)
-
-Starting in [version 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/202696), the y-axis values will automatically scale according to the data. Previously, it always started from 0.
-
-##### Anomaly chart
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16530) in GitLab 12.5.
-
-To add an anomaly chart panel type to a dashboard, add a panel with *exactly* 3 metrics.
-
-The first metric represents the current state, and the second and third metrics represent the upper and lower limit respectively:
-
-```yaml
-dashboard: 'Dashboard Title'
-panel_groups:
- - group: 'Group Title'
- panels:
- - type: anomaly-chart
- title: "Chart Title"
- y_label: "Y-Axis"
- metrics:
- - id: anomaly_requests_normal
- query_range: 'http_requests_total'
- label: "# of Requests"
- unit: "count"
- metrics:
- - id: anomaly_requests_upper_limit
- query_range: 10000
- label: "Max # of requests"
- unit: "count"
- metrics:
- - id: anomaly_requests_lower_limit
- query_range: 2000
- label: "Min # of requests"
- unit: "count"
-```
-
-Note the following properties:
-
-| Property | Type | Required | Description |
-| ------ | ------ | ------ | ------ |
-| type | string | required | Must be `anomaly-chart` for anomaly panel types |
-| query_range | yes | required | For anomaly panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) in every metric. |
-
-![anomaly panel type](img/prometheus_dashboard_anomaly_panel_type.png)
-
-##### Bar chart
-
-To add a bar chart to a dashboard, look at the following sample dashboard file:
-
-```yaml
-dashboard: 'Dashboard Title'
-panel_groups:
- - group: 'Group title'
- panels:
- - type: bar
- title: "Http Handlers"
- x_label: 'Response Size'
- y_axis:
- name: "Handlers"
- metrics:
- - id: prometheus_http_response_size_bytes_bucket
- query_range: "sum(increase(prometheus_http_response_size_bytes_bucket[1d])) by (handler)"
- unit: 'Bytes'
-```
-
-Note the following properties:
-
-| Property | Type | Required | Description |
-| ------ | ------ | ------ | ------ |
-| `type` | string | yes | Type of panel to be rendered. For bar chart types, set to `bar` |
-| `query_range` | yes | yes | For bar chart, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries)
-
-![bar chart panel type](img/prometheus_dashboard_bar_chart_panel_type_v12.10.png)
-
-##### Column chart
-
-To add a column panel type to a dashboard, look at the following sample dashboard file:
-
-```yaml
-dashboard: 'Dashboard Title'
-panel_groups:
- - group: 'Group title'
- panels:
- - title: "Column"
- type: "column"
- metrics:
- - id: 1024_memory
- query: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024'
- unit: MB
- label: "Memory Usage"
-```
-
-Note the following properties:
-
-| Property | Type | Required | Description |
-| ------ | ------ | ------ | ------ |
-| type | string | yes | Type of panel to be rendered. For column panel types, set to `column` |
-| query_range | yes | yes | For column panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
-
-![anomaly panel type](img/prometheus_dashboard_column_panel_type.png)
-
-##### Stacked column
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30583) in GitLab 12.8.
-
-To add a stacked column panel type to a dashboard, look at the following sample dashboard file:
-
-```yaml
-dashboard: 'Dashboard title'
-priority: 1
-panel_groups:
- - group: 'Group Title'
- priority: 5
- panels:
- - type: 'stacked-column'
- title: "Stacked column"
- y_label: "y label"
- x_label: 'x label'
- metrics:
- - id: memory_1
- query_range: 'memory_query'
- label: "memory query 1"
- unit: "count"
- series_name: 'group 1'
- - id: memory_2
- query_range: 'memory_query_2'
- label: "memory query 2"
- unit: "count"
- series_name: 'group 2'
-```
-
-![stacked column panel type](img/prometheus_dashboard_stacked_column_panel_type_v12_8.png)
-
-| Property | Type | Required | Description |
-| ------ | ------ | ------ | ------ |
-| `type` | string | yes | Type of panel to be rendered. For stacked column panel types, set to `stacked-column` |
-| `query_range` | yes | yes | For stacked column panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
-
-##### Single Stat
-
-To add a single stat panel type to a dashboard, look at the following sample dashboard file:
-
-```yaml
-dashboard: 'Dashboard Title'
-panel_groups:
- - group: 'Group Title'
- panels:
- - title: "Single Stat"
- type: "single-stat"
- metrics:
- - id: 10
- query: 'max(go_memstats_alloc_bytes{job="prometheus"})'
- unit: MB
- label: "Total"
-```
-
-Note the following properties:
-
-| Property | Type | Required | Description |
-| ------ | ------ | ------ | ------ |
-| type | string | yes | Type of panel to be rendered. For single stat panel types, set to `single-stat` |
-| query | string | yes | For single stat panel types, you must use an [instant query](https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries) |
-
-![single stat panel type](img/prometheus_dashboard_single_stat_panel_type.png)
-
-###### Percentile based results
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/201946) in GitLab 12.8.
-
-Query results sometimes need to be represented as a percentage value out of 100. You can use the `max_value` property at the root of the panel definition:
-
-```yaml
-dashboard: 'Dashboard Title'
-panel_groups:
- - group: 'Group Title'
- panels:
- - title: "Single Stat"
- type: "single-stat"
- max_value: 100
- metrics:
- - id: 10
- query: 'max(go_memstats_alloc_bytes{job="prometheus"})'
- unit: '%'
- label: "Total"
-```
-
-For example, if you have a query value of `53.6`, adding `%` as the unit results in a single stat value of `53.6%`, but if the maximum expected value of the query is `120`, the value would be `44.6%`. Adding the `max_value` causes the correct percentage value to display.
-
-##### Heatmaps
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30581) in GitLab 12.5.
-
-To add a heatmap panel type to a dashboard, look at the following sample dashboard file:
-
-```yaml
-dashboard: 'Dashboard Title'
-panel_groups:
- - group: 'Group Title'
- panels:
- - title: "Heatmap"
- type: "heatmap"
- metrics:
- - id: 10
- query: 'sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[60m])) by (status_code)'
- unit: req/sec
- label: "Status code"
-```
-
-Note the following properties:
-
-| Property | Type | Required | Description |
-| ------ | ------ | ------ | ------ |
-| type | string | yes | Type of panel to be rendered. For heatmap panel types, set to `heatmap` |
-| query_range | yes | yes | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
-
-![heatmap panel type](img/heatmap_panel_type.png)
-
-CAUTION: **Warning:**
-When a query returns too many data points, the heatmap data bucket dimensions tend downwards to 0, making the chart's data invisible, as shown in the image below. To fix this problem, limit the amount of data returned by changing the time range filter on the metrics dashboard UI, or adding the **step** property to your dashboard's YAML file.
-
-![heatmap chart_too_much_data](img/heatmap_chart_too_much_data_v_13_2.png)
-
### Templating variables for metrics dashboards
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214539) in GitLab 13.0.
diff --git a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
index e2aa44e78f4..37f6cd216ca 100644
--- a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
@@ -52,6 +52,7 @@ dependency_scanning:
DS_PYTHON_VERSION \
DS_PIP_VERSION \
DS_PIP_DEPENDENCY_PATH \
+ DS_JAVA_VERSION \
GEMNASIUM_DB_LOCAL_PATH \
GEMNASIUM_DB_REMOTE_URL \
GEMNASIUM_DB_REF_NAME \
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 3ccbb7eee49..d0f910c3160 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -525,9 +525,16 @@ module Gitlab
# Omitted because no user, creator or author associated: `boards`, `labels`, `milestones`, `uploads`
# Omitted because too expensive: `epics_deepest_relationship_level`
# Omitted because of encrypted properties: `projects_jira_cloud_active`, `projects_jira_server_active`
+ # rubocop: disable CodeReuse/ActiveRecord
def usage_activity_by_stage_plan(time_period)
- {}
+ {
+ issues: distinct_count(::Issue.where(time_period), :author_id),
+ notes: distinct_count(::Note.where(time_period), :author_id),
+ projects: distinct_count(::Project.where(time_period), :creator_id),
+ todos: distinct_count(::Todo.where(time_period), :author_id)
+ }
end
+ # rubocop: enable CodeReuse/ActiveRecord
# Omitted because no user, creator or author associated: `environments`, `feature_flags`, `in_review_folder`, `pages_domains`
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/package.json b/package.json
index cdfd6e2a7a4..5f2c4cac587 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,8 @@
"dev-server": "NODE_OPTIONS=\"--max-old-space-size=3584\" node scripts/frontend/webpack_dev_server.js",
"eslint": "eslint --cache --max-warnings 0 --report-unused-disable-directives --ext .js,.vue .",
"eslint-fix": "eslint --cache --max-warnings 0 --report-unused-disable-directives --ext .js,.vue --fix .",
+ "eslint-staged": "git diff --cached --name-only | grep -E \"(.*)\\.(js|vue)$\" | xargs eslint --cache --max-warnings 0 --report-unused-disable-directives",
+ "eslint-staged-fix": "git diff --cached --name-only | grep -E \"(.*)\\.(js|vue)$\" | xargs eslint --cache --max-warnings 0 --report-unused-disable-directives --fix",
"eslint-report": "eslint --max-warnings 0 --ext .js,.vue --format html --output-file ./eslint-report.html --no-inline-config .",
"file-coverage": "scripts/frontend/file_test_coverage.js",
"prejest": "yarn check-dependencies",
@@ -41,7 +43,7 @@
"@babel/preset-env": "^7.10.1",
"@gitlab/at.js": "1.5.5",
"@gitlab/svgs": "1.151.0",
- "@gitlab/ui": "17.19.1",
+ "@gitlab/ui": "17.21.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-1",
"@sentry/browser": "^5.10.2",
diff --git a/qa/qa.rb b/qa/qa.rb
index 4aa8bc62895..0c7e824e3d5 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -474,6 +474,10 @@ module QA
autoload :Templates, 'qa/page/component/project/templates'
end
end
+
+ module Modal
+ autoload :DeleteWiki, 'qa/page/modal/delete_wiki'
+ end
end
##
diff --git a/qa/qa/page/modal/delete_wiki.rb b/qa/qa/page/modal/delete_wiki.rb
new file mode 100644
index 00000000000..4f0bc34ee88
--- /dev/null
+++ b/qa/qa/page/modal/delete_wiki.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Modal
+ class DeleteWiki < Base
+ view 'app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue' do
+ element :confirm_deletion_button, required: true
+ end
+
+ def confirm_deletion
+ click_element :confirm_deletion_button
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/wiki/edit.rb b/qa/qa/page/project/wiki/edit.rb
index 41ee205429c..6f3be904eb3 100644
--- a/qa/qa/page/project/wiki/edit.rb
+++ b/qa/qa/page/project/wiki/edit.rb
@@ -15,6 +15,10 @@ module QA
element :create_page_button
end
+ view 'app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue' do
+ element :delete_button
+ end
+
def set_title(title)
fill_element :wiki_title_textbox, title
end
@@ -34,6 +38,11 @@ module QA
def click_create_page
click_element :create_page_button
end
+
+ def delete_page
+ click_element :delete_button, Page::Modal::DeleteWiki
+ Page::Modal::DeleteWiki.perform(&:confirm_deletion)
+ end
end
end
end
diff --git a/qa/qa/page/project/wiki/show.rb b/qa/qa/page/project/wiki/show.rb
index 2e68bcaae90..cdd18e420d1 100644
--- a/qa/qa/page/project/wiki/show.rb
+++ b/qa/qa/page/project/wiki/show.rb
@@ -58,6 +58,10 @@ module QA
def has_content?(content)
has_element?(:wiki_page_content, content)
end
+
+ def has_no_page?
+ has_element? :create_first_page_link
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/jira_issue_import_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/jira_issue_import_spec.rb
index ba8e8635c87..5b0b4b2970d 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/jira_issue_import_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/jira_issue_import_spec.rb
@@ -22,6 +22,8 @@ module QA
Page::Project::Menu.perform(&:click_issues)
Page::Project::Issue::Index.perform do |issues_page|
+ expect(issues_page).to have_content("2 issues successfully imported")
+
issues_page.click_issue_link(jira_issue_title)
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_page_deletion_spec.rb b/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_page_deletion_spec.rb
new file mode 100644
index 00000000000..923c7332748
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/wiki/project_based_page_deletion_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Create' do
+ context 'Wiki' do
+ let(:initial_wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! }
+
+ before do
+ Flow::Login.sign_in
+ end
+
+ context 'Page deletion' do
+ it 'has removed the deleted page correctly' do
+ initial_wiki.visit!
+
+ Page::Project::Wiki::Show.perform(&:click_edit)
+ Page::Project::Wiki::Edit.perform(&:delete_page)
+
+ Page::Project::Wiki::Show.perform do |wiki|
+ expect(wiki).to have_no_page
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/scripts/lint-doc.sh b/scripts/lint-doc.sh
index 4e5896781e7..72e6334d0fc 100755
--- a/scripts/lint-doc.sh
+++ b/scripts/lint-doc.sh
@@ -48,7 +48,8 @@ echo
if [ ${FIND_READMES} -ne $NUMBER_READMES ]
then
echo
- echo ' ✖ ERROR: New README.md file(s) detected, prefer index.md over README.md.' >&2
+ echo ' ✖ ERROR: The number of README.md file(s) has changed. Use index.md instead of README.md.' >&2
+ echo ' ✖ If removing a README.md file, update NUMBER_READMES in lint-doc.sh.' >&2
echo ' https://docs.gitlab.com/ee/development/documentation/styleguide.html#work-with-directories-and-files'
echo
((ERRORCODE++))
diff --git a/spec/controllers/concerns/renders_commits_spec.rb b/spec/controllers/concerns/renders_commits_spec.rb
index 0bffb39d608..7be5f75c19d 100644
--- a/spec/controllers/concerns/renders_commits_spec.rb
+++ b/spec/controllers/concerns/renders_commits_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe RendersCommits do
it 'avoids N + 1' do
stub_const("MergeRequestDiff::COMMITS_SAFE_SIZE", 5)
- control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do
+ control_count = ActiveRecord::QueryRecorder.new do
go
end.count
diff --git a/spec/controllers/dashboard/snippets_controller_spec.rb b/spec/controllers/dashboard/snippets_controller_spec.rb
index 3c316d07408..d981f738e70 100644
--- a/spec/controllers/dashboard/snippets_controller_spec.rb
+++ b/spec/controllers/dashboard/snippets_controller_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Dashboard::SnippetsController do
- let(:user) { create(:user) }
+ let_it_be(:user) { create(:user) }
before do
sign_in(user)
@@ -26,5 +26,7 @@ RSpec.describe Dashboard::SnippetsController do
get :index
end
+
+ it_behaves_like 'snippets sort order'
end
end
diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb
index 8bbfaa8d327..6fcb24da3cd 100644
--- a/spec/controllers/projects/snippets_controller_spec.rb
+++ b/spec/controllers/projects/snippets_controller_spec.rb
@@ -15,14 +15,18 @@ RSpec.describe Projects::SnippetsController do
end
describe 'GET #index' do
+ let(:base_params) do
+ {
+ namespace_id: project.namespace,
+ project_id: project
+ }
+ end
+
+ subject { get :index, params: base_params }
+
it_behaves_like 'paginated collection' do
let(:collection) { project.snippets }
- let(:params) do
- {
- namespace_id: project.namespace,
- project_id: project
- }
- end
+ let(:params) { base_params }
before do
create(:project_snippet, :public, project: project, author: user)
@@ -35,7 +39,11 @@ RSpec.describe Projects::SnippetsController do
.to receive(:new).with(nil, project: project)
.and_return(service)
- get :index, params: { namespace_id: project.namespace, project_id: project }
+ subject
+ end
+
+ it_behaves_like 'snippets sort order' do
+ let(:params) { base_params }
end
context 'when the project snippet is private' do
@@ -43,7 +51,7 @@ RSpec.describe Projects::SnippetsController do
context 'when anonymous' do
it 'does not include the private snippet' do
- get :index, params: { namespace_id: project.namespace, project_id: project }
+ subject
expect(assigns(:snippets)).not_to include(project_snippet)
expect(response).to have_gitlab_http_status(:ok)
@@ -56,7 +64,7 @@ RSpec.describe Projects::SnippetsController do
end
it 'renders the snippet' do
- get :index, params: { namespace_id: project.namespace, project_id: project }
+ subject
expect(assigns(:snippets)).to include(project_snippet)
expect(response).to have_gitlab_http_status(:ok)
@@ -69,7 +77,7 @@ RSpec.describe Projects::SnippetsController do
end
it 'renders the snippet' do
- get :index, params: { namespace_id: project.namespace, project_id: project }
+ subject
expect(assigns(:snippets)).to include(project_snippet)
expect(response).to have_gitlab_http_status(:ok)
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index 70df1faf7dd..92370b3381a 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -6,6 +6,8 @@ RSpec.describe SnippetsController do
let_it_be(:user) { create(:user) }
describe 'GET #index' do
+ let(:base_params) { { username: user.username } }
+
context 'when username parameter is present' do
it_behaves_like 'paginated collection' do
let(:collection) { Snippet.all }
@@ -38,6 +40,10 @@ RSpec.describe SnippetsController do
expect(response).to redirect_to(dashboard_snippets_path)
end
end
+
+ it_behaves_like 'snippets sort order' do
+ let(:params) { base_params }
+ end
end
describe 'GET #new' do
diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index 0affc832b30..cb49d5fd135 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -295,6 +295,22 @@ RSpec.describe SnippetsFinder do
expect(finder.execute).to be_empty
end
end
+
+ context 'no sort param is provided' do
+ it 'returns snippets sorted by id' do
+ snippets = described_class.new(admin).execute
+
+ expect(snippets.ids).to eq(Snippet.order_id_desc.ids)
+ end
+ end
+
+ context 'sort param is provided' do
+ it 'returns snippets sorted by sort param' do
+ snippets = described_class.new(admin, sort: 'updated_desc').execute
+
+ expect(snippets.ids).to eq(Snippet.order_updated_desc.ids)
+ end
+ end
end
it_behaves_like 'snippet visibility'
diff --git a/spec/frontend/monitoring/store/actions_spec.js b/spec/frontend/monitoring/store/actions_spec.js
index ad01e4c3a9b..ee5af6380c4 100644
--- a/spec/frontend/monitoring/store/actions_spec.js
+++ b/spec/frontend/monitoring/store/actions_spec.js
@@ -948,6 +948,25 @@ describe('Monitoring store actions', () => {
);
});
+ it('dispatches receiveDashboardValidationWarningsSuccess with false payload when the response is empty ', () => {
+ mockMutate.mockResolvedValue({
+ data: {
+ project: null,
+ },
+ });
+
+ return testAction(
+ fetchDashboardValidationWarnings,
+ null,
+ state,
+ [],
+ [{ type: 'receiveDashboardValidationWarningsSuccess', payload: false }],
+ () => {
+ expect(mockMutate).toHaveBeenCalledWith(mutationVariables);
+ },
+ );
+ });
+
it('dispatches receiveDashboardValidationWarningsFailure if the warnings API call fails', () => {
mockMutate.mockRejectedValue({});
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index ba1dfb1029b..af5fefd1bc4 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -235,6 +235,31 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
end
end
+ context 'for plan' do
+ it 'includes accurate usage_activity_by_stage data' do
+ for_defined_days_back do
+ user = create(:user)
+ project = create(:project, creator: user)
+ issue = create(:issue, project: project, author: user)
+ create(:note, project: project, noteable: issue, author: user)
+ create(:todo, project: project, target: issue, author: user)
+ end
+
+ expect(described_class.uncached_data[:usage_activity_by_stage][:plan]).to include(
+ issues: 2,
+ notes: 2,
+ projects: 2,
+ todos: 2
+ )
+ expect(described_class.uncached_data[:usage_activity_by_stage_monthly][:plan]).to include(
+ issues: 1,
+ notes: 1,
+ projects: 1,
+ todos: 1
+ )
+ end
+ end
+
context 'for release' do
it 'includes accurate usage_activity_by_stage data' do
for_defined_days_back do
diff --git a/spec/policies/merge_request_policy_spec.rb b/spec/policies/merge_request_policy_spec.rb
index 50ce1b33d17..2f3cb2e998a 100644
--- a/spec/policies/merge_request_policy_spec.rb
+++ b/spec/policies/merge_request_policy_spec.rb
@@ -24,6 +24,7 @@ RSpec.describe MergeRequestPolicy do
mr_perms = %i[create_merge_request_in
create_merge_request_from
read_merge_request
+ approve_merge_request
create_note].freeze
shared_examples_for 'a denied user' do
diff --git a/spec/services/merge_requests/approval_service_spec.rb b/spec/services/merge_requests/approval_service_spec.rb
index 68b9caa30ab..124501f17d5 100644
--- a/spec/services/merge_requests/approval_service_spec.rb
+++ b/spec/services/merge_requests/approval_service_spec.rb
@@ -11,6 +11,10 @@ RSpec.describe MergeRequests::ApprovalService do
subject(:service) { described_class.new(project, user) }
+ before do
+ project.add_developer(user)
+ end
+
context 'with invalid approval' do
before do
allow(merge_request.approvals).to receive(:new).and_return(double(save: false))
@@ -56,5 +60,15 @@ RSpec.describe MergeRequests::ApprovalService do
end
end
end
+
+ context 'user cannot update the merge request' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'does not update approvals' do
+ expect { service.execute(merge_request) }.not_to change { merge_request.approvals.size }
+ end
+ end
end
end
diff --git a/spec/services/merge_requests/remove_approval_service_spec.rb b/spec/services/merge_requests/remove_approval_service_spec.rb
index 89efd581abf..40da928e832 100644
--- a/spec/services/merge_requests/remove_approval_service_spec.rb
+++ b/spec/services/merge_requests/remove_approval_service_spec.rb
@@ -33,5 +33,14 @@ RSpec.describe MergeRequests::RemoveApprovalService do
execute!
end
end
+
+ context 'with a user who has not approved' do
+ it 'does not create an unapproval note and triggers web hook' do
+ expect(service).not_to receive(:execute_hooks)
+ expect(SystemNoteService).not_to receive(:unapprove_mr)
+
+ execute!
+ end
+ end
end
end
diff --git a/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb b/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb
new file mode 100644
index 00000000000..aa4d78b23f4
--- /dev/null
+++ b/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'snippets sort order' do
+ let(:params) { {} }
+ let(:sort_argument) { {} }
+ let(:sort_params) { params.merge(sort_argument)}
+
+ before do
+ sign_in(user)
+
+ stub_snippet_counter
+ end
+
+ subject { get :index, params: sort_params }
+
+ context 'when no sort param is provided' do
+ it 'calls SnippetsFinder with updated_at sort option' do
+ expect(SnippetsFinder).to receive(:new).with(user,
+ hash_including(sort: 'updated_desc')).and_call_original
+
+ subject
+ end
+ end
+
+ context 'when sort param is provided' do
+ let(:order) { 'created_desc' }
+ let(:sort_argument) { { sort: order } }
+
+ it 'calls SnippetsFinder with the given sort param' do
+ expect(SnippetsFinder).to receive(:new).with(user,
+ hash_including(sort: order)).and_call_original
+
+ subject
+ end
+ end
+
+ def stub_snippet_counter
+ allow(Snippets::CountService)
+ .to receive(:new).and_return(double(:count_service, execute: {}))
+ end
+end
diff --git a/spec/support/shared_examples/views/plain_text_email.rb b/spec/support/shared_examples/views/plain_text_email.rb
new file mode 100644
index 00000000000..23f9262b446
--- /dev/null
+++ b/spec/support/shared_examples/views/plain_text_email.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'renders plain text email correctly' do
+ it 'renders the email without HTML links' do
+ render
+
+ expect(rendered).to have_no_selector('a')
+ end
+end
diff --git a/spec/views/notify/push_to_merge_request_email.text.haml_spec.rb b/spec/views/notify/push_to_merge_request_email.text.haml_spec.rb
new file mode 100644
index 00000000000..ce402533496
--- /dev/null
+++ b/spec/views/notify/push_to_merge_request_email.text.haml_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe 'notify/push_to_merge_request_email.text.haml' do
+ let(:user) { create(:user, developer_projects: [project]) }
+ let(:project) { create(:project, :repository) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
+ let(:new_commits) { project.repository.commits_between('be93687618e4b132087f430a4d8fc3a609c9b77c', '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51') }
+
+ before do
+ assign(:updated_by_user, user)
+ assign(:project, project)
+ assign(:merge_request, merge_request)
+ assign(:existing_commits, [])
+ assign(:new_commits, new_commits)
+ end
+
+ it_behaves_like 'renders plain text email correctly'
+end
diff --git a/yarn.lock b/yarn.lock
index 0f5b6a4c541..04eb2022c2c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -848,10 +848,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.151.0.tgz#099905295d33eb31033f4a48eb3652da2f686239"
integrity sha512-2PTSM8CFhUjeTFKfcq6E/YwPpOVdSVWupf3NhKO/bz/cisSBS5P7aWxaXKIaxy28ySyBKEfKaAT6b4rXTwvVgg==
-"@gitlab/ui@17.19.1":
- version "17.19.1"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-17.19.1.tgz#48c6d542b19fd6a420d22017e7026190aba1fd31"
- integrity sha512-RA4QXzVWOjbK3gjX78Luhtmo1z6td1uOu8S01v+yu5Pc00HKIgN6pdDwPK8+WLCK2cnu368c457A901wSr82Gg==
+"@gitlab/ui@17.21.0":
+ version "17.21.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-17.21.0.tgz#e881bac4540e3db29ee32e1dfd452677a445cd10"
+ integrity sha512-Ijh3QPlB3Y10Sk0f0eZ/rgRIKHGSzAWZLugw9sb+ppcn9OPbb+2vk0ZgCcdIrfkrX3G8tD8q0Ndl3K1nrz6a5g==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"