{{ __('Runners are processes that pick up and execute CI/CD jobs for GitLab.') }}
-
- {{
- __(
- 'You can register runners as separate users, on separate servers, and on your local machine. Register as many runners as you want.',
- )
- }}
-
-
-
- {{ __('Runners can be:') }}
-
-
-
- - {{ __('Runs jobs from all unassigned projects.') }}
-
-
-
- - {{ __('Runs jobs from all unassigned projects in its group.') }}
-
-
-
- - {{ __('Runs jobs from assigned projects.') }}
-
-
-
- {{ s__('Runners|locked') }}
-
- - {{ __('Cannot be assigned to other projects.') }}
-
-
-
- {{ s__('Runners|paused') }}
-
- - {{ __('Not available to run jobs.') }}
-
-
-
-
-
diff --git a/app/assets/javascripts/runner/constants.js b/app/assets/javascripts/runner/constants.js
index 46e55b322c..a2fb9d9efd 100644
--- a/app/assets/javascripts/runner/constants.js
+++ b/app/assets/javascripts/runner/constants.js
@@ -7,6 +7,14 @@ export const GROUP_RUNNER_COUNT_LIMIT = 1000;
export const I18N_FETCH_ERROR = s__('Runners|Something went wrong while fetching runner data.');
export const I18N_DETAILS_TITLE = s__('Runners|Runner #%{runner_id}');
+export const I18N_INSTANCE_RUNNER_DESCRIPTION = s__('Runners|Available to all projects');
+export const I18N_GROUP_RUNNER_DESCRIPTION = s__(
+ 'Runners|Available to all projects and subgroups in the group',
+);
+export const I18N_PROJECT_RUNNER_DESCRIPTION = s__('Runners|Associated with one or more projects');
+export const I18N_LOCKED_RUNNER_DESCRIPTION = s__('Runners|You cannot assign to other projects');
+export const I18N_PAUSED_RUNNER_DESCRIPTION = s__('Runners|Not available to run jobs');
+
export const RUNNER_TAG_BADGE_VARIANT = 'info';
export const RUNNER_TAG_BG_CLASS = 'gl-bg-blue-100';
diff --git a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql b/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql
index a601ee8d61..3e5109b1ac 100644
--- a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql
+++ b/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql
@@ -24,8 +24,11 @@ query getGroupRunners(
search: $search
sort: $sort
) {
- nodes {
- ...RunnerNode
+ edges {
+ webUrl
+ node {
+ ...RunnerNode
+ }
}
pageInfo {
...PageInfo
diff --git a/app/assets/javascripts/runner/graphql/get_runners.query.graphql b/app/assets/javascripts/runner/graphql/get_runners.query.graphql
index 9f83719755..51a91b9eb9 100644
--- a/app/assets/javascripts/runner/graphql/get_runners.query.graphql
+++ b/app/assets/javascripts/runner/graphql/get_runners.query.graphql
@@ -25,6 +25,7 @@ query getRunners(
) {
nodes {
...RunnerNode
+ adminUrl
}
pageInfo {
...PageInfo
diff --git a/app/assets/javascripts/runner/group_runners/group_runners_app.vue b/app/assets/javascripts/runner/group_runners/group_runners_app.vue
index 42e1a9e1de..4bb28796df 100644
--- a/app/assets/javascripts/runner/group_runners/group_runners_app.vue
+++ b/app/assets/javascripts/runner/group_runners/group_runners_app.vue
@@ -1,13 +1,16 @@
-
+
- '
+ docs_link = ActionController::Base.helpers.link_to _('How do I set up this service?'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/unify_circuit'), target: '_blank', rel: 'noopener noreferrer'
+ s_('Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def event_field(event)
@@ -37,7 +32,7 @@ module Integrations
def default_fields
[
- { type: 'text', name: 'webhook', placeholder: "e.g. https://circuit.com/rest/v2/webhooks/incoming/…", required: true },
+ { type: 'text', name: 'webhook', placeholder: "https://yourcircuit.com/rest/v2/webhooks/incoming/…", required: true },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
{ type: 'select', name: 'branches_to_be_notified', choices: branch_choices }
]
diff --git a/app/models/issue.rb b/app/models/issue.rb
index e0b0c352c2..9c568414ec 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -81,6 +81,7 @@ class Issue < ApplicationRecord
has_and_belongs_to_many :self_managed_prometheus_alert_events, join_table: :issues_self_managed_prometheus_alert_events # rubocop: disable Rails/HasAndBelongsToMany
has_and_belongs_to_many :prometheus_alert_events, join_table: :issues_prometheus_alert_events # rubocop: disable Rails/HasAndBelongsToMany
has_many :prometheus_alerts, through: :prometheus_alert_events
+ has_and_belongs_to_many :customer_relations_contacts, join_table: :issue_customer_relations_contacts, class_name: 'CustomerRelations::Contact' # rubocop: disable Rails/HasAndBelongsToMany
accepts_nested_attributes_for :issuable_severity, update_only: true
accepts_nested_attributes_for :sentry_issue
@@ -107,8 +108,6 @@ class Issue < ApplicationRecord
scope :order_due_date_asc, -> { reorder(::Gitlab::Database.nulls_last_order('due_date', 'ASC')) }
scope :order_due_date_desc, -> { reorder(::Gitlab::Database.nulls_last_order('due_date', 'DESC')) }
scope :order_closest_future_date, -> { reorder(Arel.sql('CASE WHEN issues.due_date >= CURRENT_DATE THEN 0 ELSE 1 END ASC, ABS(CURRENT_DATE - issues.due_date) ASC')) }
- scope :order_relative_position_asc, -> { reorder(::Gitlab::Database.nulls_last_order('relative_position', 'ASC')) }
- scope :order_relative_position_desc, -> { reorder(::Gitlab::Database.nulls_first_order('relative_position', 'DESC')) }
scope :order_closed_date_desc, -> { reorder(closed_at: :desc) }
scope :order_created_at_desc, -> { reorder(created_at: :desc) }
scope :order_severity_asc, -> { includes(:issuable_severity).order('issuable_severities.severity ASC NULLS FIRST') }
@@ -127,6 +126,7 @@ class Issue < ApplicationRecord
project: [:route, { namespace: :route }])
}
scope :with_issue_type, ->(types) { where(issue_type: types) }
+ scope :without_issue_type, ->(types) { where.not(issue_type: types) }
scope :public_only, -> {
without_hidden.where(confidential: false)
@@ -166,6 +166,8 @@ class Issue < ApplicationRecord
scope :by_project_id_and_iid, ->(composites) do
where_composite(%i[project_id iid], composites)
end
+ scope :with_null_relative_position, -> { where(relative_position: nil) }
+ scope :with_non_null_relative_position, -> { where.not(relative_position: nil) }
after_commit :expire_etag_cache, unless: :importing?
after_save :ensure_metrics, unless: :importing?
@@ -266,8 +268,8 @@ class Issue < ApplicationRecord
'due_date' => -> { order_due_date_asc.with_order_id_desc },
'due_date_asc' => -> { order_due_date_asc.with_order_id_desc },
'due_date_desc' => -> { order_due_date_desc.with_order_id_desc },
- 'relative_position' => -> { order_relative_position_asc.with_order_id_desc },
- 'relative_position_asc' => -> { order_relative_position_asc.with_order_id_desc }
+ 'relative_position' => -> { order_by_relative_position },
+ 'relative_position_asc' => -> { order_by_relative_position }
}
)
end
@@ -277,7 +279,7 @@ class Issue < ApplicationRecord
when 'closest_future_date', 'closest_future_date_asc' then order_closest_future_date
when 'due_date', 'due_date_asc' then order_due_date_asc.with_order_id_desc
when 'due_date_desc' then order_due_date_desc.with_order_id_desc
- when 'relative_position', 'relative_position_asc' then order_relative_position_asc.with_order_id_desc
+ when 'relative_position', 'relative_position_asc' then order_by_relative_position
when 'severity_asc' then order_severity_asc.with_order_id_desc
when 'severity_desc' then order_severity_desc.with_order_id_desc
else
@@ -285,13 +287,8 @@ class Issue < ApplicationRecord
end
end
- # `with_cte` argument allows sorting when using CTE queries and prevents
- # errors in postgres when using CTE search optimisation
- def self.order_by_position_and_priority(with_cte: false)
- order = Gitlab::Pagination::Keyset::Order.build([column_order_relative_position, column_order_highest_priority, column_order_id_desc])
-
- order_labels_priority(with_cte: with_cte)
- .reorder(order)
+ def self.order_by_relative_position
+ reorder(Gitlab::Pagination::Keyset::Order.build([column_order_relative_position, column_order_id_asc]))
end
def self.column_order_relative_position
@@ -306,25 +303,6 @@ class Issue < ApplicationRecord
)
end
- def self.column_order_highest_priority
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'highest_priority',
- column_expression: Arel.sql('highest_priorities.label_priority'),
- order_expression: Gitlab::Database.nulls_last_order('highest_priorities.label_priority', 'ASC'),
- reversed_order_expression: Gitlab::Database.nulls_last_order('highest_priorities.label_priority', 'DESC'),
- order_direction: :asc,
- nullable: :nulls_last,
- distinct: false
- )
- end
-
- def self.column_order_id_desc
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'id',
- order_expression: arel_table[:id].desc
- )
- end
-
def self.column_order_id_asc
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
@@ -541,6 +519,10 @@ class Issue < ApplicationRecord
issue_type_supports?(:time_tracking)
end
+ def supports_move_and_clone?
+ issue_type_supports?(:move_and_clone)
+ end
+
def email_participants_emails
issue_email_participants.pluck(:email)
end
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index 25e90036a5..ede2057885 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -13,7 +13,7 @@ class LegacyDiffNote < Note
validates :line_code, presence: true, line_code: true
- before_create :set_diff
+ before_create :set_diff, unless: :skip_setting_st_diff?
def discussion_class(*)
LegacyDiffDiscussion
@@ -90,6 +90,10 @@ class LegacyDiffNote < Note
self.st_diff = diff.to_hash if diff
end
+ def skip_setting_st_diff?
+ st_diff.present? && importing?
+ end
+
def diff_for_line_code
attributes = {
noteable_type: noteable_type,
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index 53e7d52c55..9765ac6f2e 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -49,7 +49,7 @@ class LfsObject < ApplicationRecord
end
def self.calculate_oid(path)
- self.hexdigest(path)
+ self.sha256_hexdigest(path)
end
end
diff --git a/app/models/loose_foreign_keys/deleted_record.rb b/app/models/loose_foreign_keys/deleted_record.rb
index a39d88b2e4..ca5a2800a0 100644
--- a/app/models/loose_foreign_keys/deleted_record.rb
+++ b/app/models/loose_foreign_keys/deleted_record.rb
@@ -2,48 +2,4 @@
class LooseForeignKeys::DeletedRecord < ApplicationRecord
extend SuppressCompositePrimaryKeyWarning
- include PartitionedTable
-
- partitioned_by :created_at, strategy: :monthly, retain_for: 3.months, retain_non_empty_partitions: true
-
- scope :ordered_by_primary_keys, -> { order(:created_at, :deleted_table_name, :deleted_table_primary_key_value) }
-
- def self.load_batch(batch_size)
- ordered_by_primary_keys
- .limit(batch_size)
- .to_a
- end
-
- # Because the table has composite primary keys, the delete_all or delete methods are not going to work.
- # This method implements deletion that benefits from the primary key index, example:
- #
- # > DELETE
- # > FROM "loose_foreign_keys_deleted_records"
- # > WHERE (created_at,
- # > deleted_table_name,
- # > deleted_table_primary_key_value) IN
- # > (SELECT created_at::TIMESTAMP WITH TIME ZONE,
- # > deleted_table_name,
- # > deleted_table_primary_key_value
- # > FROM (VALUES (LIST_OF_VALUES)) AS primary_key_values (created_at, deleted_table_name, deleted_table_primary_key_value))
- def self.delete_records(records)
- values = records.pluck(:created_at, :deleted_table_name, :deleted_table_primary_key_value)
-
- primary_keys = connection.primary_keys(table_name).join(', ')
-
- primary_keys_with_type_cast = [
- Arel.sql('created_at::timestamp with time zone'),
- Arel.sql('deleted_table_name'),
- Arel.sql('deleted_table_primary_key_value')
- ]
-
- value_list = Arel::Nodes::ValuesList.new(values)
-
- # (SELECT primary keys FROM VALUES)
- inner_query = Arel::SelectManager.new
- inner_query.from("#{Arel::Nodes::Grouping.new([value_list]).as('primary_key_values').to_sql} (#{primary_keys})")
- inner_query.projections = primary_keys_with_type_cast
-
- where(Arel::Nodes::Grouping.new([Arel.sql(primary_keys)]).in(inner_query)).delete_all
- end
end
diff --git a/app/models/member.rb b/app/models/member.rb
index beb4c05f2a..21fd4aebd7 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -50,6 +50,11 @@ class Member < ApplicationRecord
},
if: :project_bot?
+ scope :with_invited_user_state, -> do
+ joins('LEFT JOIN users as invited_user ON invited_user.email = members.invite_email')
+ .select('members.*', 'invited_user.state as invited_user_state')
+ end
+
scope :in_hierarchy, ->(source) do
groups = source.root_ancestor.self_and_descendants
group_members = Member.default_scoped.where(source: groups)
@@ -178,7 +183,13 @@ class Member < ApplicationRecord
after_destroy :post_destroy_hook, unless: :pending?, if: :hook_prerequisites_met?
after_save :log_invitation_token_cleanup
- after_commit :refresh_member_authorized_projects, unless: :importing?
+ after_commit on: [:create, :update], unless: :importing? do
+ refresh_member_authorized_projects(blocking: true)
+ end
+
+ after_commit on: [:destroy], unless: :importing? do
+ refresh_member_authorized_projects(blocking: false)
+ end
default_value_for :notification_level, NotificationSetting.levels[:global]
@@ -395,8 +406,8 @@ class Member < ApplicationRecord
# transaction has been committed, resulting in the job either throwing an
# error or not doing any meaningful work.
# rubocop: disable CodeReuse/ServiceClass
- def refresh_member_authorized_projects
- UserProjectAccessChangedService.new(user_id).execute
+ def refresh_member_authorized_projects(blocking:)
+ UserProjectAccessChangedService.new(user_id).execute(blocking: blocking)
end
# rubocop: enable CodeReuse/ServiceClass
@@ -442,6 +453,14 @@ class Member < ApplicationRecord
errors.add(:user, error) if error
end
+ def signup_email_invalid_message
+ if source_type == 'Project'
+ _("is not allowed for this project.")
+ else
+ _("is not allowed for this group.")
+ end
+ end
+
def update_highest_role?
return unless user_id.present?
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index a13133c90e..9062a40521 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -43,15 +43,17 @@ class GroupMember < Member
# Because source_type is `Namespace`...
def real_source_type
- 'Group'
+ Group.sti_name
end
def notifiable_options
{ group: group }
end
+ private
+
override :refresh_member_authorized_projects
- def refresh_member_authorized_projects
+ def refresh_member_authorized_projects(blocking:)
# Here, `destroyed_by_association` will be present if the
# GroupMember is being destroyed due to the `dependent: :destroy`
# callback on Group. In this case, there is no need to refresh the
@@ -63,8 +65,6 @@ class GroupMember < Member
super
end
- private
-
def access_level_inclusion
return if access_level.in?(Gitlab::Access.all_values)
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index 72cb831cc8..eec46b3493 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -90,24 +90,28 @@ class ProjectMember < Member
{ project: project }
end
+ private
+
override :refresh_member_authorized_projects
- def refresh_member_authorized_projects
+ def refresh_member_authorized_projects(blocking:)
return super unless Feature.enabled?(:specialized_service_for_project_member_auth_refresh)
return unless user
# rubocop:disable CodeReuse/ServiceClass
- AuthorizedProjectUpdate::ProjectRecalculatePerUserService.new(project, user).execute
+ if blocking
+ AuthorizedProjectUpdate::ProjectRecalculatePerUserService.new(project, user).execute
+ else
+ AuthorizedProjectUpdate::ProjectRecalculatePerUserWorker.perform_async(project.id, user.id)
+ end
# Until we compare the inconsistency rates of the new, specialized service and
# the old approach, we still run AuthorizedProjectsWorker
# but with some delay and lower urgency as a safety net.
UserProjectAccessChangedService.new(user_id)
- .execute(blocking: false, priority: UserProjectAccessChangedService::LOW_PRIORITY)
+ .execute(blocking: false, priority: UserProjectAccessChangedService::LOW_PRIORITY)
# rubocop:enable CodeReuse/ServiceClass
end
- private
-
def send_invite
run_after_commit_or_now { notification_service.invite_project_member(self, @raw_invite_token) }
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index db49ec6f41..15862fb2bf 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -1111,15 +1111,23 @@ class MergeRequest < ApplicationRecord
can_be_merged? && !should_be_rebased?
end
+ # rubocop: disable CodeReuse/ServiceClass
def mergeable_state?(skip_ci_check: false, skip_discussions_check: false)
return false unless open?
return false if work_in_progress?
return false if broken?
- return false unless skip_ci_check || mergeable_ci_state?
return false unless skip_discussions_check || mergeable_discussions_state?
- true
+ if Feature.enabled?(:improved_mergeability_checks, self.project, default_enabled: :yaml)
+ additional_checks = MergeRequests::Mergeability::RunChecksService.new(merge_request: self, params: { skip_ci_check: skip_ci_check })
+ additional_checks.execute.all?(&:success?)
+ else
+ return false unless skip_ci_check || mergeable_ci_state?
+
+ true
+ end
end
+ # rubocop: enable CodeReuse/ServiceClass
def ff_merge_possible?
project.repository.ancestor?(target_branch_sha, diff_head_sha)
@@ -1658,6 +1666,10 @@ class MergeRequest < ApplicationRecord
service_class.new(project, current_user, id: id, report_type: report_type).execute(comparison_base_pipeline(identifier), actual_head_pipeline)
end
+ def recent_diff_head_shas(limit = 100)
+ merge_request_diffs.recent(limit).pluck(:head_commit_sha)
+ end
+
def all_commits
MergeRequestDiffCommit
.where(merge_request_diff: merge_request_diffs.recent)
@@ -1857,7 +1869,7 @@ class MergeRequest < ApplicationRecord
override :ensure_metrics
def ensure_metrics
- if Feature.enabled?(:use_upsert_query_for_mr_metrics)
+ if Feature.enabled?(:use_upsert_query_for_mr_metrics, default_enabled: :yaml)
MergeRequest::Metrics.record!(self)
else
# Backward compatibility: some merge request metrics records will not have target_project_id filled in.
@@ -1918,20 +1930,6 @@ class MergeRequest < ApplicationRecord
end
end
- def lazy_upvotes_count
- BatchLoader.for(id).batch(default_value: 0) do |ids, loader|
- counts = AwardEmoji
- .where(awardable_id: ids)
- .upvotes
- .group(:awardable_id)
- .count
-
- counts.each do |id, count|
- loader.call(id, count)
- end
- end
- end
-
private
def set_draft_status
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index d2b3ca753b..bd94c0ad30 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -66,7 +66,7 @@ class MergeRequestDiff < ApplicationRecord
joins(:merge_request).where(merge_requests: { target_project_id: project_id })
end
- scope :recent, -> { order(id: :desc).limit(100) }
+ scope :recent, -> (limit = 100) { order(id: :desc).limit(limit) }
scope :files_in_database, -> do
where(stored_externally: [false, nil]).where(arel_table[:files_count].gt(0))
diff --git a/app/models/metrics/dashboard/annotation.rb b/app/models/metrics/dashboard/annotation.rb
index 3383dda20c..d3d3f97339 100644
--- a/app/models/metrics/dashboard/annotation.rb
+++ b/app/models/metrics/dashboard/annotation.rb
@@ -32,19 +32,19 @@ module Metrics
def ending_at_after_starting_at
return if ending_at.blank? || starting_at.blank? || starting_at <= ending_at
- errors.add(:ending_at, s_("Metrics::Dashboard::Annotation|can't be before starting_at time"))
+ errors.add(:ending_at, s_("MetricsDashboardAnnotation|can't be before starting_at time"))
end
def single_ownership
return if cluster.nil? ^ environment.nil?
- errors.add(:base, s_("Metrics::Dashboard::Annotation|Annotation can't belong to both a cluster and an environment at the same time"))
+ errors.add(:base, s_("MetricsDashboardAnnotation|Annotation can't belong to both a cluster and an environment at the same time"))
end
def orphaned_annotation
return if cluster.present? || environment.present?
- errors.add(:base, s_("Metrics::Dashboard::Annotation|Annotation must belong to a cluster or an environment"))
+ errors.add(:base, s_("MetricsDashboardAnnotation|Annotation must belong to a cluster or an environment"))
end
end
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index aba61fb7ba..07f9bb9995 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -28,7 +28,10 @@ class Namespace < ApplicationRecord
# Android repo (15) + some extra backup.
NUMBER_OF_ANCESTORS_ALLOWED = 20
- SHARED_RUNNERS_SETTINGS = %w[disabled_and_unoverridable disabled_with_override enabled].freeze
+ SR_DISABLED_AND_UNOVERRIDABLE = 'disabled_and_unoverridable'
+ SR_DISABLED_WITH_OVERRIDE = 'disabled_with_override'
+ SR_ENABLED = 'enabled'
+ SHARED_RUNNERS_SETTINGS = [SR_DISABLED_AND_UNOVERRIDABLE, SR_DISABLED_WITH_OVERRIDE, SR_ENABLED].freeze
URL_MAX_LENGTH = 255
PATH_TRAILING_VIOLATIONS = %w[.git .atom .].freeze
@@ -46,6 +49,8 @@ class Namespace < ApplicationRecord
# This should _not_ be `inverse_of: :namespace`, because that would also set
# `user.namespace` when this user creates a group with themselves as `owner`.
+ # TODO: can this be moved into the UserNamespace class?
+ # evaluate in issue https://gitlab.com/gitlab-org/gitlab/-/issues/341070
belongs_to :owner, class_name: "User"
belongs_to :parent, class_name: "Namespace"
@@ -65,21 +70,31 @@ class Namespace < ApplicationRecord
length: { maximum: 255 }
validates :description, length: { maximum: 255 }
+
validates :path,
presence: true,
- length: { maximum: URL_MAX_LENGTH },
- namespace_path: true
+ length: { maximum: URL_MAX_LENGTH }
+
+ validates :path, namespace_path: true, if: ->(n) { !n.project_namespace? }
+ # Project path validator is used for project namespaces for now to assure
+ # compatibility with project paths
+ # Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/341764
+ validates :path, project_path: true, if: ->(n) { n.project_namespace? }
# Introduce minimal path length of 2 characters.
# Allow change of other attributes without forcing users to
# rename their user or group. At the same time prevent changing
# the path without complying with new 2 chars requirement.
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/225214
- validates :path, length: { minimum: 2 }, if: :path_changed?
+ #
+ # For ProjectNamespace we don't check minimal path length to keep
+ # compatibility with existing project restrictions.
+ # Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/341764
+ validates :path, length: { minimum: 2 }, if: :enforce_minimum_path_length?
validates :max_artifacts_size, numericality: { only_integer: true, greater_than: 0, allow_nil: true }
- validate :validate_parent_type, if: -> { Feature.enabled?(:validate_namespace_parent_type) }
+ validate :validate_parent_type, if: -> { Feature.enabled?(:validate_namespace_parent_type, default_enabled: :yaml) }
validate :nesting_level_allowed
validate :changing_shared_runners_enabled_is_allowed
validate :changing_allow_descendants_override_disabled_shared_runners_is_allowed
@@ -95,7 +110,7 @@ class Namespace < ApplicationRecord
# Legacy Storage specific hooks
- after_update :move_dir, if: :saved_change_to_path_or_parent?
+ after_update :move_dir, if: :saved_change_to_path_or_parent?, unless: -> { is_a?(Namespaces::ProjectNamespace) }
before_destroy(prepend: true) { prepare_for_destroy }
after_destroy :rm_dir
after_commit :expire_child_caches, on: :update, if: -> {
@@ -103,7 +118,12 @@ class Namespace < ApplicationRecord
saved_change_to_name? || saved_change_to_path? || saved_change_to_parent_id?
}
- scope :for_user, -> { where(type: nil) }
+ # TODO: change to `type: Namespaces::UserNamespace.sti_name` when
+ # working on issue https://gitlab.com/gitlab-org/gitlab/-/issues/341070
+ scope :user_namespaces, -> { where(type: [nil, Namespaces::UserNamespace.sti_name]) }
+ # TODO: this can be simplified with `type != 'Project'` when working on issue
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/341070
+ scope :without_project_namespaces, -> { where("type IS DISTINCT FROM ?", Namespaces::ProjectNamespace.sti_name) }
scope :sort_by_type, -> { order(Gitlab::Database.nulls_first_order(:type)) }
scope :include_route, -> { includes(:route) }
scope :by_parent, -> (parent) { where(parent_id: parent) }
@@ -140,14 +160,12 @@ class Namespace < ApplicationRecord
class << self
def sti_class_for(type_name)
case type_name
- when 'Group'
+ when Group.sti_name
Group
- when 'Project'
+ when Namespaces::ProjectNamespace.sti_name
Namespaces::ProjectNamespace
- when 'User'
- # TODO: We create a normal Namespace until
- # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68894 is ready
- Namespace
+ when Namespaces::UserNamespace.sti_name
+ Namespaces::UserNamespace
else
Namespace
end
@@ -254,27 +272,27 @@ class Namespace < ApplicationRecord
end
def kind
- return 'group' if group?
- return 'project' if project?
+ return 'group' if group_namespace?
+ return 'project' if project_namespace?
'user' # defaults to user
end
- def group?
+ def group_namespace?
type == Group.sti_name
end
- def project?
+ def project_namespace?
type == Namespaces::ProjectNamespace.sti_name
end
- def user?
+ def user_namespace?
# That last bit ensures we're considered a user namespace as a default
- type.nil? || type == Namespaces::UserNamespace.sti_name || !(group? || project?)
+ type.nil? || type == Namespaces::UserNamespace.sti_name || !(group_namespace? || project_namespace?)
end
def owner_required?
- user?
+ user_namespace?
end
def find_fork_of(project)
@@ -321,7 +339,7 @@ class Namespace < ApplicationRecord
# that belongs to this namespace
def all_projects
if Feature.enabled?(:recursive_approach_for_all_projects, default_enabled: :yaml)
- namespace = user? ? self : self_and_descendant_ids
+ namespace = user_namespace? ? self : self_and_descendant_ids
Project.where(namespace: namespace)
else
Project.inside_path(full_path)
@@ -423,7 +441,7 @@ class Namespace < ApplicationRecord
def changing_shared_runners_enabled_is_allowed
return unless new_record? || changes.has_key?(:shared_runners_enabled)
- if shared_runners_enabled && has_parent? && parent.shared_runners_setting == 'disabled_and_unoverridable'
+ if shared_runners_enabled && has_parent? && parent.shared_runners_setting == SR_DISABLED_AND_UNOVERRIDABLE
errors.add(:shared_runners_enabled, _('cannot be enabled because parent group has shared Runners disabled'))
end
end
@@ -435,30 +453,30 @@ class Namespace < ApplicationRecord
errors.add(:allow_descendants_override_disabled_shared_runners, _('cannot be changed if shared runners are enabled'))
end
- if allow_descendants_override_disabled_shared_runners && has_parent? && parent.shared_runners_setting == 'disabled_and_unoverridable'
+ if allow_descendants_override_disabled_shared_runners && has_parent? && parent.shared_runners_setting == SR_DISABLED_AND_UNOVERRIDABLE
errors.add(:allow_descendants_override_disabled_shared_runners, _('cannot be enabled because parent group does not allow it'))
end
end
def shared_runners_setting
if shared_runners_enabled
- 'enabled'
+ SR_ENABLED
else
if allow_descendants_override_disabled_shared_runners
- 'disabled_with_override'
+ SR_DISABLED_WITH_OVERRIDE
else
- 'disabled_and_unoverridable'
+ SR_DISABLED_AND_UNOVERRIDABLE
end
end
end
def shared_runners_setting_higher_than?(other_setting)
- if other_setting == 'enabled'
+ if other_setting == SR_ENABLED
false
- elsif other_setting == 'disabled_with_override'
- shared_runners_setting == 'enabled'
- elsif other_setting == 'disabled_and_unoverridable'
- shared_runners_setting == 'enabled' || shared_runners_setting == 'disabled_with_override'
+ elsif other_setting == SR_DISABLED_WITH_OVERRIDE
+ shared_runners_setting == SR_ENABLED
+ elsif other_setting == SR_DISABLED_AND_UNOVERRIDABLE
+ shared_runners_setting == SR_ENABLED || shared_runners_setting == SR_DISABLED_WITH_OVERRIDE
else
raise ArgumentError
end
@@ -543,21 +561,21 @@ class Namespace < ApplicationRecord
def validate_parent_type
unless has_parent?
- if project?
+ if project_namespace?
errors.add(:parent_id, _('must be set for a project namespace'))
end
return
end
- if parent.project?
+ if parent.project_namespace?
errors.add(:parent_id, _('project namespace cannot be the parent of another namespace'))
end
- if user?
+ if user_namespace?
errors.add(:parent_id, _('cannot not be used for user namespace'))
- elsif group?
- errors.add(:parent_id, _('user namespace cannot be the parent of another namespace')) if parent.user?
+ elsif group_namespace?
+ errors.add(:parent_id, _('user namespace cannot be the parent of another namespace')) if parent.user_namespace?
end
end
@@ -582,6 +600,10 @@ class Namespace < ApplicationRecord
project.track_project_repository
end
end
+
+ def enforce_minimum_path_length?
+ path_changed? && !project_namespace?
+ end
end
Namespace.prepend_mod_with('Namespace')
diff --git a/app/models/namespace/root_storage_statistics.rb b/app/models/namespace/root_storage_statistics.rb
index 73061b7863..99e3253759 100644
--- a/app/models/namespace/root_storage_statistics.rb
+++ b/app/models/namespace/root_storage_statistics.rb
@@ -57,7 +57,7 @@ class Namespace::RootStorageStatistics < ApplicationRecord
end
def attributes_from_personal_snippets
- return {} unless namespace.user?
+ return {} unless namespace.user_namespace?
from_personal_snippets.take.slice(SNIPPETS_SIZE_STAT_NAME)
end
diff --git a/app/models/namespaces/user_namespace.rb b/app/models/namespaces/user_namespace.rb
index 517d68b118..22b7a0a3b2 100644
--- a/app/models/namespaces/user_namespace.rb
+++ b/app/models/namespaces/user_namespace.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# TODO: currently not created/mapped in the database, will be done in another issue
-# https://gitlab.com/gitlab-org/gitlab/-/issues/337102
+# https://gitlab.com/gitlab-org/gitlab/-/issues/341070
module Namespaces
class UserNamespace < Namespace
def self.sti_name
diff --git a/app/models/note.rb b/app/models/note.rb
index a8f5c305d9..3747351889 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -149,7 +149,7 @@ class Note < ApplicationRecord
scope :like_note_or_capitalized_note, ->(text) { where('(note LIKE ? OR note LIKE ?)', text, text.capitalize) }
before_validation :nullify_blank_type, :nullify_blank_line_code
- after_save :keep_around_commit, if: :for_project_noteable?, unless: :importing?
+ after_save :keep_around_commit, if: :for_project_noteable?, unless: -> { importing? || skip_keep_around_commits }
after_save :expire_etag_cache, unless: :importing?
after_save :touch_noteable, unless: :importing?
after_destroy :expire_etag_cache
@@ -355,8 +355,6 @@ class Note < ApplicationRecord
end
def noteable_author?(noteable)
- return false unless ::Feature.enabled?(:show_author_on_note, project)
-
noteable.author == self.author
end
diff --git a/app/models/operations/feature_flag.rb b/app/models/operations/feature_flag.rb
index 46810749b1..7db396bcad 100644
--- a/app/models/operations/feature_flag.rb
+++ b/app/models/operations/feature_flag.rb
@@ -19,13 +19,10 @@ module Operations
default_value_for :active, true
default_value_for :version, :new_version_flag
- # scopes exists only for the first version
- has_many :scopes, class_name: 'Operations::FeatureFlagScope'
# strategies exists only for the second version
has_many :strategies, class_name: 'Operations::FeatureFlags::Strategy'
has_many :feature_flag_issues
has_many :issues, through: :feature_flag_issues
- has_one :default_scope, -> { where(environment_scope: '*') }, class_name: 'Operations::FeatureFlagScope'
validates :project, presence: true
validates :name,
@@ -37,10 +34,7 @@ module Operations
}
validates :name, uniqueness: { scope: :project_id }
validates :description, allow_blank: true, length: 0..255
- validate :first_default_scope, on: :create, if: :has_scopes?
- validate :version_associations
- accepts_nested_attributes_for :scopes, allow_destroy: true
accepts_nested_attributes_for :strategies, allow_destroy: true
scope :ordered, -> { order(:name) }
@@ -56,7 +50,7 @@ module Operations
class << self
def preload_relations
- preload(:scopes, strategies: :scopes)
+ preload(strategies: :scopes)
end
def for_unleash_client(project, environment)
@@ -104,13 +98,6 @@ module Operations
Ability.issues_readable_by_user(issues, current_user)
end
- def execute_hooks(current_user)
- run_after_commit do
- feature_flag_data = Gitlab::DataBuilder::FeatureFlag.build(self, current_user)
- project.execute_hooks(feature_flag_data, :feature_flag_hooks)
- end
- end
-
def hook_attrs
{
id: id,
@@ -119,27 +106,5 @@ module Operations
active: active
}
end
-
- private
-
- def version_associations
- if new_version_flag? && scopes.any?
- errors.add(:version_associations, 'version 2 feature flags may not have scopes')
- end
- end
-
- def first_default_scope
- unless scopes.first.environment_scope == '*'
- errors.add(:default_scope, 'has to be the first element')
- end
- end
-
- def build_default_scope
- scopes.build(environment_scope: '*', active: self.active)
- end
-
- def has_scopes?
- scopes.any?
- end
end
end
diff --git a/app/models/operations/feature_flag_scope.rb b/app/models/operations/feature_flag_scope.rb
deleted file mode 100644
index 9068ca0f58..0000000000
--- a/app/models/operations/feature_flag_scope.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# frozen_string_literal: true
-
-# All of the legacy flags have been removed in 14.1, including all of the
-# `operations_feature_flag_scopes` rows. Therefore, this model and the database
-# table are unused and should be removed.
-
-module Operations
- class FeatureFlagScope < ApplicationRecord
- prepend HasEnvironmentScope
- include Gitlab::Utils::StrongMemoize
-
- self.table_name = 'operations_feature_flag_scopes'
-
- belongs_to :feature_flag
-
- validates :environment_scope, uniqueness: {
- scope: :feature_flag,
- message: "(%{value}) has already been taken"
- }
-
- validates :environment_scope,
- if: :default_scope?, on: :update,
- inclusion: { in: %w(*), message: 'cannot be changed from default scope' }
-
- validates :strategies, feature_flag_strategies: true
-
- before_destroy :prevent_destroy_default_scope, if: :default_scope?
-
- scope :ordered, -> { order(:id) }
- scope :enabled, -> { where(active: true) }
- scope :disabled, -> { where(active: false) }
-
- def self.with_name_and_description
- joins(:feature_flag)
- .select(FeatureFlag.arel_table[:name], FeatureFlag.arel_table[:description])
- end
-
- def self.for_unleash_client(project, environment)
- select_columns = [
- 'DISTINCT ON (operations_feature_flag_scopes.feature_flag_id) operations_feature_flag_scopes.id',
- '(operations_feature_flags.active AND operations_feature_flag_scopes.active) AS active',
- 'operations_feature_flag_scopes.strategies',
- 'operations_feature_flag_scopes.environment_scope',
- 'operations_feature_flag_scopes.created_at',
- 'operations_feature_flag_scopes.updated_at'
- ]
-
- select(select_columns)
- .with_name_and_description
- .where(feature_flag_id: project.operations_feature_flags.select(:id))
- .order(:feature_flag_id)
- .on_environment(environment)
- .reverse_order
- end
-
- private
-
- def default_scope?
- environment_scope_was == '*'
- end
-
- def prevent_destroy_default_scope
- raise ActiveRecord::ReadOnlyRecord, "default scope cannot be destroyed"
- end
- end
-end
diff --git a/app/models/packages/composer/cache_file.rb b/app/models/packages/composer/cache_file.rb
index ecd7596b98..5222101d17 100644
--- a/app/models/packages/composer/cache_file.rb
+++ b/app/models/packages/composer/cache_file.rb
@@ -9,15 +9,13 @@ module Packages
mount_file_store_uploader Packages::Composer::CacheUploader
- belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id'
+ belongs_to :group, -> { where(type: Group.sti_name) }, foreign_key: 'namespace_id'
belongs_to :namespace
validates :namespace, presence: true
scope :with_namespace, ->(namespace) { where(namespace: namespace) }
scope :with_sha, ->(sha) { where(file_sha256: sha) }
- scope :expired, -> { where("delete_at <= ?", Time.current) }
- scope :without_namespace, -> { where(namespace_id: nil) }
end
end
end
diff --git a/app/models/packages/helm/file_metadatum.rb b/app/models/packages/helm/file_metadatum.rb
index 1771003d1f..dfa4ab6df8 100644
--- a/app/models/packages/helm/file_metadatum.rb
+++ b/app/models/packages/helm/file_metadatum.rb
@@ -12,7 +12,7 @@ module Packages
validates :channel,
presence: true,
- length: { maximum: 63 },
+ length: { maximum: 255 },
format: { with: Gitlab::Regex.helm_channel_regex }
validates :metadata,
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index c932d0bf80..0c5a155d48 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -129,18 +129,15 @@ class PagesDomain < ApplicationRecord
store = OpenSSL::X509::Store.new
store.set_default_paths
- # This forces to load all intermediate certificates stored in `certificate`
- Tempfile.open('certificate_chain') do |f|
- f.write(certificate)
- f.flush
- store.add_file(f.path)
- end
-
- store.verify(x509)
+ store.verify(x509, untrusted_ca_certs_bundle)
rescue OpenSSL::X509::StoreError
false
end
+ def untrusted_ca_certs_bundle
+ ::Gitlab::X509::Certificate.load_ca_certs_bundle(certificate)
+ end
+
def expired?
return false unless x509
diff --git a/app/models/preloaders/merge_requests_preloader.rb b/app/models/preloaders/merge_requests_preloader.rb
deleted file mode 100644
index cefe8408ca..0000000000
--- a/app/models/preloaders/merge_requests_preloader.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-module Preloaders
- class MergeRequestsPreloader
- attr_reader :merge_requests
-
- def initialize(merge_requests)
- @merge_requests = merge_requests
- end
-
- def execute
- preloader = ActiveRecord::Associations::Preloader.new
- preloader.preload(merge_requests, { target_project: [:project_feature] })
- merge_requests.each do |merge_request|
- merge_request.lazy_upvotes_count
- end
- end
- end
-end
diff --git a/app/models/product_analytics_event.rb b/app/models/product_analytics_event.rb
index d2026d3b33..52baa3be6c 100644
--- a/app/models/product_analytics_event.rb
+++ b/app/models/product_analytics_event.rb
@@ -20,8 +20,6 @@ class ProductAnalyticsEvent < ApplicationRecord
where('collector_tstamp BETWEEN ? AND ? ', today - duration + 1, today + 1)
}
- scope :by_category_and_action, ->(category, action) { where(se_category: category, se_action: action) }
-
def self.count_by_graph(graph, days)
group(graph).timerange(days).count
end
diff --git a/app/models/project.rb b/app/models/project.rb
index f68fdadf51..2ceba10e86 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -98,6 +98,7 @@ class Project < ApplicationRecord
before_validation :mark_remote_mirrors_for_removal, if: -> { RemoteMirror.table_exists? }
before_save :ensure_runners_token
+ before_save :ensure_project_namespace_in_sync
after_save :update_project_statistics, if: :saved_change_to_namespace_id?
@@ -128,26 +129,9 @@ class Project < ApplicationRecord
after_initialize :use_hashed_storage
after_create :check_repository_absence!
- # Required during the `ActsAsTaggableOn::Tag -> Topic` migration
- # TODO: remove 'acts_as_ordered_taggable_on' and ':topics_acts_as_taggable' in the further process of the migration
- # https://gitlab.com/gitlab-org/gitlab/-/issues/335946
- acts_as_ordered_taggable_on :topics
- has_many :topics_acts_as_taggable, -> { order("#{ActsAsTaggableOn::Tagging.table_name}.id") },
- class_name: 'ActsAsTaggableOn::Tag',
- through: :topic_taggings,
- source: :tag
-
has_many :project_topics, -> { order(:id) }, class_name: 'Projects::ProjectTopic'
has_many :topics, through: :project_topics, class_name: 'Projects::Topic'
- # Required during the `ActsAsTaggableOn::Tag -> Topic` migration
- # TODO: remove 'topics' in the further process of the migration
- # https://gitlab.com/gitlab-org/gitlab/-/issues/335946
- alias_method :topics_new, :topics
- def topics
- self.topics_acts_as_taggable + self.topics_new
- end
-
attr_accessor :old_path_with_namespace
attr_accessor :template_name
attr_writer :pipeline_status
@@ -159,11 +143,11 @@ class Project < ApplicationRecord
# Relations
belongs_to :pool_repository
belongs_to :creator, class_name: 'User'
- belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id'
+ belongs_to :group, -> { where(type: Group.sti_name) }, foreign_key: 'namespace_id'
belongs_to :namespace
# Sync deletion via DB Trigger to ensure we do not have
# a project without a project_namespace (or vice-versa)
- belongs_to :project_namespace, class_name: 'Namespaces::ProjectNamespace', foreign_key: 'project_namespace_id', inverse_of: :project
+ belongs_to :project_namespace, autosave: true, class_name: 'Namespaces::ProjectNamespace', foreign_key: 'project_namespace_id', inverse_of: :project
alias_method :parent, :namespace
alias_attribute :parent_id, :namespace_id
@@ -233,6 +217,7 @@ class Project < ApplicationRecord
has_one :import_state, autosave: true, class_name: 'ProjectImportState', inverse_of: :project
has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :export_jobs, class_name: 'ProjectExportJob'
+ has_many :bulk_import_exports, class_name: 'BulkImports::Export', inverse_of: :project
has_one :project_repository, inverse_of: :project
has_one :tracing_setting, class_name: 'ProjectTracingSetting'
has_one :incident_management_setting, inverse_of: :project, class_name: 'IncidentManagement::ProjectIncidentManagementSetting'
@@ -652,15 +637,8 @@ class Project < ApplicationRecord
scope :with_topic, ->(topic_name) do
topic = Projects::Topic.find_by_name(topic_name)
- acts_as_taggable_on_topic = ActsAsTaggableOn::Tag.find_by_name(topic_name)
- return none unless topic || acts_as_taggable_on_topic
-
- relations = []
- relations << where(id: topic.project_topics.select(:project_id)) if topic
- relations << where(id: acts_as_taggable_on_topic.taggings.select(:taggable_id)) if acts_as_taggable_on_topic
-
- Project.from_union(relations)
+ topic ? where(id: topic.project_topics.select(:project_id)) : none
end
enum auto_cancel_pending_pipelines: { disabled: 0, enabled: 1 }
@@ -678,7 +656,7 @@ class Project < ApplicationRecord
mount_uploader :bfg_object_map, AttachmentUploader
def self.with_api_entity_associations
- preload(:project_feature, :route, :topics, :topics_acts_as_taggable, :group, :timelogs, namespace: [:route, :owner])
+ preload(:project_feature, :route, :topics, :group, :timelogs, namespace: [:route, :owner])
end
def self.with_web_entity_associations
@@ -851,7 +829,7 @@ class Project < ApplicationRecord
end
def group_ids
- joins(:namespace).where(namespaces: { type: 'Group' }).select(:namespace_id)
+ joins(:namespace).where(namespaces: { type: Group.sti_name }).select(:namespace_id)
end
# Returns ids of projects with issuables available for given user
@@ -1200,7 +1178,7 @@ class Project < ApplicationRecord
end
def import?
- external_import? || forked? || gitlab_project_import? || jira_import? || bare_repository_import?
+ external_import? || forked? || gitlab_project_import? || jira_import? || bare_repository_import? || gitlab_project_migration?
end
def external_import?
@@ -1223,6 +1201,10 @@ class Project < ApplicationRecord
import_type == 'gitlab_project'
end
+ def gitlab_project_migration?
+ import_type == 'gitlab_project_migration'
+ end
+
def gitea_import?
import_type == 'gitea'
end
@@ -1327,11 +1309,21 @@ class Project < ApplicationRecord
def changing_shared_runners_enabled_is_allowed
return unless new_record? || changes.has_key?(:shared_runners_enabled)
- if shared_runners_enabled && group && group.shared_runners_setting == 'disabled_and_unoverridable'
+ if shared_runners_setting_conflicting_with_group?
errors.add(:shared_runners_enabled, _('cannot be enabled because parent group does not allow it'))
end
end
+ def shared_runners_setting_conflicting_with_group?
+ shared_runners_enabled && group&.shared_runners_setting == Namespace::SR_DISABLED_AND_UNOVERRIDABLE
+ end
+
+ def reconcile_shared_runners_setting!
+ if shared_runners_setting_conflicting_with_group?
+ self.shared_runners_enabled = false
+ end
+ end
+
def to_param
if persisted? && errors.include?(:path)
path_was
@@ -1814,7 +1806,7 @@ class Project < ApplicationRecord
def open_issues_count(current_user = nil)
return Projects::OpenIssuesCountService.new(self, current_user).count unless current_user.nil?
- BatchLoader.for(self).batch(replace_methods: false) do |projects, loader|
+ BatchLoader.for(self).batch do |projects, loader|
issues_count_per_project = ::Projects::BatchOpenIssuesCountService.new(projects).refresh_cache_and_retrieve_data
issues_count_per_project.each do |project, count|
@@ -2279,7 +2271,7 @@ class Project < ApplicationRecord
# rubocop: disable CodeReuse/ServiceClass
def forks_count
- BatchLoader.for(self).batch(replace_methods: false) do |projects, loader|
+ BatchLoader.for(self).batch do |projects, loader|
fork_count_per_project = ::Projects::BatchForksCountService.new(projects).refresh_cache_and_retrieve_data
fork_count_per_project.each do |project, count|
@@ -2418,7 +2410,7 @@ class Project < ApplicationRecord
end
def mark_primary_write_location
- ::Gitlab::Database::LoadBalancing::Sticking.mark_primary_write_location(:project, self.id)
+ self.class.sticking.mark_primary_write_location(:project, self.id)
end
def toggle_ci_cd_settings!(settings_attribute)
@@ -2677,10 +2669,6 @@ class Project < ApplicationRecord
ProjectStatistics.increment_statistic(self, statistic, delta)
end
- def merge_requests_author_approval
- !!read_attribute(:merge_requests_author_approval)
- end
-
def ci_forward_deployment_enabled?
return false unless ci_cd_settings
@@ -2749,15 +2737,9 @@ class Project < ApplicationRecord
@topic_list = @topic_list.split(',') if @topic_list.instance_of?(String)
@topic_list = @topic_list.map(&:strip).uniq.reject(&:empty?)
- if @topic_list != self.topic_list || self.topics_acts_as_taggable.any?
- self.topics_new.delete_all
+ if @topic_list != self.topic_list
+ self.topics.delete_all
self.topics = @topic_list.map { |topic| Projects::Topic.find_or_create_by(name: topic) }
-
- # Remove old topics (ActsAsTaggableOn::Tag)
- # Required during the `ActsAsTaggableOn::Tag -> Topic` migration
- # TODO: remove in the further process of the migration
- # https://gitlab.com/gitlab-org/gitlab/-/issues/335946
- self.topic_taggings.clear
end
@topic_list = nil
@@ -2927,6 +2909,15 @@ class Project < ApplicationRecord
def online_runners_with_tags
@online_runners_with_tags ||= active_runners.with_tags.online
end
+
+ def ensure_project_namespace_in_sync
+ if changes.keys & [:name, :path, :namespace_id, :visibility_level] && project_namespace.present?
+ project_namespace.name = name
+ project_namespace.path = path
+ project_namespace.parent = namespace
+ project_namespace.visibility_level = visibility_level
+ end
+ end
end
Project.prepend_mod_with('Project')
diff --git a/app/models/project_setting.rb b/app/models/project_setting.rb
index b2559636f3..24d892290a 100644
--- a/app/models/project_setting.rb
+++ b/app/models/project_setting.rb
@@ -1,10 +1,6 @@
# frozen_string_literal: true
class ProjectSetting < ApplicationRecord
- include IgnorableColumns
-
- ignore_column :allow_editing_commit_messages, remove_with: '14.4', remove_after: '2021-09-10'
-
belongs_to :project, inverse_of: :project_setting
enum squash_option: {
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index 387732cf15..99cec647a9 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -31,7 +31,6 @@ class ProjectStatistics < ApplicationRecord
scope :for_project_ids, ->(project_ids) { where(project_id: project_ids) }
scope :for_namespaces, -> (namespaces) { where(namespace: namespaces) }
- scope :with_any_ci_minutes_used, -> { where.not(shared_runners_seconds: 0) }
def total_repository_size
repository_size + lfs_objects_size
@@ -70,7 +69,7 @@ class ProjectStatistics < ApplicationRecord
end
def update_lfs_objects_size
- self.lfs_objects_size = project.lfs_objects.sum(:size)
+ self.lfs_objects_size = LfsObject.joins(:lfs_objects_projects).where(lfs_objects_projects: { project_id: project.id }).sum(:size)
end
def update_uploads_size
diff --git a/app/models/projects/project_topic.rb b/app/models/projects/project_topic.rb
index d4b456ef48..7021a48646 100644
--- a/app/models/projects/project_topic.rb
+++ b/app/models/projects/project_topic.rb
@@ -3,6 +3,6 @@
module Projects
class ProjectTopic < ApplicationRecord
belongs_to :project
- belongs_to :topic
+ belongs_to :topic, counter_cache: :total_projects_count
end
end
diff --git a/app/models/projects/topic.rb b/app/models/projects/topic.rb
index a17aa550ed..f3352ecc5e 100644
--- a/app/models/projects/topic.rb
+++ b/app/models/projects/topic.rb
@@ -1,10 +1,30 @@
# frozen_string_literal: true
+require 'carrierwave/orm/activerecord'
+
module Projects
class Topic < ApplicationRecord
+ include Avatarable
+ include Gitlab::SQL::Pattern
+
validates :name, presence: true, uniqueness: true, length: { maximum: 255 }
+ validates :description, length: { maximum: 1024 }
has_many :project_topics, class_name: 'Projects::ProjectTopic'
has_many :projects, through: :project_topics
+
+ scope :order_by_total_projects_count, -> { order(total_projects_count: :desc).order(id: :asc) }
+ scope :reorder_by_similarity, -> (search) do
+ order_expression = Gitlab::Database::SimilarityScore.build_expression(search: search, rules: [
+ { column: arel_table['name'] }
+ ])
+ reorder(order_expression.desc, arel_table['total_projects_count'].desc, arel_table['id'])
+ end
+
+ class << self
+ def search(query)
+ fuzzy_search(query, [:name])
+ end
+ end
end
end
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 3d32144e0f..96002c8668 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -10,6 +10,8 @@ class ProtectedBranch < ApplicationRecord
scope :allowing_force_push,
-> { where(allow_force_push: true) }
+ scope :get_ids_by_name, -> (name) { where(name: name).pluck(:id) }
+
protected_ref_access_levels :merge, :push
def self.protected_ref_accessible_to?(ref, user, project:, action:, protected_refs: nil)
@@ -25,12 +27,17 @@ class ProtectedBranch < ApplicationRecord
# Check if branch name is marked as protected in the system
def self.protected?(project, ref_name)
return true if project.empty_repo? && project.default_branch_protected?
+ return false if ref_name.blank?
- Rails.cache.fetch("protected_ref-#{ref_name}-#{project.cache_key}") do
+ Rails.cache.fetch(protected_ref_cache_key(project, ref_name)) do
self.matching(ref_name, protected_refs: protected_refs(project)).present?
end
end
+ def self.protected_ref_cache_key(project, ref_name)
+ "protected_ref-#{project.cache_key}-#{Digest::SHA1.hexdigest(ref_name)}"
+ end
+
def self.allow_force_push?(project, ref_name)
project.protected_branches.allowing_force_push.matching(ref_name).any?
end
diff --git a/app/models/release.rb b/app/models/release.rb
index 0dd71c6ebf..eac6346cc6 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -33,6 +33,7 @@ class Release < ApplicationRecord
includes(:author, :evidences, :milestones, :links, :sorted_links,
project: [:project_feature, :route, { namespace: :route }])
}
+ scope :with_milestones, -> { joins(:milestone_releases) }
scope :recent, -> { sorted.limit(MAX_NUMBER_TO_DISPLAY) }
scope :without_evidence, -> { left_joins(:evidences).where(::Releases::Evidence.arel_table[:id].eq(nil)) }
scope :released_within_2hrs, -> { where(released_at: Time.zone.now - 1.hour..Time.zone.now + 1.hour) }
diff --git a/app/models/repository.rb b/app/models/repository.rb
index f20b306c80..119d874a6e 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -732,7 +732,7 @@ class Repository
end
def tags_sorted_by(value)
- return raw_repository.tags(sort_by: value) if Feature.enabled?(:gitaly_tags_finder, project, default_enabled: :yaml)
+ return raw_repository.tags(sort_by: value) if Feature.enabled?(:tags_finder_gitaly, project, default_enabled: :yaml)
tags_ruby_sort(value)
end
@@ -1054,10 +1054,10 @@ class Repository
end
def squash(user, merge_request, message)
- raw.squash(user, merge_request.id, start_sha: merge_request.diff_start_sha,
- end_sha: merge_request.diff_head_sha,
- author: merge_request.author,
- message: message)
+ raw.squash(user, start_sha: merge_request.diff_start_sha,
+ end_sha: merge_request.diff_head_sha,
+ author: merge_request.author,
+ message: message)
end
def submodule_links
diff --git a/app/models/upload.rb b/app/models/upload.rb
index 0a4acdfc7e..c1a3df8245 100644
--- a/app/models/upload.rb
+++ b/app/models/upload.rb
@@ -18,6 +18,8 @@ class Upload < ApplicationRecord
before_save :calculate_checksum!, if: :foreground_checksummable?
after_commit :schedule_checksum, if: :needs_checksum?
+ after_commit :update_project_statistics, on: [:create, :destroy], if: :project?
+
# as the FileUploader is not mounted, the default CarrierWave ActiveRecord
# hooks are not executed and the file will not be deleted
after_destroy :delete_file!, if: -> { uploader_class <= FileUploader }
@@ -67,7 +69,7 @@ class Upload < ApplicationRecord
self.checksum = nil
return unless needs_checksum?
- self.checksum = self.class.hexdigest(absolute_path)
+ self.checksum = self.class.sha256_hexdigest(absolute_path)
end
# Initialize the associated Uploader class with current model
@@ -161,6 +163,14 @@ class Upload < ApplicationRecord
def mount_point
super&.to_sym
end
+
+ def project?
+ model_type == "Project"
+ end
+
+ def update_project_statistics
+ ProjectCacheWorker.perform_async(model_id, [], [:uploads_size])
+ end
end
Upload.prepend_mod_with('Upload')
diff --git a/app/models/user.rb b/app/models/user.rb
index 71db475816..0e19e6e4a7 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -112,7 +112,14 @@ class User < ApplicationRecord
#
# Namespace for personal projects
- has_one :namespace, -> { where(type: nil) }, dependent: :destroy, foreign_key: :owner_id, inverse_of: :owner, autosave: true # rubocop:disable Cop/ActiveRecordDependent
+ # TODO: change to `:namespace, -> { where(type: Namespaces::UserNamespace.sti_name}, class_name: 'Namespaces::UserNamespace'...`
+ # when working on issue https://gitlab.com/gitlab-org/gitlab/-/issues/341070
+ has_one :namespace,
+ -> { where(type: [nil, Namespaces::UserNamespace.sti_name]) },
+ dependent: :destroy, # rubocop:disable Cop/ActiveRecordDependent
+ foreign_key: :owner_id,
+ inverse_of: :owner,
+ autosave: true # rubocop:disable Cop/ActiveRecordDependent
# Profile
has_many :keys, -> { regular_keys }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
@@ -229,9 +236,9 @@ class User < ApplicationRecord
validates :first_name, length: { maximum: 127 }
validates :last_name, length: { maximum: 127 }
validates :email, confirmation: true
- validates :notification_email, devise_email: true, allow_blank: true, if: ->(user) { user.notification_email != user.email }
+ validates :notification_email, devise_email: true, allow_blank: true
validates :public_email, uniqueness: true, devise_email: true, allow_blank: true
- validates :commit_email, devise_email: true, allow_blank: true, if: ->(user) { user.commit_email != user.email && user.commit_email != Gitlab::PrivateCommitEmail::TOKEN }
+ validates :commit_email, devise_email: true, allow_blank: true, unless: ->(user) { user.commit_email == Gitlab::PrivateCommitEmail::TOKEN }
validates :projects_limit,
presence: true,
numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Gitlab::Database::MAX_INT_VALUE }
@@ -316,6 +323,7 @@ class User < ApplicationRecord
delegate :webauthn_xid, :webauthn_xid=, to: :user_detail, allow_nil: true
delegate :pronouns, :pronouns=, to: :user_detail, allow_nil: true
delegate :pronunciation, :pronunciation=, to: :user_detail, allow_nil: true
+ delegate :registration_objective, :registration_objective=, to: :user_detail, allow_nil: true
accepts_nested_attributes_for :user_preference, update_only: true
accepts_nested_attributes_for :user_detail, update_only: true
@@ -449,11 +457,12 @@ class User < ApplicationRecord
scope :dormant, -> { active.where('last_activity_on <= ?', MINIMUM_INACTIVE_DAYS.day.ago.to_date) }
scope :with_no_activity, -> { active.where(last_activity_on: nil) }
scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) }
+ scope :get_ids_by_username, -> (username) { where(username: username).pluck(:id) }
def preferred_language
read_attribute('preferred_language') ||
I18n.default_locale.to_s.presence_in(Gitlab::I18n.available_locales) ||
- 'en'
+ default_preferred_language
end
def active_for_authentication?
@@ -728,7 +737,7 @@ class User < ApplicationRecord
end
def find_by_full_path(path, follow_redirects: false)
- namespace = Namespace.for_user.find_by_full_path(path, follow_redirects: follow_redirects)
+ namespace = Namespace.user_namespaces.find_by_full_path(path, follow_redirects: follow_redirects)
namespace&.owner
end
@@ -1434,7 +1443,10 @@ class User < ApplicationRecord
namespace.path = username if username_changed?
namespace.name = name if name_changed?
else
- namespace = build_namespace(path: username, name: name)
+ # TODO: we should no longer need the `type` parameter once we can make the
+ # the `has_one :namespace` association use the correct class.
+ # issue https://gitlab.com/gitlab-org/gitlab/-/issues/341070
+ namespace = build_namespace(path: username, name: name, type: ::Namespaces::UserNamespace.sti_name)
namespace.build_namespace_settings
end
end
@@ -2003,6 +2015,11 @@ class User < ApplicationRecord
private
+ # To enable JiHu repository to modify the default language options
+ def default_preferred_language
+ 'en'
+ end
+
def notification_email_verified
return if notification_email.blank? || temp_oauth_email?
@@ -2094,10 +2111,14 @@ class User < ApplicationRecord
errors.add(:email, error) if error
end
+ def signup_email_invalid_message
+ _('is not allowed for sign-up.')
+ end
+
def check_username_format
return if username.blank? || Mime::EXTENSION_LOOKUP.keys.none? { |type| username.end_with?(".#{type}") }
- errors.add(:username, _('ending with a file extension is not allowed.'))
+ errors.add(:username, _('ending with a reserved file extension is not allowed.'))
end
def groups_with_developer_maintainer_project_access
diff --git a/app/models/user_callout.rb b/app/models/user_callout.rb
index 04bc29755f..b990aedd4f 100644
--- a/app/models/user_callout.rb
+++ b/app/models/user_callout.rb
@@ -36,7 +36,8 @@ class UserCallout < ApplicationRecord
trial_status_reminder_d3: 35, # EE-only
security_configuration_devops_alert: 36, # EE-only
profile_personal_access_token_expiry: 37, # EE-only
- terraform_notification_dismissed: 38
+ terraform_notification_dismissed: 38,
+ security_newsletter_callout: 39
}
validates :feature_name,
diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb
index c41cff6786..6b0ed89c68 100644
--- a/app/models/user_detail.rb
+++ b/app/models/user_detail.rb
@@ -3,7 +3,10 @@
class UserDetail < ApplicationRecord
extend ::Gitlab::Utils::Override
include IgnorableColumns
- ignore_columns %i[bio_html cached_markdown_version], remove_with: '13.6', remove_after: '2021-10-22'
+
+ ignore_columns %i[bio_html cached_markdown_version], remove_with: '14.5', remove_after: '2021-10-22'
+
+ REGISTRATION_OBJECTIVE_PAIRS = { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5, joining_team: 6 }.freeze
belongs_to :user
@@ -14,6 +17,8 @@ class UserDetail < ApplicationRecord
before_save :prevent_nil_bio
+ enum registration_objective: REGISTRATION_OBJECTIVE_PAIRS, _suffix: true
+
private
def prevent_nil_bio
diff --git a/app/models/user_highest_role.rb b/app/models/user_highest_role.rb
index 4853fc3d24..dd5c85a5a8 100644
--- a/app/models/user_highest_role.rb
+++ b/app/models/user_highest_role.rb
@@ -3,7 +3,13 @@
class UserHighestRole < ApplicationRecord
belongs_to :user, optional: false
- validates :highest_access_level, allow_nil: true, inclusion: { in: Gitlab::Access.all_values }
+ validates :highest_access_level, allow_nil: true, inclusion: { in: ->(_) { self.allowed_values } }
scope :with_highest_access_level, -> (highest_access_level) { where(highest_access_level: highest_access_level) }
+
+ def self.allowed_values
+ Gitlab::Access.all_values
+ end
end
+
+UserHighestRole.prepend_mod
diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb
index 337ae7125f..7687430cfd 100644
--- a/app/models/user_preference.rb
+++ b/app/models/user_preference.rb
@@ -23,7 +23,6 @@ class UserPreference < ApplicationRecord
ignore_columns :experience_level, remove_with: '14.10', remove_after: '2021-03-22'
default_value_for :tab_width, value: Gitlab::TabWidth::DEFAULT, allows_nil: false
- default_value_for :timezone, value: Time.zone.tzinfo.name, allows_nil: false
default_value_for :time_display_relative, value: true, allows_nil: false
default_value_for :time_format_in_24h, value: false, allows_nil: false
default_value_for :render_whitespace_in_code, value: false, allows_nil: false
diff --git a/app/models/users/credit_card_validation.rb b/app/models/users/credit_card_validation.rb
index 5e255acd88..a4cc43d1f1 100644
--- a/app/models/users/credit_card_validation.rb
+++ b/app/models/users/credit_card_validation.rb
@@ -7,5 +7,18 @@ module Users
self.table_name = 'user_credit_card_validations'
belongs_to :user
+
+ validates :holder_name, length: { maximum: 26 }
+ validates :last_digits, allow_nil: true, numericality: {
+ greater_than_or_equal_to: 0, less_than_or_equal_to: 9999
+ }
+
+ def similar_records
+ self.class.where(
+ expiration_date: expiration_date,
+ last_digits: last_digits,
+ holder_name: holder_name
+ ).order(credit_card_validated_at: :desc).includes(:user)
+ end
end
end
diff --git a/app/policies/ci/resource_group_policy.rb b/app/policies/ci/resource_group_policy.rb
new file mode 100644
index 0000000000..ef384265b1
--- /dev/null
+++ b/app/policies/ci/resource_group_policy.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module Ci
+ class ResourceGroupPolicy < BasePolicy
+ delegate { @subject.project }
+ end
+end
diff --git a/app/policies/clusters/agent_policy.rb b/app/policies/clusters/agent_policy.rb
new file mode 100644
index 0000000000..25e78c8480
--- /dev/null
+++ b/app/policies/clusters/agent_policy.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Clusters
+ class AgentPolicy < BasePolicy
+ alias_method :cluster_agent, :subject
+
+ delegate { cluster_agent.project }
+ end
+end
diff --git a/app/policies/clusters/agent_token_policy.rb b/app/policies/clusters/agent_token_policy.rb
new file mode 100644
index 0000000000..e876ecfac2
--- /dev/null
+++ b/app/policies/clusters/agent_token_policy.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Clusters
+ class AgentTokenPolicy < BasePolicy
+ alias_method :token, :subject
+
+ delegate { token.agent }
+ end
+end
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 7abffd2c35..64395f69c4 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -134,6 +134,8 @@ class GroupPolicy < BasePolicy
enable :create_package
enable :create_package_settings
enable :developer_access
+ enable :admin_organization
+ enable :admin_contact
end
rule { reporter }.policy do
@@ -147,7 +149,6 @@ class GroupPolicy < BasePolicy
enable :read_prometheus
enable :read_package
enable :read_package_settings
- enable :admin_organization
end
rule { maintainer }.policy do
@@ -162,7 +163,6 @@ class GroupPolicy < BasePolicy
enable :admin_cluster
enable :read_deploy_token
enable :create_jira_connect_subscription
- enable :update_runners_registration_token
enable :maintainer_access
end
@@ -179,6 +179,7 @@ class GroupPolicy < BasePolicy
enable :update_default_branch_protection
enable :create_deploy_token
enable :destroy_deploy_token
+ enable :update_runners_registration_token
enable :owner_access
end
diff --git a/app/policies/list_policy.rb b/app/policies/list_policy.rb
new file mode 100644
index 0000000000..9784574654
--- /dev/null
+++ b/app/policies/list_policy.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ListPolicy < BasePolicy # rubocop:disable Gitlab/NamespacedClass
+ delegate { @subject.board.resource_parent }
+end
diff --git a/app/policies/namespace_policy.rb b/app/policies/namespace_policy.rb
index dcbeda9f5d..0cf1bcb973 100644
--- a/app/policies/namespace_policy.rb
+++ b/app/policies/namespace_policy.rb
@@ -1,26 +1,9 @@
# frozen_string_literal: true
-class NamespacePolicy < BasePolicy
- rule { anonymous }.prevent_all
-
- condition(:personal_project, scope: :subject) { @subject.kind == 'user' }
- condition(:can_create_personal_project, scope: :user) { @user.can_create_project? }
- condition(:owner) { @subject.owner == @user }
-
- rule { owner | admin }.policy do
- enable :owner_access
- enable :create_projects
- enable :admin_namespace
- enable :read_namespace
- enable :read_statistics
- enable :create_jira_connect_subscription
- enable :create_package_settings
- enable :read_package_settings
- end
-
- rule { personal_project & ~can_create_personal_project }.prevent :create_projects
-
- rule { (owner | admin) & can?(:create_projects) }.enable :transfer_projects
+class NamespacePolicy < ::Namespaces::UserNamespacePolicy
+ # NamespacePolicy has been traditionally for user namespaces.
+ # So these policies have been moved into Namespaces::UserNamespacePolicy.
+ # Once the user namespace conversion is complete, we can look at
+ # either removing this file or locating common namespace policy items
+ # here.
end
-
-NamespacePolicy.prepend_mod_with('NamespacePolicy')
diff --git a/app/policies/namespaces/project_namespace_policy.rb b/app/policies/namespaces/project_namespace_policy.rb
new file mode 100644
index 0000000000..bc08a7a45e
--- /dev/null
+++ b/app/policies/namespaces/project_namespace_policy.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Namespaces
+ class ProjectNamespacePolicy < BasePolicy
+ # For now users are not granted any permissions on project namespace
+ # as it's completely hidden to them. When we start using project
+ # namespaces in queries, we will have to extend this policy.
+ end
+end
diff --git a/app/policies/namespaces/user_namespace_policy.rb b/app/policies/namespaces/user_namespace_policy.rb
new file mode 100644
index 0000000000..f8b285e531
--- /dev/null
+++ b/app/policies/namespaces/user_namespace_policy.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Namespaces
+ class UserNamespacePolicy < BasePolicy
+ rule { anonymous }.prevent_all
+
+ condition(:personal_project, scope: :subject) { @subject.kind == 'user' }
+ condition(:can_create_personal_project, scope: :user) { @user.can_create_project? }
+ condition(:owner) { @subject.owner == @user }
+
+ rule { owner | admin }.policy do
+ enable :owner_access
+ enable :create_projects
+ enable :admin_namespace
+ enable :read_namespace
+ enable :read_statistics
+ enable :create_jira_connect_subscription
+ enable :create_package_settings
+ enable :read_package_settings
+ end
+
+ rule { personal_project & ~can_create_personal_project }.prevent :create_projects
+
+ rule { (owner | admin) & can?(:create_projects) }.enable :transfer_projects
+ end
+end
+
+Namespaces::UserNamespacePolicy.prepend_mod_with('Namespaces::UserNamespacePolicy')
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index b40bfbf5a7..87573c9ad1 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -358,6 +358,8 @@ class ProjectPolicy < BasePolicy
enable :update_commit_status
enable :create_build
enable :update_build
+ enable :read_resource_group
+ enable :update_resource_group
enable :create_merge_request_from
enable :create_wiki
enable :push_code
@@ -437,6 +439,7 @@ class ProjectPolicy < BasePolicy
enable :destroy_freeze_period
enable :admin_feature_flags_client
enable :update_runners_registration_token
+ enable :manage_project_google_cloud
end
rule { public_project & metrics_dashboard_allowed }.policy do
diff --git a/app/presenters/README.md b/app/presenters/README.md
index 62aec4fc8a..dfd1818f97 100644
--- a/app/presenters/README.md
+++ b/app/presenters/README.md
@@ -66,14 +66,15 @@ we gain the following benefits:
### Presenter definition
-Every presenter should inherit from `Gitlab::View::Presenter::Simple`, which
-provides a `.presents` the method which allows you to define an accessor for the
+If you need a presenter class that has only necessary interfaces for the view-related context,
+inherit from `Gitlab::View::Presenter::Simple`.
+It provides a `.presents` the method which allows you to define an accessor for the
presented object. It also includes common helpers like `Gitlab::Routing` and
`Gitlab::Allowable`.
```ruby
class LabelPresenter < Gitlab::View::Presenter::Simple
- presents :label
+ presents ::Label, as: :label
def text_color
label.color.to_s
@@ -85,13 +86,14 @@ class LabelPresenter < Gitlab::View::Presenter::Simple
end
```
-In some cases, it can be more practical to transparently delegate all missing
-method calls to the presented object, in these cases, you can make your
-presenter inherit from `Gitlab::View::Presenter::Delegated`:
+If you need a presenter class that delegates missing method calls to the presented object,
+inherit from `Gitlab::View::Presenter::Delegated`.
+This is more like an "extension" in the sense that the produced object is going to have
+all of interfaces of the presented object **AND** all of the interfaces in the presenter class:
```ruby
class LabelPresenter < Gitlab::View::Presenter::Delegated
- presents :label
+ presents ::Label, as: :label
def text_color
# color is delegated to label
@@ -152,3 +154,82 @@ You can also present the model in the view:
%div{ class: label.text_color }
= render partial: label, label: label
```
+
+### Validate accidental overrides
+
+We use presenters in many places, such as Controller, Haml, GraphQL/Rest API,
+it's very handy to extend the core/backend logic of Active Record models,
+however, there is a risk that it accidentally overrides important logic.
+
+For example, [this production incident](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/5498)
+was caused by [including `ActionView::Helpers::UrlHelper` in a presenter](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69537/diffs#4b581cff00ef3cc9780efd23682af383de302e7d_3_3).
+The `tag` accesor in `Ci::Build` was accidentally overridden by `ActionView::Helpers::TagHelper#tag`,
+and as a conseuqence, a wrong `tag` value was persited into database.
+
+Starting from GitLab 14.4, we validate the presenters (specifically all of the subclasses of `Gitlab::View::Presenter::Delegated`)
+that they do not accidentally override core/backend logic. In such case, a pipeline in merge requests fails with an error message,
+here is an example:
+
+```plaintext
+We've detected that a presetner is overriding a specific method(s) on a subject model.
+There is a risk that it accidentally modifies the backend/core logic that leads to production incident.
+Please follow https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/presenters/README.md#validate-accidental-overrides
+to resolve this error with caution.
+
+Here are the conflict details.
+
+- Ci::PipelinePresenter#tag is overriding Ci::Pipeline#tag. delegator_location: /devkitkat/services/rails/cache/ruby/2.7.0/gems/actionview-6.1.3.2/lib/action_view/helpers/tag_helper.rb:271 original_location: /devkitkat/services/rails/cache/ruby/2.7.0/gems/activemodel-6.1.3.2/lib/active_model/attribute_methods.rb:254
+```
+
+Here are the potential solutions:
+
+- If the conflict happens on an instance method in the presenter:
+ - If you intend to override the core/backend logic, define `delegator_override ` on top of the conflicted method.
+ This explicitly adds the method to an allowlist.
+ - If you do NOT intend to override the core/backend logic, rename the method name in the presenter.
+- If the conflict happens on an included module in the presenter, remove the module from the presenter and find a workaround.
+
+### How to use the `Gitlab::Utils::DelegatorOverride` validator
+
+If a presenter class inhertis from `Gitlab::View::Presenter::Delegated`,
+you should define what object class is presented:
+
+```ruby
+class WebHookLogPresenter < Gitlab::View::Presenter::Delegated
+ presents ::WebHookLog, as: :web_hook_log # This defines that the presenter presents `WebHookLog` Active Record model.
+```
+
+These presenters are validated not to accidentaly override the methods in the presented object.
+You can run the validation locally with:
+
+```shell
+bundle exec rake lint:static_verification
+```
+
+To add a method to an allowlist, use `delegator_override`. For example:
+
+```ruby
+class VulnerabilityPresenter < Gitlab::View::Presenter::Delegated
+ presents ::Vulnerability, as: :vulnerability
+
+ delegator_override :description # This adds the `description` method to an allowlist that the override is intentional.
+ def description
+ vulnerability.description || finding.description
+ end
+```
+
+To add methods of a module to an allowlist, use `delegator_override_with`. For example:
+
+```ruby
+module Ci
+ class PipelinePresenter < Gitlab::View::Presenter::Delegated
+ include Gitlab::Utils::StrongMemoize
+ include ActionView::Helpers::UrlHelper
+
+ delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
+ delegator_override_with ActionView::Helpers::TagHelper # TODO: Remove `ActionView::Helpers::UrlHelper` inclusion as it overrides `Ci::Pipeline#tag`
+```
+
+Keep in mind that if you use `delegator_override_with`,
+there is a high chance that you're doing **something wrong**.
+Read the [Validate Accidental Overrides](#validate-accidental-overrides) for more information.
diff --git a/app/presenters/alert_management/alert_presenter.rb b/app/presenters/alert_management/alert_presenter.rb
index c6c6fe837a..86fe985927 100644
--- a/app/presenters/alert_management/alert_presenter.rb
+++ b/app/presenters/alert_management/alert_presenter.rb
@@ -2,10 +2,12 @@
module AlertManagement
class AlertPresenter < Gitlab::View::Presenter::Delegated
- include Gitlab::Utils::StrongMemoize
include IncidentManagement::Settings
include ActionView::Helpers::UrlHelper
+ presents ::AlertManagement::Alert
+ delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
+
MARKDOWN_LINE_BREAK = " \n"
HORIZONTAL_LINE = "\n\n---\n\n"
@@ -30,6 +32,7 @@ module AlertManagement
started_at&.strftime('%d %B %Y, %-l:%M%p (%Z)')
end
+ delegator_override :details_url
def details_url
details_project_alert_management_url(project, alert.iid)
end
@@ -65,6 +68,7 @@ module AlertManagement
private
attr_reader :alert, :project
+
delegate :alert_markdown, :full_query, to: :parsed_payload
def issue_summary_markdown
diff --git a/app/presenters/award_emoji_presenter.rb b/app/presenters/award_emoji_presenter.rb
index 98713855d3..8a7b58e0ab 100644
--- a/app/presenters/award_emoji_presenter.rb
+++ b/app/presenters/award_emoji_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class AwardEmojiPresenter < Gitlab::View::Presenter::Delegated
- presents :award_emoji
+ presents ::AwardEmoji, as: :award_emoji
def description
as_emoji['description']
diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb
index ecc16e2840..c198859aa4 100644
--- a/app/presenters/blob_presenter.rb
+++ b/app/presenters/blob_presenter.rb
@@ -7,7 +7,7 @@ class BlobPresenter < Gitlab::View::Presenter::Delegated
include TreeHelper
include ChecksCollaboration
- presents :blob
+ presents ::Blob, as: :blob
def highlight(to: nil, plain: nil)
load_all_blob_data
diff --git a/app/presenters/blobs/unfold_presenter.rb b/app/presenters/blobs/unfold_presenter.rb
index 487c6fe075..b921b5bf67 100644
--- a/app/presenters/blobs/unfold_presenter.rb
+++ b/app/presenters/blobs/unfold_presenter.rb
@@ -6,6 +6,8 @@ module Blobs
include ActiveModel::AttributeAssignment
include Gitlab::Utils::StrongMemoize
+ presents ::Blob
+
attribute :full, :boolean, default: false
attribute :since, :integer, default: 1
attribute :to, :integer, default: 1
diff --git a/app/presenters/board_presenter.rb b/app/presenters/board_presenter.rb
index d7cecd44dd..bb3e96b8fa 100644
--- a/app/presenters/board_presenter.rb
+++ b/app/presenters/board_presenter.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class BoardPresenter < Gitlab::View::Presenter::Delegated
- presents :board
+ presents ::Board, as: :board
end
diff --git a/app/presenters/ci/bridge_presenter.rb b/app/presenters/ci/bridge_presenter.rb
index 724e10c26c..a62d7cdbbd 100644
--- a/app/presenters/ci/bridge_presenter.rb
+++ b/app/presenters/ci/bridge_presenter.rb
@@ -2,6 +2,9 @@
module Ci
class BridgePresenter < ProcessablePresenter
+ presents ::Ci::Bridge
+
+ delegator_override :detailed_status
def detailed_status
@detailed_status ||= subject.detailed_status(user)
end
diff --git a/app/presenters/ci/build_metadata_presenter.rb b/app/presenters/ci/build_metadata_presenter.rb
index 4871bb3a91..2f559adf77 100644
--- a/app/presenters/ci/build_metadata_presenter.rb
+++ b/app/presenters/ci/build_metadata_presenter.rb
@@ -9,8 +9,9 @@ module Ci
job_timeout_source: 'job'
}.freeze
- presents :metadata
+ presents ::Ci::BuildMetadata, as: :metadata
+ delegator_override :timeout_source
def timeout_source
return unless metadata.timeout_source?
diff --git a/app/presenters/ci/build_presenter.rb b/app/presenters/ci/build_presenter.rb
index 06ed6791bb..65e1c80085 100644
--- a/app/presenters/ci/build_presenter.rb
+++ b/app/presenters/ci/build_presenter.rb
@@ -2,6 +2,8 @@
module Ci
class BuildPresenter < ProcessablePresenter
+ presents ::Ci::Build
+
def erased_by_user?
# Build can be erased through API, therefore it does not have
# `erased_by` user assigned in that case.
diff --git a/app/presenters/ci/group_variable_presenter.rb b/app/presenters/ci/group_variable_presenter.rb
index 99011150c8..dea9a42b62 100644
--- a/app/presenters/ci/group_variable_presenter.rb
+++ b/app/presenters/ci/group_variable_presenter.rb
@@ -2,7 +2,7 @@
module Ci
class GroupVariablePresenter < Gitlab::View::Presenter::Delegated
- presents :variable
+ presents ::Ci::GroupVariable, as: :variable
def placeholder
'GROUP_VARIABLE'
diff --git a/app/presenters/ci/legacy_stage_presenter.rb b/app/presenters/ci/legacy_stage_presenter.rb
index d5c21baba2..c803abfab6 100644
--- a/app/presenters/ci/legacy_stage_presenter.rb
+++ b/app/presenters/ci/legacy_stage_presenter.rb
@@ -2,7 +2,7 @@
module Ci
class LegacyStagePresenter < Gitlab::View::Presenter::Delegated
- presents :legacy_stage
+ presents ::Ci::LegacyStage, as: :legacy_stage
def latest_ordered_statuses
preload_statuses(legacy_stage.statuses.latest_ordered)
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index 82f00f7469..e0cb899c9d 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -5,6 +5,9 @@ module Ci
include Gitlab::Utils::StrongMemoize
include ActionView::Helpers::UrlHelper
+ delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
+ delegator_override_with ActionView::Helpers::TagHelper # TODO: Remove `ActionView::Helpers::UrlHelper` inclusion as it overrides `Ci::Pipeline#tag`
+
# We use a class method here instead of a constant, allowing EE to redefine
# the returned `Hash` more easily.
def self.failure_reasons
@@ -20,8 +23,9 @@ module Ci
user_blocked: 'The user who created this pipeline is blocked.' }
end
- presents :pipeline
+ presents ::Ci::Pipeline, as: :pipeline
+ delegator_override :failed_builds
def failed_builds
return [] unless can?(current_user, :read_build, pipeline)
@@ -30,6 +34,7 @@ module Ci
end
end
+ delegator_override :failure_reason
def failure_reason
return unless pipeline.failure_reason?
diff --git a/app/presenters/ci/processable_presenter.rb b/app/presenters/ci/processable_presenter.rb
index 5a8a664907..9f3a83a00f 100644
--- a/app/presenters/ci/processable_presenter.rb
+++ b/app/presenters/ci/processable_presenter.rb
@@ -2,5 +2,6 @@
module Ci
class ProcessablePresenter < CommitStatusPresenter
+ presents ::Ci::Processable
end
end
diff --git a/app/presenters/ci/runner_presenter.rb b/app/presenters/ci/runner_presenter.rb
index 273328afc5..ad889d444f 100644
--- a/app/presenters/ci/runner_presenter.rb
+++ b/app/presenters/ci/runner_presenter.rb
@@ -2,11 +2,14 @@
module Ci
class RunnerPresenter < Gitlab::View::Presenter::Delegated
- presents :runner
+ presents ::Ci::Runner, as: :runner
+ delegator_override :locked?
def locked?
read_attribute(:locked) && project_type?
end
+
+ delegator_override :locked
alias_method :locked, :locked?
end
end
diff --git a/app/presenters/ci/stage_presenter.rb b/app/presenters/ci/stage_presenter.rb
index 21bda86cde..bd5bf08abb 100644
--- a/app/presenters/ci/stage_presenter.rb
+++ b/app/presenters/ci/stage_presenter.rb
@@ -2,7 +2,7 @@
module Ci
class StagePresenter < Gitlab::View::Presenter::Delegated
- presents :stage
+ presents ::Ci::Stage, as: :stage
PRELOADED_RELATIONS = [:pipeline, :metadata, :tags, :job_artifacts_archive, :downstream_pipeline].freeze
diff --git a/app/presenters/ci/trigger_presenter.rb b/app/presenters/ci/trigger_presenter.rb
index 605c8f328a..5f0bd4b3a8 100644
--- a/app/presenters/ci/trigger_presenter.rb
+++ b/app/presenters/ci/trigger_presenter.rb
@@ -2,12 +2,13 @@
module Ci
class TriggerPresenter < Gitlab::View::Presenter::Delegated
- presents :trigger
+ presents ::Ci::Trigger, as: :trigger
def has_token_exposed?
can?(current_user, :admin_trigger, trigger)
end
+ delegator_override :token
def token
if has_token_exposed?
trigger.token
diff --git a/app/presenters/ci/variable_presenter.rb b/app/presenters/ci/variable_presenter.rb
index f027f3aa56..ec04dd5e9f 100644
--- a/app/presenters/ci/variable_presenter.rb
+++ b/app/presenters/ci/variable_presenter.rb
@@ -2,7 +2,7 @@
module Ci
class VariablePresenter < Gitlab::View::Presenter::Delegated
- presents :variable
+ presents ::Ci::Variable, as: :variable
def placeholder
'PROJECT_VARIABLE'
diff --git a/app/presenters/clusterable_presenter.rb b/app/presenters/clusterable_presenter.rb
index 03e26b9292..4b645510b5 100644
--- a/app/presenters/clusterable_presenter.rb
+++ b/app/presenters/clusterable_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class ClusterablePresenter < Gitlab::View::Presenter::Delegated
- presents :clusterable
+ presents ::Project, ::Group, ::Clusters::Instance, as: :clusterable
def self.fabricate(clusterable, **attributes)
presenter_class = "#{clusterable.class.name}ClusterablePresenter".constantize
diff --git a/app/presenters/clusters/cluster_presenter.rb b/app/presenters/clusters/cluster_presenter.rb
index eb4bd8532a..ce060476cf 100644
--- a/app/presenters/clusters/cluster_presenter.rb
+++ b/app/presenters/clusters/cluster_presenter.rb
@@ -3,21 +3,10 @@
module Clusters
class ClusterPresenter < Gitlab::View::Presenter::Delegated
include ::Gitlab::Utils::StrongMemoize
- include ActionView::Helpers::SanitizeHelper
- include ActionView::Helpers::UrlHelper
- include IconsHelper
- presents :cluster
+ delegator_override_with ::Gitlab::Utils::StrongMemoize # TODO: Remove `::Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
- # We do not want to show the group path for clusters belonging to the
- # clusterable, only for the ancestor clusters.
- def item_link(clusterable_presenter, *html_options)
- if cluster.group_type? && clusterable != clusterable_presenter.subject
- contracted_group_name(cluster.group) + ' / ' + link_to_cluster
- else
- link_to_cluster(*html_options)
- end
- end
+ presents ::Clusters::Cluster, as: :cluster
def provider_label
if aws?
@@ -39,16 +28,6 @@ module Clusters
can?(current_user, :read_cluster, cluster)
end
- def cluster_type_description
- if cluster.project_type?
- s_("ClusterIntegration|Project cluster")
- elsif cluster.group_type?
- s_("ClusterIntegration|Group cluster")
- elsif cluster.instance_type?
- s_("ClusterIntegration|Instance cluster")
- end
- end
-
def show_path(params: {})
if cluster.project_type?
project_cluster_path(project, cluster, params)
@@ -107,7 +86,7 @@ module Clusters
private
def image_path(path)
- ActionController::Base.helpers.image_path(path)
+ ApplicationController.helpers.image_path(path)
end
# currently log explorer is only available in the scope of the project
@@ -127,20 +106,6 @@ module Clusters
cluster.project
end
end
-
- def contracted_group_name(group)
- sanitize(group.full_name)
- .sub(%r{\/.*\/}, "/ #{contracted_icon} /")
- .html_safe
- end
-
- def contracted_icon
- sprite_icon('ellipsis_h', size: 12, css_class: 'vertical-align-middle')
- end
-
- def link_to_cluster(html_options: {})
- link_to_if(can_read_cluster?, cluster.name, show_path, html_options)
- end
end
end
diff --git a/app/presenters/clusters/integration_presenter.rb b/app/presenters/clusters/integration_presenter.rb
index 57608be29b..f7be59f00f 100644
--- a/app/presenters/clusters/integration_presenter.rb
+++ b/app/presenters/clusters/integration_presenter.rb
@@ -2,7 +2,7 @@
module Clusters
class IntegrationPresenter < Gitlab::View::Presenter::Delegated
- presents :integration
+ presents ::Clusters::Integrations::Prometheus, ::Clusters::Integrations::ElasticStack, as: :integration
def application_type
integration.class.name.demodulize.underscore
diff --git a/app/presenters/commit_presenter.rb b/app/presenters/commit_presenter.rb
index c14dcab600..7df45ca03b 100644
--- a/app/presenters/commit_presenter.rb
+++ b/app/presenters/commit_presenter.rb
@@ -3,7 +3,7 @@
class CommitPresenter < Gitlab::View::Presenter::Delegated
include GlobalID::Identification
- presents :commit
+ presents ::Commit, as: :commit
def status_for(ref)
return unless can?(current_user, :read_commit_status, commit.project)
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index 3c39470b73..7919e501bf 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -28,19 +28,36 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
ci_quota_exceeded: 'No more CI minutes available',
no_matching_runner: 'No matching runner available',
trace_size_exceeded: 'The job log size limit was reached',
- builds_disabled: 'The CI/CD is disabled for this project'
+ builds_disabled: 'The CI/CD is disabled for this project',
+ environment_creation_failure: 'This job could not be executed because it would create an environment with an invalid parameter.'
+ }.freeze
+
+ TROUBLESHOOTING_DOC = {
+ environment_creation_failure: { path: 'ci/environments/index', anchor: 'a-deployment-job-failed-with-this-job-could-not-be-executed-because-it-would-create-an-environment-with-an-invalid-parameter-error' }
}.freeze
private_constant :CALLOUT_FAILURE_MESSAGES
- presents :build
+ presents ::CommitStatus, as: :build
def self.callout_failure_messages
CALLOUT_FAILURE_MESSAGES
end
def callout_failure_message
- self.class.callout_failure_messages.fetch(failure_reason.to_sym)
+ message = self.class.callout_failure_messages.fetch(failure_reason.to_sym)
+
+ if doc = TROUBLESHOOTING_DOC[failure_reason.to_sym]
+ message += " #{help_page_link(doc[:path], doc[:anchor])}"
+ end
+
+ message
+ end
+
+ private
+
+ def help_page_link(path, anchor)
+ ActionController::Base.helpers.link_to('How do I fix it?', help_page_path(path, anchor: anchor))
end
end
diff --git a/app/presenters/dev_ops_report/metric_presenter.rb b/app/presenters/dev_ops_report/metric_presenter.rb
index 4d7ac1cd3e..55326f8f67 100644
--- a/app/presenters/dev_ops_report/metric_presenter.rb
+++ b/app/presenters/dev_ops_report/metric_presenter.rb
@@ -2,6 +2,8 @@
module DevOpsReport
class MetricPresenter < Gitlab::View::Presenter::Simple
+ presents ::DevOpsReport::Metric
+
delegate :created_at, to: :subject
def cards
diff --git a/app/presenters/environment_presenter.rb b/app/presenters/environment_presenter.rb
index 6f67bbe2a5..6c8da86187 100644
--- a/app/presenters/environment_presenter.rb
+++ b/app/presenters/environment_presenter.rb
@@ -3,7 +3,7 @@
class EnvironmentPresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::UrlHelper
- presents :environment
+ presents ::Environment, as: :environment
def path
project_environment_path(project, self)
diff --git a/app/presenters/event_presenter.rb b/app/presenters/event_presenter.rb
index c37721f721..4c787b88e2 100644
--- a/app/presenters/event_presenter.rb
+++ b/app/presenters/event_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class EventPresenter < Gitlab::View::Presenter::Delegated
- presents :event
+ presents ::Event, as: :event
def initialize(subject, **attributes)
super
@@ -10,6 +10,7 @@ class EventPresenter < Gitlab::View::Presenter::Delegated
end
# Caching `visible_to_user?` method in the presenter beause it might be called multiple times.
+ delegator_override :visible_to_user?
def visible_to_user?(user = nil)
@visible_to_user_cache.fetch(user&.id) { super(user) }
end
diff --git a/app/presenters/gitlab/blame_presenter.rb b/app/presenters/gitlab/blame_presenter.rb
index 1f2445b04a..e9340a42e5 100644
--- a/app/presenters/gitlab/blame_presenter.rb
+++ b/app/presenters/gitlab/blame_presenter.rb
@@ -12,7 +12,7 @@ module Gitlab
include TreeHelper
include IconsHelper
- presents :blame
+ presents nil, as: :blame
CommitData = Struct.new(
:author_avatar,
diff --git a/app/presenters/group_clusterable_presenter.rb b/app/presenters/group_clusterable_presenter.rb
index 34e7084ab0..c51cd41502 100644
--- a/app/presenters/group_clusterable_presenter.rb
+++ b/app/presenters/group_clusterable_presenter.rb
@@ -2,7 +2,8 @@
class GroupClusterablePresenter < ClusterablePresenter
extend ::Gitlab::Utils::Override
- include ActionView::Helpers::UrlHelper
+
+ presents ::Group
override :cluster_status_cluster_path
def cluster_status_cluster_path(cluster, params = {})
@@ -31,7 +32,7 @@ class GroupClusterablePresenter < ClusterablePresenter
override :learn_more_link
def learn_more_link
- link_to(s_('ClusterIntegration|Learn more about group Kubernetes clusters'), help_page_path('user/group/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
+ ApplicationController.helpers.link_to(s_('ClusterIntegration|Learn more about group Kubernetes clusters'), help_page_path('user/group/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
end
def metrics_dashboard_path(cluster)
diff --git a/app/presenters/group_member_presenter.rb b/app/presenters/group_member_presenter.rb
index 5ab4b51f47..88facc3608 100644
--- a/app/presenters/group_member_presenter.rb
+++ b/app/presenters/group_member_presenter.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class GroupMemberPresenter < MemberPresenter
+ presents ::GroupMember
+
private
def admin_member_permission
diff --git a/app/presenters/instance_clusterable_presenter.rb b/app/presenters/instance_clusterable_presenter.rb
index 56d91f90b2..f2550eb17e 100644
--- a/app/presenters/instance_clusterable_presenter.rb
+++ b/app/presenters/instance_clusterable_presenter.rb
@@ -2,7 +2,8 @@
class InstanceClusterablePresenter < ClusterablePresenter
extend ::Gitlab::Utils::Override
- include ActionView::Helpers::UrlHelper
+
+ presents ::Clusters::Instance
def self.fabricate(clusterable, **attributes)
attributes_with_presenter_class = attributes.merge(presenter_class: InstanceClusterablePresenter)
@@ -69,7 +70,7 @@ class InstanceClusterablePresenter < ClusterablePresenter
override :learn_more_link
def learn_more_link
- link_to(s_('ClusterIntegration|Learn more about instance Kubernetes clusters'), help_page_path('user/instance/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
+ ApplicationController.helpers.link_to(s_('ClusterIntegration|Learn more about instance Kubernetes clusters'), help_page_path('user/instance/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
end
def metrics_dashboard_path(cluster)
diff --git a/app/presenters/invitation_presenter.rb b/app/presenters/invitation_presenter.rb
index d8c07f327d..ada8227a47 100644
--- a/app/presenters/invitation_presenter.rb
+++ b/app/presenters/invitation_presenter.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class InvitationPresenter < Gitlab::View::Presenter::Delegated
- presents :invitation
+ presents nil, as: :invitation
end
diff --git a/app/presenters/issue_presenter.rb b/app/presenters/issue_presenter.rb
index b7f4ac0555..72fe14d224 100644
--- a/app/presenters/issue_presenter.rb
+++ b/app/presenters/issue_presenter.rb
@@ -1,12 +1,13 @@
# frozen_string_literal: true
class IssuePresenter < Gitlab::View::Presenter::Delegated
- presents :issue
+ presents ::Issue, as: :issue
def issue_path
url_builder.build(issue, only_path: true)
end
+ delegator_override :subscribed?
def subscribed?
issue.subscribed?(current_user, issue.project)
end
diff --git a/app/presenters/label_presenter.rb b/app/presenters/label_presenter.rb
index 9e51e6fa4b..fafade2828 100644
--- a/app/presenters/label_presenter.rb
+++ b/app/presenters/label_presenter.rb
@@ -1,9 +1,11 @@
# frozen_string_literal: true
class LabelPresenter < Gitlab::View::Presenter::Delegated
- presents :label
+ presents ::Label, as: :label
delegate :name, :full_name, to: :label_subject, prefix: :subject
+ delegator_override :subject # TODO: Fix `Gitlab::View::Presenter::Delegated#subject` not to override `Label#subject`.
+
def edit_path
case label
when GroupLabel then edit_group_label_path(label.group, label)
diff --git a/app/presenters/member_presenter.rb b/app/presenters/member_presenter.rb
index b37a43bf25..67d044dd01 100644
--- a/app/presenters/member_presenter.rb
+++ b/app/presenters/member_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class MemberPresenter < Gitlab::View::Presenter::Delegated
- presents :member
+ presents ::Member, as: :member
def access_level_roles
member.class.access_level_roles
diff --git a/app/presenters/members_presenter.rb b/app/presenters/members_presenter.rb
index 03ebea36d4..b572cf9623 100644
--- a/app/presenters/members_presenter.rb
+++ b/app/presenters/members_presenter.rb
@@ -3,7 +3,7 @@
class MembersPresenter < Gitlab::View::Presenter::Delegated
include Enumerable
- presents :members
+ presents nil, as: :members
def to_ary
to_a
diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb
index fc8a290f5f..d19d496452 100644
--- a/app/presenters/merge_request_presenter.rb
+++ b/app/presenters/merge_request_presenter.rb
@@ -8,9 +8,11 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
include ChecksCollaboration
include Gitlab::Utils::StrongMemoize
+ delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
+
APPROVALS_WIDGET_BASE_TYPE = 'base'
- presents :merge_request
+ presents ::MergeRequest, as: :merge_request
def ci_status
if pipeline
@@ -183,6 +185,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
.can_push_to_branch?(source_branch)
end
+ delegator_override :can_remove_source_branch?
def can_remove_source_branch?
source_branch_exists? && merge_request.can_remove_source_branch?(current_user)
end
@@ -202,6 +205,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end
end
+ delegator_override :subscribed?
def subscribed?
merge_request.subscribed?(current_user, merge_request.target_project)
end
diff --git a/app/presenters/milestone_presenter.rb b/app/presenters/milestone_presenter.rb
index 6bf8203702..4084c8740f 100644
--- a/app/presenters/milestone_presenter.rb
+++ b/app/presenters/milestone_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class MilestonePresenter < Gitlab::View::Presenter::Delegated
- presents :milestone
+ presents ::Milestone, as: :milestone
def milestone_path
url_builder.build(milestone, only_path: true)
diff --git a/app/presenters/pages_domain_presenter.rb b/app/presenters/pages_domain_presenter.rb
index 6ef89760be..0523f70241 100644
--- a/app/presenters/pages_domain_presenter.rb
+++ b/app/presenters/pages_domain_presenter.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
class PagesDomainPresenter < Gitlab::View::Presenter::Delegated
- presents :pages_domain
+ presents ::PagesDomain, as: :pages_domain
+
+ delegator_override :subject # TODO: Fix `Gitlab::View::Presenter::Delegated#subject` not to override `PagesDomain#subject`.
def needs_verification?
Gitlab::CurrentSettings.pages_domain_verification_enabled? && unverified?
diff --git a/app/presenters/project_clusterable_presenter.rb b/app/presenters/project_clusterable_presenter.rb
index 920304e743..6c4d1143c0 100644
--- a/app/presenters/project_clusterable_presenter.rb
+++ b/app/presenters/project_clusterable_presenter.rb
@@ -2,7 +2,8 @@
class ProjectClusterablePresenter < ClusterablePresenter
extend ::Gitlab::Utils::Override
- include ActionView::Helpers::UrlHelper
+
+ presents ::Project
override :cluster_status_cluster_path
def cluster_status_cluster_path(cluster, params = {})
@@ -26,7 +27,7 @@ class ProjectClusterablePresenter < ClusterablePresenter
override :learn_more_link
def learn_more_link
- link_to(s_('ClusterIntegration|Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
+ ApplicationController.helpers.link_to(s_('ClusterIntegration|Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
end
def metrics_dashboard_path(cluster)
diff --git a/app/presenters/project_hook_presenter.rb b/app/presenters/project_hook_presenter.rb
index a65c7221b5..a696e9fd0e 100644
--- a/app/presenters/project_hook_presenter.rb
+++ b/app/presenters/project_hook_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class ProjectHookPresenter < Gitlab::View::Presenter::Delegated
- presents :project_hook
+ presents ::ProjectHook, as: :project_hook
def logs_details_path(log)
project_hook_hook_log_path(project, self, log)
diff --git a/app/presenters/project_member_presenter.rb b/app/presenters/project_member_presenter.rb
index 17947266ed..91d3ae9687 100644
--- a/app/presenters/project_member_presenter.rb
+++ b/app/presenters/project_member_presenter.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class ProjectMemberPresenter < MemberPresenter
+ presents ::ProjectMember
+
private
def admin_member_permission
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index 066f4786cf..bbd8c715f5 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -12,7 +12,10 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
include Gitlab::Experiment::Dsl
- presents :project
+ delegator_override_with GitlabRoutingHelper # TODO: Remove `GitlabRoutingHelper` inclusion as it's duplicate
+ delegator_override_with Gitlab::Utils::StrongMemoize # TODO: Remove `Gitlab::Utils::StrongMemoize` inclusion as it's duplicate
+
+ presents ::Project, as: :project
AnchorData = Struct.new(:is_link, :label, :link, :class_modifier, :icon, :itemprop, :data)
MAX_TOPICS_TO_SHOW = 3
diff --git a/app/presenters/projects/import_export/project_export_presenter.rb b/app/presenters/projects/import_export/project_export_presenter.rb
index f56760b55d..7b2ffb6d75 100644
--- a/app/presenters/projects/import_export/project_export_presenter.rb
+++ b/app/presenters/projects/import_export/project_export_presenter.rb
@@ -5,16 +5,24 @@ module Projects
class ProjectExportPresenter < Gitlab::View::Presenter::Delegated
include ActiveModel::Serializers::JSON
- presents :project
+ presents ::Project, as: :project
+ # TODO: Remove `ActiveModel::Serializers::JSON` inclusion as it's duplicate
+ delegator_override_with ActiveModel::Serializers::JSON
+ delegator_override_with ActiveModel::Naming
+ delegator_override :include_root_in_json, :include_root_in_json?
+
+ delegator_override :project_members
def project_members
super + converted_group_members
end
+ delegator_override :description
def description
self.respond_to?(:override_description) ? override_description : super
end
+ delegator_override :protected_branches
def protected_branches
project.exported_protected_branches
end
diff --git a/app/presenters/projects/settings/deploy_keys_presenter.rb b/app/presenters/projects/settings/deploy_keys_presenter.rb
index 13290a8e63..e3323b7518 100644
--- a/app/presenters/projects/settings/deploy_keys_presenter.rb
+++ b/app/presenters/projects/settings/deploy_keys_presenter.rb
@@ -5,7 +5,7 @@ module Projects
class DeployKeysPresenter < Gitlab::View::Presenter::Simple
include Gitlab::Utils::StrongMemoize
- presents :project
+ presents ::Project, as: :project
delegate :size, to: :enabled_keys, prefix: true
delegate :size, to: :available_project_keys, prefix: true
delegate :size, to: :available_public_keys, prefix: true
diff --git a/app/presenters/prometheus_alert_presenter.rb b/app/presenters/prometheus_alert_presenter.rb
index 99e24bdcdb..714329ede7 100644
--- a/app/presenters/prometheus_alert_presenter.rb
+++ b/app/presenters/prometheus_alert_presenter.rb
@@ -3,7 +3,7 @@
class PrometheusAlertPresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::UrlHelper
- presents :prometheus_alert
+ presents ::PrometheusAlert, as: :prometheus_alert
def humanized_text
operator_text =
diff --git a/app/presenters/release_presenter.rb b/app/presenters/release_presenter.rb
index ac27e997b4..c919c7f4c6 100644
--- a/app/presenters/release_presenter.rb
+++ b/app/presenters/release_presenter.rb
@@ -3,8 +3,10 @@
class ReleasePresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::UrlHelper
- presents :release
+ presents ::Release, as: :release
+ # TODO: Remove `delegate` as it's redundant due to SimpleDelegator.
+ delegator_override :tag, :project
delegate :project, :tag, to: :release
def commit_path
@@ -51,6 +53,7 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated
edit_project_release_url(project, release)
end
+ delegator_override :assets_count
def assets_count
if can_download_code?
release.assets_count
@@ -59,6 +62,7 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated
end
end
+ delegator_override :name
def name
can_download_code? ? release.name : "Release-#{release.id}"
end
diff --git a/app/presenters/releases/evidence_presenter.rb b/app/presenters/releases/evidence_presenter.rb
index a00cbacb7d..bdc053a303 100644
--- a/app/presenters/releases/evidence_presenter.rb
+++ b/app/presenters/releases/evidence_presenter.rb
@@ -4,7 +4,7 @@ module Releases
class EvidencePresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::UrlHelper
- presents :evidence
+ presents ::Releases::Evidence, as: :evidence
def filepath
release = evidence.release
diff --git a/app/presenters/search_service_presenter.rb b/app/presenters/search_service_presenter.rb
index ab43800b9f..72f967b8be 100644
--- a/app/presenters/search_service_presenter.rb
+++ b/app/presenters/search_service_presenter.rb
@@ -3,7 +3,7 @@
class SearchServicePresenter < Gitlab::View::Presenter::Delegated
include RendersCommits
- presents :search_service
+ presents ::SearchService, as: :search_service
SCOPE_PRELOAD_METHOD = {
projects: :with_web_entity_associations,
@@ -18,6 +18,7 @@ class SearchServicePresenter < Gitlab::View::Presenter::Delegated
SORT_ENABLED_SCOPES = %w(issues merge_requests epics).freeze
+ delegator_override :search_objects
def search_objects
@search_objects ||= begin
objects = search_service.search_objects(SCOPE_PRELOAD_METHOD[scope.to_sym])
diff --git a/app/presenters/sentry_error_presenter.rb b/app/presenters/sentry_error_presenter.rb
index 669bcb68b7..5862e54dfc 100644
--- a/app/presenters/sentry_error_presenter.rb
+++ b/app/presenters/sentry_error_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class SentryErrorPresenter < Gitlab::View::Presenter::Delegated
- presents :error
+ presents nil, as: :error
FrequencyStruct = Struct.new(:time, :count, keyword_init: true)
diff --git a/app/presenters/service_hook_presenter.rb b/app/presenters/service_hook_presenter.rb
index 8f2ba1a905..91911eb3df 100644
--- a/app/presenters/service_hook_presenter.rb
+++ b/app/presenters/service_hook_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class ServiceHookPresenter < Gitlab::View::Presenter::Delegated
- presents :service_hook
+ presents ::ServiceHook, as: :service_hook
def logs_details_path(log)
project_service_hook_log_path(integration.project, integration, log)
diff --git a/app/presenters/snippet_blob_presenter.rb b/app/presenters/snippet_blob_presenter.rb
index ab8fc0f905..4072696eb8 100644
--- a/app/presenters/snippet_blob_presenter.rb
+++ b/app/presenters/snippet_blob_presenter.rb
@@ -3,6 +3,8 @@
class SnippetBlobPresenter < BlobPresenter
include GitlabRoutingHelper
+ presents ::SnippetBlob
+
def rich_data
return unless blob.rich_viewer
diff --git a/app/presenters/snippet_presenter.rb b/app/presenters/snippet_presenter.rb
index 695aa266e2..8badbe7f54 100644
--- a/app/presenters/snippet_presenter.rb
+++ b/app/presenters/snippet_presenter.rb
@@ -1,16 +1,18 @@
# frozen_string_literal: true
class SnippetPresenter < Gitlab::View::Presenter::Delegated
- presents :snippet
+ presents ::Snippet, as: :snippet
def raw_url
url_builder.build(snippet, raw: true)
end
+ delegator_override :ssh_url_to_repo
def ssh_url_to_repo
snippet.ssh_url_to_repo if snippet.repository_exists?
end
+ delegator_override :http_url_to_repo
def http_url_to_repo
snippet.http_url_to_repo if snippet.repository_exists?
end
@@ -31,6 +33,7 @@ class SnippetPresenter < Gitlab::View::Presenter::Delegated
snippet.submittable_as_spam_by?(current_user)
end
+ delegator_override :blob
def blob
return snippet.blob if snippet.empty_repo?
diff --git a/app/presenters/terraform/modules_presenter.rb b/app/presenters/terraform/modules_presenter.rb
index 608f69e201..9e9c6a5cd2 100644
--- a/app/presenters/terraform/modules_presenter.rb
+++ b/app/presenters/terraform/modules_presenter.rb
@@ -4,7 +4,7 @@ module Terraform
class ModulesPresenter < Gitlab::View::Presenter::Simple
attr_accessor :packages, :system
- presents :modules
+ presents nil, as: :modules
def initialize(packages, system)
@packages = packages
diff --git a/app/presenters/todo_presenter.rb b/app/presenters/todo_presenter.rb
index 291be7848e..cb8d37be22 100644
--- a/app/presenters/todo_presenter.rb
+++ b/app/presenters/todo_presenter.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class TodoPresenter < Gitlab::View::Presenter::Delegated
- presents :todo
+ presents ::Todo, as: :todo
end
diff --git a/app/presenters/tree_entry_presenter.rb b/app/presenters/tree_entry_presenter.rb
index 216b3b0d4c..0b313d8136 100644
--- a/app/presenters/tree_entry_presenter.rb
+++ b/app/presenters/tree_entry_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class TreeEntryPresenter < Gitlab::View::Presenter::Delegated
- presents :tree
+ presents nil, as: :tree
def web_url
Gitlab::Routing.url_helpers.project_tree_url(tree.repository.project, File.join(tree.commit_id, tree.path))
diff --git a/app/presenters/user_presenter.rb b/app/presenters/user_presenter.rb
index 7cd94082ba..5a99f10b6e 100644
--- a/app/presenters/user_presenter.rb
+++ b/app/presenters/user_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class UserPresenter < Gitlab::View::Presenter::Delegated
- presents :user
+ presents ::User, as: :user
def group_memberships
should_be_private? ? GroupMember.none : user.group_members
diff --git a/app/presenters/web_hook_log_presenter.rb b/app/presenters/web_hook_log_presenter.rb
index fca03ddb5d..a516658907 100644
--- a/app/presenters/web_hook_log_presenter.rb
+++ b/app/presenters/web_hook_log_presenter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class WebHookLogPresenter < Gitlab::View::Presenter::Delegated
- presents :web_hook_log
+ presents ::WebHookLog, as: :web_hook_log
def details_path
web_hook.present.logs_details_path(self)
diff --git a/app/serializers/feature_flag_entity.rb b/app/serializers/feature_flag_entity.rb
index 80cf869a38..196a4cd504 100644
--- a/app/serializers/feature_flag_entity.rb
+++ b/app/serializers/feature_flag_entity.rb
@@ -24,8 +24,8 @@ class FeatureFlagEntity < Grape::Entity
project_feature_flag_path(feature_flag.project, feature_flag)
end
- expose :scopes, with: FeatureFlagScopeEntity do |feature_flag|
- feature_flag.scopes.sort_by(&:id)
+ expose :scopes do |_ff|
+ []
end
expose :strategies, with: FeatureFlags::StrategyEntity do |feature_flag|
diff --git a/app/serializers/feature_flag_scope_entity.rb b/app/serializers/feature_flag_scope_entity.rb
deleted file mode 100644
index 0450797a54..0000000000
--- a/app/serializers/feature_flag_scope_entity.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-
-class FeatureFlagScopeEntity < Grape::Entity
- include RequestAwareEntity
-
- expose :id
- expose :active
- expose :environment_scope
- expose :created_at
- expose :updated_at
- expose :strategies
-end
diff --git a/app/serializers/member_entity.rb b/app/serializers/member_entity.rb
index 5100a41638..d7221109ec 100644
--- a/app/serializers/member_entity.rb
+++ b/app/serializers/member_entity.rb
@@ -44,6 +44,8 @@ class MemberEntity < Grape::Entity
MemberUserEntity.represent(member.user, source: options[:source])
end
+ expose :state
+
expose :invite, if: -> (member) { member.invite? } do
expose :email do |member|
member.invite_email
@@ -56,6 +58,10 @@ class MemberEntity < Grape::Entity
expose :can_resend do |member|
member.can_resend_invite?
end
+
+ expose :user_state do |member|
+ member.respond_to?(:invited_user_state) ? member.invited_user_state : ""
+ end
end
end
diff --git a/app/serializers/merge_request_metrics_helper.rb b/app/serializers/merge_request_metrics_helper.rb
new file mode 100644
index 0000000000..fb1769d0aa
--- /dev/null
+++ b/app/serializers/merge_request_metrics_helper.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module MergeRequestMetricsHelper
+ # There are cases where where metrics object doesn't exist and it needs to be rebuilt.
+ # TODO: Once https://gitlab.com/gitlab-org/gitlab/-/issues/342508 has been resolved and
+ # all merge requests have metrics we can remove this helper method.
+ def build_metrics(merge_request)
+ # There's no need to query and serialize metrics data for merge requests that are not
+ # merged or closed.
+ return unless merge_request.merged? || merge_request.closed?
+ return merge_request.metrics if merge_request.merged? && merge_request.metrics&.merged_by_id
+ return merge_request.metrics if merge_request.closed? && merge_request.metrics&.latest_closed_by_id
+
+ build_metrics_from_events(merge_request)
+ end
+
+ private
+
+ def build_metrics_from_events(merge_request)
+ closed_event = merge_request.closed_event
+ merge_event = merge_request.merge_event
+
+ MergeRequest::Metrics.new(latest_closed_at: closed_event&.updated_at,
+ latest_closed_by: closed_event&.author,
+ merged_at: merge_event&.updated_at,
+ merged_by: merge_event&.author)
+ end
+end
diff --git a/app/serializers/merge_request_poll_cached_widget_entity.rb b/app/serializers/merge_request_poll_cached_widget_entity.rb
index 7fba52cbe1..8b0f3c8eb7 100644
--- a/app/serializers/merge_request_poll_cached_widget_entity.rb
+++ b/app/serializers/merge_request_poll_cached_widget_entity.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class MergeRequestPollCachedWidgetEntity < IssuableEntity
+ include MergeRequestMetricsHelper
+
expose :auto_merge_enabled
expose :state
expose :merged_commit_sha
@@ -158,29 +160,6 @@ class MergeRequestPollCachedWidgetEntity < IssuableEntity
@presenters ||= {}
@presenters[merge_request] ||= MergeRequestPresenter.new(merge_request, current_user: current_user) # rubocop: disable CodeReuse/Presenter
end
-
- # Once SchedulePopulateMergeRequestMetricsWithEventsData fully runs,
- # we can remove this method and just serialize MergeRequest#metrics
- # instead. See https://gitlab.com/gitlab-org/gitlab-foss/issues/41587
- def build_metrics(merge_request)
- # There's no need to query and serialize metrics data for merge requests that are not
- # merged or closed.
- return unless merge_request.merged? || merge_request.closed?
- return merge_request.metrics if merge_request.merged? && merge_request.metrics&.merged_by_id
- return merge_request.metrics if merge_request.closed? && merge_request.metrics&.latest_closed_by_id
-
- build_metrics_from_events(merge_request)
- end
-
- def build_metrics_from_events(merge_request)
- closed_event = merge_request.closed_event
- merge_event = merge_request.merge_event
-
- MergeRequest::Metrics.new(latest_closed_at: closed_event&.updated_at,
- latest_closed_by: closed_event&.author,
- merged_at: merge_event&.updated_at,
- merged_by: merge_event&.author)
- end
end
MergeRequestPollCachedWidgetEntity.prepend_mod_with('MergeRequestPollCachedWidgetEntity')
diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb
index 1c033dee5f..1e4289ce77 100644
--- a/app/serializers/merge_request_widget_entity.rb
+++ b/app/serializers/merge_request_widget_entity.rb
@@ -83,7 +83,10 @@ class MergeRequestWidgetEntity < Grape::Entity
end
expose :is_dismissed_suggest_pipeline do |_merge_request|
- current_user && current_user.dismissed_callout?(feature_name: SUGGEST_PIPELINE)
+ next true unless current_user
+ next true unless Gitlab::CurrentSettings.suggest_pipeline_enabled?
+
+ current_user.dismissed_callout?(feature_name: SUGGEST_PIPELINE)
end
expose :human_access do |merge_request|
diff --git a/app/services/base_project_service.rb b/app/services/base_project_service.rb
index fb466e6167..1bf4a235a7 100644
--- a/app/services/base_project_service.rb
+++ b/app/services/base_project_service.rb
@@ -2,6 +2,8 @@
# Base class, scoped by project
class BaseProjectService < ::BaseContainerService
+ include ::Gitlab::Utils::StrongMemoize
+
attr_accessor :project
def initialize(project:, current_user: nil, params: {})
@@ -11,4 +13,12 @@ class BaseProjectService < ::BaseContainerService
end
delegate :repository, to: :project
+
+ private
+
+ def project_group
+ strong_memoize(:project_group) do
+ project.group
+ end
+ end
end
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index 9a3e3bc3bd..6021d634f8 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -22,7 +22,7 @@ module Boards
def order(items)
return items.order_closed_date_desc if list&.closed?
- items.order_by_position_and_priority(with_cte: params[:search].present?)
+ items.order_by_relative_position
end
def finder
diff --git a/app/services/bulk_import_service.rb b/app/services/bulk_import_service.rb
deleted file mode 100644
index 4e13e967db..0000000000
--- a/app/services/bulk_import_service.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-# frozen_string_literal: true
-
-# Entry point of the BulkImport feature.
-# This service receives a Gitlab Instance connection params
-# and a list of groups to be imported.
-#
-# Process topography:
-#
-# sync | async
-# |
-# User +--> P1 +----> Pn +---+
-# | ^ | Enqueue new job
-# | +-----+
-#
-# P1 (sync)
-#
-# - Create a BulkImport record
-# - Create a BulkImport::Entity for each group to be imported
-# - Enqueue a BulkImportWorker job (P2) to import the given groups (entities)
-#
-# Pn (async)
-#
-# - For each group to be imported (BulkImport::Entity.with_status(:created))
-# - Import the group data
-# - Create entities for each subgroup of the imported group
-# - Enqueue a BulkImportService job (Pn) to import the new entities (subgroups)
-#
-class BulkImportService
- attr_reader :current_user, :params, :credentials
-
- def initialize(current_user, params, credentials)
- @current_user = current_user
- @params = params
- @credentials = credentials
- end
-
- def execute
- bulk_import = create_bulk_import
-
- BulkImportWorker.perform_async(bulk_import.id)
-
- ServiceResponse.success(payload: bulk_import)
- rescue ActiveRecord::RecordInvalid => e
- ServiceResponse.error(
- message: e.message,
- http_status: :unprocessable_entity
- )
- end
-
- private
-
- def create_bulk_import
- BulkImport.transaction do
- bulk_import = BulkImport.create!(user: current_user, source_type: 'gitlab')
- bulk_import.create_configuration!(credentials.slice(:url, :access_token))
-
- params.each do |entity|
- BulkImports::Entity.create!(
- bulk_import: bulk_import,
- source_type: entity[:source_type],
- source_full_path: entity[:source_full_path],
- destination_name: entity[:destination_name],
- destination_namespace: entity[:destination_namespace]
- )
- end
-
- bulk_import
- end
- end
-end
diff --git a/app/services/bulk_imports/create_service.rb b/app/services/bulk_imports/create_service.rb
new file mode 100644
index 0000000000..c1becbb560
--- /dev/null
+++ b/app/services/bulk_imports/create_service.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+# Entry point of the BulkImport feature.
+# This service receives a Gitlab Instance connection params
+# and a list of groups to be imported.
+#
+# Process topography:
+#
+# sync | async
+# |
+# User +--> P1 +----> Pn +---+
+# | ^ | Enqueue new job
+# | +-----+
+#
+# P1 (sync)
+#
+# - Create a BulkImport record
+# - Create a BulkImport::Entity for each group to be imported
+# - Enqueue a BulkImportWorker job (P2) to import the given groups (entities)
+#
+# Pn (async)
+#
+# - For each group to be imported (BulkImport::Entity.with_status(:created))
+# - Import the group data
+# - Create entities for each subgroup of the imported group
+# - Enqueue a BulkImports::CreateService job (Pn) to import the new entities (subgroups)
+#
+module BulkImports
+ class CreateService
+ attr_reader :current_user, :params, :credentials
+
+ def initialize(current_user, params, credentials)
+ @current_user = current_user
+ @params = params
+ @credentials = credentials
+ end
+
+ def execute
+ bulk_import = create_bulk_import
+
+ BulkImportWorker.perform_async(bulk_import.id)
+
+ ServiceResponse.success(payload: bulk_import)
+ rescue ActiveRecord::RecordInvalid => e
+ ServiceResponse.error(
+ message: e.message,
+ http_status: :unprocessable_entity
+ )
+ end
+
+ private
+
+ def create_bulk_import
+ BulkImport.transaction do
+ bulk_import = BulkImport.create!(
+ user: current_user,
+ source_type: 'gitlab',
+ source_version: client.instance_version
+ )
+ bulk_import.create_configuration!(credentials.slice(:url, :access_token))
+
+ params.each do |entity|
+ BulkImports::Entity.create!(
+ bulk_import: bulk_import,
+ source_type: entity[:source_type],
+ source_full_path: entity[:source_full_path],
+ destination_name: entity[:destination_name],
+ destination_namespace: entity[:destination_namespace]
+ )
+ end
+
+ bulk_import
+ end
+ end
+
+ def client
+ @client ||= BulkImports::Clients::HTTP.new(
+ url: @credentials[:url],
+ token: @credentials[:access_token]
+ )
+ end
+ end
+end
diff --git a/app/services/bulk_imports/file_export_service.rb b/app/services/bulk_imports/file_export_service.rb
new file mode 100644
index 0000000000..a7e0f99866
--- /dev/null
+++ b/app/services/bulk_imports/file_export_service.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module BulkImports
+ class FileExportService
+ include Gitlab::ImportExport::CommandLineUtil
+
+ def initialize(portable, export_path, relation)
+ @portable = portable
+ @export_path = export_path
+ @relation = relation
+ end
+
+ def execute
+ export_service.execute
+
+ archive_exported_data
+ end
+
+ def exported_filename
+ "#{relation}.tar"
+ end
+
+ private
+
+ attr_reader :export_path, :portable, :relation
+
+ def export_service
+ case relation
+ when FileTransfer::ProjectConfig::UPLOADS_RELATION
+ UploadsExportService.new(portable, export_path)
+ else
+ raise BulkImports::Error, 'Unsupported relation export type'
+ end
+ end
+
+ def archive_exported_data
+ archive_file = File.join(export_path, exported_filename)
+
+ tar_cf(archive: archive_file, dir: export_path)
+ end
+ end
+end
diff --git a/app/services/bulk_imports/get_importable_data_service.rb b/app/services/bulk_imports/get_importable_data_service.rb
new file mode 100644
index 0000000000..07e0b3976a
--- /dev/null
+++ b/app/services/bulk_imports/get_importable_data_service.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module BulkImports
+ class GetImportableDataService
+ def initialize(params, query_params, credentials)
+ @params = params
+ @query_params = query_params
+ @credentials = credentials
+ end
+
+ def execute
+ {
+ version_validation: version_validation,
+ response: importables
+ }
+ end
+
+ private
+
+ def importables
+ client.get('groups', @query_params)
+ end
+
+ def version_validation
+ {
+ features: {
+ project_migration: {
+ available: client.compatible_for_project_migration?,
+ min_version: BulkImport.min_gl_version_for_project_migration.to_s
+ },
+ source_instance_version: client.instance_version.to_s
+ }
+ }
+ end
+
+ def client
+ @client ||= BulkImports::Clients::HTTP.new(
+ url: @credentials[:url],
+ token: @credentials[:access_token],
+ per_page: @params[:per_page],
+ page: @params[:page]
+ )
+ end
+ end
+end
diff --git a/app/services/bulk_imports/relation_export_service.rb b/app/services/bulk_imports/relation_export_service.rb
index 055f9cafd1..4718b3914b 100644
--- a/app/services/bulk_imports/relation_export_service.rb
+++ b/app/services/bulk_imports/relation_export_service.rb
@@ -9,20 +9,23 @@ module BulkImports
@portable = portable
@relation = relation
@jid = jid
+ @config = FileTransfer.config_for(portable)
end
def execute
find_or_create_export! do |export|
remove_existing_export_file!(export)
- serialize_relation_to_file(export.relation_definition)
+ export_service.execute
compress_exported_relation
upload_compressed_file(export)
end
+ ensure
+ FileUtils.remove_entry(config.export_path)
end
private
- attr_reader :user, :portable, :relation, :jid
+ attr_reader :user, :portable, :relation, :jid, :config
def find_or_create_export!
validate_user_permissions!
@@ -55,52 +58,28 @@ module BulkImports
upload.save!
end
- def serialize_relation_to_file(relation_definition)
- serializer.serialize_relation(relation_definition)
- end
-
- def compress_exported_relation
- gzip(dir: export_path, filename: ndjson_filename)
+ def export_service
+ @export_service ||= if config.tree_relation?(relation)
+ TreeExportService.new(portable, config.export_path, relation)
+ elsif config.file_relation?(relation)
+ FileExportService.new(portable, config.export_path, relation)
+ else
+ raise BulkImports::Error, 'Unsupported export relation'
+ end
end
def upload_compressed_file(export)
- compressed_filename = File.join(export_path, "#{ndjson_filename}.gz")
+ compressed_file = File.join(config.export_path, "#{export_service.exported_filename}.gz")
+
upload = ExportUpload.find_or_initialize_by(export_id: export.id) # rubocop: disable CodeReuse/ActiveRecord
- File.open(compressed_filename) { |file| upload.export_file = file }
+ File.open(compressed_file) { |file| upload.export_file = file }
upload.save!
end
- def config
- @config ||= FileTransfer.config_for(portable)
- end
-
- def export_path
- @export_path ||= config.export_path
- end
-
- def portable_tree
- @portable_tree ||= config.portable_tree
- end
-
- # rubocop: disable CodeReuse/Serializer
- def serializer
- @serializer ||= ::Gitlab::ImportExport::Json::StreamingSerializer.new(
- portable,
- portable_tree,
- json_writer,
- exportable_path: ''
- )
- end
- # rubocop: enable CodeReuse/Serializer
-
- def json_writer
- @json_writer ||= ::Gitlab::ImportExport::Json::NdjsonWriter.new(export_path)
- end
-
- def ndjson_filename
- @ndjson_filename ||= "#{relation}.ndjson"
+ def compress_exported_relation
+ gzip(dir: config.export_path, filename: export_service.exported_filename)
end
end
end
diff --git a/app/services/bulk_imports/tree_export_service.rb b/app/services/bulk_imports/tree_export_service.rb
new file mode 100644
index 0000000000..b8e7ac4574
--- /dev/null
+++ b/app/services/bulk_imports/tree_export_service.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module BulkImports
+ class TreeExportService
+ def initialize(portable, export_path, relation)
+ @portable = portable
+ @export_path = export_path
+ @relation = relation
+ @config = FileTransfer.config_for(portable)
+ end
+
+ def execute
+ relation_definition = config.tree_relation_definition_for(relation)
+
+ raise BulkImports::Error, 'Unsupported relation export type' unless relation_definition
+
+ serializer.serialize_relation(relation_definition)
+ end
+
+ def exported_filename
+ "#{relation}.ndjson"
+ end
+
+ private
+
+ attr_reader :export_path, :portable, :relation, :config
+
+ # rubocop: disable CodeReuse/Serializer
+ def serializer
+ ::Gitlab::ImportExport::Json::StreamingSerializer.new(
+ portable,
+ config.portable_tree,
+ json_writer,
+ exportable_path: ''
+ )
+ end
+ # rubocop: enable CodeReuse/Serializer
+
+ def json_writer
+ ::Gitlab::ImportExport::Json::NdjsonWriter.new(export_path)
+ end
+ end
+end
diff --git a/app/services/bulk_imports/uploads_export_service.rb b/app/services/bulk_imports/uploads_export_service.rb
new file mode 100644
index 0000000000..32cc48c152
--- /dev/null
+++ b/app/services/bulk_imports/uploads_export_service.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module BulkImports
+ class UploadsExportService
+ include Gitlab::ImportExport::CommandLineUtil
+
+ BATCH_SIZE = 100
+
+ def initialize(portable, export_path)
+ @portable = portable
+ @export_path = export_path
+ end
+
+ def execute
+ portable.uploads.find_each(batch_size: BATCH_SIZE) do |upload| # rubocop: disable CodeReuse/ActiveRecord
+ uploader = upload.retrieve_uploader
+
+ next unless upload.exist?
+ next unless uploader.file
+
+ subdir_path = export_subdir_path(upload)
+ mkdir_p(subdir_path)
+ download_or_copy_upload(uploader, File.join(subdir_path, uploader.filename))
+ rescue Errno::ENAMETOOLONG => e
+ # Do not fail entire export process if downloaded file has filename that exceeds 255 characters.
+ # Ignore raised exception, skip such upload, log the error and keep going with the export instead.
+ Gitlab::ErrorTracking.log_exception(e, portable_id: portable.id, portable_class: portable.class.name, upload_id: upload.id)
+ end
+ end
+
+ private
+
+ attr_reader :portable, :export_path
+
+ def export_subdir_path(upload)
+ subdir = if upload.path == avatar_path
+ 'avatar'
+ else
+ upload.try(:secret).to_s
+ end
+
+ File.join(export_path, subdir)
+ end
+
+ def avatar_path
+ @avatar_path ||= portable.avatar&.upload&.path
+ end
+ end
+end
diff --git a/app/services/ci/archive_trace_service.rb b/app/services/ci/archive_trace_service.rb
index 995b58c688..17cac38ace 100644
--- a/app/services/ci/archive_trace_service.rb
+++ b/app/services/ci/archive_trace_service.rb
@@ -3,10 +3,15 @@
module Ci
class ArchiveTraceService
def execute(job, worker_name:)
+ unless job.trace.archival_attempts_available?
+ Sidekiq.logger.warn(class: worker_name, message: 'The job is out of archival attempts.', job_id: job.id)
+
+ job.trace.attempt_archive_cleanup!
+ return
+ end
+
unless job.trace.can_attempt_archival_now?
- Sidekiq.logger.warn(class: worker_name,
- message: job.trace.archival_attempts_message,
- job_id: job.id)
+ Sidekiq.logger.warn(class: worker_name, message: 'The job can not be archived right now.', job_id: job.id)
return
end
diff --git a/app/services/ci/destroy_pipeline_service.rb b/app/services/ci/destroy_pipeline_service.rb
index dd5c8e0379..476c7523d6 100644
--- a/app/services/ci/destroy_pipeline_service.rb
+++ b/app/services/ci/destroy_pipeline_service.rb
@@ -9,6 +9,9 @@ module Ci
pipeline.cancel_running if pipeline.cancelable?
+ # Ci::Pipeline#destroy triggers `use_fast_destroy :job_artifacts` and
+ # ci_builds has ON DELETE CASCADE to ci_pipelines. The pipeline, the builds,
+ # job and pipeline artifacts all get destroyed here.
pipeline.reset.destroy!
ServiceResponse.success(message: 'Pipeline not found')
diff --git a/app/services/ci/pipelines/add_job_service.rb b/app/services/ci/pipelines/add_job_service.rb
index 53536b6fdf..703bb22fb5 100644
--- a/app/services/ci/pipelines/add_job_service.rb
+++ b/app/services/ci/pipelines/add_job_service.rb
@@ -16,15 +16,7 @@ module Ci
def execute!(job, &block)
assign_pipeline_attributes(job)
- if Feature.enabled?(:ci_pipeline_add_job_with_lock, pipeline.project, default_enabled: :yaml)
- in_lock("ci:pipelines:#{pipeline.id}:add-job", ttl: LOCK_TIMEOUT, sleep_sec: LOCK_SLEEP, retries: LOCK_RETRIES) do
- Ci::Pipeline.transaction do
- yield(job)
-
- job.update_older_statuses_retried!
- end
- end
- else
+ in_lock("ci:pipelines:#{pipeline.id}:add-job", ttl: LOCK_TIMEOUT, sleep_sec: LOCK_SLEEP, retries: LOCK_RETRIES) do
Ci::Pipeline.transaction do
yield(job)
diff --git a/app/services/ci/pipelines/hook_service.rb b/app/services/ci/pipelines/hook_service.rb
new file mode 100644
index 0000000000..629ed7e1eb
--- /dev/null
+++ b/app/services/ci/pipelines/hook_service.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Ci
+ module Pipelines
+ class HookService
+ include Gitlab::Utils::StrongMemoize
+
+ HOOK_NAME = :pipeline_hooks
+
+ def initialize(pipeline)
+ @pipeline = pipeline
+ end
+
+ def execute
+ project.execute_hooks(hook_data, HOOK_NAME) if project.has_active_hooks?(HOOK_NAME)
+ project.execute_integrations(hook_data, HOOK_NAME) if project.has_active_integrations?(HOOK_NAME)
+ end
+
+ private
+
+ attr_reader :pipeline
+
+ def project
+ @project ||= pipeline.project
+ end
+
+ def hook_data
+ strong_memoize(:hook_data) do
+ Gitlab::DataBuilder::Pipeline.build(pipeline)
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index fb26d5d335..664915c5e2 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -11,8 +11,6 @@ module Ci
def execute
increment_processing_counter
- update_retried
-
Ci::PipelineProcessing::AtomicProcessingService
.new(pipeline)
.execute
@@ -24,41 +22,6 @@ module Ci
private
- # This method is for compatibility and data consistency and should be removed with 9.3 version of GitLab
- # This replicates what is db/post_migrate/20170416103934_upate_retried_for_ci_build.rb
- # and ensures that functionality will not be broken before migration is run
- # this updates only when there are data that needs to be updated, there are two groups with no retried flag
- # rubocop: disable CodeReuse/ActiveRecord
- def update_retried
- return if Feature.enabled?(:ci_remove_update_retried_from_process_pipeline, pipeline.project, default_enabled: :yaml)
-
- # find the latest builds for each name
- latest_statuses = pipeline.latest_statuses
- .group(:name)
- .having('count(*) > 1')
- .pluck(Arel.sql('MAX(id)'), 'name')
-
- # mark builds that are retried
- if latest_statuses.any?
- updated_count = pipeline.latest_statuses
- .where(name: latest_statuses.map(&:second))
- .where.not(id: latest_statuses.map(&:first))
- .update_all(retried: true)
-
- # This counter is temporary. It will be used to check whether if we still use this method or not
- # after setting correct value of `GenericCommitStatus#retried`.
- # More info: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50465#note_491657115
- if updated_count > 0
- Gitlab::AppJsonLogger.info(event: 'update_retried_is_used',
- project_id: pipeline.project.id,
- pipeline_id: pipeline.id)
-
- metrics.legacy_update_jobs_counter.increment
- end
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
def increment_processing_counter
metrics.pipeline_processing_events_counter.increment
end
diff --git a/app/services/ci/queue/build_queue_service.rb b/app/services/ci/queue/build_queue_service.rb
index 3276c42792..3c886cb023 100644
--- a/app/services/ci/queue/build_queue_service.rb
+++ b/app/services/ci/queue/build_queue_service.rb
@@ -90,7 +90,7 @@ module Ci
def runner_projects_relation
if ::Feature.enabled?(:ci_pending_builds_project_runners_decoupling, runner, default_enabled: :yaml)
- runner.runner_projects.select(:project_id)
+ runner.runner_projects.select('"ci_runner_projects"."project_id"::bigint')
else
runner.projects.without_deleted.with_builds_enabled
end
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index c46ddd2255..67ef4f1070 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -22,7 +22,8 @@ module Ci
end
def execute(params = {})
- db_all_caught_up = ::Gitlab::Database::LoadBalancing::Sticking.all_caught_up?(:runner, runner.id)
+ db_all_caught_up =
+ ::Ci::Runner.sticking.all_caught_up?(:runner, runner.id)
@metrics.increment_queue_operation(:queue_attempt)
@@ -103,42 +104,40 @@ module Ci
# rubocop: disable CodeReuse/ActiveRecord
def each_build(params, &blk)
- ::Gitlab::Database.allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/339429') do
- queue = ::Ci::Queue::BuildQueueService.new(runner)
+ queue = ::Ci::Queue::BuildQueueService.new(runner)
- builds = begin
- if runner.instance_type?
- queue.builds_for_shared_runner
- elsif runner.group_type?
- queue.builds_for_group_runner
- else
- queue.builds_for_project_runner
- end
+ builds = begin
+ if runner.instance_type?
+ queue.builds_for_shared_runner
+ elsif runner.group_type?
+ queue.builds_for_group_runner
+ else
+ queue.builds_for_project_runner
end
-
- if runner.ref_protected?
- builds = queue.builds_for_protected_runner(builds)
- end
-
- # pick builds that does not have other tags than runner's one
- builds = queue.builds_matching_tag_ids(builds, runner.tags.ids)
-
- # pick builds that have at least one tag
- unless runner.run_untagged?
- builds = queue.builds_with_any_tags(builds)
- end
-
- # pick builds that older than specified age
- if params.key?(:job_age)
- builds = queue.builds_queued_before(builds, params[:job_age].seconds.ago)
- end
-
- build_ids = retrieve_queue(-> { queue.execute(builds) })
-
- @metrics.observe_queue_size(-> { build_ids.size }, @runner.runner_type)
-
- build_ids.each { |build_id| yield Ci::Build.find(build_id) }
end
+
+ if runner.ref_protected?
+ builds = queue.builds_for_protected_runner(builds)
+ end
+
+ # pick builds that does not have other tags than runner's one
+ builds = queue.builds_matching_tag_ids(builds, runner.tags.ids)
+
+ # pick builds that have at least one tag
+ unless runner.run_untagged?
+ builds = queue.builds_with_any_tags(builds)
+ end
+
+ # pick builds that older than specified age
+ if params.key?(:job_age)
+ builds = queue.builds_queued_before(builds, params[:job_age].seconds.ago)
+ end
+
+ build_ids = retrieve_queue(-> { queue.execute(builds) })
+
+ @metrics.observe_queue_size(-> { build_ids.size }, @runner.runner_type)
+
+ build_ids.each { |build_id| yield Ci::Build.find(build_id) }
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb b/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb
index 1d329fe7b5..dfd97498fc 100644
--- a/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb
+++ b/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb
@@ -9,7 +9,7 @@ module Ci
free_resources = resource_group.resources.free.count
- resource_group.processables.waiting_for_resource.take(free_resources).each do |processable|
+ resource_group.upcoming_processables.take(free_resources).each do |processable|
processable.enqueue_waiting_for_resource
end
end
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
index 08520c9514..07cfbb9ce3 100644
--- a/app/services/ci/retry_build_service.rb
+++ b/app/services/ci/retry_build_service.rb
@@ -17,7 +17,7 @@ module Ci
def execute(build)
build.ensure_scheduling_type!
- reprocess!(build).tap do |new_build|
+ clone!(build).tap do |new_build|
check_assignable_runners!(new_build)
next if new_build.failed?
@@ -31,7 +31,12 @@ module Ci
end
# rubocop: disable CodeReuse/ActiveRecord
- def reprocess!(build)
+ def clone!(build)
+ # Cloning a build requires a strict type check to ensure
+ # the attributes being used for the clone are taken straight
+ # from the model and not overridden by other abstractions.
+ raise TypeError unless build.instance_of?(Ci::Build)
+
check_access!(build)
new_build = clone_build(build)
diff --git a/app/services/ci/retry_pipeline_service.rb b/app/services/ci/retry_pipeline_service.rb
index 02ee40d2cf..9ad46ca758 100644
--- a/app/services/ci/retry_pipeline_service.rb
+++ b/app/services/ci/retry_pipeline_service.rb
@@ -9,20 +9,15 @@ module Ci
raise Gitlab::Access::AccessDeniedError
end
- needs = Set.new
-
pipeline.ensure_scheduling_type!
builds_relation(pipeline).find_each do |build|
next unless can_be_retried?(build)
- Ci::RetryBuildService.new(project, current_user)
- .reprocess!(build)
-
- needs += build.needs.map(&:name)
+ Ci::RetryBuildService.new(project, current_user).clone!(build)
end
- pipeline.builds.latest.skipped.find_each do |skipped|
+ pipeline.processables.latest.skipped.find_each do |skipped|
retry_optimistic_lock(skipped, name: 'ci_retry_pipeline') { |build| build.process(current_user) }
end
diff --git a/app/services/ci/stuck_builds/drop_pending_service.rb b/app/services/ci/stuck_builds/drop_pending_service.rb
new file mode 100644
index 0000000000..4653e70197
--- /dev/null
+++ b/app/services/ci/stuck_builds/drop_pending_service.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Ci
+ module StuckBuilds
+ class DropPendingService
+ include DropHelpers
+
+ BUILD_PENDING_OUTDATED_TIMEOUT = 1.day
+ BUILD_PENDING_STUCK_TIMEOUT = 1.hour
+ BUILD_LOOKBACK = 5.days
+
+ def execute
+ Gitlab::AppLogger.info "#{self.class}: Cleaning pending timed-out builds"
+
+ drop(
+ pending_builds(BUILD_PENDING_OUTDATED_TIMEOUT.ago),
+ failure_reason: :stuck_or_timeout_failure
+ )
+
+ drop_stuck(
+ pending_builds(BUILD_PENDING_STUCK_TIMEOUT.ago),
+ failure_reason: :stuck_or_timeout_failure
+ )
+ end
+
+ private
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ # We're adding the ordering clause by `created_at` and `project_id`
+ # because we want to force the query planner to use the
+ # `ci_builds_gitlab_monitor_metrics` index all the time.
+ def pending_builds(timeout)
+ if Feature.enabled?(:ci_new_query_for_pending_stuck_jobs)
+ Ci::Build.pending.created_at_before(timeout).updated_at_before(timeout).order(created_at: :asc, project_id: :asc)
+ else
+ Ci::Build.pending.updated_before(lookback: BUILD_LOOKBACK.ago, timeout: timeout)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+end
diff --git a/app/services/ci/stuck_builds/drop_running_service.rb b/app/services/ci/stuck_builds/drop_running_service.rb
new file mode 100644
index 0000000000..a79224cc23
--- /dev/null
+++ b/app/services/ci/stuck_builds/drop_running_service.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Ci
+ module StuckBuilds
+ class DropRunningService
+ include DropHelpers
+
+ BUILD_RUNNING_OUTDATED_TIMEOUT = 1.hour
+
+ def execute
+ Gitlab::AppLogger.info "#{self.class}: Cleaning running, timed-out builds"
+
+ drop(running_timed_out_builds, failure_reason: :stuck_or_timeout_failure)
+ end
+
+ private
+
+ def running_timed_out_builds
+ if Feature.enabled?(:ci_new_query_for_running_stuck_jobs, default_enabled: :yaml)
+ Ci::Build
+ .running
+ .created_at_before(BUILD_RUNNING_OUTDATED_TIMEOUT.ago)
+ .updated_at_before(BUILD_RUNNING_OUTDATED_TIMEOUT.ago)
+ .order(created_at: :asc, project_id: :asc) # rubocop:disable CodeReuse/ActiveRecord
+ else
+ Ci::Build.running.updated_at_before(BUILD_RUNNING_OUTDATED_TIMEOUT.ago)
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/ci/stuck_builds/drop_scheduled_service.rb b/app/services/ci/stuck_builds/drop_scheduled_service.rb
new file mode 100644
index 0000000000..d4f4252c2c
--- /dev/null
+++ b/app/services/ci/stuck_builds/drop_scheduled_service.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Ci
+ module StuckBuilds
+ class DropScheduledService
+ include DropHelpers
+
+ BUILD_SCHEDULED_OUTDATED_TIMEOUT = 1.hour
+
+ def execute
+ Gitlab::AppLogger.info "#{self.class}: Cleaning scheduled, timed-out builds"
+
+ drop(scheduled_timed_out_builds, failure_reason: :stale_schedule)
+ end
+
+ private
+
+ def scheduled_timed_out_builds
+ Ci::Build.scheduled.scheduled_at_before(BUILD_SCHEDULED_OUTDATED_TIMEOUT.ago)
+ end
+ end
+ end
+end
diff --git a/app/services/ci/stuck_builds/drop_service.rb b/app/services/ci/stuck_builds/drop_service.rb
deleted file mode 100644
index 3fee9a9438..0000000000
--- a/app/services/ci/stuck_builds/drop_service.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- module StuckBuilds
- class DropService
- include DropHelpers
-
- BUILD_RUNNING_OUTDATED_TIMEOUT = 1.hour
- BUILD_PENDING_OUTDATED_TIMEOUT = 1.day
- BUILD_SCHEDULED_OUTDATED_TIMEOUT = 1.hour
- BUILD_PENDING_STUCK_TIMEOUT = 1.hour
- BUILD_LOOKBACK = 5.days
-
- def execute
- Gitlab::AppLogger.info "#{self.class}: Cleaning stuck builds"
-
- drop(running_timed_out_builds, failure_reason: :stuck_or_timeout_failure)
-
- drop(
- pending_builds(BUILD_PENDING_OUTDATED_TIMEOUT.ago),
- failure_reason: :stuck_or_timeout_failure
- )
-
- drop(scheduled_timed_out_builds, failure_reason: :stale_schedule)
-
- drop_stuck(
- pending_builds(BUILD_PENDING_STUCK_TIMEOUT.ago),
- failure_reason: :stuck_or_timeout_failure
- )
- end
-
- private
-
- # rubocop: disable CodeReuse/ActiveRecord
- # We're adding the ordering clause by `created_at` and `project_id`
- # because we want to force the query planner to use the
- # `ci_builds_gitlab_monitor_metrics` index all the time.
- def pending_builds(timeout)
- if Feature.enabled?(:ci_new_query_for_pending_stuck_jobs)
- Ci::Build.pending.created_at_before(timeout).updated_at_before(timeout).order(created_at: :asc, project_id: :asc)
- else
- Ci::Build.pending.updated_before(lookback: BUILD_LOOKBACK.ago, timeout: timeout)
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- def scheduled_timed_out_builds
- Ci::Build.where(status: :scheduled).where( # rubocop: disable CodeReuse/ActiveRecord
- 'ci_builds.scheduled_at IS NOT NULL AND ci_builds.scheduled_at < ?',
- BUILD_SCHEDULED_OUTDATED_TIMEOUT.ago
- )
- end
-
- def running_timed_out_builds
- Ci::Build.running.where( # rubocop: disable CodeReuse/ActiveRecord
- 'ci_builds.updated_at < ?',
- BUILD_RUNNING_OUTDATED_TIMEOUT.ago
- )
- end
- end
- end
-end
diff --git a/app/services/ci/update_build_state_service.rb b/app/services/ci/update_build_state_service.rb
index abd50d2f11..3b403f9248 100644
--- a/app/services/ci/update_build_state_service.rb
+++ b/app/services/ci/update_build_state_service.rb
@@ -73,9 +73,11 @@ module Ci
::Gitlab::Ci::Trace::Checksum.new(build).then do |checksum|
unless checksum.valid?
metrics.increment_trace_operation(operation: :invalid)
+ metrics.increment_error_counter(type: :chunks_invalid_checksum)
if checksum.corrupted?
metrics.increment_trace_operation(operation: :corrupted)
+ metrics.increment_error_counter(type: :chunks_invalid_size)
end
next unless log_invalid_chunks?
diff --git a/app/services/ci/update_pending_build_service.rb b/app/services/ci/update_pending_build_service.rb
index dcba06e60b..d546dbcfe3 100644
--- a/app/services/ci/update_pending_build_service.rb
+++ b/app/services/ci/update_pending_build_service.rb
@@ -2,7 +2,7 @@
module Ci
class UpdatePendingBuildService
- VALID_PARAMS = %i[instance_runners_enabled].freeze
+ VALID_PARAMS = %i[instance_runners_enabled namespace_id namespace_traversal_ids].freeze
InvalidParamsError = Class.new(StandardError)
InvalidModelError = Class.new(StandardError)
diff --git a/app/services/clusters/agent_tokens/create_service.rb b/app/services/clusters/agent_tokens/create_service.rb
new file mode 100644
index 0000000000..ae2617f510
--- /dev/null
+++ b/app/services/clusters/agent_tokens/create_service.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Clusters
+ module AgentTokens
+ class CreateService < ::BaseContainerService
+ ALLOWED_PARAMS = %i[agent_id description name].freeze
+
+ def execute
+ return error_no_permissions unless current_user.can?(:create_cluster, container)
+
+ token = ::Clusters::AgentToken.new(filtered_params.merge(created_by_user: current_user))
+
+ if token.save
+ ServiceResponse.success(payload: { secret: token.token, token: token })
+ else
+ ServiceResponse.error(message: token.errors.full_messages)
+ end
+ end
+
+ private
+
+ def error_no_permissions
+ ServiceResponse.error(message: s_('ClusterAgent|User has insufficient permissions to create a token for this project'))
+ end
+
+ def filtered_params
+ params.slice(*ALLOWED_PARAMS)
+ end
+ end
+ end
+end
diff --git a/app/services/clusters/agents/create_service.rb b/app/services/clusters/agents/create_service.rb
new file mode 100644
index 0000000000..568f168d63
--- /dev/null
+++ b/app/services/clusters/agents/create_service.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Agents
+ class CreateService < BaseService
+ def execute(name:)
+ return error_no_permissions unless cluster_agent_permissions?
+
+ agent = ::Clusters::Agent.new(name: name, project: project, created_by_user: current_user)
+
+ if agent.save
+ success.merge(cluster_agent: agent)
+ else
+ error(agent.errors.full_messages)
+ end
+ end
+
+ private
+
+ def cluster_agent_permissions?
+ current_user.can?(:admin_pipeline, project) && current_user.can?(:create_cluster, project)
+ end
+
+ def error_no_permissions
+ error(s_('ClusterAgent|You have insufficient permissions to create a cluster agent for this project'))
+ end
+ end
+ end
+end
diff --git a/app/services/clusters/agents/delete_service.rb b/app/services/clusters/agents/delete_service.rb
new file mode 100644
index 0000000000..2132dffa60
--- /dev/null
+++ b/app/services/clusters/agents/delete_service.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Agents
+ class DeleteService < ::BaseContainerService
+ def execute(cluster_agent)
+ return error_no_permissions unless current_user.can?(:admin_cluster, cluster_agent)
+
+ if cluster_agent.destroy
+ ServiceResponse.success
+ else
+ ServiceResponse.error(message: cluster_agent.errors.full_messages)
+ end
+ end
+
+ private
+
+ def error_no_permissions
+ ServiceResponse.error(message: s_('ClusterAgent|You have insufficient permissions to delete this cluster agent'))
+ end
+ end
+ end
+end
diff --git a/app/services/clusters/agents/refresh_authorization_service.rb b/app/services/clusters/agents/refresh_authorization_service.rb
index a9e3340dbf..7f401eef72 100644
--- a/app/services/clusters/agents/refresh_authorization_service.rb
+++ b/app/services/clusters/agents/refresh_authorization_service.rb
@@ -99,7 +99,7 @@ module Clusters
end
def group_root_ancestor?
- root_ancestor.group?
+ root_ancestor.group_namespace?
end
end
end
diff --git a/app/services/concerns/rate_limited_service.rb b/app/services/concerns/rate_limited_service.rb
new file mode 100644
index 0000000000..87cba7814f
--- /dev/null
+++ b/app/services/concerns/rate_limited_service.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module RateLimitedService
+ extend ActiveSupport::Concern
+
+ RateLimitedNotSetupError = Class.new(StandardError)
+
+ class RateLimitedError < StandardError
+ def initialize(key:, rate_limiter:)
+ @key = key
+ @rate_limiter = rate_limiter
+ end
+
+ def headers
+ # TODO: This will be fleshed out in https://gitlab.com/gitlab-org/gitlab/-/issues/342370
+ {}
+ end
+
+ def log_request(request, current_user)
+ rate_limiter.class.log_request(request, "#{key}_request_limit".to_sym, current_user)
+ end
+
+ private
+
+ attr_reader :key, :rate_limiter
+ end
+
+ class RateLimiterScopedAndKeyed
+ attr_reader :key, :opts, :rate_limiter_klass
+
+ def initialize(key:, opts:, rate_limiter_klass:)
+ @key = key
+ @opts = opts
+ @rate_limiter_klass = rate_limiter_klass
+ end
+
+ def rate_limit!(service)
+ evaluated_scope = evaluated_scope_for(service)
+ return if feature_flag_disabled?(evaluated_scope[:project])
+
+ rate_limiter = new_rate_limiter(evaluated_scope)
+ if rate_limiter.throttled?
+ raise RateLimitedError.new(key: key, rate_limiter: rate_limiter), _('This endpoint has been requested too many times. Try again later.')
+ end
+ end
+
+ private
+
+ def users_allowlist
+ @users_allowlist ||= opts[:users_allowlist] ? opts[:users_allowlist].call : []
+ end
+
+ def evaluated_scope_for(service)
+ opts[:scope].each_with_object({}) do |var, all|
+ all[var] = service.public_send(var) # rubocop: disable GitlabSecurity/PublicSend
+ end
+ end
+
+ def feature_flag_disabled?(project)
+ Feature.disabled?("rate_limited_service_#{key}", project, default_enabled: :yaml)
+ end
+
+ def new_rate_limiter(evaluated_scope)
+ rate_limiter_klass.new(key, **opts.merge(scope: evaluated_scope.values, users_allowlist: users_allowlist))
+ end
+ end
+
+ prepended do
+ attr_accessor :rate_limiter_bypassed
+ cattr_accessor :rate_limiter_scoped_and_keyed
+
+ def self.rate_limit(key:, opts:, rate_limiter_klass: ::Gitlab::ApplicationRateLimiter)
+ self.rate_limiter_scoped_and_keyed = RateLimiterScopedAndKeyed.new(key: key,
+ opts: opts,
+ rate_limiter_klass: rate_limiter_klass)
+ end
+ end
+
+ def execute_without_rate_limiting(*args, **kwargs)
+ self.rate_limiter_bypassed = true
+ execute(*args, **kwargs)
+ ensure
+ self.rate_limiter_bypassed = false
+ end
+
+ def execute(*args, **kwargs)
+ raise RateLimitedNotSetupError if rate_limiter_scoped_and_keyed.nil?
+
+ rate_limiter_scoped_and_keyed.rate_limit!(self) unless rate_limiter_bypassed
+
+ super
+ end
+end
diff --git a/app/services/container_expiration_policies/cleanup_service.rb b/app/services/container_expiration_policies/cleanup_service.rb
index cd988cdc5f..0da5e552c4 100644
--- a/app/services/container_expiration_policies/cleanup_service.rb
+++ b/app/services/container_expiration_policies/cleanup_service.rb
@@ -4,7 +4,7 @@ module ContainerExpirationPolicies
class CleanupService
attr_reader :repository
- SERVICE_RESULT_FIELDS = %i[original_size before_truncate_size after_truncate_size before_delete_size deleted_size].freeze
+ SERVICE_RESULT_FIELDS = %i[original_size before_truncate_size after_truncate_size before_delete_size deleted_size cached_tags_count].freeze
def initialize(repository)
@repository = repository
@@ -24,8 +24,8 @@ module ContainerExpirationPolicies
begin
service_result = Projects::ContainerRepository::CleanupTagsService
- .new(project, nil, policy_params.merge('container_expiration_policy' => true))
- .execute(repository)
+ .new(repository, nil, policy_params.merge('container_expiration_policy' => true))
+ .execute
rescue StandardError
repository.cleanup_unfinished!
diff --git a/app/services/customer_relations/contacts/base_service.rb b/app/services/customer_relations/contacts/base_service.rb
new file mode 100644
index 0000000000..89f6f2c3f1
--- /dev/null
+++ b/app/services/customer_relations/contacts/base_service.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module CustomerRelations
+ module Contacts
+ class BaseService < ::BaseGroupService
+ private
+
+ def allowed?
+ current_user&.can?(:admin_contact, group)
+ end
+
+ def error(message)
+ ServiceResponse.error(message: Array(message))
+ end
+ end
+ end
+end
diff --git a/app/services/customer_relations/contacts/create_service.rb b/app/services/customer_relations/contacts/create_service.rb
new file mode 100644
index 0000000000..7ff8b731e0
--- /dev/null
+++ b/app/services/customer_relations/contacts/create_service.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module CustomerRelations
+ module Contacts
+ class CreateService < BaseService
+ def execute
+ return error_no_permissions unless allowed?
+ return error_organization_invalid unless organization_valid?
+
+ contact = Contact.create(params.merge(group_id: group.id))
+
+ return error_creating(contact) unless contact.persisted?
+
+ ServiceResponse.success(payload: contact)
+ end
+
+ private
+
+ def organization_valid?
+ return true unless params[:organization_id]
+
+ organization = Organization.find(params[:organization_id])
+ organization.group_id == group.id
+ rescue ActiveRecord::RecordNotFound
+ false
+ end
+
+ def error_organization_invalid
+ error('The specified organization was not found or does not belong to this group')
+ end
+
+ def error_no_permissions
+ error('You have insufficient permissions to create a contact for this group')
+ end
+
+ def error_creating(contact)
+ error(contact&.errors&.full_messages || 'Failed to create contact')
+ end
+ end
+ end
+end
diff --git a/app/services/customer_relations/contacts/update_service.rb b/app/services/customer_relations/contacts/update_service.rb
new file mode 100644
index 0000000000..473a80be26
--- /dev/null
+++ b/app/services/customer_relations/contacts/update_service.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module CustomerRelations
+ module Contacts
+ class UpdateService < BaseService
+ def execute(contact)
+ return error_no_permissions unless allowed?
+ return error_updating(contact) unless contact.update(params)
+
+ ServiceResponse.success(payload: contact)
+ end
+
+ private
+
+ def error_no_permissions
+ error('You have insufficient permissions to update a contact for this group')
+ end
+
+ def error_updating(contact)
+ error(contact&.errors&.full_messages || 'Failed to update contact')
+ end
+ end
+ end
+end
diff --git a/app/services/customer_relations/organizations/base_service.rb b/app/services/customer_relations/organizations/base_service.rb
index 63261534b3..8f8480d697 100644
--- a/app/services/customer_relations/organizations/base_service.rb
+++ b/app/services/customer_relations/organizations/base_service.rb
@@ -10,7 +10,7 @@ module CustomerRelations
end
def error(message)
- ServiceResponse.error(message: message)
+ ServiceResponse.error(message: Array(message))
end
end
end
diff --git a/app/services/customer_relations/organizations/create_service.rb b/app/services/customer_relations/organizations/create_service.rb
index 9c223796ea..aad1b7e2ca 100644
--- a/app/services/customer_relations/organizations/create_service.rb
+++ b/app/services/customer_relations/organizations/create_service.rb
@@ -7,9 +7,7 @@ module CustomerRelations
def execute
return error_no_permissions unless allowed?
- params[:group_id] = group.id
-
- organization = Organization.create(params)
+ organization = Organization.create(params.merge(group_id: group.id))
return error_creating(organization) unless organization.persisted?
diff --git a/app/services/dependency_proxy/auth_token_service.rb b/app/services/dependency_proxy/auth_token_service.rb
index 16279ed12b..c6c9eb534b 100644
--- a/app/services/dependency_proxy/auth_token_service.rb
+++ b/app/services/dependency_proxy/auth_token_service.rb
@@ -12,10 +12,16 @@ module DependencyProxy
JSONWebToken::HMACToken.decode(token, ::Auth::DependencyProxyAuthenticationService.secret).first
end
- class << self
- def decoded_token_payload(token)
- self.new(token).execute
+ def self.user_or_deploy_token_from_jwt(raw_jwt)
+ token_payload = self.new(raw_jwt).execute
+
+ if token_payload['user_id']
+ User.find(token_payload['user_id'])
+ elsif token_payload['deploy_token']
+ DeployToken.active.find_by_token(token_payload['deploy_token'])
end
+ rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature
+ nil
end
end
end
diff --git a/app/services/dependency_proxy/find_or_create_blob_service.rb b/app/services/dependency_proxy/find_or_create_blob_service.rb
index f3dbf31dcd..0a6db6e3d3 100644
--- a/app/services/dependency_proxy/find_or_create_blob_service.rb
+++ b/app/services/dependency_proxy/find_or_create_blob_service.rb
@@ -12,7 +12,7 @@ module DependencyProxy
def execute
from_cache = true
file_name = @blob_sha.sub('sha256:', '') + '.gz'
- blob = @group.dependency_proxy_blobs.find_or_build(file_name)
+ blob = @group.dependency_proxy_blobs.active.find_or_build(file_name)
unless blob.persisted?
from_cache = false
@@ -30,6 +30,8 @@ module DependencyProxy
blob.save!
end
+ # Technical debt: change to read_at https://gitlab.com/gitlab-org/gitlab/-/issues/341536
+ blob.touch if from_cache
success(blob: blob, from_cache: from_cache)
end
diff --git a/app/services/dependency_proxy/find_or_create_manifest_service.rb b/app/services/dependency_proxy/find_or_create_manifest_service.rb
index 0eb990ab7f..1976d4d47f 100644
--- a/app/services/dependency_proxy/find_or_create_manifest_service.rb
+++ b/app/services/dependency_proxy/find_or_create_manifest_service.rb
@@ -13,11 +13,16 @@ module DependencyProxy
def execute
@manifest = @group.dependency_proxy_manifests
+ .active
.find_or_initialize_by_file_name_or_digest(file_name: @file_name, digest: @tag)
head_result = DependencyProxy::HeadManifestService.new(@image, @tag, @token).execute
- return success(manifest: @manifest, from_cache: true) if cached_manifest_matches?(head_result)
+ if cached_manifest_matches?(head_result)
+ @manifest.touch
+
+ return success(manifest: @manifest, from_cache: true)
+ end
pull_new_manifest
respond(from_cache: false)
@@ -46,6 +51,9 @@ module DependencyProxy
def respond(from_cache: true)
if @manifest.persisted?
+ # Technical debt: change to read_at https://gitlab.com/gitlab-org/gitlab/-/issues/341536
+ @manifest.touch if from_cache
+
success(manifest: @manifest, from_cache: from_cache)
else
error('Failed to download the manifest from the external registry', 503)
diff --git a/app/services/dependency_proxy/group_settings/update_service.rb b/app/services/dependency_proxy/group_settings/update_service.rb
new file mode 100644
index 0000000000..ba43452def
--- /dev/null
+++ b/app/services/dependency_proxy/group_settings/update_service.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ module GroupSettings
+ class UpdateService < BaseContainerService
+ ALLOWED_ATTRIBUTES = %i[enabled].freeze
+
+ def execute
+ return ServiceResponse.error(message: 'Access Denied', http_status: 403) unless allowed?
+ return ServiceResponse.error(message: 'Dependency proxy setting not found', http_status: 404) unless dependency_proxy_setting
+
+ if dependency_proxy_setting.update(dependency_proxy_setting_params)
+ ServiceResponse.success(payload: { dependency_proxy_setting: dependency_proxy_setting })
+ else
+ ServiceResponse.error(
+ message: dependency_proxy_setting.errors.full_messages.to_sentence || 'Bad request',
+ http_status: 400
+ )
+ end
+ end
+
+ private
+
+ def dependency_proxy_setting
+ container.dependency_proxy_setting
+ end
+
+ def allowed?
+ Ability.allowed?(current_user, :admin_dependency_proxy, container)
+ end
+
+ def dependency_proxy_setting_params
+ params.slice(*ALLOWED_ATTRIBUTES)
+ end
+ end
+ end
+end
diff --git a/app/services/deployments/older_deployments_drop_service.rb b/app/services/deployments/older_deployments_drop_service.rb
index 100d126784..504b55b99a 100644
--- a/app/services/deployments/older_deployments_drop_service.rb
+++ b/app/services/deployments/older_deployments_drop_service.rb
@@ -11,23 +11,23 @@ module Deployments
def execute
return unless @deployment&.running?
- older_deployments.find_each do |older_deployment|
- Gitlab::OptimisticLocking.retry_lock(older_deployment.deployable, name: 'older_deployments_drop') do |deployable|
- deployable.drop(:forward_deployment_failure)
+ older_deployments_builds.each do |build|
+ Gitlab::OptimisticLocking.retry_lock(build, name: 'older_deployments_drop') do |build|
+ build.drop(:forward_deployment_failure)
end
rescue StandardError => e
- Gitlab::ErrorTracking.track_exception(e, subject_id: @deployment.id, deployment_id: older_deployment.id)
+ Gitlab::ErrorTracking.track_exception(e, subject_id: @deployment.id, build_id: build.id)
end
end
private
- def older_deployments
+ def older_deployments_builds
@deployment
.environment
.active_deployments
.older_than(@deployment)
- .with_deployable
+ .builds
end
end
end
diff --git a/app/services/error_tracking/list_issues_service.rb b/app/services/error_tracking/list_issues_service.rb
index 86c7791e75..1979816b88 100644
--- a/app/services/error_tracking/list_issues_service.rb
+++ b/app/services/error_tracking/list_issues_service.rb
@@ -76,16 +76,21 @@ module ErrorTracking
filter_opts = {
status: opts[:issue_status],
sort: opts[:sort],
- limit: opts[:limit]
+ limit: opts[:limit],
+ cursor: opts[:cursor]
}
errors = ErrorTracking::ErrorsFinder.new(current_user, project, filter_opts).execute
+ pagination = {}
+ pagination[:next] = { cursor: errors.cursor_for_next_page } if errors.has_next_page?
+ pagination[:previous] = { cursor: errors.cursor_for_previous_page } if errors.has_previous_page?
+
# We use the same response format as project_error_tracking_setting
# method below for compatibility with existing code.
{
issues: errors.map(&:to_sentry_error),
- pagination: {}
+ pagination: pagination
}
else
project_error_tracking_setting.list_sentry_issues(**opts)
diff --git a/app/services/feature_flags/base_service.rb b/app/services/feature_flags/base_service.rb
index 9ae9ab4de6..ca0b6b8919 100644
--- a/app/services/feature_flags/base_service.rb
+++ b/app/services/feature_flags/base_service.rb
@@ -7,6 +7,8 @@ module FeatureFlags
AUDITABLE_ATTRIBUTES = %w(name description active).freeze
def success(**args)
+ audit_event = args.fetch(:audit_event) { audit_event(args[:feature_flag]) }
+ save_audit_event(audit_event)
sync_to_jira(args[:feature_flag])
super
end
@@ -66,5 +68,11 @@ module FeatureFlags
feature_flag_by_name.scopes.find_by_environment_scope(params[:environment_scope])
end
end
+
+ private
+
+ def audit_message(feature_flag)
+ raise NotImplementedError, "This method should be overriden by subclasses"
+ end
end
end
diff --git a/app/services/feature_flags/create_service.rb b/app/services/feature_flags/create_service.rb
index 65f8f8e33f..ebbe71f39c 100644
--- a/app/services/feature_flags/create_service.rb
+++ b/app/services/feature_flags/create_service.rb
@@ -10,8 +10,6 @@ module FeatureFlags
feature_flag = project.operations_feature_flags.new(params)
if feature_flag.save
- save_audit_event(audit_event(feature_flag))
-
success(feature_flag: feature_flag)
else
error(feature_flag.errors.full_messages, 400)
diff --git a/app/services/feature_flags/destroy_service.rb b/app/services/feature_flags/destroy_service.rb
index 986fe004db..817a80940c 100644
--- a/app/services/feature_flags/destroy_service.rb
+++ b/app/services/feature_flags/destroy_service.rb
@@ -13,8 +13,6 @@ module FeatureFlags
ApplicationRecord.transaction do
if feature_flag.destroy
- save_audit_event(audit_event(feature_flag))
-
success(feature_flag: feature_flag)
else
error(feature_flag.errors.full_messages)
diff --git a/app/services/feature_flags/hook_service.rb b/app/services/feature_flags/hook_service.rb
new file mode 100644
index 0000000000..6f77a70bd0
--- /dev/null
+++ b/app/services/feature_flags/hook_service.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module FeatureFlags
+ class HookService
+ HOOK_NAME = :feature_flag_hooks
+
+ def initialize(feature_flag, current_user)
+ @feature_flag = feature_flag
+ @current_user = current_user
+ end
+
+ def execute
+ project.execute_hooks(hook_data, HOOK_NAME)
+ end
+
+ private
+
+ attr_reader :feature_flag, :current_user
+
+ def project
+ @project ||= feature_flag.project
+ end
+
+ def hook_data
+ Gitlab::DataBuilder::FeatureFlag.build(feature_flag, current_user)
+ end
+ end
+end
diff --git a/app/services/feature_flags/update_service.rb b/app/services/feature_flags/update_service.rb
index ccfd1b57d4..bcfd2c1518 100644
--- a/app/services/feature_flags/update_service.rb
+++ b/app/services/feature_flags/update_service.rb
@@ -7,6 +7,11 @@ module FeatureFlags
'parameters' => 'parameters'
}.freeze
+ def success(**args)
+ execute_hooks_after_commit(args[:feature_flag])
+ super
+ end
+
def execute(feature_flag)
return error('Access Denied', 403) unless can_update?(feature_flag)
return error('Not Found', 404) unless valid_user_list_ids?(feature_flag, user_list_ids(params))
@@ -20,16 +25,11 @@ module FeatureFlags
end
end
+ # We generate the audit event before the feature flag is saved as #changed_strategies_messages depends on the strategies' states before save
audit_event = audit_event(feature_flag)
- if feature_flag.active_changed?
- feature_flag.execute_hooks(current_user)
- end
-
if feature_flag.save
- save_audit_event(audit_event)
-
- success(feature_flag: feature_flag)
+ success(feature_flag: feature_flag, audit_event: audit_event)
else
error(feature_flag.errors.full_messages, :bad_request)
end
@@ -38,6 +38,16 @@ module FeatureFlags
private
+ def execute_hooks_after_commit(feature_flag)
+ return unless feature_flag.active_previously_changed?
+
+ # The `current_user` method (defined in `BaseService`) is not available within the `run_after_commit` block
+ user = current_user
+ feature_flag.run_after_commit do
+ HookService.new(feature_flag, user).execute
+ end
+ end
+
def audit_message(feature_flag)
changes = changed_attributes_messages(feature_flag)
changes += changed_strategies_messages(feature_flag)
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index d0aea848af..334083a859 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -29,6 +29,7 @@ module Groups
update_group_attributes
ensure_ownership
update_integrations
+ update_pending_builds!
end
post_update_hooks(@updated_project_ids)
@@ -139,6 +140,10 @@ module Groups
# these records again.
@updated_project_ids = projects_to_update.pluck(:id)
+ Namespaces::ProjectNamespace
+ .where(id: projects_to_update.select(:project_namespace_id))
+ .update_all(visibility_level: @new_parent_group.visibility_level)
+
projects_to_update
.update_all(visibility_level: @new_parent_group.visibility_level)
end
@@ -225,6 +230,15 @@ module Groups
PropagateIntegrationWorker.perform_async(integration.id)
end
end
+
+ def update_pending_builds!
+ update_params = {
+ namespace_traversal_ids: group.traversal_ids,
+ namespace_id: group.id
+ }
+
+ ::Ci::UpdatePendingBuildService.new(group, update_params).execute
+ end
end
end
diff --git a/app/services/import/validate_remote_git_endpoint_service.rb b/app/services/import/validate_remote_git_endpoint_service.rb
new file mode 100644
index 0000000000..afccb5373a
--- /dev/null
+++ b/app/services/import/validate_remote_git_endpoint_service.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+module Import
+ class ValidateRemoteGitEndpointService
+ # Validates if the remote endpoint is a valid GIT repository
+ # Only smart protocol is supported
+ # Validation rules are taken from https://git-scm.com/docs/http-protocol#_smart_clients
+
+ GIT_SERVICE_NAME = "git-upload-pack"
+ GIT_EXPECTED_FIRST_PACKET_LINE = "# service=#{GIT_SERVICE_NAME}"
+ GIT_BODY_MESSAGE_REGEXP = /^[0-9a-f]{4}#{GIT_EXPECTED_FIRST_PACKET_LINE}/.freeze
+ # https://github.com/git/git/blob/master/Documentation/technical/protocol-common.txt#L56-L59
+ GIT_PROTOCOL_PKT_LEN = 4
+ GIT_MINIMUM_RESPONSE_LENGTH = GIT_PROTOCOL_PKT_LEN + GIT_EXPECTED_FIRST_PACKET_LINE.length
+ EXPECTED_CONTENT_TYPE = "application/x-#{GIT_SERVICE_NAME}-advertisement"
+
+ def initialize(params)
+ @params = params
+ end
+
+ def execute
+ uri = Gitlab::Utils.parse_url(@params[:url])
+
+ return ServiceResponse.error(message: "#{@params[:url]} is not a valid URL") unless uri
+
+ uri.fragment = nil
+ url = Gitlab::Utils.append_path(uri.to_s, "/info/refs?service=#{GIT_SERVICE_NAME}")
+
+ response_body = ''
+ result = nil
+ Gitlab::HTTP.try_get(url, stream_body: true, follow_redirects: false, basic_auth: auth) do |fragment|
+ response_body += fragment
+ next if response_body.length < GIT_MINIMUM_RESPONSE_LENGTH
+
+ result = if status_code_is_valid(fragment) && content_type_is_valid(fragment) && response_body_is_valid(response_body)
+ :success
+ else
+ :error
+ end
+
+ # We are interested only in the first chunks of the response
+ # So we're using stream_body: true and breaking when receive enough body
+ break
+ end
+
+ if result == :success
+ ServiceResponse.success
+ else
+ ServiceResponse.error(message: "#{uri} is not a valid HTTP Git repository")
+ end
+ end
+
+ private
+
+ def auth
+ unless @params[:user].to_s.blank?
+ {
+ username: @params[:user],
+ password: @params[:password]
+ }
+ end
+ end
+
+ def status_code_is_valid(fragment)
+ fragment.http_response.code == '200'
+ end
+
+ def content_type_is_valid(fragment)
+ fragment.http_response['content-type'] == EXPECTED_CONTENT_TYPE
+ end
+
+ def response_body_is_valid(response_body)
+ response_body.match?(GIT_BODY_MESSAGE_REGEXP)
+ end
+ end
+end
diff --git a/app/services/issuable/import_csv/base_service.rb b/app/services/issuable/import_csv/base_service.rb
index 4a6b7540de..4a2078a4e6 100644
--- a/app/services/issuable/import_csv/base_service.rb
+++ b/app/services/issuable/import_csv/base_service.rb
@@ -71,7 +71,14 @@ module Issuable
# NOTE: CSV imports are performed by workers, so we do not have a request context in order
# to create a SpamParams object to pass to the issuable create service.
spam_params = nil
- create_issuable_class.new(project: @project, current_user: @user, params: attributes, spam_params: spam_params).execute
+ create_service = create_issuable_class.new(project: @project, current_user: @user, params: attributes, spam_params: spam_params)
+
+ # For now, if create_issuable_class prepends RateLimitedService let's bypass rate limiting
+ if create_issuable_class < RateLimitedService
+ create_service.execute_without_rate_limiting
+ else
+ create_service.execute
+ end
end
def email_results_to_user
diff --git a/app/services/issues/clone_service.rb b/app/services/issues/clone_service.rb
index cb42334fe3..c675f957cd 100644
--- a/app/services/issues/clone_service.rb
+++ b/app/services/issues/clone_service.rb
@@ -8,13 +8,7 @@ module Issues
@target_project = target_project
@with_notes = with_notes
- unless issue.can_clone?(current_user, target_project)
- raise CloneError, s_('CloneIssue|Cannot clone issue due to insufficient permissions!')
- end
-
- if target_project.pending_delete?
- raise CloneError, s_('CloneIssue|Cannot clone issue to target project as it is pending deletion.')
- end
+ verify_can_clone_issue!(issue, target_project)
super(issue, target_project)
@@ -30,6 +24,20 @@ module Issues
attr_reader :target_project
attr_reader :with_notes
+ def verify_can_clone_issue!(issue, target_project)
+ unless issue.supports_move_and_clone?
+ raise CloneError, s_('CloneIssue|Cannot clone issues of \'%{issue_type}\' type.') % { issue_type: issue.issue_type }
+ end
+
+ unless issue.can_clone?(current_user, target_project)
+ raise CloneError, s_('CloneIssue|Cannot clone issue due to insufficient permissions!')
+ end
+
+ if target_project.pending_delete?
+ raise CloneError, s_('CloneIssue|Cannot clone issue to target project as it is pending deletion.')
+ end
+ end
+
def update_new_entity
# we don't call `super` because we want to be able to decide whether or not to copy all comments over.
update_new_entity_description
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index ea64239dd9..ac846c769a 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -3,8 +3,8 @@
module Issues
class CloseService < Issues::BaseService
# Closes the supplied issue if the current user is able to do so.
- def execute(issue, commit: nil, notifications: true, system_note: true)
- return issue unless can?(current_user, :update_issue, issue) || issue.is_a?(ExternalIssue)
+ def execute(issue, commit: nil, notifications: true, system_note: true, skip_authorization: false)
+ return issue unless can_close?(issue, skip_authorization: skip_authorization)
close_issue(issue,
closed_via: commit,
@@ -24,7 +24,7 @@ module Issues
return issue
end
- if project.issues_enabled? && issue.close(current_user)
+ if perform_close(issue)
event_service.close_issue(issue, current_user)
create_note(issue, closed_via) if system_note
@@ -51,6 +51,15 @@ module Issues
private
+ # Overridden on EE
+ def perform_close(issue)
+ issue.close(current_user)
+ end
+
+ def can_close?(issue, skip_authorization: false)
+ skip_authorization || can?(current_user, :update_issue, issue) || issue.is_a?(ExternalIssue)
+ end
+
def perform_incident_management_actions(issue)
resolve_alert(issue)
end
@@ -82,11 +91,11 @@ module Issues
end
end
- def store_first_mentioned_in_commit_at(issue, merge_request)
+ def store_first_mentioned_in_commit_at(issue, merge_request, max_commit_lookup: 100)
metrics = issue.metrics
return if metrics.nil? || metrics.first_mentioned_in_commit_at
- first_commit_timestamp = merge_request.commits(limit: 1).first.try(:authored_date)
+ first_commit_timestamp = merge_request.commits(limit: max_commit_lookup).last.try(:authored_date)
return unless first_commit_timestamp
metrics.update!(first_mentioned_in_commit_at: first_commit_timestamp)
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index b15b3e49c9..fcedd1c1c8 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -3,6 +3,10 @@
module Issues
class CreateService < Issues::BaseService
include ResolveDiscussions
+ prepend RateLimitedService
+
+ rate_limit key: :issues_create,
+ opts: { scope: [:project, :current_user], users_allowlist: -> { [User.support_bot.username] } }
# NOTE: For Issues::CreateService, we require the spam_params and do not default it to nil, because
# spam_checking is likely to be necessary. However, if there is not a request available in scope
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index ff78221c94..4418b4eb2b 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -7,13 +7,7 @@ module Issues
def execute(issue, target_project)
@target_project = target_project
- unless issue.can_move?(current_user, @target_project)
- raise MoveError, s_('MoveIssue|Cannot move issue due to insufficient permissions!')
- end
-
- if @project == @target_project
- raise MoveError, s_('MoveIssue|Cannot move issue to project it originates from!')
- end
+ verify_can_move_issue!(issue, target_project)
super
@@ -32,6 +26,20 @@ module Issues
attr_reader :target_project
+ def verify_can_move_issue!(issue, target_project)
+ unless issue.supports_move_and_clone?
+ raise MoveError, s_('MoveIssue|Cannot move issues of \'%{issue_type}\' type.') % { issue_type: issue.issue_type }
+ end
+
+ unless issue.can_move?(current_user, @target_project)
+ raise MoveError, s_('MoveIssue|Cannot move issue due to insufficient permissions!')
+ end
+
+ if @project == @target_project
+ raise MoveError, s_('MoveIssue|Cannot move issue to project it originates from!')
+ end
+ end
+
def update_service_desk_sent_notifications
return unless original_entity.from_service_desk?
diff --git a/app/services/issues/relative_position_rebalancing_service.rb b/app/services/issues/relative_position_rebalancing_service.rb
index 7d199f99a2..23bb409f3c 100644
--- a/app/services/issues/relative_position_rebalancing_service.rb
+++ b/app/services/issues/relative_position_rebalancing_service.rb
@@ -82,7 +82,7 @@ module Issues
collection.each do |project|
caching.cache_current_project_id(project.id)
index += 1
- scope = Issue.in_projects(project).reorder(custom_reorder).select(:id, :relative_position)
+ scope = Issue.in_projects(project).order_by_relative_position.with_non_null_relative_position.select(:id, :relative_position)
with_retry(PREFETCH_ISSUES_BATCH_SIZE, 100) do |batch_size|
Gitlab::Pagination::Keyset::Iterator.new(scope: scope).each_batch(of: batch_size) do |batch|
@@ -166,10 +166,6 @@ module Issues
@start_position ||= (RelativePositioning::START_POSITION - (gaps / 2) * gap_size).to_i
end
- def custom_reorder
- ::Gitlab::Pagination::Keyset::Order.build([Issue.column_order_relative_position, Issue.column_order_id_asc])
- end
-
def with_retry(initial_batch_size, exit_batch_size)
retries = 0
batch_size = initial_batch_size
diff --git a/app/services/issues/reopen_service.rb b/app/services/issues/reopen_service.rb
index 977b924ed7..4abd1dfbf4 100644
--- a/app/services/issues/reopen_service.rb
+++ b/app/services/issues/reopen_service.rb
@@ -2,10 +2,10 @@
module Issues
class ReopenService < Issues::BaseService
- def execute(issue)
- return issue unless can?(current_user, :reopen_issue, issue)
+ def execute(issue, skip_authorization: false)
+ return issue unless can_reopen?(issue, skip_authorization: skip_authorization)
- if issue.reopen
+ if perform_reopen(issue)
event_service.reopen_issue(issue, current_user)
create_note(issue, 'reopened')
notification_service.async.reopen_issue(issue, current_user)
@@ -22,6 +22,15 @@ module Issues
private
+ # Overriden on EE
+ def perform_reopen(issue)
+ issue.reopen
+ end
+
+ def can_reopen?(issue, skip_authorization: false)
+ skip_authorization || can?(current_user, :reopen_issue, issue)
+ end
+
def perform_incident_management_actions(issue)
end
diff --git a/app/services/merge_requests/mergeability/check_base_service.rb b/app/services/merge_requests/mergeability/check_base_service.rb
new file mode 100644
index 0000000000..d5ddcb4b82
--- /dev/null
+++ b/app/services/merge_requests/mergeability/check_base_service.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+module MergeRequests
+ module Mergeability
+ class CheckBaseService
+ attr_reader :merge_request, :params
+
+ def initialize(merge_request:, params:)
+ @merge_request = merge_request
+ @params = params
+ end
+
+ def skip?
+ raise NotImplementedError
+ end
+
+ # When this method is true, we need to implement a cache_key
+ def cacheable?
+ raise NotImplementedError
+ end
+
+ def cache_key
+ raise NotImplementedError
+ end
+
+ private
+
+ def success(*args)
+ Gitlab::MergeRequests::Mergeability::CheckResult.success(*args)
+ end
+
+ def failure(*args)
+ Gitlab::MergeRequests::Mergeability::CheckResult.failed(*args)
+ end
+ end
+ end
+end
diff --git a/app/services/merge_requests/mergeability/check_ci_status_service.rb b/app/services/merge_requests/mergeability/check_ci_status_service.rb
new file mode 100644
index 0000000000..c0ef5ba1c3
--- /dev/null
+++ b/app/services/merge_requests/mergeability/check_ci_status_service.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+module MergeRequests
+ module Mergeability
+ class CheckCiStatusService < CheckBaseService
+ def execute
+ if merge_request.mergeable_ci_state?
+ success
+ else
+ failure
+ end
+ end
+
+ def skip?
+ params[:skip_ci_check].present?
+ end
+
+ def cacheable?
+ false
+ end
+ end
+ end
+end
diff --git a/app/services/merge_requests/mergeability/run_checks_service.rb b/app/services/merge_requests/mergeability/run_checks_service.rb
new file mode 100644
index 0000000000..c1d65fb65c
--- /dev/null
+++ b/app/services/merge_requests/mergeability/run_checks_service.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+module MergeRequests
+ module Mergeability
+ class RunChecksService
+ include Gitlab::Utils::StrongMemoize
+
+ # We want to have the cheapest checks first in the list,
+ # that way we can fail fast before running the more expensive ones
+ CHECKS = [
+ CheckCiStatusService
+ ].freeze
+
+ def initialize(merge_request:, params:)
+ @merge_request = merge_request
+ @params = params
+ end
+
+ def execute
+ CHECKS.each_with_object([]) do |check_class, results|
+ check = check_class.new(merge_request: merge_request, params: params)
+
+ next if check.skip?
+
+ check_result = run_check(check)
+ results << check_result
+
+ break results if check_result.failed?
+ end
+ end
+
+ private
+
+ attr_reader :merge_request, :params
+
+ def run_check(check)
+ return check.execute unless Feature.enabled?(:mergeability_caching, merge_request.project, default_enabled: :yaml)
+ return check.execute unless check.cacheable?
+
+ cached_result = results.read(merge_check: check)
+ return cached_result if cached_result.respond_to?(:status)
+
+ check.execute.tap do |result|
+ results.write(merge_check: check, result_hash: result.to_hash)
+ end
+ end
+
+ def results
+ strong_memoize(:results) do
+ Gitlab::MergeRequests::Mergeability::ResultsStore.new(merge_request: merge_request)
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index af041de559..c539513890 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -248,7 +248,7 @@ module MergeRequests
def merge_from_quick_action(merge_request)
last_diff_sha = params.delete(:merge)
- MergeRequests::MergeOrchestrationService
+ ::MergeRequests::MergeOrchestrationService
.new(project, current_user, { sha: last_diff_sha })
.execute(merge_request)
end
diff --git a/app/services/metrics/dashboard/annotations/create_service.rb b/app/services/metrics/dashboard/annotations/create_service.rb
index 54f4e96378..b86fa82a5e 100644
--- a/app/services/metrics/dashboard/annotations/create_service.rb
+++ b/app/services/metrics/dashboard/annotations/create_service.rb
@@ -30,7 +30,7 @@ module Metrics
options[:environment] = environment
success(options)
else
- error(s_('Metrics::Dashboard::Annotation|You are not authorized to create annotation for selected environment'))
+ error(s_('MetricsDashboardAnnotation|You are not authorized to create annotation for selected environment'))
end
end
@@ -39,7 +39,7 @@ module Metrics
options[:cluster] = cluster
success(options)
else
- error(s_('Metrics::Dashboard::Annotation|You are not authorized to create annotation for selected cluster'))
+ error(s_('MetricsDashboardAnnotation|You are not authorized to create annotation for selected cluster'))
end
end
@@ -51,7 +51,7 @@ module Metrics
success(options)
rescue Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError
- error(s_('Metrics::Dashboard::Annotation|Dashboard with requested path can not be found'))
+ error(s_('MetricsDashboardAnnotation|Dashboard with requested path can not be found'))
end
def create(options)
diff --git a/app/services/metrics/dashboard/annotations/delete_service.rb b/app/services/metrics/dashboard/annotations/delete_service.rb
index 3efe6924a9..3cb22f8d3d 100644
--- a/app/services/metrics/dashboard/annotations/delete_service.rb
+++ b/app/services/metrics/dashboard/annotations/delete_service.rb
@@ -27,7 +27,7 @@ module Metrics
if Ability.allowed?(user, :delete_metrics_dashboard_annotation, annotation)
success
else
- error(s_('Metrics::Dashboard::Annotation|You are not authorized to delete this annotation'))
+ error(s_('MetricsDashboardAnnotation|You are not authorized to delete this annotation'))
end
end
@@ -35,7 +35,7 @@ module Metrics
if annotation.destroy
success
else
- error(s_('Metrics::Dashboard::Annotation|Annotation has not been deleted'))
+ error(s_('MetricsDashboardAnnotation|Annotation has not been deleted'))
end
end
end
diff --git a/app/services/metrics/users_starred_dashboards/create_service.rb b/app/services/metrics/users_starred_dashboards/create_service.rb
index 9642df8786..0d028f120d 100644
--- a/app/services/metrics/users_starred_dashboards/create_service.rb
+++ b/app/services/metrics/users_starred_dashboards/create_service.rb
@@ -35,7 +35,7 @@ module Metrics
if Ability.allowed?(user, :create_metrics_user_starred_dashboard, project)
success(user: user, project: project)
else
- error(s_('Metrics::UsersStarredDashboards|You are not authorized to add star to this dashboard'))
+ error(s_('MetricsUsersStarredDashboards|You are not authorized to add star to this dashboard'))
end
end
@@ -44,7 +44,7 @@ module Metrics
options[:dashboard_path] = dashboard_path
success(options)
else
- error(s_('Metrics::UsersStarredDashboards|Dashboard with requested path can not be found'))
+ error(s_('MetricsUsersStarredDashboards|Dashboard with requested path can not be found'))
end
end
diff --git a/app/services/packages/composer/create_package_service.rb b/app/services/packages/composer/create_package_service.rb
index 8215a3385a..0f5429f667 100644
--- a/app/services/packages/composer/create_package_service.rb
+++ b/app/services/packages/composer/create_package_service.rb
@@ -17,10 +17,6 @@ module Packages
})
end
- unless Feature.enabled?(:remove_composer_v1_cache_code, project)
- ::Packages::Composer::CacheUpdateWorker.perform_async(created_package.project_id, created_package.name, nil)
- end
-
created_package
end
diff --git a/app/services/projects/after_rename_service.rb b/app/services/projects/after_rename_service.rb
index 953b386b75..a3d54bc6b5 100644
--- a/app/services/projects/after_rename_service.rb
+++ b/app/services/projects/after_rename_service.rb
@@ -12,6 +12,8 @@ module Projects
#
# Projects::AfterRenameService.new(project).execute
class AfterRenameService
+ include BaseServiceUtility
+
# @return [String] The Project being renamed.
attr_reader :project
@@ -78,7 +80,7 @@ module Projects
def execute_system_hooks
project.old_path_with_namespace = full_path_before
- SystemHooksService.new.execute_hooks_for(project, :rename)
+ system_hook_service.execute_hooks_for(project, :rename)
end
def update_repository_configuration
@@ -110,7 +112,7 @@ module Projects
end
def log_completion
- Gitlab::AppLogger.info(
+ log_info(
"Project #{project.id} has been renamed from " \
"#{full_path_before} to #{full_path_after}"
)
@@ -140,7 +142,7 @@ module Projects
def rename_failed!
error = "Repository #{full_path_before} could not be renamed to #{full_path_after}"
- Gitlab::AppLogger.error(error)
+ log_error(error)
raise RenameFailedError, error
end
diff --git a/app/services/projects/container_repository/cache_tags_created_at_service.rb b/app/services/projects/container_repository/cache_tags_created_at_service.rb
new file mode 100644
index 0000000000..3a5346d7a2
--- /dev/null
+++ b/app/services/projects/container_repository/cache_tags_created_at_service.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module Projects
+ module ContainerRepository
+ class CacheTagsCreatedAtService
+ def initialize(container_repository)
+ @container_repository = container_repository
+ @cached_tag_names = Set.new
+ end
+
+ def populate(tags)
+ return if tags.empty?
+
+ # This will load all tags in one Redis roundtrip
+ # the maximum number of tags is configurable and is set to 200 by default.
+ # https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/packages/container_registry/index.md#set-cleanup-limits-to-conserve-resources
+ keys = tags.map(&method(:cache_key))
+ cached_tags_count = 0
+
+ ::Gitlab::Redis::Cache.with do |redis|
+ tags.zip(redis.mget(keys)).each do |tag, created_at|
+ next unless created_at
+
+ tag.created_at = DateTime.rfc3339(created_at)
+ @cached_tag_names << tag.name
+ cached_tags_count += 1
+ end
+ end
+
+ cached_tags_count
+ end
+
+ def insert(tags, max_ttl_in_seconds)
+ return unless max_ttl_in_seconds
+ return if tags.empty?
+
+ # tags with nil created_at are not cacheable
+ # tags already cached don't need to be cached again
+ cacheable_tags = tags.select do |tag|
+ tag.created_at.present? && !tag.name.in?(@cached_tag_names)
+ end
+
+ return if cacheable_tags.empty?
+
+ now = Time.zone.now
+
+ ::Gitlab::Redis::Cache.with do |redis|
+ # we use a pipeline instead of a MSET because each tag has
+ # a specific ttl
+ redis.pipelined do
+ cacheable_tags.each do |tag|
+ created_at = tag.created_at
+ # ttl is the max_ttl_in_seconds reduced by the number
+ # of seconds that the tag has already existed
+ ttl = max_ttl_in_seconds - (now - created_at).seconds
+ ttl = ttl.to_i
+ redis.set(cache_key(tag), created_at.rfc3339, ex: ttl) if ttl > 0
+ end
+ end
+ end
+ end
+
+ private
+
+ def cache_key(tag)
+ "container_repository:{#{@container_repository.id}}:tag:#{tag.name}:created_at"
+ end
+ end
+ end
+end
diff --git a/app/services/projects/container_repository/cleanup_tags_service.rb b/app/services/projects/container_repository/cleanup_tags_service.rb
index 793d2fec03..3a60de0f1e 100644
--- a/app/services/projects/container_repository/cleanup_tags_service.rb
+++ b/app/services/projects/container_repository/cleanup_tags_service.rb
@@ -2,116 +2,152 @@
module Projects
module ContainerRepository
- class CleanupTagsService < BaseService
- def execute(container_repository)
+ class CleanupTagsService
+ include BaseServiceUtility
+ include ::Gitlab::Utils::StrongMemoize
+
+ def initialize(container_repository, user = nil, params = {})
+ @container_repository = container_repository
+ @current_user = user
+ @params = params.dup
+
+ @project = container_repository.project
+ @tags = container_repository.tags
+ tags_size = @tags.size
+ @counts = {
+ original_size: tags_size,
+ cached_tags_count: 0
+ }
+ end
+
+ def execute
return error('access denied') unless can_destroy?
return error('invalid regex') unless valid_regex?
- tags = container_repository.tags
- original_size = tags.size
+ filter_out_latest
+ filter_by_name
- tags = without_latest(tags)
- tags = filter_by_name(tags)
+ truncate
+ populate_from_cache
- before_truncate_size = tags.size
- tags = truncate(tags)
- after_truncate_size = tags.size
+ filter_keep_n
+ filter_by_older_than
- tags = filter_keep_n(tags)
- tags = filter_by_older_than(tags)
-
- delete_tags(container_repository, tags).tap do |result|
- result[:original_size] = original_size
- result[:before_truncate_size] = before_truncate_size
- result[:after_truncate_size] = after_truncate_size
- result[:before_delete_size] = tags.size
+ delete_tags.merge(@counts).tap do |result|
+ result[:before_delete_size] = @tags.size
result[:deleted_size] = result[:deleted]&.size
- result[:status] = :error if before_truncate_size != after_truncate_size
+ result[:status] = :error if @counts[:before_truncate_size] != @counts[:after_truncate_size]
end
end
private
- def delete_tags(container_repository, tags)
- return success(deleted: []) unless tags.any?
-
- tag_names = tags.map(&:name)
+ def delete_tags
+ return success(deleted: []) unless @tags.any?
service = Projects::ContainerRepository::DeleteTagsService.new(
- container_repository.project,
- current_user,
- tags: tag_names,
- container_expiration_policy: params['container_expiration_policy']
+ @project,
+ @current_user,
+ tags: @tags.map(&:name),
+ container_expiration_policy: container_expiration_policy
)
- service.execute(container_repository)
+ service.execute(@container_repository)
end
- def without_latest(tags)
- tags.reject(&:latest?)
+ def filter_out_latest
+ @tags.reject!(&:latest?)
end
- def order_by_date(tags)
+ def order_by_date
now = DateTime.current
- tags.sort_by { |tag| tag.created_at || now }.reverse
+ @tags.sort_by! { |tag| tag.created_at || now }
+ .reverse!
end
- def filter_by_name(tags)
- regex_delete = ::Gitlab::UntrustedRegexp.new("\\A#{params['name_regex_delete'] || params['name_regex']}\\z")
- regex_retain = ::Gitlab::UntrustedRegexp.new("\\A#{params['name_regex_keep']}\\z")
+ def filter_by_name
+ regex_delete = ::Gitlab::UntrustedRegexp.new("\\A#{name_regex_delete || name_regex}\\z")
+ regex_retain = ::Gitlab::UntrustedRegexp.new("\\A#{name_regex_keep}\\z")
- tags.select do |tag|
+ @tags.select! do |tag|
# regex_retain will override any overlapping matches by regex_delete
regex_delete.match?(tag.name) && !regex_retain.match?(tag.name)
end
end
- def filter_keep_n(tags)
- return tags unless params['keep_n']
+ def filter_keep_n
+ return unless keep_n
- tags = order_by_date(tags)
- tags.drop(keep_n)
+ order_by_date
+ cache_tags(@tags.first(keep_n_as_integer))
+ @tags = @tags.drop(keep_n_as_integer)
end
- def filter_by_older_than(tags)
- return tags unless params['older_than']
+ def filter_by_older_than
+ return unless older_than
- older_than = ChronicDuration.parse(params['older_than']).seconds.ago
+ older_than_timestamp = older_than_in_seconds.ago
- tags.select do |tag|
- tag.created_at && tag.created_at < older_than
+ @tags, tags_to_keep = @tags.partition do |tag|
+ tag.created_at && tag.created_at < older_than_timestamp
end
+
+ cache_tags(tags_to_keep)
end
def can_destroy?
- return true if params['container_expiration_policy']
+ return true if container_expiration_policy
- can?(current_user, :destroy_container_image, project)
+ can?(@current_user, :destroy_container_image, @project)
end
def valid_regex?
%w(name_regex_delete name_regex name_regex_keep).each do |param_name|
- regex = params[param_name]
+ regex = @params[param_name]
::Gitlab::UntrustedRegexp.new(regex) unless regex.blank?
end
true
rescue RegexpError => e
- ::Gitlab::ErrorTracking.log_exception(e, project_id: project.id)
+ ::Gitlab::ErrorTracking.log_exception(e, project_id: @project.id)
false
end
- def truncate(tags)
- return tags unless throttling_enabled?
- return tags if max_list_size == 0
+ def truncate
+ @counts[:before_truncate_size] = @tags.size
+ @counts[:after_truncate_size] = @tags.size
+
+ return unless throttling_enabled?
+ return if max_list_size == 0
# truncate the list to make sure that after the #filter_keep_n
# execution, the resulting list will be max_list_size
- truncated_size = max_list_size + keep_n
+ truncated_size = max_list_size + keep_n_as_integer
- return tags if tags.size <= truncated_size
+ return if @tags.size <= truncated_size
- tags.sample(truncated_size)
+ @tags = @tags.sample(truncated_size)
+ @counts[:after_truncate_size] = @tags.size
+ end
+
+ def populate_from_cache
+ @counts[:cached_tags_count] = cache.populate(@tags) if caching_enabled?
+ end
+
+ def cache_tags(tags)
+ cache.insert(tags, older_than_in_seconds) if caching_enabled?
+ end
+
+ def cache
+ strong_memoize(:cache) do
+ ::Projects::ContainerRepository::CacheTagsCreatedAtService.new(@container_repository)
+ end
+ end
+
+ def caching_enabled?
+ container_expiration_policy &&
+ older_than.present? &&
+ Feature.enabled?(:container_registry_expiration_policies_caching, @project)
end
def throttling_enabled?
@@ -123,7 +159,37 @@ module Projects
end
def keep_n
- params['keep_n'].to_i
+ @params['keep_n']
+ end
+
+ def keep_n_as_integer
+ keep_n.to_i
+ end
+
+ def older_than_in_seconds
+ strong_memoize(:older_than_in_seconds) do
+ ChronicDuration.parse(older_than).seconds
+ end
+ end
+
+ def older_than
+ @params['older_than']
+ end
+
+ def name_regex_delete
+ @params['name_regex_delete']
+ end
+
+ def name_regex
+ @params['name_regex']
+ end
+
+ def name_regex_keep
+ @params['name_regex_keep']
+ end
+
+ def container_expiration_policy
+ @params['container_expiration_policy']
end
end
end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index e717491b19..1536f0a22b 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -8,6 +8,7 @@ module Projects
@current_user = user
@params = params.dup
@skip_wiki = @params.delete(:skip_wiki)
+ @initialize_with_sast = Gitlab::Utils.to_boolean(@params.delete(:initialize_with_sast))
@initialize_with_readme = Gitlab::Utils.to_boolean(@params.delete(:initialize_with_readme))
@import_data = @params.delete(:import_data)
@relations_block = @params.delete(:relations_block)
@@ -118,6 +119,7 @@ module Projects
Projects::PostCreationWorker.perform_async(@project.id)
create_readme if @initialize_with_readme
+ create_sast_commit if @initialize_with_sast
end
# Add an authorization for the current user authorizations inline
@@ -160,6 +162,10 @@ module Projects
Files::CreateService.new(@project, current_user, commit_attrs).execute
end
+ def create_sast_commit
+ ::Security::CiConfiguration::SastCreateService.new(@project, current_user, {}, commit_on_default: true).execute
+ end
+
def readme_content
@readme_template.presence || experiment(:new_project_readme_content, namespace: @project.namespace).run_with(@project)
end
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index afa8de04fc..27f813f466 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -5,6 +5,7 @@ module Projects
include Gitlab::ShellAdapter
DestroyError = Class.new(StandardError)
+ BATCH_SIZE = 100
def async_execute
project.update_attribute(:pending_delete, true)
@@ -119,6 +120,12 @@ module Projects
destroy_web_hooks!
destroy_project_bots!
+ if ::Feature.enabled?(:ci_optimize_project_records_destruction, project, default_enabled: :yaml) &&
+ Feature.enabled?(:abort_deleted_project_pipelines, default_enabled: :yaml)
+
+ destroy_ci_records!
+ end
+
# Rails attempts to load all related records into memory before
# destroying: https://github.com/rails/rails/issues/22510
# This ensures we delete records in batches.
@@ -133,6 +140,23 @@ module Projects
log_info("Attempting to destroy #{project.full_path} (#{project.id})")
end
+ def destroy_ci_records!
+ project.all_pipelines.find_each(batch_size: BATCH_SIZE) do |pipeline| # rubocop: disable CodeReuse/ActiveRecord
+ # Destroy artifacts, then builds, then pipelines
+ # All builds have already been dropped by Ci::AbortPipelinesService,
+ # so no Ci::Build-instantiating cancellations happen here.
+ # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71342#note_691523196
+
+ ::Ci::DestroyPipelineService.new(project, current_user).execute(pipeline)
+ end
+
+ deleted_count = project.commit_statuses.delete_all
+
+ if deleted_count > 0
+ Gitlab::AppLogger.info "Projects::DestroyService - Project #{project.id} - #{deleted_count} leftover commit statuses"
+ end
+ end
+
# The project can have multiple webhooks with hundreds of thousands of web_hook_logs.
# By default, they are removed with "DELETE CASCADE" option defined via foreign_key.
# But such queries can exceed the statement_timeout limit and fail to delete the project.
diff --git a/app/services/projects/group_links/update_service.rb b/app/services/projects/group_links/update_service.rb
index 475ab17f1a..a836b96cac 100644
--- a/app/services/projects/group_links/update_service.rb
+++ b/app/services/projects/group_links/update_service.rb
@@ -20,19 +20,15 @@ module Projects
attr_reader :group_link
def refresh_authorizations
- if Feature.enabled?(:specialized_worker_for_project_share_update_auth_recalculation)
- AuthorizedProjectUpdate::ProjectRecalculateWorker.perform_async(project.id)
+ AuthorizedProjectUpdate::ProjectRecalculateWorker.perform_async(project.id)
- # Until we compare the inconsistency rates of the new specialized worker and
- # the old approach, we still run AuthorizedProjectsWorker
- # but with some delay and lower urgency as a safety net.
- group_link.group.refresh_members_authorized_projects(
- blocking: false,
- priority: UserProjectAccessChangedService::LOW_PRIORITY
- )
- else
- group_link.group.refresh_members_authorized_projects
- end
+ # Until we compare the inconsistency rates of the new specialized worker and
+ # the old approach, we still run AuthorizedProjectsWorker
+ # but with some delay and lower urgency as a safety net.
+ group_link.group.refresh_members_authorized_projects(
+ blocking: false,
+ priority: UserProjectAccessChangedService::LOW_PRIORITY
+ )
end
def requires_authorization_refresh?(params)
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index b5288aad6f..4979af6dfe 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -16,6 +16,8 @@ module Projects
end
def execute
+ track_start_import
+
add_repository_to_project
download_lfs_objects
@@ -25,16 +27,17 @@ module Projects
after_execute_hook
success
- rescue Gitlab::UrlBlocker::BlockedUrlError => e
- Gitlab::ErrorTracking.track_exception(e, project_path: project.full_path, importer: project.import_type)
+ rescue Gitlab::UrlBlocker::BlockedUrlError, StandardError => e
+ Gitlab::Import::ImportFailureService.track(
+ project_id: project.id,
+ error_source: self.class.name,
+ exception: e,
+ metrics: true
+ )
- error(s_("ImportProjects|Error importing repository %{project_safe_import_url} into %{project_full_path} - %{message}") % { project_safe_import_url: project.safe_import_url, project_full_path: project.full_path, message: e.message })
- rescue StandardError => e
message = Projects::ImportErrorFilter.filter_message(e.message)
-
- Gitlab::ErrorTracking.track_exception(e, project_path: project.full_path, importer: project.import_type)
-
- error(s_("ImportProjects|Error importing repository %{project_safe_import_url} into %{project_full_path} - %{message}") % { project_safe_import_url: project.safe_import_url, project_full_path: project.full_path, message: message })
+ error(s_("ImportProjects|Error importing repository %{project_safe_import_url} into %{project_full_path} - %{message}") %
+ { project_safe_import_url: project.safe_import_url, project_full_path: project.full_path, message: message })
end
protected
@@ -54,6 +57,10 @@ module Projects
# Defined in EE::Projects::ImportService
end
+ def track_start_import
+ has_importer? && importer_class.try(:track_start_import, project)
+ end
+
def add_repository_to_project
if project.external_import? && !unknown_url?
begin
diff --git a/app/services/projects/overwrite_project_service.rb b/app/services/projects/overwrite_project_service.rb
index f35370c427..2612001eb9 100644
--- a/app/services/projects/overwrite_project_service.rb
+++ b/app/services/projects/overwrite_project_service.rb
@@ -3,7 +3,7 @@
module Projects
class OverwriteProjectService < BaseService
def execute(source_project)
- return unless source_project && source_project.namespace == @project.namespace
+ return unless source_project && source_project.namespace_id == @project.namespace_id
start_time = ::Gitlab::Metrics::System.monotonic_time
@@ -40,7 +40,7 @@ module Projects
duration = ::Gitlab::Metrics::System.monotonic_time - start_time
Gitlab::AppJsonLogger.info(class: self.class.name,
- namespace_id: source_project.namespace.id,
+ namespace_id: source_project.namespace_id,
project_id: source_project.id,
duration_s: duration.to_f,
error: exception.class.name)
diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb
index 228115d72b..1616a8a406 100644
--- a/app/services/projects/participants_service.rb
+++ b/app/services/projects/participants_service.rb
@@ -36,14 +36,17 @@ module Projects
private
def project_members_through_invited_groups
- groups_with_ancestors_ids = Gitlab::ObjectHierarchy
- .new(visible_groups)
- .base_and_ancestors
- .pluck_primary_key
+ groups_with_ancestors = if ::Feature.enabled?(:linear_participants_service_ancestor_scopes, current_user, default_enabled: :yaml)
+ visible_groups.self_and_ancestors
+ else
+ Gitlab::ObjectHierarchy
+ .new(visible_groups)
+ .base_and_ancestors
+ end
GroupMember
.active_without_invites_and_requests
- .with_source_id(groups_with_ancestors_ids)
+ .with_source_id(groups_with_ancestors.pluck_primary_key)
end
def visible_groups
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 27376173f0..a69e6488eb 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -81,7 +81,7 @@ module Projects
# Apply changes to the project
update_namespace_and_visibility(@new_namespace)
- update_shared_runners_settings
+ project.reconcile_shared_runners_setting!
project.save!
# Notifications
@@ -104,6 +104,8 @@ module Projects
update_repository_configuration(@new_path)
execute_system_hooks
+
+ update_pending_builds!
end
post_update_hooks(project)
@@ -154,19 +156,15 @@ module Projects
user_ids = @old_namespace.user_ids_for_project_authorizations |
@new_namespace.user_ids_for_project_authorizations
- if Feature.enabled?(:specialized_worker_for_project_transfer_auth_recalculation)
- AuthorizedProjectUpdate::ProjectRecalculateWorker.perform_async(project.id)
+ AuthorizedProjectUpdate::ProjectRecalculateWorker.perform_async(project.id)
- # Until we compare the inconsistency rates of the new specialized worker and
- # the old approach, we still run AuthorizedProjectsWorker
- # but with some delay and lower urgency as a safety net.
- UserProjectAccessChangedService.new(user_ids).execute(
- blocking: false,
- priority: UserProjectAccessChangedService::LOW_PRIORITY
- )
- else
- UserProjectAccessChangedService.new(user_ids).execute
- end
+ # Until we compare the inconsistency rates of the new specialized worker and
+ # the old approach, we still run AuthorizedProjectsWorker
+ # but with some delay and lower urgency as a safety net.
+ UserProjectAccessChangedService.new(user_ids).execute(
+ blocking: false,
+ priority: UserProjectAccessChangedService::LOW_PRIORITY
+ )
end
def rollback_side_effects
@@ -189,7 +187,7 @@ module Projects
end
def execute_system_hooks
- SystemHooksService.new.execute_hooks_for(project, :transfer)
+ system_hook_service.execute_hooks_for(project, :transfer)
end
def move_project_folders(project)
@@ -241,18 +239,19 @@ module Projects
"#{new_path}#{::Gitlab::GlRepository::DESIGN.path_suffix}"
end
- def update_shared_runners_settings
- # If a project is being transferred to another group it means it can already
- # have shared runners enabled but we need to check whether the new group allows that.
- if project.group && project.group.shared_runners_setting == 'disabled_and_unoverridable'
- project.shared_runners_enabled = false
- end
- end
-
def update_integrations
project.integrations.with_default_settings.delete_all
Integration.create_from_active_default_integrations(project, :project_id)
end
+
+ def update_pending_builds!
+ update_params = {
+ namespace_id: new_namespace.id,
+ namespace_traversal_ids: new_namespace.traversal_ids
+ }
+
+ ::Ci::UpdatePendingBuildService.new(project, update_params).execute
+ end
end
end
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index dc75fe1014..0000e713cb 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -136,13 +136,11 @@ module Projects
def validate_outdated_sha!
return if latest?
- if Feature.enabled?(:pages_smart_check_outdated_sha, project, default_enabled: :yaml)
- # use pipeline_id in case the build is retried
- last_deployed_pipeline_id = project.pages_metadatum&.pages_deployment&.ci_build&.pipeline_id
+ # use pipeline_id in case the build is retried
+ last_deployed_pipeline_id = project.pages_metadatum&.pages_deployment&.ci_build&.pipeline_id
- return unless last_deployed_pipeline_id
- return if last_deployed_pipeline_id <= build.pipeline_id
- end
+ return unless last_deployed_pipeline_id
+ return if last_deployed_pipeline_id <= build.pipeline_id
raise InvalidStateError, 'build SHA is outdated for this ref'
end
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index a89db1e9fe..b34ecf06e5 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -105,7 +105,7 @@ module Projects
end
update_pages_config if changing_pages_related_config?
- update_pending_builds if shared_runners_toggled?
+ update_pending_builds if runners_settings_toggled?
end
def after_rename_service(project)
@@ -181,13 +181,36 @@ module Projects
end
def update_pending_builds
- update_params = { instance_runners_enabled: project.shared_runners_enabled }
+ update_params = {
+ instance_runners_enabled: project.shared_runners_enabled?,
+ namespace_traversal_ids: group_runner_traversal_ids
+ }
- ::Ci::UpdatePendingBuildService.new(project, update_params).execute
+ ::Ci::UpdatePendingBuildService
+ .new(project, update_params)
+ .execute
end
- def shared_runners_toggled?
- project.previous_changes.include?('shared_runners_enabled')
+ def shared_runners_settings_toggled?
+ project.previous_changes.include?(:shared_runners_enabled)
+ end
+
+ def group_runners_settings_toggled?
+ return false unless project.ci_cd_settings.present?
+
+ project.ci_cd_settings.previous_changes.include?(:group_runners_enabled)
+ end
+
+ def runners_settings_toggled?
+ shared_runners_settings_toggled? || group_runners_settings_toggled?
+ end
+
+ def group_runner_traversal_ids
+ if project.group_runners_enabled?
+ project.namespace.traversal_ids
+ else
+ []
+ end
end
end
end
diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb
index 33faf2d669..cee59360b4 100644
--- a/app/services/search/global_service.rb
+++ b/app/services/search/global_service.rb
@@ -24,7 +24,7 @@ module Search
# rubocop: disable CodeReuse/ActiveRecord
def projects
- @projects ||= ProjectsFinder.new(params: { non_archived: true }, current_user: current_user).execute.preload(:topics, :taggings)
+ @projects ||= ProjectsFinder.new(params: { non_archived: true }, current_user: current_user).execute.preload(:topics, :project_topics)
end
def allowed_scopes
diff --git a/app/services/security/ci_configuration/base_create_service.rb b/app/services/security/ci_configuration/base_create_service.rb
index adb45244ad..ea77cd98ba 100644
--- a/app/services/security/ci_configuration/base_create_service.rb
+++ b/app/services/security/ci_configuration/base_create_service.rb
@@ -25,7 +25,7 @@ module Security
rescue Gitlab::Git::PreReceiveError => e
ServiceResponse.error(message: e.message)
rescue StandardError
- project.repository.rm_branch(current_user, branch_name) if project.repository.branch_exists?(branch_name)
+ remove_branch_on_exception
raise
end
@@ -50,6 +50,10 @@ module Security
Gitlab::Routing.url_helpers.project_new_merge_request_url(project, merge_request: merge_request_params)
end
+ def remove_branch_on_exception
+ project.repository.rm_branch(current_user, branch_name) if project.repository.branch_exists?(branch_name)
+ end
+
def track_event(attributes_for_commit)
action = attributes_for_commit[:actions].first
diff --git a/app/services/security/ci_configuration/sast_create_service.rb b/app/services/security/ci_configuration/sast_create_service.rb
index f495cac18f..47e01847b1 100644
--- a/app/services/security/ci_configuration/sast_create_service.rb
+++ b/app/services/security/ci_configuration/sast_create_service.rb
@@ -5,15 +5,28 @@ module Security
class SastCreateService < ::Security::CiConfiguration::BaseCreateService
attr_reader :params
- def initialize(project, current_user, params)
+ def initialize(project, current_user, params, commit_on_default: false)
super(project, current_user)
@params = params
+
+ @commit_on_default = commit_on_default
+ @branch_name = project.default_branch if @commit_on_default
end
private
+ def remove_branch_on_exception
+ super unless @commit_on_default
+ end
+
def action
- Security::CiConfiguration::SastBuildAction.new(project.auto_devops_enabled?, params, existing_gitlab_ci_content).generate
+ existing_content = begin
+ existing_gitlab_ci_content # this can fail on the very first commit
+ rescue StandardError
+ nil
+ end
+
+ Security::CiConfiguration::SastBuildAction.new(project.auto_devops_enabled?, params, existing_content).generate
end
def next_branch
diff --git a/app/services/service_ping/submit_service.rb b/app/services/service_ping/submit_service.rb
index 3417ce4f58..63e01603d4 100644
--- a/app/services/service_ping/submit_service.rb
+++ b/app/services/service_ping/submit_service.rb
@@ -78,7 +78,7 @@ module ServicePing
def store_metrics(response)
metrics = response['conv_index'] || response['dev_ops_score'] # leaving dev_ops_score here, as the response data comes from the gitlab-version-com
- return unless metrics.present?
+ return unless metrics.except('usage_data_id').present?
DevOpsReport::Metric.create!(
metrics.slice(*METRICS)
diff --git a/app/services/terraform/remote_state_handler.rb b/app/services/terraform/remote_state_handler.rb
index e9a13cee76..f13477b8b3 100644
--- a/app/services/terraform/remote_state_handler.rb
+++ b/app/services/terraform/remote_state_handler.rb
@@ -2,8 +2,6 @@
module Terraform
class RemoteStateHandler < BaseService
- include Gitlab::OptimisticLocking
-
StateLockedError = Class.new(StandardError)
UnauthorizedError = Class.new(StandardError)
@@ -60,7 +58,7 @@ module Terraform
private
def retrieve_with_lock(find_only: false)
- create_or_find!(find_only: find_only).tap { |state| retry_optimistic_lock(state, name: 'terraform_remote_state_handler_retrieve') { |state| yield state } }
+ create_or_find!(find_only: find_only).tap { |state| state.with_lock { yield state } }
end
def create_or_find!(find_only:)
diff --git a/app/services/user_project_access_changed_service.rb b/app/services/user_project_access_changed_service.rb
index 5f48f410bf..5bba986f4a 100644
--- a/app/services/user_project_access_changed_service.rb
+++ b/app/services/user_project_access_changed_service.rb
@@ -30,7 +30,7 @@ class UserProjectAccessChangedService
end
end
- ::Gitlab::Database::LoadBalancing::Sticking.bulk_stick(:user, @user_ids)
+ ::User.sticking.bulk_stick(:user, @user_ids)
result
end
diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb
index 23c67231a2..c3df9b153a 100644
--- a/app/services/users/update_service.rb
+++ b/app/services/users/update_service.rb
@@ -5,15 +5,18 @@ module Users
include NewUserNotifier
attr_reader :user, :identity_params
+ ATTRS_REQUIRING_PASSWORD_CHECK = %w[email].freeze
+
def initialize(current_user, params = {})
@current_user = current_user
+ @validation_password = params.delete(:validation_password)
@user = params.delete(:user)
@status_params = params.delete(:status)
@identity_params = params.slice(*identity_attributes)
@params = params.dup
end
- def execute(validate: true, &block)
+ def execute(validate: true, check_password: false, &block)
yield(@user) if block_given?
user_exists = @user.persisted?
@@ -21,6 +24,11 @@ module Users
discard_read_only_attributes
assign_attributes
+
+ if check_password && require_password_check? && !@user.valid_password?(@validation_password)
+ return error(s_("Profiles|Invalid password"))
+ end
+
assign_identity
build_canonical_email
@@ -32,8 +40,8 @@ module Users
end
end
- def execute!(*args, &block)
- result = execute(*args, &block)
+ def execute!(*args, **kargs, &block)
+ result = execute(*args, **kargs, &block)
raise ActiveRecord::RecordInvalid, @user unless result[:status] == :success
@@ -42,6 +50,14 @@ module Users
private
+ def require_password_check?
+ return false unless @user.persisted?
+ return false if @user.password_automatically_set?
+
+ changes = @user.changed
+ ATTRS_REQUIRING_PASSWORD_CHECK.any? { |param| changes.include?(param) }
+ end
+
def build_canonical_email
return unless @user.email_changed?
diff --git a/app/services/users/upsert_credit_card_validation_service.rb b/app/services/users/upsert_credit_card_validation_service.rb
index 70a96b3ec6..86b5b92341 100644
--- a/app/services/users/upsert_credit_card_validation_service.rb
+++ b/app/services/users/upsert_credit_card_validation_service.rb
@@ -7,6 +7,14 @@ module Users
end
def execute
+ @params = {
+ user_id: params.fetch(:user_id),
+ credit_card_validated_at: params.fetch(:credit_card_validated_at),
+ expiration_date: get_expiration_date(params),
+ last_digits: Integer(params.fetch(:credit_card_mask_number), 10),
+ holder_name: params.fetch(:credit_card_holder_name)
+ }
+
::Users::CreditCardValidation.upsert(@params)
ServiceResponse.success(message: 'CreditCardValidation was set')
@@ -16,5 +24,14 @@ module Users
Gitlab::ErrorTracking.track_exception(e, params: @params, class: self.class.to_s)
ServiceResponse.error(message: "Could not set CreditCardValidation: #{e.message}")
end
+
+ private
+
+ def get_expiration_date(params)
+ year = params.fetch(:credit_card_expiration_year)
+ month = params.fetch(:credit_card_expiration_month)
+
+ Date.new(year, month, -1) # last day of the month
+ end
end
end
diff --git a/app/uploaders/dependency_proxy/file_uploader.rb b/app/uploaders/dependency_proxy/file_uploader.rb
index 5154f18045..f0222d4cf0 100644
--- a/app/uploaders/dependency_proxy/file_uploader.rb
+++ b/app/uploaders/dependency_proxy/file_uploader.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
class DependencyProxy::FileUploader < GitlabUploader
+ extend Workhorse::UploadPath
include ObjectStorage::Concern
before :cache, :set_content_type
diff --git a/app/views/admin/application_settings/_abuse.html.haml b/app/views/admin/application_settings/_abuse.html.haml
index fab3ce584f..96fb848b56 100644
--- a/app/views/admin/application_settings/_abuse.html.haml
+++ b/app/views/admin/application_settings/_abuse.html.haml
@@ -5,7 +5,5 @@
.form-group
= f.label :abuse_notification_email, _('Abuse reports notification email'), class: 'label-bold'
= f.text_field :abuse_notification_email, class: 'form-control gl-form-input'
- .form-text.text-muted
- = _('Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.')
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/app/views/admin/application_settings/_account_and_limit.html.haml b/app/views/admin/application_settings/_account_and_limit.html.haml
index eb30efabb9..19c38d7be6 100644
--- a/app/views/admin/application_settings/_account_and_limit.html.haml
+++ b/app/views/admin/application_settings/_account_and_limit.html.haml
@@ -23,11 +23,11 @@
.form-group
= f.label :max_import_size, _('Maximum import size (MB)'), class: 'label-light'
= f.number_field :max_import_size, class: 'form-control gl-form-input qa-receive-max-import-size-field', title: _('Maximum size of import files.'), data: { toggle: 'tooltip', container: 'body' }
- %span.form-text.text-muted= _('0 for unlimited, only effective with remote storage enabled.')
+ %span.form-text.text-muted= _('Only effective when remote storage is enabled. Set to 0 for no size limit.')
.form-group
= f.label :session_expire_delay, _('Session duration (minutes)'), class: 'label-light'
= f.number_field :session_expire_delay, class: 'form-control gl-form-input', title: _('Maximum duration of a session.'), data: { toggle: 'tooltip', container: 'body' }
- %span.form-text.text-muted#session_expire_delay_help_block= _('GitLab restart is required to apply changes.')
+ %span.form-text.text-muted#session_expire_delay_help_block= _('Restart GitLab to apply changes.')
= render_if_exists 'admin/application_settings/git_two_factor_session_expiry', form: f
= render_if_exists 'admin/application_settings/personal_access_token_expiration_policy', form: f
@@ -45,13 +45,13 @@
.form-check
= f.check_box :user_default_external, class: 'form-check-input'
= f.label :user_default_external, class: 'form-check-label' do
- = _('Newly registered users will by default be external')
+ = _('Newly-registered users are external by default')
.gl-mt-3
= _('Internal users')
= f.text_field :user_default_internal_regex, placeholder: _('Regex pattern'), class: 'form-control gl-form-input gl-mt-2'
.help-block
- = _('Specify an e-mail address regex pattern to identify default internal users.')
- = link_to _('More information'), help_page_path('user/permissions', anchor: 'setting-new-users-to-external'),
+ = _('Specify an email address regex pattern to identify default internal users.')
+ = link_to _('Learn more'), help_page_path('user/permissions', anchor: 'setting-new-users-to-external'),
target: '_blank'
- unless Gitlab.com?
.form-group
@@ -59,11 +59,13 @@
.form-check
= f.check_box :deactivate_dormant_users, class: 'form-check-input'
= f.label :deactivate_dormant_users, class: 'form-check-label' do
- = _('Deactivate dormant users after 90 days of inactivity. Users can return to active status by signing in to their account. While inactive, a user is not counted as an active user in the instance.')
- = link_to _('More information'), help_page_path('user/admin_area/moderate_users', anchor: 'automatically-deactivate-dormant-users'), target: '_blank'
+ = _('Deactivate dormant users after 90 days of inactivity')
+ .help-block
+ = _('Users can reactivate their account by signing in.')
+ = link_to _('Learn more'), help_page_path('user/admin_area/moderate_users', anchor: 'automatically-deactivate-dormant-users'), target: '_blank'
.form-group
= f.label :personal_access_token_prefix, _('Personal Access Token prefix'), class: 'label-light'
- = f.text_field :personal_access_token_prefix, placeholder: _('Max 20 characters'), class: 'form-control gl-form-input'
+ = f.text_field :personal_access_token_prefix, placeholder: _('Maximum 20 characters'), class: 'form-control gl-form-input'
.form-group
= f.label :user_show_add_ssh_key_message, _('Prompt users to upload SSH keys'), class: 'label-bold'
.form-check
diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml
index fea116bd41..8026ec4702 100644
--- a/app/views/admin/application_settings/_ci_cd.html.haml
+++ b/app/views/admin/application_settings/_ci_cd.html.haml
@@ -69,5 +69,12 @@
%p.form-text.text-muted
= _("The default CI/CD configuration file and path for new projects.").html_safe
= link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'specify-a-custom-cicd-configuration-file'), target: '_blank'
+ .form-group
+ .form-check
+ = f.check_box :suggest_pipeline_enabled, class: 'form-check-input'
+ = f.label :suggest_pipeline_enabled, class: 'form-check-label' do
+ = s_('AdminSettings|Enable pipeline suggestion banner')
+ .form-text.text-muted
+ = s_('AdminSettings|Display a banner on merge requests in projects with no pipelines to initiate steps to add a .gitlab-ci.yml file.')
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/app/views/admin/application_settings/_files_limits.html.haml b/app/views/admin/application_settings/_files_limits.html.haml
deleted file mode 100644
index 9cd12fa1ca..0000000000
--- a/app/views/admin/application_settings/_files_limits.html.haml
+++ /dev/null
@@ -1,34 +0,0 @@
-= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-files-limits-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting)
-
- %fieldset
- %legend.h5.gl-border-none
- = _('Unauthenticated API request rate limit')
- .form-group
- = f.gitlab_ui_checkbox_component :throttle_unauthenticated_files_api_enabled,
- _('Enable unauthenticated API request rate limit'),
- help_text: _('Helps reduce request volume (e.g. from crawlers or abusive bots)'),
- checkbox_options: { data: { qa_selector: 'throttle_unauthenticated_files_api_checkbox' } }
- .form-group
- = f.label :throttle_unauthenticated_files_api_requests_per_period, 'Max unauthenticated API requests per period per IP', class: 'label-bold'
- = f.number_field :throttle_unauthenticated_files_api_requests_per_period, class: 'form-control gl-form-input'
- .form-group
- = f.label :throttle_unauthenticated_files_api_period_in_seconds, 'Unauthenticated API rate limit period in seconds', class: 'label-bold'
- = f.number_field :throttle_unauthenticated_files_api_period_in_seconds, class: 'form-control gl-form-input'
-
- %fieldset
- %legend.h5.gl-border-none
- = _('Authenticated API request rate limit')
- .form-group
- = f.gitlab_ui_checkbox_component :throttle_authenticated_files_api_enabled,
- _('Enable authenticated API request rate limit'),
- help_text: _('Helps reduce request volume (e.g. from crawlers or abusive bots)'),
- checkbox_options: { data: { qa_selector: 'throttle_authenticated_files_api_checkbox' } }
- .form-group
- = f.label :throttle_authenticated_files_api_requests_per_period, 'Max authenticated API requests per period per user', class: 'label-bold'
- = f.number_field :throttle_authenticated_files_api_requests_per_period, class: 'form-control gl-form-input'
- .form-group
- = f.label :throttle_authenticated_files_api_period_in_seconds, 'Authenticated API rate limit period in seconds', class: 'label-bold'
- = f.number_field :throttle_authenticated_files_api_period_in_seconds, class: 'form-control gl-form-input'
-
- = f.submit 'Save changes', class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
diff --git a/app/views/admin/application_settings/_help_page.html.haml b/app/views/admin/application_settings/_help_page.html.haml
index ecf3203df9..cd7eaa1896 100644
--- a/app/views/admin/application_settings/_help_page.html.haml
+++ b/app/views/admin/application_settings/_help_page.html.haml
@@ -18,11 +18,10 @@
= f.text_field :help_page_support_url, class: 'form-control gl-form-input', placeholder: 'https://company.example.com/getting-help', :'aria-describedby' => 'support_help_block'
%span.form-text.text-muted#support_help_block= _('Alternate support URL for Help page and Help dropdown.')
- - if show_documentation_base_url_field?
- .form-group
- = f.label :help_page_documentation_base_url, _('Documentation pages URL'), class: 'label-bold'
- = f.text_field :help_page_documentation_base_url, class: 'form-control gl-form-input', placeholder: 'https://docs.gitlab.com'
- - docs_link_url = help_page_path('user/admin_area/settings/help_page', anchor: 'destination-requirements')
- - docs_link_start = ''.html_safe % { url: docs_link_url }
- %span.form-text.text-muted#support_help_block= html_escape(_('Requests for pages at %{code_start}%{help_text_url}%{code_end} redirect to the URL. The destination must meet certain requirements. %{docs_link_start}Learn more.%{docs_link_end}')) % { code_start: ''.html_safe, help_text_url: help_url, code_end: ''.html_safe, docs_link_start: docs_link_start, docs_link_end: ''.html_safe }
+ .form-group
+ = f.label :help_page_documentation_base_url, _('Documentation pages URL'), class: 'gl-font-weight-bold'
+ = f.text_field :help_page_documentation_base_url, class: 'form-control gl-form-input', placeholder: 'https://docs.gitlab.com'
+ - docs_link_url = help_page_path('user/admin_area/settings/help_page', anchor: 'destination-requirements')
+ - docs_link_start = ''.html_safe % { url: docs_link_url }
+ %span.form-text.text-muted#support_help_block= html_escape(_('Requests for pages at %{code_start}%{help_text_url}%{code_end} redirect to the URL. The destination must meet certain requirements. %{docs_link_start}Learn more.%{docs_link_end}')) % { code_start: ''.html_safe, help_text_url: help_url, code_end: ''.html_safe, docs_link_start: docs_link_start, docs_link_end: ''.html_safe }
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/app/views/admin/application_settings/_mailgun.html.haml b/app/views/admin/application_settings/_mailgun.html.haml
index 40b4d5cac6..ad9e84ffda 100644
--- a/app/views/admin/application_settings/_mailgun.html.haml
+++ b/app/views/admin/application_settings/_mailgun.html.haml
@@ -6,7 +6,7 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
- = _('Configure the %{link} integration.').html_safe % { link: link_to(_('Mailgun events'), 'https://documentation.mailgun.com/en/latest/user_manual.html#webhooks', target: '_blank') }
+ = _('Configure the %{link} integration.').html_safe % { link: link_to(_('Mailgun events'), 'https://documentation.mailgun.com/en/latest/user_manual.html#webhooks', target: '_blank', rel: 'noopener noreferrer') }
.settings-content
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-mailgun-settings'), html: { class: 'fieldset-form', id: 'mailgun-settings' } do |f|
= form_errors(@application_setting) if expanded
diff --git a/app/views/admin/application_settings/_network_rate_limits.html.haml b/app/views/admin/application_settings/_network_rate_limits.html.haml
new file mode 100644
index 0000000000..f1857a9749
--- /dev/null
+++ b/app/views/admin/application_settings/_network_rate_limits.html.haml
@@ -0,0 +1,33 @@
+= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: anchor), html: { class: 'fieldset-form' } do |f|
+ = form_errors(@application_setting)
+
+ %fieldset
+ = _("Rate limits can help reduce request volume (like from crawlers or abusive bots).")
+
+ %fieldset
+ .form-group
+ = f.gitlab_ui_checkbox_component :"throttle_unauthenticated_#{setting_fragment}_enabled",
+ _('Enable unauthenticated API request rate limit'),
+ checkbox_options: { data: { qa_selector: "throttle_unauthenticated_#{setting_fragment}_checkbox" } },
+ label_options: { class: 'label-bold' }
+ .form-group
+ = f.label :"throttle_unauthenticated_#{setting_fragment}_requests_per_period", _('Maximum unauthenticated API requests per rate limit period per IP'), class: 'label-bold'
+ = f.number_field :"throttle_unauthenticated_#{setting_fragment}_requests_per_period", class: 'form-control gl-form-input'
+ .form-group
+ = f.label :"throttle_unauthenticated_#{setting_fragment}_period_in_seconds", _('Unauthenticated API rate limit period in seconds'), class: 'label-bold'
+ = f.number_field :"throttle_unauthenticated_#{setting_fragment}_period_in_seconds", class: 'form-control gl-form-input'
+
+ %fieldset
+ .form-group
+ = f.gitlab_ui_checkbox_component :"throttle_authenticated_#{setting_fragment}_enabled",
+ _('Enable authenticated API request rate limit'),
+ checkbox_options: { data: { qa_selector: "throttle_authenticated_#{setting_fragment}_checkbox" } },
+ label_options: { class: 'label-bold' }
+ .form-group
+ = f.label :"throttle_authenticated_#{setting_fragment}_requests_per_period", _('Maximum authenticated API requests per rate limit period per user'), class: 'label-bold'
+ = f.number_field :"throttle_authenticated_#{setting_fragment}_requests_per_period", class: 'form-control gl-form-input'
+ .form-group
+ = f.label :"throttle_authenticated_#{setting_fragment}_period_in_seconds", _('Authenticated API rate limit period in seconds'), class: 'label-bold'
+ = f.number_field :"throttle_authenticated_#{setting_fragment}_period_in_seconds", class: 'form-control gl-form-input'
+
+ = f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
diff --git a/app/views/admin/application_settings/_package_registry_limits.html.haml b/app/views/admin/application_settings/_package_registry_limits.html.haml
deleted file mode 100644
index 8769171c9e..0000000000
--- a/app/views/admin/application_settings/_package_registry_limits.html.haml
+++ /dev/null
@@ -1,32 +0,0 @@
-= form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-packages-limits-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting)
-
- %fieldset
- = _("The package registry rate limits can help reduce request volume (like from crawlers or abusive bots).")
-
- %fieldset
- .form-group
- .form-check
- = f.check_box :throttle_unauthenticated_packages_api_enabled, class: 'form-check-input', data: { qa_selector: 'throttle_unauthenticated_packages_api_checkbox' }
- = f.label :throttle_unauthenticated_packages_api_enabled, class: 'form-check-label label-bold' do
- = _('Enable unauthenticated API request rate limit')
- .form-group
- = f.label :throttle_unauthenticated_packages_api_requests_per_period, _('Maximum unauthenticated API requests per rate limit period per IP'), class: 'label-bold'
- = f.number_field :throttle_unauthenticated_packages_api_requests_per_period, class: 'form-control gl-form-input'
- .form-group
- = f.label :throttle_unauthenticated_packages_api_period_in_seconds, _('Unauthenticated API rate limit period in seconds'), class: 'label-bold'
- = f.number_field :throttle_unauthenticated_packages_api_period_in_seconds, class: 'form-control gl-form-input'
- %hr
- .form-group
- .form-check
- = f.check_box :throttle_authenticated_packages_api_enabled, class: 'form-check-input', data: { qa_selector: 'throttle_authenticated_packages_api_checkbox' }
- = f.label :throttle_authenticated_packages_api_enabled, class: 'form-check-label label-bold' do
- = _('Enable authenticated API request rate limit')
- .form-group
- = f.label :throttle_authenticated_packages_api_requests_per_period, _('Maximum authenticated API requests per rate limit period per user'), class: 'label-bold'
- = f.number_field :throttle_authenticated_packages_api_requests_per_period, class: 'form-control gl-form-input'
- .form-group
- = f.label :throttle_authenticated_packages_api_period_in_seconds, _('Authenticated API rate limit period in seconds'), class: 'label-bold'
- = f.number_field :throttle_authenticated_packages_api_period_in_seconds, class: 'form-control gl-form-input'
-
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
diff --git a/app/views/admin/application_settings/_performance.html.haml b/app/views/admin/application_settings/_performance.html.haml
index 50fc11ec7f..82e56cf8b8 100644
--- a/app/views/admin/application_settings/_performance.html.haml
+++ b/app/views/admin/application_settings/_performance.html.haml
@@ -6,29 +6,24 @@
.form-check
= f.check_box :authorized_keys_enabled, class: 'form-check-input'
= f.label :authorized_keys_enabled, class: 'form-check-label' do
- = _('Write to "authorized_keys" file')
+ = _('Use authorized_keys file to authenticate SSH keys')
.form-text.text-muted
- By default, we write to the "authorized_keys" file to support Git
- over SSH without additional configuration. GitLab can be optimized
- to authenticate SSH keys via the database file. Only uncheck this
- if you have configured your OpenSSH server to use the
- AuthorizedKeysCommand. Click on the help icon for more details.
- = link_to sprite_icon('question-o'), help_page_path('administration/operations/fast_ssh_key_lookup')
-
+ = _('Authenticate user SSH keys without requiring additional configuration. Performance of GitLab can be improved by using the GitLab database instead.')
+ = link_to _('How do I configure authentication using the GitLab database?'), help_page_path('administration/operations/fast_ssh_key_lookup'), target: '_blank', rel: 'noopener noreferrer'
.form-group
= f.label :raw_blob_request_limit, _('Raw blob request rate limit per minute'), class: 'label-bold'
= f.number_field :raw_blob_request_limit, class: 'form-control gl-form-input'
.form-text.text-muted
- = _('Highest number of requests per minute for each raw path, default to 300. To disable throttling set to 0.')
+ = _('Maximum number of requests per minute for each raw path (default is 300). Set to 0 to disable throttling.')
.form-group
= f.label :push_event_hooks_limit, class: 'label-bold'
= f.number_field :push_event_hooks_limit, class: 'form-control gl-form-input'
.form-text.text-muted
- = _("Number of changes (branches or tags) in a single push to determine whether webhooks and services will be fired or not. Webhooks and services won't be submitted if it surpasses that value.")
+ = _('Maximum number of changes (branches or tags) in a single push for which webhooks and services trigger (default is 3).')
.form-group
= f.label :push_event_activities_limit, class: 'label-bold'
= f.number_field :push_event_activities_limit, class: 'form-control gl-form-input'
.form-text.text-muted
- = _('Number of changes (branches or tags) in a single push to determine whether individual push events or bulk push event will be created. Bulk push event will be created if it surpasses that value.')
+ = _('Threshold number of changes (branches or tags) in a single push above which a bulk push event is created (default is 3).')
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/app/views/admin/application_settings/_snowplow.html.haml b/app/views/admin/application_settings/_snowplow.html.haml
index 8c98778147..756c0e770a 100644
--- a/app/views/admin/application_settings/_snowplow.html.haml
+++ b/app/views/admin/application_settings/_snowplow.html.haml
@@ -7,7 +7,7 @@
= expanded ? _('Collapse') : _('Expand')
%p
- link_start = ''.html_safe % { url: help_page_path('development/snowplow/index') }
- = html_escape(_('Configure %{link} to track events. %{link_start}Learn more.%{link_end}')) % { link: link_to('Snowplow', 'https://snowplowanalytics.com/', target: '_blank').html_safe, link_start: link_start, link_end: ''.html_safe }
+ = html_escape(_('Configure %{link} to track events. %{link_start}Learn more.%{link_end}')) % { link: link_to('Snowplow', 'https://snowplowanalytics.com/', target: '_blank', rel: 'noopener noreferrer').html_safe, link_start: link_start, link_end: ''.html_safe }
.settings-content
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-snowplow-settings'), html: { class: 'fieldset-form', id: 'snowplow-settings' } do |f|
= form_errors(@application_setting) if expanded
diff --git a/app/views/admin/application_settings/_spam.html.haml b/app/views/admin/application_settings/_spam.html.haml
index 011bce3ca9..53ca4d4aa7 100644
--- a/app/views/admin/application_settings/_spam.html.haml
+++ b/app/views/admin/application_settings/_spam.html.haml
@@ -2,6 +2,11 @@
= form_errors(@application_setting)
%fieldset
+ %h5
+ = _('reCAPTCHA')
+ %p
+ = _('reCAPTCHA helps prevent credential stuffing.')
+ = link_to _('Only reCAPTCHA v2 is supported:'), 'https://developers.google.com/recaptcha/docs/versions', target: '_blank', rel: 'noopener noreferrer'
.form-group
.form-check
= f.check_box :recaptcha_enabled, class: 'form-check-input'
@@ -9,25 +14,31 @@
= _("Enable reCAPTCHA")
%span.form-text.text-muted#recaptcha_help_block
= _('Helps prevent bots from creating accounts.')
+ = link_to _('How do I configure it?'), help_page_path('integration/recaptcha.md'), target: '_blank', rel: 'noopener noreferrer'
.form-group
.form-check
= f.check_box :login_recaptcha_protection_enabled, class: 'form-check-input'
= f.label :login_recaptcha_protection_enabled, class: 'form-check-label' do
- = _("Enable reCAPTCHA for login")
+ = _('Enable reCAPTCHA for login.')
%span.form-text.text-muted#recaptcha_help_block
= _('Helps prevent bots from brute-force attacks.')
.form-group
- = f.label :recaptcha_site_key, _('reCAPTCHA Site Key'), class: 'label-bold'
+ = f.label :recaptcha_site_key, _('reCAPTCHA site key'), class: 'label-bold'
= f.text_field :recaptcha_site_key, class: 'form-control gl-form-input'
.form-text.text-muted
= _("Generate site and private keys at")
%a{ href: 'http://www.google.com/recaptcha', target: 'blank' } http://www.google.com/recaptcha
.form-group
- = f.label :recaptcha_private_key, _('reCAPTCHA Private Key'), class: 'label-bold'
- .form-group
+ = f.label :recaptcha_private_key, _('reCAPTCHA private key'), class: 'label-bold'
= f.text_field :recaptcha_private_key, class: 'form-control gl-form-input'
+ %h5
+ = _('Invisible Captcha')
+ %p
+ = _('Invisible Captcha helps prevent the creation of spam accounts. It adds a honeypot field and time-sensitive form submission to the account signup form.')
+ = link_to _('Read their documentation.'), 'https://github.com/markets/invisible_captcha', target: '_blank', rel: 'noopener noreferrer'
+
.form-group
.form-check
= f.check_box :invisible_captcha_enabled, class: 'form-check-input'
@@ -36,12 +47,18 @@
%span.form-text.text-muted
= _('Helps prevent bots from creating accounts.')
+ %h5
+ = _('Akismet')
+ %p
+ = _('Akismet helps prevent the creation of spam issues in public projects.')
+ = link_to _('How do I configure Akismet?'), help_page_path('integration/akismet.md'), target: '_blank', rel: 'noopener noreferrer'
+
.form-group
.form-check
= f.check_box :akismet_enabled, class: 'form-check-input'
= f.label :akismet_enabled, class: 'form-check-label' do
Enable Akismet
- %span.form-text.text-muted#akismet_help_block= _("Helps prevent bots from creating issues")
+ %span.form-text.text-muted#akismet_help_block= _("Helps prevent bots from creating issues.")
.form-group
= f.label :akismet_api_key, _('Akismet API Key'), class: 'label-bold'
@@ -50,25 +67,31 @@
Generate API key at
%a{ href: 'http://www.akismet.com', target: 'blank' } http://www.akismet.com
+ %h5
+ = _('IP address restrictions')
+
.form-group
.form-check
= f.check_box :unique_ips_limit_enabled, class: 'form-check-input'
= f.label :unique_ips_limit_enabled, class: 'form-check-label' do
- = _("Limit sign in from multiple ips")
+ = _("Limit sign in from multiple IP addresses")
%span.form-text.text-muted#unique_ip_help_block
- = _("Helps prevent malicious users hide their activity")
+ = _("Helps prevent malicious users hide their activity.")
.form-group
- = f.label :unique_ips_limit_per_user, _('IPs per user'), class: 'label-bold'
+ = f.label :unique_ips_limit_per_user, _('IP addresses per user'), class: 'label-bold'
= f.number_field :unique_ips_limit_per_user, class: 'form-control gl-form-input'
.form-text.text-muted
- = _("Maximum number of unique IPs per user")
+ = _("Maximum number of unique IP addresses per user.")
.form-group
- = f.label :unique_ips_limit_time_window, _('IP expiration time'), class: 'label-bold'
+ = f.label :unique_ips_limit_time_window, _('IP address expiration time'), class: 'label-bold'
= f.number_field :unique_ips_limit_time_window, class: 'form-control gl-form-input'
.form-text.text-muted
- = _("How many seconds an IP will be counted towards the limit")
+ = _("How many seconds an IP counts toward the IP address limit.")
+
+ %h5
+ = _('Spam Check')
.form-group
.form-check
@@ -79,8 +102,8 @@
= f.label :spam_check_endpoint_url, _('URL of the external Spam Check endpoint'), class: 'label-bold'
= f.text_field :spam_check_endpoint_url, class: 'form-control gl-form-input'
.form-group
- = f.label :spam_check_api_key, _('Spam Check API Key'), class: 'gl-font-weight-bold'
+ = f.label :spam_check_api_key, _('Spam Check API key'), class: 'gl-font-weight-bold'
= f.text_field :spam_check_api_key, class: 'form-control gl-form-input'
- .form-text.text-muted= _('The API key used by GitLab for accessing the Spam Check service endpoint')
+ .form-text.text-muted= _('The API key used by GitLab for accessing the Spam Check service endpoint.')
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/app/views/admin/application_settings/_terminal.html.haml b/app/views/admin/application_settings/_terminal.html.haml
index d6e31a24cf..c53f63e124 100644
--- a/app/views/admin/application_settings/_terminal.html.haml
+++ b/app/views/admin/application_settings/_terminal.html.haml
@@ -6,5 +6,5 @@
= f.label :terminal_max_session_time, _('Max session time'), class: 'label-bold'
= f.number_field :terminal_max_session_time, class: 'form-control gl-form-input'
.form-text.text-muted
- = _('Maximum time for web terminal websocket connection (in seconds). 0 for unlimited.')
+ = _('Maximum time, in seconds, for a web terminal websocket connection. 0 for unlimited.')
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/app/views/admin/application_settings/_usage.html.haml b/app/views/admin/application_settings/_usage.html.haml
index ddd0abb4c3..5bdad50c16 100644
--- a/app/views/admin/application_settings/_usage.html.haml
+++ b/app/views/admin/application_settings/_usage.html.haml
@@ -10,21 +10,21 @@
= f.label :version_check_enabled, class: 'form-check-label' do
= _("Enable version check")
.form-text.text-muted
- = _("GitLab will inform you if a new version is available.")
- = _("%{link_start}Learn more%{link_end} about what information is shared with GitLab Inc.").html_safe % { link_start: "".html_safe, link_end: ''.html_safe }
+ = _("GitLab informs you if a new version is available.")
+ = _("%{link_start}What information does GitLab Inc. collect?%{link_end}").html_safe % { link_start: "".html_safe, link_end: ''.html_safe }
.form-group
- can_be_configured = @application_setting.usage_ping_can_be_configured?
.form-check
= f.check_box :usage_ping_enabled, disabled: !can_be_configured, class: 'form-check-input'
= f.label :usage_ping_enabled, class: 'form-check-label' do
- = _('Enable service ping')
+ = _('Enable Service Ping')
.form-text.text-muted
- if can_be_configured
- %p.mb-2= _('To help improve GitLab and its user experience, GitLab will periodically collect usage information.')
+ %p.mb-2= _('To help improve GitLab and its user experience, GitLab periodically collects usage information.')
- - service_ping_path = help_page_path('user/admin_area/settings/usage_statistics', anchor: 'service-ping')
+ - service_ping_path = help_page_path('development/service_ping/index.md')
- service_ping_link_start = ''.html_safe % { url: service_ping_path }
- %p.mb-2= s_('%{service_ping_link_start}Learn more%{service_ping_link_end} about what information is shared with GitLab Inc.').html_safe % { service_ping_link_start: service_ping_link_start, service_ping_link_end: ''.html_safe }
+ %p.mb-2= s_('%{service_ping_link_start}What information is shared with GitLab Inc.?%{service_ping_link_end}').html_safe % { service_ping_link_start: service_ping_link_start, service_ping_link_end: ''.html_safe }
%button.gl-button.btn.btn-default.js-payload-preview-trigger{ type: 'button', data: { payload_selector: ".#{payload_class}" } }
.gl-spinner.js-spinner.gl-display-none.gl-mr-2
@@ -46,15 +46,23 @@
- if usage_ping_enabled
%p.gl-mb-3.text-muted{ id: 'service_ping_features_helper_text' }= _('You can enable Registration Features because Service Ping is enabled. To continue using Registration Features in the future, you will also need to register with GitLab via a new cloud licensing service.')
- else
- %p.gl-mb-3.text-muted{ id: 'service_ping_features_helper_text' }= _('To enable Registration Features, make sure "Enable service ping" is checked.')
+ %p.gl-mb-3.text-muted{ id: 'service_ping_features_helper_text' }= _('To enable Registration Features, first enable Service Ping.')
%p.gl-mb-3.text-muted= _('Registration Features include:')
.form-text
- email_from_gitlab_path = help_page_path('tools/email.md')
+ - repo_size_limit_path = help_page_path('user/admin_area/settings/account_and_limit_settings.md', anchor: 'repository-size-limit')
+ - restrict_ip_path = help_page_path('user/group/index.md', anchor: 'restrict-group-access-by-ip-address')
- link_end = ''.html_safe
- email_from_gitlab_link = ''.html_safe % { url: email_from_gitlab_path }
+ - repo_size_limit_link = ''.html_safe % { url: repo_size_limit_path }
+ - restrict_ip_link = ''.html_safe % { url: restrict_ip_path }
%ul
%li
= _('Email from GitLab - email users right from the Admin Area. %{link_start}Learn more%{link_end}.').html_safe % { link_start: email_from_gitlab_link, link_end: link_end }
+ %li
+ = _('Limit project size at a global, group, and project level. %{link_start}Learn more%{link_end}.').html_safe % { link_start: repo_size_limit_link, link_end: link_end }
+ %li
+ = _('Restrict group access by IP address. %{link_start}Learn more%{link_end}.').html_safe % { link_start: restrict_ip_link, link_end: link_end }
= f.submit _('Save changes'), class: "gl-button btn btn-confirm"
diff --git a/app/views/admin/application_settings/appearances/preview_sign_in.html.haml b/app/views/admin/application_settings/appearances/preview_sign_in.html.haml
index 77c37abbee..2e4ab71404 100644
--- a/app/views/admin/application_settings/appearances/preview_sign_in.html.haml
+++ b/app/views/admin/application_settings/appearances/preview_sign_in.html.haml
@@ -1,12 +1,13 @@
= render 'devise/shared/tab_single', tab_title: _('Sign in preview')
.login-box
%form.gl-show-field-errors
+ - title = _('This form is disabled in preview')
.form-group
= label_tag :login
- = text_field_tag :login, nil, class: "form-control gl-form-input top", title: _('Please provide your username or email address.')
+ = text_field_tag :login, nil, disabled: true, class: "form-control gl-form-input top", title: title
.form-group
= label_tag :password
- = password_field_tag :password, nil, class: "form-control gl-form-input bottom", title: _('This field is required.')
+ = password_field_tag :password, nil, disabled: true, class: "form-control gl-form-input bottom", title: title
.form-group
- = button_tag _("Sign in"), class: "btn gl-button btn-confirm", type: "button"
+ = button_tag _("Sign in"), disabled: true, class: "btn gl-button btn-confirm", type: "button", title: title
diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml
index 9102769cc6..a72c96bb57 100644
--- a/app/views/admin/application_settings/general.html.haml
+++ b/app/views/admin/application_settings/general.html.haml
@@ -79,7 +79,8 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Set max session time for web terminal.')
+ = _('Set the maximum session time for a web terminal.')
+ = link_to _('How do I use a web terminal?'), help_page_path('ci/environments/index.md', anchor: 'web-terminals'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'terminal'
diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml
index f1e37c7613..6087551d7c 100644
--- a/app/views/admin/application_settings/metrics_and_profiling.html.haml
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -49,7 +49,7 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Enable or disable version check and service ping.')
+ = _('Enable or disable version check and Service Ping.')
.settings-content
= render 'usage'
diff --git a/app/views/admin/application_settings/network.html.haml b/app/views/admin/application_settings/network.html.haml
index 8dff2bc36c..58e3f3f113 100644
--- a/app/views/admin/application_settings/network.html.haml
+++ b/app/views/admin/application_settings/network.html.haml
@@ -35,9 +35,10 @@
= _('Set rate limits for package registry API requests that supersede the general user and IP rate limits.')
= link_to _('Learn more.'), help_page_path('user/admin_area/settings/package_registry_rate_limits.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
- = render 'package_registry_limits'
+ = render partial: 'network_rate_limits', locals: { anchor: 'js-packages-limits-settings', setting_fragment: 'packages_api' }
+
- if Feature.enabled?(:files_api_throttling, default_enabled: :yaml)
- %section.settings.as-files-limits.no-animate#js-files-limits-settings{ class: ('expanded' if expanded_by_default?), data: { testid: 'files-limits-settings' } }
+ %section.settings.as-files-limits.no-animate#js-files-limits-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Files API Rate Limits')
@@ -46,7 +47,19 @@
%p
= _('Configure specific limits for Files API requests that supersede the general user and IP rate limits.')
.settings-content
- = render 'files_limits'
+ = render partial: 'network_rate_limits', locals: { anchor: 'js-files-limits-settings', setting_fragment: 'files_api' }
+
+%section.settings.as-deprecated-limits.no-animate#js-deprecated-limits-settings{ class: ('expanded' if expanded_by_default?) }
+ .settings-header
+ %h4
+ = _('Deprecated API rate limits')
+ %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
+ = expanded_by_default? ? _('Collapse') : _('Expand')
+ %p
+ = _('Configure specific limits for deprecated API requests that supersede the general user and IP rate limits.')
+ = link_to _('Which API requests are affected?'), help_page_path('user/admin_area/settings/deprecated_api_rate_limits.md'), target: '_blank', rel: 'noopener noreferrer'
+ .settings-content
+ = render partial: 'network_rate_limits', locals: { anchor: 'js-deprecated-limits-settings', setting_fragment: 'deprecated_api' }
%section.settings.as-git-lfs-limits.no-animate#js-git-lfs-limits-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'git_lfs_limits_content' } }
.settings-header
diff --git a/app/views/admin/application_settings/reporting.html.haml b/app/views/admin/application_settings/reporting.html.haml
index 914a09ff5d..d2e118f062 100644
--- a/app/views/admin/application_settings/reporting.html.haml
+++ b/app/views/admin/application_settings/reporting.html.haml
@@ -9,9 +9,7 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- - recaptcha_v2_link_url = 'https://developers.google.com/recaptcha/docs/versions'
- - recaptcha_v2_link_start = ''.html_safe % { url: recaptcha_v2_link_url }
- = _('Enable reCAPTCHA, Invisible Captcha, Akismet and set IP limits. For reCAPTCHA, we currently only support %{recaptcha_v2_link_start}v2%{recaptcha_v2_link_end}').html_safe % { recaptcha_v2_link_start: recaptcha_v2_link_start, recaptcha_v2_link_end: ''.html_safe }
+ = _('Configure CAPTCHAs, IP address limits, and other anti-spam measures.')
.settings-content
= render 'spam'
@@ -22,6 +20,7 @@
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Set notification email for abuse reports.')
+ = _('Receive notification of abuse reports by email.')
+ = link_to _('Learn more.'), help_page_path('user/admin_area/review_abuse_reports.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= render 'abuse'
diff --git a/app/views/admin/dashboard/_security_newsletter_callout.html.haml b/app/views/admin/dashboard/_security_newsletter_callout.html.haml
new file mode 100644
index 0000000000..ece0f7ca4d
--- /dev/null
+++ b/app/views/admin/dashboard/_security_newsletter_callout.html.haml
@@ -0,0 +1,14 @@
+- return unless show_security_newsletter_user_callout?
+
+= render 'shared/global_alert',
+ title: s_('AdminArea|Get security updates from GitLab and stay up to date'),
+ variant: :tip,
+ alert_class: 'js-security-newsletter-callout',
+ is_contained: true,
+ alert_data: { feature_id: UserCalloutsHelper::SECURITY_NEWSLETTER_CALLOUT, dismiss_endpoint: user_callouts_path, defer_links: 'true' },
+ close_button_data: { testid: 'close-security-newsletter-callout' } do
+ .gl-alert-body
+ = s_('AdminArea|Sign up for the GitLab Security Newsletter to get notified for security updates.')
+ .gl-alert-actions
+ = link_to 'https://about.gitlab.com/company/preference-center/', target: '_blank', rel: 'noreferrer noopener', class: 'deferred-link gl-alert-action btn-confirm btn-md gl-button' do
+ = s_('AdminArea|Sign up for the GitLab newsletter')
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 97b3a757a3..681e7ccb61 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -4,6 +4,7 @@
- billable_users_link_start = ''.html_safe % { url: billable_users_url }
= render_if_exists 'shared/qrtly_reconciliation_alert'
+= render 'admin/dashboard/security_newsletter_callout'
- if @notices
- @notices.each do |notice|
diff --git a/app/views/admin/hook_logs/_index.html.haml b/app/views/admin/hook_logs/_index.html.haml
index a7f947f96e..6a46b0b351 100644
--- a/app/views/admin/hook_logs/_index.html.haml
+++ b/app/views/admin/hook_logs/_index.html.haml
@@ -1,37 +1,11 @@
+- docs_link_url = help_page_path('user/project/integrations/webhooks', anchor: 'troubleshoot-webhooks')
+- link_start = ''.html_safe % { url: docs_link_url }
+- link_end = ''.html_safe
+
.row.gl-mt-3.gl-mb-3
.col-lg-3
%h4.gl-mt-0
- = _('Recent Deliveries')
- %p= _('When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong.')
+ = _('Recent events')
+ %p= _('GitLab events trigger webhooks. Use the request details of a webhook to help troubleshoot problems. %{link_start}How do I troubleshoot?%{link_end}').html_safe % { link_start: link_start, link_end: link_end }
.col-lg-9
- - if hook_logs.present?
- %table.table
- %thead
- %tr
- %th= _('Status')
- %th= _('Trigger')
- %th= _('URL')
- %th= _('Elapsed time')
- %th= _('Request time')
- %th
- - hook_logs.each do |hook_log|
- %tr
- %td
- = render partial: 'shared/hook_logs/status_label', locals: { hook_log: hook_log }
- %td.d-none.d-sm-block
- %span.badge.badge-gray.deploy-project-label
- = hook_log.trigger.singularize.titleize
- %td
- = truncate(hook_log.url, length: 50)
- %td.light
- #{number_with_precision(hook_log.execution_duration, precision: 2)} sec
- %td.light
- = time_ago_with_tooltip(hook_log.created_at)
- %td
- = link_to _('View details'), admin_hook_hook_log_path(hook, hook_log)
-
- = paginate hook_logs, theme: 'gitlab'
-
- - else
- .settings-message.text-center
- = _("You don't have any webhooks deliveries")
+ = render partial: 'shared/hook_logs/recent_deliveries_table', locals: { hook: hook, hook_logs: hook_logs }
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 5ebfd296e2..f947e17499 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -1,33 +1,24 @@
- page_title _('Projects')
- params[:visibility_level] ||= []
-- active_tab_classes = 'active gl-tab-nav-item-active gl-tab-nav-item-active-indigo'
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
- %ul.nav.gl-tabs-nav.gl-overflow-x-auto.gl-display-flex.gl-flex-grow-1.gl-flex-shrink-1.gl-border-b-0.gl-flex-nowrap.gl-webkit-scrollbar-display-none
- = nav_link(html_options: { class: "nav-item" } ) do
- = link_to _('All'), admin_projects_path, class: "nav-link gl-tab-nav-item #{active_tab_classes if params[:visibility_level].empty?}"
- = nav_link(html_options: { class: "nav-item" } ) do
- = link_to _('Private'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PRIVATE), class: "nav-link gl-tab-nav-item #{active_tab_classes if params[:visibility_level] == Gitlab::VisibilityLevel::PRIVATE.to_s}"
- = nav_link(html_options: { class: "nav-item" } ) do
- = link_to _('Internal'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL), class: "nav-link gl-tab-nav-item #{active_tab_classes if params[:visibility_level] == Gitlab::VisibilityLevel::INTERNAL.to_s}"
- = nav_link(html_options: { class: "nav-item" } ) do
- = link_to _('Public'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC), class: "nav-link gl-tab-nav-item #{active_tab_classes if params[:visibility_level] == Gitlab::VisibilityLevel::PUBLIC.to_s}"
+ = gl_tabs_nav({ class: 'gl-border-b-0 gl-overflow-x-auto gl-flex-grow-1 gl-flex-nowrap gl-webkit-scrollbar-display-none' }) do
+ = gl_tab_link_to _('All'), admin_projects_path(visibility_level: nil), { item_active: params[:visibility_level].empty? }
+ = gl_tab_link_to _('Private'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ = gl_tab_link_to _('Internal'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ = gl_tab_link_to _('Public'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
.nav-controls
.search-holder
= render 'shared/projects/search_form', autofocus: true, admin_view: true
- .dropdown
- - toggle_text = _('Namespace')
- - if params[:namespace_id].present?
- = hidden_field_tag :namespace_id, params[:namespace_id]
- - namespace = Namespace.find(params[:namespace_id])
- - toggle_text = "#{namespace.kind}: #{namespace.full_path}"
- = dropdown_toggle(toggle_text, { toggle: 'dropdown', is_filter: 'true' }, { toggle_class: 'js-namespace-select large' })
- .dropdown-menu.dropdown-select.dropdown-menu-right
- = dropdown_title(_('Namespaces'))
- = dropdown_filter(_("Search for Namespace"))
- = dropdown_content
- = dropdown_loading
+ - current_namespace = _('Namespace')
+ - if params[:namespace_id].present?
+ - namespace = Namespace.find(params[:namespace_id])
+ - current_namespace = "#{namespace.kind}: #{namespace.full_path}"
+ %button.dropdown-menu-toggle.btn.btn-default.btn-md.gl-button.js-namespace-select{ data: { show_any: 'true', field_name: 'namespace_id', placeholder: current_namespace, update_location: 'true' }, type: 'button' }
+ %span.gl-new-dropdown-button-text
+ = current_namespace
+
= render 'shared/projects/dropdown'
= link_to new_project_path, class: 'gl-button btn btn-confirm' do
= _('New Project')
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 1a87b21351..3069aab271 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -143,13 +143,10 @@
.col-sm-3.col-form-label
= f.label :new_namespace_id, _("Namespace")
.col-sm-9
- .dropdown
- = dropdown_toggle(_('Search for Namespace'), { toggle: 'dropdown', field_name: 'new_namespace_id' }, { toggle_class: 'js-namespace-select large' })
- .dropdown-menu.dropdown-select
- = dropdown_title(_('Namespaces'))
- = dropdown_filter(_('Search for Namespace'))
- = dropdown_content
- = dropdown_loading
+ - placeholder = _('Search for Namespace')
+ %button.dropdown-menu-toggle.btn.btn-default.btn-md.gl-button.js-namespace-select{ data: { field_name: 'new_namespace_id', placeholder: placeholder }, type: 'button' }
+ %span.gl-new-dropdown-button-text
+ = placeholder
.form-group.row
.offset-sm-3.col-sm-9
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index 59523ed3a0..808b2bb4f8 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -9,7 +9,7 @@
.row
.col-md-6
%h4= _('Restrict projects for this runner')
- - if @runner.projects.any?
+ - if @runner.runner_projects.any?
%table.table{ data: { testid: 'assigned-projects' } }
%thead
%tr
diff --git a/app/views/admin/serverless/domains/_form.html.haml b/app/views/admin/serverless/domains/_form.html.haml
deleted file mode 100644
index a3e1ccc5d4..0000000000
--- a/app/views/admin/serverless/domains/_form.html.haml
+++ /dev/null
@@ -1,99 +0,0 @@
-- form_name = 'js-serverless-domain-settings'
-- form_url = @domain.persisted? ? admin_serverless_domain_path(@domain.id, anchor: form_name) : admin_serverless_domains_path(anchor: form_name)
-- show_certificate_card = @domain.persisted? && @domain.errors.blank?
-= form_for @domain, url: form_url, html: { class: 'fieldset-form' } do |f|
- = form_errors(@domain)
-
- %fieldset
- - if @domain.persisted?
- - dns_record = "*.#{@domain.domain} CNAME #{Settings.pages.host}."
- - verification_record = "#{@domain.verification_domain} TXT #{@domain.keyed_verification_code}"
- .form-group.row
- .col-sm-6.position-relative
- = f.label :domain, _('Domain'), class: 'label-bold'
- = f.text_field :domain, class: 'form-control has-floating-status-badge', readonly: true
- .status-badge.floating-status-badge
- - text, status = @domain.unverified? ? [_('Unverified'), 'badge-danger'] : [_('Verified'), 'badge-success']
- .badge{ class: status }
- = text
- = link_to sprite_icon("redo"), verify_admin_serverless_domain_path(@domain.id), method: :post, class: "gl-button btn has-tooltip", title: _("Retry verification")
-
- .col-sm-6
- = f.label :serverless_domain_dns, _('DNS'), class: 'label-bold'
- .input-group
- = text_field_tag :serverless_domain_dns, dns_record , class: "monospace js-select-on-focus form-control", readonly: true
- .input-group-append
- = clipboard_button(target: '#serverless_domain_dns', class: 'btn-default input-group-text d-none d-sm-block')
-
- .col-sm-12.form-text.text-muted
- = _("To access this domain create a new DNS record")
-
- .form-group
- = f.label :serverless_domain_verification, _('Verification status'), class: 'label-bold'
- .input-group
- = text_field_tag :serverless_domain_verification, verification_record, class: "monospace js-select-on-focus form-control", readonly: true
- .input-group-append
- = clipboard_button(target: '#serverless_domain_verification', class: 'btn-default d-none d-sm-block')
- %p.form-text.text-muted
- - link_to_help = link_to(_('verify ownership'), help_page_path('user/project/pages/custom_domains_ssl_tls_certification/index.md', anchor: '4-verify-the-domains-ownership'))
- = _("To %{link_to_help} of your domain, add the above key to a TXT record within your DNS configuration.").html_safe % { link_to_help: link_to_help }
-
- - else
- .form-group
- = f.label :domain, _('Domain'), class: 'label-bold'
- = f.text_field :domain, class: 'form-control'
-
- - if show_certificate_card
- .card.js-domain-cert-show
- .card-header
- = _('Certificate')
- .d-flex.justify-content-between.align-items-center.p-3
- %span
- = @domain.subject || _('missing')
- %button.gl-button.btn.btn-danger.btn-sm.js-domain-cert-replace-btn{ type: 'button' }
- = _('Replace')
-
- .js-domain-cert-inputs{ class: ('hidden' if show_certificate_card) }
- .form-group
- = f.label :user_provided_certificate, _('Certificate (PEM)'), class: 'label-bold'
- = f.text_area :user_provided_certificate, rows: 5, class: 'form-control', value: ''
- %span.form-text.text-muted
- = _("Upload a certificate for your domain with all intermediates")
- .form-group
- = f.label :user_provided_key, _('Key (PEM)'), class: 'label-bold'
- = f.text_area :user_provided_key, rows: 5, class: 'form-control', value: ''
- %span.form-text.text-muted
- = _("Upload a private key for your certificate")
-
- = f.submit @domain.persisted? ? _('Save changes') : _('Add domain'), class: "gl-button btn btn-confirm js-serverless-domain-submit", disabled: @domain.persisted?
- - if @domain.persisted?
- %button.gl-button.btn.btn-danger{ type: 'button', data: { toggle: 'modal', target: "#modal-delete-domain" } }
- = _('Delete domain')
-
--# haml-lint:disable NoPlainNodes
-- if @domain.persisted?
- - domain_attached = @domain.serverless_domain_clusters.count > 0
- .modal{ id: "modal-delete-domain", tabindex: -1 }
- .modal-dialog
- .modal-content
- .modal-header
- %h3.page-title= _('Delete serverless domain?')
- %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
- %span{ "aria-hidden": "true" } ×
-
- .modal-body
- - if domain_attached
- = _("You must disassociate %{domain} from all clusters it is attached to before deletion.").html_safe % { domain: "#{@domain.domain}".html_safe }
- - else
- = _("You are about to delete %{domain} from your instance. This domain will no longer be available to any Knative application.").html_safe % { domain: "#{@domain.domain}".html_safe }
-
- .modal-footer
- %a{ href: '#', data: { dismiss: 'modal' }, class: 'gl-button btn btn-default' }
- = _('Cancel')
-
- = link_to _('Delete domain'),
- admin_serverless_domain_path(@domain.id),
- title: _('Delete'),
- method: :delete,
- class: "gl-button btn btn-danger",
- disabled: domain_attached
diff --git a/app/views/admin/serverless/domains/index.html.haml b/app/views/admin/serverless/domains/index.html.haml
deleted file mode 100644
index c2b6baed4d..0000000000
--- a/app/views/admin/serverless/domains/index.html.haml
+++ /dev/null
@@ -1,25 +0,0 @@
-- breadcrumb_title _("Operations")
-- page_title _("Operations")
-- @content_class = "limit-container-width" unless fluid_layout
-
--# normally expanded_by_default? is used here, but since this is the only panel
--# in this settings page, let's leave it always open by default
-- expanded = true
-
-%section.settings.as-serverless-domain.no-animate#js-serverless-domain-settings{ class: ('expanded' if expanded) }
- .settings-header
- %h4
- = _('Serverless domain')
- %button.gl-button.btn.btn-default.js-settings-toggle{ type: 'button' }
- = expanded ? _('Collapse') : _('Expand')
- %p
- = _('Set an instance-wide domain that will be available to all clusters when installing Knative.')
- .settings-content
- - if Gitlab.config.pages.enabled
- = render 'form'
- - else
- .card
- .card-header
- = s_('GitLabPages|Domains')
- .nothing-here-block
- = s_("GitLabPages|Support for domains and certificates is disabled. Ask your system's administrator to enable it.")
diff --git a/app/views/admin/sessions/_new_base.html.haml b/app/views/admin/sessions/_new_base.html.haml
index 47ef4f2688..c9b002a4dd 100644
--- a/app/views/admin/sessions/_new_base.html.haml
+++ b/app/views/admin/sessions/_new_base.html.haml
@@ -1,7 +1,7 @@
= form_tag(admin_session_path, method: :post, class: 'new_user gl-show-field-errors', 'aria-live': 'assertive') do
.form-group
= label_tag :user_password, _('Password'), class: 'label-bold'
- = password_field_tag 'user[password]', nil, class: 'form-control', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field' }
+ = password_field_tag 'user[password]', nil, class: 'form-control', autocomplete: 'current-password', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field' }
.submit-container.move-submit-down
= submit_tag _('Enter Admin Mode'), class: 'gl-button btn btn-success', data: { qa_selector: 'enter_admin_mode_button' }
diff --git a/app/views/admin/topics/_form.html.haml b/app/views/admin/topics/_form.html.haml
new file mode 100644
index 0000000000..21a1d74a8c
--- /dev/null
+++ b/app/views/admin/topics/_form.html.haml
@@ -0,0 +1,40 @@
+= gitlab_ui_form_for @topic, url: url, html: { multipart: true, class: 'js-project-topic-form gl-show-field-errors common-note-form js-quick-submit js-requires-input' }, authenticity_token: true do |f|
+ = form_errors(@topic)
+
+ .form-group
+ = f.label :name do
+ = _("Topic name")
+ = f.text_field :name, placeholder: _('My topic'), class: 'form-control input-lg', data: { qa_selector: 'topic_name_field' },
+ required: true,
+ title: _('Please fill in a name for your topic.'),
+ autofocus: true
+
+ .form-group
+ = f.label :description, _("Description")
+ = render layout: 'shared/md_preview', locals: { url: preview_markdown_admin_topics_path, referenced_users: false } do
+ = render 'shared/zen', f: f, attr: :description,
+ classes: 'note-textarea',
+ placeholder: _('Write a description…'),
+ supports_quick_actions: false,
+ supports_autocomplete: false,
+ qa_selector: 'topic_form_description'
+ = render 'shared/notes/hints', supports_file_upload: false
+
+ .form-group.gl-mt-3.gl-mb-3
+ = f.label :avatar, _('Topic avatar'), class: 'gl-display-block'
+ - if @topic.avatar?
+ .avatar-container.rect-avatar.s90
+ = topic_icon(@topic, alt: _('Topic avatar'), class: 'avatar topic-avatar s90')
+ = render 'shared/choose_avatar_button', f: f
+ - if @topic.avatar?
+ = link_to _('Remove avatar'), admin_topic_avatar_path(@topic), data: { confirm: _('Avatar will be removed. Are you sure?')}, method: :delete, class: 'gl-button btn btn-danger-secondary gl-mt-2'
+
+ - if @topic.new_record?
+ .form-actions
+ = f.submit _('Create topic'), class: "gl-button btn btn-confirm"
+ = link_to _('Cancel'), admin_topics_path, class: "gl-button btn btn-default btn-cancel"
+
+ - else
+ .form-actions
+ = f.submit _('Save changes'), class: "gl-button btn btn-confirm", data: { qa_selector: 'save_changes_button' }
+ = link_to _('Cancel'), admin_topics_path, class: "gl-button btn btn-cancel"
diff --git a/app/views/admin/topics/_topic.html.haml b/app/views/admin/topics/_topic.html.haml
new file mode 100644
index 0000000000..abf3cffa42
--- /dev/null
+++ b/app/views/admin/topics/_topic.html.haml
@@ -0,0 +1,17 @@
+- topic = local_assigns.fetch(:topic)
+
+%li.topic-row.gl-py-3.gl-align-items-center{ class: 'gl-display-flex!', data: { qa_selector: 'topic_row_content' } }
+ .avatar-container.rect-avatar.s40.gl-flex-shrink-0
+ = topic_icon(topic, class: "avatar s40")
+
+ .gl-min-w-0.gl-flex-grow-1
+ .title
+ = topic.name
+
+ .stats.gl-text-gray-500.gl-flex-shrink-0.gl-display-none.gl-sm-display-flex
+ %span.gl-ml-5.has-tooltip{ title: n_('%d project', '%d projects', topic.total_projects_count) % topic.total_projects_count }
+ = sprite_icon('bookmark', css_class: 'gl-vertical-align-text-bottom')
+ = number_with_delimiter(topic.total_projects_count)
+
+ .controls.gl-flex-shrink-0.gl-ml-5
+ = link_to _('Edit'), edit_admin_topic_path(topic), id: "edit_#{dom_id(topic)}", class: 'btn gl-button btn-default'
diff --git a/app/views/admin/topics/edit.html.haml b/app/views/admin/topics/edit.html.haml
new file mode 100644
index 0000000000..4416bb0fe1
--- /dev/null
+++ b/app/views/admin/topics/edit.html.haml
@@ -0,0 +1,4 @@
+- page_title _("Edit"), @topic.name, _("Topics")
+%h3.page-title= _('Edit topic: %{topic_name}') % { topic_name: @topic.name }
+%hr
+= render 'form', url: admin_topic_path(@topic)
diff --git a/app/views/admin/topics/index.html.haml b/app/views/admin/topics/index.html.haml
new file mode 100644
index 0000000000..6485b8aa41
--- /dev/null
+++ b/app/views/admin/topics/index.html.haml
@@ -0,0 +1,19 @@
+- page_title _("Topics")
+
+= form_tag admin_topics_path, method: :get do |f|
+ .gl-py-3.gl-display-flex.gl-flex-direction-column-reverse.gl-md-flex-direction-row.gl-border-b-solid.gl-border-gray-100.gl-border-b-1
+ .gl-flex-grow-1.gl-mt-3.gl-md-mt-0
+ .inline.gl-w-full.gl-md-w-auto
+ - search = params.fetch(:search, nil)
+ .search-field-holder
+ = search_field_tag :search, search, class: "form-control gl-form-input search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: _('Search by name'), data: { qa_selector: 'topic_search_field' }
+ = sprite_icon('search', css_class: 'search-icon')
+ .nav-controls
+ = link_to new_admin_topic_path, class: "gl-button btn btn-confirm gl-w-full gl-md-w-auto" do
+ = _('New topic')
+%ul.content-list
+ = render partial: 'topic', collection: @topics
+
+= paginate_collection @topics
+- if @topics.empty?
+ = render 'shared/empty_states/topics'
diff --git a/app/views/admin/topics/new.html.haml b/app/views/admin/topics/new.html.haml
new file mode 100644
index 0000000000..8b4a8ac269
--- /dev/null
+++ b/app/views/admin/topics/new.html.haml
@@ -0,0 +1,4 @@
+- page_title _("New topic")
+%h3.page-title= _('New topic')
+%hr
+= render 'form', url: admin_topics_path(@topic)
diff --git a/app/views/admin/users/_access_levels.html.haml b/app/views/admin/users/_access_levels.html.haml
index aeb274fe2c..6a5f07dd2d 100644
--- a/app/views/admin/users/_access_levels.html.haml
+++ b/app/views/admin/users/_access_levels.html.haml
@@ -19,22 +19,20 @@
.col-sm-10
- editing_current_user = (current_user == @user)
- = f.radio_button :access_level, :regular, disabled: editing_current_user
- = f.label :access_level_regular, class: 'font-weight-bold' do
- = s_('AdminUsers|Regular')
- %p.light
- = s_('AdminUsers|Regular users have access to their groups and projects')
+ = f.gitlab_ui_radio_component :access_level, :regular,
+ s_('AdminUsers|Regular'),
+ radio_options: { disabled: editing_current_user },
+ help_text: s_('AdminUsers|Regular users have access to their groups and projects.')
= render_if_exists 'admin/users/auditor_access_level_radio', f: f, disabled: editing_current_user
- = f.radio_button :access_level, :admin, disabled: editing_current_user
- = f.label :access_level_admin, class: 'font-weight-bold' do
- = s_('AdminUsers|Admin')
- %p.light
- = s_('AdminUsers|Administrators have access to all groups, projects and users and can manage all features in this installation')
- - if editing_current_user
- %p.light
- = s_('AdminUsers|You cannot remove your own admin rights.')
+ - help_text = s_('AdminUsers|Administrators have access to all groups, projects and users and can manage all features in this installation.')
+ - help_text += ' ' + s_('AdminUsers|You cannot remove your own admin rights.') if editing_current_user
+ = f.gitlab_ui_radio_component :access_level, :admin,
+ s_('AdminUsers|Admin'),
+ radio_options: { disabled: editing_current_user },
+ help_text: help_text
+
.form-group.row
.col-sm-2.col-form-label.gl-pt-0
diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml
index 9d62c19e2f..3869a2b6dc 100644
--- a/app/views/admin/users/_form.html.haml
+++ b/app/views/admin/users/_form.html.haml
@@ -1,5 +1,5 @@
.user_new
- = form_for [:admin, @user], html: { class: 'fieldset-form' } do |f|
+ = gitlab_ui_form_for [:admin, @user], html: { class: 'fieldset-form' } do |f|
= form_errors(@user)
%fieldset
@@ -39,12 +39,12 @@
.col-sm-2.col-form-label
= f.label :password
.col-sm-10
- = f.password_field :password, disabled: f.object.force_random_password, class: 'form-control gl-form-input'
+ = f.password_field :password, disabled: f.object.force_random_password, autocomplete: 'new-password', class: 'form-control gl-form-input'
.form-group.row
.col-sm-2.col-form-label
= f.label :password_confirmation
.col-sm-10
- = f.password_field :password_confirmation, disabled: f.object.force_random_password, class: 'form-control gl-form-input'
+ = f.password_field :password_confirmation, disabled: f.object.force_random_password, autocomplete: 'new-password', class: 'form-control gl-form-input'
= render partial: 'access_levels', locals: { f: f }
diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml
index f4b1a2853f..bafb208558 100644
--- a/app/views/admin/users/_head.html.haml
+++ b/app/views/admin/users/_head.html.haml
@@ -33,16 +33,11 @@
- if can_force_email_confirmation?(@user)
%button.btn.gl-button.btn-info.js-confirm-modal-button{ data: confirm_user_data(@user) }
= _('Confirm user')
-%ul.nav-links.nav.nav-tabs
- = nav_link(path: 'users#show') do
- = link_to _("Account"), admin_user_path(@user)
- = nav_link(path: 'users#projects') do
- = link_to _("Groups and projects"), projects_admin_user_path(@user)
- = nav_link(path: 'users#keys') do
- = link_to _("SSH keys"), keys_admin_user_path(@user)
- = nav_link(controller: :identities) do
- = link_to _("Identities"), admin_user_identities_path(@user)
+= gl_tabs_nav do
+ = gl_tab_link_to _("Account"), admin_user_path(@user)
+ = gl_tab_link_to _("Groups and projects"), projects_admin_user_path(@user)
+ = gl_tab_link_to _("SSH keys"), keys_admin_user_path(@user)
+ = gl_tab_link_to _("Identities"), admin_user_identities_path(@user)
- if impersonation_enabled?
- = nav_link(controller: :impersonation_tokens) do
- = link_to _("Impersonation Tokens"), admin_user_impersonation_tokens_path(@user)
+ = gl_tab_link_to _("Impersonation Tokens"), admin_user_impersonation_tokens_path(@user)
.gl-mb-3
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index ad8d9d1f04..2a9b4694e7 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -61,7 +61,6 @@
= _('Disabled')
= render_if_exists 'admin/namespace_plan_info', namespace: @user.namespace
- = render_if_exists 'admin/users/credit_card_info', user: @user
%li
%span.light= _('External User:')
@@ -139,6 +138,8 @@
= render_if_exists 'namespaces/shared_runner_status', namespace: @user.namespace
+ = render_if_exists 'admin/users/credit_card_info', user: @user, link_to_match_page: true
+
= render 'shared/custom_attributes', custom_attributes: @user.custom_attributes
-# Rendered on desktop only so order of cards can be different on desktop vs mobile
diff --git a/app/views/authentication/_authenticate.html.haml b/app/views/authentication/_authenticate.html.haml
index 5a2ae3f44c..7dcec50573 100644
--- a/app/views/authentication/_authenticate.html.haml
+++ b/app/views/authentication/_authenticate.html.haml
@@ -1,14 +1,17 @@
#js-authenticate-token-2fa
%a.gl-button.btn.btn-block.btn-confirm#js-login-2fa-device{ href: '#' }= _("Sign in via 2FA code")
+-# haml-lint:disable InlineJavaScript
%script#js-authenticate-token-2fa-in-progress{ type: "text/template" }
%p= _("Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.")
+-# haml-lint:disable InlineJavaScript
%script#js-authenticate-token-2fa-error{ type: "text/template" }
%div
%p <%= error_message %> (<%= error_name %>)
%a.btn.btn-default.gl-button.btn-block#js-token-2fa-try-again= _("Try again?")
+-# haml-lint:disable InlineJavaScript
%script#js-authenticate-token-2fa-authenticated{ type: "text/template" }
%div
%p= _("We heard back from your device. You have been authenticated.")
diff --git a/app/views/authentication/_register.html.haml b/app/views/authentication/_register.html.haml
index 678fd3c8e8..5eed969ed3 100644
--- a/app/views/authentication/_register.html.haml
+++ b/app/views/authentication/_register.html.haml
@@ -1,8 +1,10 @@
#js-register-token-2fa
+-# haml-lint:disable InlineJavaScript
%script#js-register-2fa-message{ type: "text/template" }
%p <%= message %>
+-# haml-lint:disable InlineJavaScript
%script#js-register-token-2fa-setup{ type: "text/template" }
- if current_user.two_factor_otp_enabled?
.row.gl-mb-3
@@ -17,12 +19,14 @@
.col-md-8
%p= _("You need to register a two-factor authentication app before you can set up a device.")
+-# haml-lint:disable InlineJavaScript
%script#js-register-token-2fa-error{ type: "text/template" }
%div
%p
%span <%= error_message %> (<%= error_name %>)
%a.btn.btn-default.gl-button#js-token-2fa-try-again= _("Try again?")
+-# haml-lint:disable InlineJavaScript
%script#js-register-token-2fa-registered{ type: "text/template" }
.row.gl-mb-3
.col-md-12
diff --git a/app/views/clusters/clusters/_advanced_settings_tab.html.haml b/app/views/clusters/clusters/_advanced_settings_tab.html.haml
index b491a64e43..8c9d98604d 100644
--- a/app/views/clusters/clusters/_advanced_settings_tab.html.haml
+++ b/app/views/clusters/clusters/_advanced_settings_tab.html.haml
@@ -1,6 +1,5 @@
- active = params[:tab] == 'settings'
- if can_admin_cluster?(current_user, @cluster)
- %li.nav-item{ role: 'presentation' }
- %a#cluster-settings-tab.nav-link{ class: active_when(active), href: clusterable.cluster_path(@cluster.id, params: {tab: 'settings'}) }
- %span= _('Advanced Settings')
+ = gl_tab_link_to clusterable.cluster_path(@cluster.id, params: { tab: 'settings' }), { item_active: active } do
+ = _('Advanced Settings')
diff --git a/app/views/clusters/clusters/_details_tab.html.haml b/app/views/clusters/clusters/_details_tab.html.haml
index 564c5103d3..734910686e 100644
--- a/app/views/clusters/clusters/_details_tab.html.haml
+++ b/app/views/clusters/clusters/_details_tab.html.haml
@@ -1,5 +1,4 @@
- active = params[:tab] == 'details' || !params[:tab].present?
-%li.nav-item{ role: 'presentation' }
- %a#cluster-details-tab.nav-link.qa-details{ class: active_when(active), href: clusterable.cluster_path(@cluster.id, params: {tab: 'details'}) }
- %span= _('Details')
+= gl_tab_link_to clusterable.cluster_path(@cluster.id, params: { tab: 'details' }), { item_active: active } do
+ = _('Details')
diff --git a/app/views/clusters/clusters/_health_tab.html.haml b/app/views/clusters/clusters/_health_tab.html.haml
index fda392693f..4292066cc6 100644
--- a/app/views/clusters/clusters/_health_tab.html.haml
+++ b/app/views/clusters/clusters/_health_tab.html.haml
@@ -1,5 +1,4 @@
- active = params[:tab] == 'health'
-%li.nav-item{ role: 'presentation' }
- %a#cluster-health-tab.nav-link.qa-health{ class: active_when(active), href: clusterable.cluster_path(@cluster.id, params: {tab: 'health'}) }
- %span= _('Health')
+= gl_tab_link_to clusterable.cluster_path(@cluster.id, params: { tab: 'health' }), { item_active: active, data: { testid: 'cluster-health-tab' } } do
+ = _('Health')
diff --git a/app/views/clusters/clusters/_integrations_tab.html.haml b/app/views/clusters/clusters/_integrations_tab.html.haml
index 77b8b6ca3e..e229c1fbe1 100644
--- a/app/views/clusters/clusters/_integrations_tab.html.haml
+++ b/app/views/clusters/clusters/_integrations_tab.html.haml
@@ -1,6 +1,4 @@
-- tab_name = 'integrations'
-- active = params[:tab] == tab_name
+- active = params[:tab] == 'integrations'
-%li.nav-item{ role: 'presentation' }
- %a#cluster-apps-tab.nav-link{ class: active_when(active), href: clusterable.cluster_path(@cluster.id, params: {tab: tab_name}) }
- %span= _('Integrations')
+= gl_tab_link_to clusterable.cluster_path(@cluster.id, params: { tab: 'integrations' }), { item_active: active } do
+ = _('Integrations')
diff --git a/app/views/clusters/clusters/_namespace.html.haml b/app/views/clusters/clusters/_namespace.html.haml
index 9b728e7a89..6412972e19 100644
--- a/app/views/clusters/clusters/_namespace.html.haml
+++ b/app/views/clusters/clusters/_namespace.html.haml
@@ -1,7 +1,6 @@
- managed_namespace_help_text = s_('ClusterIntegration|Set a prefix for your namespaces. If not set, defaults to your project path. If modified, existing environments will use their current namespaces until the cluster cache is cleared.')
- non_managed_namespace_help_text = s_('ClusterIntegration|The namespace associated with your project. This will be used for deploy boards, logs, and Web terminals.')
-- managed_namespace_help_link = link_to _('More information'), help_page_path('user/project/clusters/index.md',
- anchor: 'gitlab-managed-clusters'), target: '_blank'
+- managed_namespace_help_link = link_to _('More information'), help_page_path('user/project/clusters/gitlab_managed_clusters.md'), target: '_blank'
.js-namespace-prefixed
= platform_field.text_field :namespace,
diff --git a/app/views/clusters/clusters/aws/_new.html.haml b/app/views/clusters/clusters/aws/_new.html.haml
index 93db7db06b..f6d50410e9 100644
--- a/app/views/clusters/clusters/aws/_new.html.haml
+++ b/app/views/clusters/clusters/aws/_new.html.haml
@@ -12,6 +12,6 @@
'role-arn' => @aws_role.role_arn,
'instance-types' => @instance_types,
'kubernetes-integration-help-path' => help_page_path('user/project/clusters/index'),
- 'account-and-external-ids-help-path' => help_page_path('user/project/clusters/add_eks_clusters.md', anchor: 'create-a-new-certificate-based-eks-cluster'),
- 'create-role-arn-help-path' => help_page_path('user/project/clusters/add_eks_clusters.md', anchor: 'create-a-new-certificate-based-eks-cluster'),
+ 'account-and-external-ids-help-path' => help_page_path('user/project/clusters/add_eks_clusters.md', anchor: 'how-to-create-a-new-cluster-on-eks-through-cluster-certificates-deprecated'),
+ 'create-role-arn-help-path' => help_page_path('user/project/clusters/add_eks_clusters.md', anchor: 'how-to-create-a-new-cluster-on-eks-through-cluster-certificates-deprecated'),
'external-link-icon' => sprite_icon('external-link') } }
diff --git a/app/views/clusters/clusters/show.html.haml b/app/views/clusters/clusters/show.html.haml
index 0a482f1eb0..2a09d8d8cc 100644
--- a/app/views/clusters/clusters/show.html.haml
+++ b/app/views/clusters/clusters/show.html.haml
@@ -15,7 +15,7 @@
provider_type: @cluster.provider_type,
help_path: help_page_path('user/project/clusters/index.md'),
environments_help_path: help_page_path('ci/environments/index.md', anchor: 'create-a-static-environment'),
- clusters_help_path: help_page_path('user/project/clusters/index.md', anchor: 'deploying-to-a-kubernetes-cluster'),
+ clusters_help_path: help_page_path('user/project/clusters/deploy_to_cluster.md'),
deploy_boards_help_path: help_page_path('user/project/deploy_boards.md', anchor: 'enabling-deploy-boards'),
cluster_id: @cluster.id } }
@@ -24,11 +24,10 @@
.js-serverless-survey-banner{ data: { user_name: current_user.name, user_email: current_user.email } }
- .d-flex.my-3
- %p.badge.badge-light.p-2.mr-2
+ %h4.gl-my-5
+ = @cluster.name
+ %span.badge.badge-info.badge-pill.gl-badge.md.gl-vertical-align-middle
= cluster_type_label(@cluster.cluster_type)
- %h4.m-0
- = @cluster.name
= render 'banner'
@@ -42,12 +41,12 @@
- if cluster_created?(@cluster)
.js-toggle-container
- %ul.nav-links.mobile-separator.nav.nav-tabs{ role: 'tablist' }
- = render 'details_tab'
+ = gl_tabs_nav do
+ = render 'clusters/clusters/details_tab'
= render_if_exists 'clusters/clusters/environments_tab'
= render 'clusters/clusters/health_tab'
- = render 'integrations_tab'
- = render 'advanced_settings_tab'
+ = render 'clusters/clusters/integrations_tab'
+ = render 'clusters/clusters/advanced_settings_tab'
.tab-content.py-3
.tab-pane.active{ role: 'tabpanel' }
diff --git a/app/views/dashboard/_activity_head.html.haml b/app/views/dashboard/_activity_head.html.haml
index 0daadd20f5..c65b947d1b 100644
--- a/app/views/dashboard/_activity_head.html.haml
+++ b/app/views/dashboard/_activity_head.html.haml
@@ -2,13 +2,7 @@
%h1.page-title= _('Activity')
.top-area
- %ul.nav-links.nav.nav-tabs
- %li{ class: active_when(params[:filter].nil?) }>
- = link_to activity_dashboard_path, class: 'shortcuts-activity', data: {placement: 'right'} do
- = _('Your projects')
- %li{ class: active_when(params[:filter] == 'starred') }>
- = link_to activity_dashboard_path(filter: 'starred'), data: {placement: 'right'} do
- = _('Starred projects')
- %li{ class: active_when(params[:filter] == 'followed') }>
- = link_to activity_dashboard_path(filter: 'followed'), data: {placement: 'right'} do
- = _('Followed users')
+ = gl_tabs_nav({ class: 'gl-border-b-0', data: { testid: 'dashboard-activity-tabs' } }) do
+ = gl_tab_link_to _("Your projects"), activity_dashboard_path, { item_active: params[:filter].nil? }
+ = gl_tab_link_to _("Starred projects"), activity_dashboard_path(filter: 'starred')
+ = gl_tab_link_to _("Followed users"), activity_dashboard_path(filter: 'followed')
diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml
index b92f35c108..7b1d25b9b4 100644
--- a/app/views/dashboard/_groups_head.html.haml
+++ b/app/views/dashboard/_groups_head.html.haml
@@ -6,13 +6,9 @@
= link_to _("New group"), new_group_path, class: "gl-button btn btn-confirm", data: { testid: "new-group-button" }
.top-area
- %ul.nav-links.mobile-separator.nav.nav-tabs
- = nav_link(page: dashboard_groups_path) do
- = link_to dashboard_groups_path, title: _("Your groups") do
- Your groups
- = nav_link(page: explore_groups_path) do
- = link_to explore_groups_path, title: _("Explore public groups") do
- Explore public groups
+ = gl_tabs_nav({ class: 'gl-flex-grow-1 gl-border-0' }) do
+ = gl_tab_link_to _("Your groups"), dashboard_groups_path
+ = gl_tab_link_to _("Explore public groups"), explore_groups_path
.nav-controls
= render 'shared/groups/search_form'
= render 'shared/groups/dropdown'
diff --git a/app/views/devise/confirmations/almost_there.haml b/app/views/devise/confirmations/almost_there.haml
index 037b2f247c..9fb0fb734f 100644
--- a/app/views/devise/confirmations/almost_there.haml
+++ b/app/views/devise/confirmations/almost_there.haml
@@ -1,16 +1,15 @@
- user_email = "(#{params[:email]})" if params[:email].present?
+- request_link_start = ''.html_safe % { new_user_confirmation_path: new_user_confirmation_path }
+- request_link_end = ''.html_safe
.well-confirmation.gl-text-center.gl-mb-6
%h1.gl-mt-0
= _("Almost there...")
- %p.lead.gl-mb-6
+ %p{ class: 'gl-mb-6 gl-font-lg!' }
= _('Please check your email %{email} to confirm your account') % { email: user_email }
%hr
- if Gitlab::CurrentSettings.after_sign_up_text.present?
.well-confirmation.gl-text-center
= markdown_field(Gitlab::CurrentSettings, :after_sign_up_text)
-%p.text-center
- = _("No confirmation email received? Please check your spam folder or")
-.gl-mb-6.prepend-top-20.gl-text-center
- %a.gl-link{ href: new_user_confirmation_path }
- = _("Request new confirmation email")
+%p.gl-text-center
+ = _("No confirmation email received? Check your spam folder or %{request_link_start}request new confirmation email%{request_link_end}.").html_safe % { request_link_start: request_link_start, request_link_end: request_link_end }
diff --git a/app/views/devise/mailer/_confirmation_instructions_secondary.text.erb b/app/views/devise/mailer/_confirmation_instructions_secondary.text.erb
index ab46aaaca1..32e88047a9 100644
--- a/app/views/devise/mailer/_confirmation_instructions_secondary.text.erb
+++ b/app/views/devise/mailer/_confirmation_instructions_secondary.text.erb
@@ -1,4 +1,4 @@
-<%= _(" %{name}, confirm your email address now! ") % { name: @resource.user.name } %>
+<%= _("%{name}, confirm your email address now!") % { name: @resource.user.name } %>
<%= _("Use the link below to confirm your email address (%{email})") % { email: @resource.email } %>
diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml
index 10c0442358..56bd30fac7 100644
--- a/app/views/devise/passwords/edit.html.haml
+++ b/app/views/devise/passwords/edit.html.haml
@@ -7,10 +7,10 @@
= f.hidden_field :reset_password_token
.form-group
= f.label _('New password'), for: "user_password"
- = f.password_field :password, class: "form-control gl-form-input top", required: true, title: _('This field is required.'), data: { qa_selector: 'password_field'}
+ = f.password_field :password, autocomplete: 'new-password', class: "form-control gl-form-input top", required: true, title: _('This field is required.'), data: { qa_selector: 'password_field'}
.form-group
= f.label _('Confirm new password'), for: "user_password_confirmation"
- = f.password_field :password_confirmation, class: "form-control gl-form-input bottom", title: _('This field is required.'), data: { qa_selector: 'password_confirmation_field' }, required: true
+ = f.password_field :password_confirmation, autocomplete: 'new-password', class: "form-control gl-form-input bottom", title: _('This field is required.'), data: { qa_selector: 'password_confirmation_field' }, required: true
.clearfix
= f.submit _("Change your password"), class: "gl-button btn btn-confirm", data: { qa_selector: 'change_password_button' }
diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
index 4ec3fcde33..87108c8ea7 100644
--- a/app/views/devise/registrations/new.html.haml
+++ b/app/views/devise/registrations/new.html.haml
@@ -2,6 +2,7 @@
- add_page_specific_style 'page_bundles/signup'
- content_for :page_specific_javascripts do
= render "layouts/google_tag_manager_head"
+ = render "layouts/one_trust"
= render "layouts/google_tag_manager_body"
.signup-page
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index 82c0df354d..b40373ecc3 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -4,12 +4,12 @@
= f.text_field :login, value: @invite_email, class: 'form-control gl-form-input top', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off', required: true, title: _('This field is required.'), data: { qa_selector: 'login_field' }
.form-group
= f.label :password, class: 'label-bold'
- = f.password_field :password, class: 'form-control gl-form-input bottom', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field' }
+ = f.password_field :password, class: 'form-control gl-form-input bottom', autocomplete: 'current-password', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field' }
- if devise_mapping.rememberable?
- .remember-me
+ %div
%label{ for: 'user_remember_me' }
- = f.check_box :remember_me, class: 'remember-me-checkbox'
- %span Remember me
+ = f.check_box :remember_me
+ %span= _('Remember me')
.float-right
- if unconfirmed_email?
= link_to _('Resend confirmation email'), new_user_confirmation_path
diff --git a/app/views/devise/sessions/_new_crowd.html.haml b/app/views/devise/sessions/_new_crowd.html.haml
index 769268748f..fb4c011dd4 100644
--- a/app/views/devise/sessions/_new_crowd.html.haml
+++ b/app/views/devise/sessions/_new_crowd.html.haml
@@ -4,7 +4,7 @@
= text_field_tag :username, nil, { class: "form-control top", title: _("This field is required."), autofocus: "autofocus", required: true }
.form-group
= label_tag :password
- = password_field_tag :password, nil, { class: "form-control bottom", title: _("This field is required."), required: true }
+ = password_field_tag :password, nil, { autocomplete: 'current-password', class: "form-control bottom", title: _("This field is required."), required: true }
- if devise_mapping.rememberable?
.remember-me
%label{ for: "remember_me" }
diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml
index f599a652b7..fea58779c1 100644
--- a/app/views/devise/sessions/_new_ldap.html.haml
+++ b/app/views/devise/sessions/_new_ldap.html.haml
@@ -8,7 +8,7 @@
= text_field_tag :username, nil, { class: "form-control gl-form-input top", title: _("This field is required."), autofocus: "autofocus", data: { qa_selector: 'username_field' }, required: true }
.form-group
= label_tag :password
- = password_field_tag :password, nil, { class: "form-control gl-form-input bottom", title: _("This field is required."), data: { qa_selector: 'password_field' }, required: true }
+ = password_field_tag :password, nil, { autocomplete: 'current-password', class: "form-control gl-form-input bottom", title: _("This field is required."), data: { qa_selector: 'password_field' }, required: true }
- if !hide_remember_me && devise_mapping.rememberable?
.remember-me
%label{ for: "remember_me" }
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index 74f3e3e7e3..da6232b2a2 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -1,6 +1,7 @@
- page_title _("Sign in")
- content_for :page_specific_javascripts do
= render "layouts/google_tag_manager_head"
+ = render "layouts/one_trust"
= render "layouts/google_tag_manager_body"
#signin-container
diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml
index 1752a43b03..bd7fe41ae8 100644
--- a/app/views/devise/shared/_omniauth_box.html.haml
+++ b/app/views/devise/shared/_omniauth_box.html.haml
@@ -1,20 +1,20 @@
- hide_remember_me = local_assigns.fetch(:hide_remember_me, false)
.omniauth-container.gl-mt-5
- %label.label-bold.d-block
+ %label.gl-font-weight-bold
= _('Sign in with')
- providers = enabled_button_based_providers
- .d-flex.justify-content-between.flex-wrap
+ .gl-display-flex.gl-justify-content-between.gl-flex-wrap
- providers.each do |provider|
- has_icon = provider_has_icon?(provider)
- = button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default omniauth-btn oauth-login #{qa_class_for_provider(provider)}", form: { class: 'gl-w-full' } do
+ = button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default gl-w-full js-oauth-login #{qa_class_for_provider(provider)}", form: { class: 'gl-w-full gl-mb-3' } do
- if has_icon
= provider_image_tag(provider)
%span.gl-button-text
= label_for_provider(provider)
- unless hide_remember_me
- %fieldset.remember-me
+ %fieldset
%label
- = check_box_tag :remember_me, nil, false, class: 'remember-me-checkbox'
+ = check_box_tag :remember_me, nil, false
%span
= _('Remember me')
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index f964987553..15143684b8 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -53,6 +53,7 @@
= f.password_field :password,
class: 'form-control gl-form-input bottom',
data: { qa_selector: 'new_user_password_field' },
+ autocomplete: 'new-password',
required: true,
pattern: ".{#{@minimum_password_length},}",
title: s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
diff --git a/app/views/devise/shared/_signup_omniauth_provider_list.haml b/app/views/devise/shared/_signup_omniauth_provider_list.haml
index 43e0802ee2..c24e8770f0 100644
--- a/app/views/devise/shared/_signup_omniauth_provider_list.haml
+++ b/app/views/devise/shared/_signup_omniauth_provider_list.haml
@@ -1,9 +1,9 @@
-%label.label-bold.d-block
+%label.gl-font-weight-bold
= _("Create an account using:")
-.d-flex.justify-content-between.flex-wrap
+.gl-display-flex.gl-justify-content-between.gl-flex-wrap
- providers.each do |provider|
- = link_to omniauth_authorize_path(:user, provider), method: :post, class: "btn gl-button btn-default gl-display-flex gl-align-items-center gl-text-left gl-mb-2 gl-p-2 omniauth-btn oauth-login #{qa_class_for_provider(provider)}", id: "oauth-login-#{provider}" do
+ = link_to omniauth_authorize_path(:user, provider), method: :post, class: "btn gl-button btn-default gl-w-full gl-mb-3 js-oauth-login #{qa_class_for_provider(provider)}", id: "oauth-login-#{provider}" do
- if provider_has_icon?(provider)
= provider_image_tag(provider)
- %span.ml-2
+ %span.gl-button-text
= label_for_provider(provider)
diff --git a/app/views/devise/shared/_signup_omniauth_providers.haml b/app/views/devise/shared/_signup_omniauth_providers.haml
index a653d44d69..30a54ab86a 100644
--- a/app/views/devise/shared/_signup_omniauth_providers.haml
+++ b/app/views/devise/shared/_signup_omniauth_providers.haml
@@ -1,3 +1,3 @@
-.omniauth-divider.d-flex.align-items-center.text-center
+.omniauth-divider.gl-display-flex.gl-align-items-center
= _("or")
= render 'devise/shared/signup_omniauth_provider_list', providers: enabled_button_based_providers
diff --git a/app/views/devise/shared/_signup_omniauth_providers_top.haml b/app/views/devise/shared/_signup_omniauth_providers_top.haml
index 9a2629443e..8eb22c0b02 100644
--- a/app/views/devise/shared/_signup_omniauth_providers_top.haml
+++ b/app/views/devise/shared/_signup_omniauth_providers_top.haml
@@ -1,3 +1,3 @@
= render 'devise/shared/signup_omniauth_provider_list', providers: popular_enabled_button_based_providers
-.omniauth-divider.d-flex.align-items-center.text-center
+.omniauth-divider.gl-display-flex.gl-align-items-center
= _("or")
diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml
index ea191449fe..ab6861b5f2 100644
--- a/app/views/groups/_group_admin_settings.html.haml
+++ b/app/views/groups/_group_admin_settings.html.haml
@@ -36,4 +36,4 @@
.offset-sm-2.col-sm-10
.form-check
= f.text_field :two_factor_grace_period, class: 'form-control'
- .form-text.text-muted= _("Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication")
+ .form-text.text-muted= _("Time (in hours) that users are allowed to skip forced configuration of two-factor authentication.")
diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml
index 0352f366f5..e530d9c60b 100644
--- a/app/views/groups/_home_panel.html.haml
+++ b/app/views/groups/_home_panel.html.haml
@@ -5,17 +5,19 @@
.group-home-panel
.row.mb-3
.home-panel-title-row.col-md-12.col-lg-6.d-flex
- .avatar-container.rect-avatar.s64.home-panel-avatar.gl-mr-3.float-none
+ .avatar-container.rect-avatar.s64.home-panel-avatar.gl-flex-shrink-0.float-none{ class: 'gl-mr-3!' }
= group_icon(@group, class: 'avatar avatar-tile s64', width: 64, height: 64, itemprop: 'logo')
.d-flex.flex-column.flex-wrap.align-items-baseline
.d-inline-flex.align-items-baseline
- %h1.home-panel-title.gl-mt-3.gl-mb-2{ itemprop: 'name' }
+ %h1.home-panel-title.gl-mt-3.gl-mb-2.gl-ml-3{ itemprop: 'name' }
= @group.name
%span.visibility-icon.text-secondary.gl-ml-2.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
= visibility_level_icon(@group.visibility_level, options: {class: 'icon'})
- .home-panel-metadata.text-secondary
- %span
- = _("Group ID: %{group_id}") % { group_id: @group.id }
+ .home-panel-metadata.text-secondary.gl-font-base.gl-font-weight-normal.gl-line-height-normal
+ - if can?(current_user, :read_group, @group)
+ - button_class = "btn gl-button btn-sm btn-tertiary btn-default-tertiary home-panel-metadata"
+ - button_text = s_('GroupPage|Group ID: %{group_id}') % { group_id: @group.id }
+ = clipboard_button(title: s_('GroupPage|Copy group ID'), text: @group.id, hide_button_icon: true, button_text: button_text, class: button_class, qa_selector: 'group_id_content', itemprop: 'identifier')
- if current_user
%span.gl-ml-3
= render 'shared/members/access_request_links', source: @group
diff --git a/app/views/groups/_import_group_from_another_instance_panel.html.haml b/app/views/groups/_import_group_from_another_instance_panel.html.haml
index 2b9277c67e..06a86c2465 100644
--- a/app/views/groups/_import_group_from_another_instance_panel.html.haml
+++ b/app/views/groups/_import_group_from_another_instance_panel.html.haml
@@ -1,16 +1,15 @@
= form_with url: configure_import_bulk_imports_path, class: 'group-form gl-show-field-errors' do |f|
.gl-border-l-solid.gl-border-r-solid.gl-border-gray-100.gl-border-1.gl-p-5
- %h4.gl-display-flex
- = s_('GroupsNew|Import groups from another instance of GitLab')
- %span.badge.badge-info.badge-pill.gl-badge.md.gl-ml-3
- = _('Beta')
+ .gl-display-flex.gl-align-items-center
+ %h4.gl-display-flex
+ = s_('GroupsNew|Import groups from another instance of GitLab')
+ = link_to _('History'), history_import_bulk_imports_path, class: 'gl-link gl-ml-auto'
.gl-alert.gl-alert-warning{ role: 'alert' }
= sprite_icon('warning', css_class: 'gl-icon s16 gl-alert-icon gl-alert-icon-no-title')
.gl-alert-body
- docs_link_start = ''.html_safe % { url: help_page_path('user/group/import/index.md') }
- - feedback_link_start = ''.html_safe
- - link_end = ''.html_safe
- = s_('GroupsNew|Not all related objects are migrated, as %{docs_link_start}described here%{docs_link_end}. Please %{feedback_link_start}leave feedback%{feedback_link_end} on this feature.').html_safe % { docs_link_start: docs_link_start, docs_link_end: link_end, feedback_link_start: feedback_link_start, feedback_link_end: link_end }
+ - docs_link_end = ''.html_safe
+ = s_('GroupsNew|Not all related objects are migrated. %{docs_link_start}More info%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: docs_link_end }
%p.gl-mt-3
= s_('GroupsNew|Provide credentials for another instance of GitLab to import your groups directly.')
.form-group.gl-display-flex.gl-flex-direction-column
diff --git a/app/views/groups/dependency_proxies/_url.html.haml b/app/views/groups/dependency_proxies/_url.html.haml
deleted file mode 100644
index 9a76da63a7..0000000000
--- a/app/views/groups/dependency_proxies/_url.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-- proxy_url = @group.dependency_proxy_image_prefix
-
-%h5.prepend-top-20= _('Dependency proxy image prefix')
-
-.row
- .col-lg-8.col-md-12.input-group
- = text_field_tag :url, "#{proxy_url}", class: 'js-dependency-proxy-url form-control', readonly: true
- = clipboard_button(text: "#{proxy_url}", title: _("Copy %{proxy_url}") % { proxy_url: proxy_url })
-
-.row
- .col-12.help-block.gl-mt-3{ data: { qa_selector: 'dependency_proxy_count' } }
- = _('Contains %{count} blobs of images (%{size})') % { count: @blobs_count, size: number_to_human_size(@blobs_total_size) }
diff --git a/app/views/groups/dependency_proxies/show.html.haml b/app/views/groups/dependency_proxies/show.html.haml
index 177018af83..8936c4dcbb 100644
--- a/app/views/groups/dependency_proxies/show.html.haml
+++ b/app/views/groups/dependency_proxies/show.html.haml
@@ -1,30 +1,5 @@
- page_title _("Dependency Proxy")
+- dependency_proxy_available = Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: true) || @group.public?
-.settings-header
- %h4= _('Dependency proxy')
-
- %p
- - link_start = ''.html_safe % { url: help_page_path('user/packages/dependency_proxy/index') }
- = _('Create a local proxy for storing frequently used upstream images. %{link_start}Learn more%{link_end} about dependency proxies.').html_safe % { link_start: link_start, link_end: ''.html_safe }
-
-- if Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: true) || @group.public?
- - if can?(current_user, :admin_dependency_proxy, @group)
- = form_for(@dependency_proxy, method: :put, url: group_dependency_proxy_path(@group)) do |f|
- .form-group
- %h5.prepend-top-20= _('Enable proxy')
- .js-dependency-proxy-toggle-area
- = render "shared/buttons/project_feature_toggle", is_checked: @dependency_proxy.enabled?, label: s_("DependencyProxy|Toggle Dependency Proxy"), data: { qa_selector: 'dependency_proxy_setting_toggle' } do
- = f.hidden_field :enabled, { class: 'js-project-feature-toggle-input'}
-
- - if @dependency_proxy.enabled
- = render 'groups/dependency_proxies/url'
-
- - else
- - if @dependency_proxy.enabled
- = render 'groups/dependency_proxies/url'
-- else
- .gl-alert.gl-alert-info
- .gl-alert-container
- = sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
- .gl-alert-content
- = _('Dependency proxy feature is limited to public groups for now.')
+#js-dependency-proxy{ data: { group_path: @group.full_path,
+ dependency_proxy_available: dependency_proxy_available.to_s } }
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index 6e355d3120..420771257c 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -1,5 +1,5 @@
-- breadcrumb_title _("General Settings")
-- page_title _("General Settings")
+- breadcrumb_title _("General settings")
+- page_title _("General settings")
- @content_class = "limit-container-width" unless fluid_layout
- expanded = expanded_by_default?
@@ -13,6 +13,7 @@
= _('Collapse')
%p
= _('Update your group name, description, avatar, and visibility.')
+ = link_to s_('Learn more about groups.'), help_page_path('user/group/index')
.settings-content
= render 'groups/settings/general'
@@ -23,7 +24,7 @@
%button.btn.gl-button.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
- = _('Advanced permissions, Large File Storage and Two-Factor authentication settings.')
+ = _('Configure advanced permissions, Large File Storage, and two-factor authentication settings.')
.settings-content
= render 'groups/settings/permissions'
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 1f746484b7..0c6776a603 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -6,7 +6,7 @@
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@group.name} issues")
- if Feature.enabled?(:vue_issues_list, @group, default_enabled: :yaml)
- .js-issues-list{ data: group_issues_list_data(@group, current_user, @issues) }
+ .js-issues-list{ data: group_issues_list_data(@group, current_user, @issues, @projects) }
- if @can_bulk_update
= render_if_exists 'shared/issuable/group_bulk_update_sidebar', group: @group, type: :issues
- else
diff --git a/app/views/groups/registry/repositories/index.html.haml b/app/views/groups/registry/repositories/index.html.haml
index fa6bd021e4..2901c8fa46 100644
--- a/app/views/groups/registry/repositories/index.html.haml
+++ b/app/views/groups/registry/repositories/index.html.haml
@@ -16,7 +16,8 @@
is_group_page: "true",
"group_path": @group.full_path,
"gid_prefix": container_repository_gid_prefix,
- character_error: @character_error.to_s,
+ connection_error: (!!@connection_error).to_s,
+ invalid_path_error: (!!@invalid_path_error).to_s,
user_callouts_path: user_callouts_path,
user_callout_id: UserCalloutsHelper::UNFINISHED_TAG_CLEANUP_CALLOUT,
show_unfinished_tag_cleanup_callout: show_unfinished_tag_cleanup_callout?.to_s } }
diff --git a/app/views/groups/runners/_runner.html.haml b/app/views/groups/runners/_runner.html.haml
index 66ffef9855..a76701ea5d 100644
--- a/app/views/groups/runners/_runner.html.haml
+++ b/app/views/groups/runners/_runner.html.haml
@@ -39,7 +39,7 @@
- if runner.group_type?
= _('n/a')
- else
- = runner.projects.count(:all)
+ = runner.runner_projects.count(:all)
.table-section.section-5
.table-mobile-header{ role: 'rowheader' }= _('Jobs')
diff --git a/app/views/groups/settings/_general.html.haml b/app/views/groups/settings/_general.html.haml
index 7a2d5c91af..ed76a9fe25 100644
--- a/app/views/groups/settings/_general.html.haml
+++ b/app/views/groups/settings/_general.html.haml
@@ -14,8 +14,9 @@
.row.gl-mt-3
.form-group.col-md-9
- = f.label :description, _('Group description (optional)'), class: 'label-bold'
+ = f.label :description, _('Group description'), class: 'label-bold'
= f.text_area :description, class: 'form-control', rows: 3, maxlength: 250
+ .form-text.text-muted= _('Optional.')
= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group
diff --git a/app/views/groups/settings/_lfs.html.haml b/app/views/groups/settings/_lfs.html.haml
index 1255a2901e..9f04b579a9 100644
--- a/app/views/groups/settings/_lfs.html.haml
+++ b/app/views/groups/settings/_lfs.html.haml
@@ -3,10 +3,10 @@
%h5= _('Large File Storage')
-%p= s_('Check the %{docs_link_start}documentation%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: ''.html_safe }
+%p= s_('%{docs_link_start}What is Large File Storage?%{docs_link_end}').html_safe % { docs_link_start: docs_link_start, docs_link_end: ''.html_safe }
.form-group.gl-mb-3
= f.gitlab_ui_checkbox_component :lfs_enabled,
_('Allow projects within this group to use Git LFS'),
- help_text: _('This setting can be overridden in each project.'),
+ help_text: _('Can be overridden in each project.'),
checkbox_options: { checked: @group.lfs_enabled?, data: { qa_selector: 'lfs_checkbox' } }
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index 8f428909e6..eb38aa4388 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -7,7 +7,7 @@
- if @group.root?
.form-group.gl-mb-3
= f.gitlab_ui_checkbox_component :prevent_sharing_groups_outside_hierarchy,
- s_('GroupSettings|Prevent members from sending invitations to groups outside of %{group} and its subgroups.').html_safe % { group: link_to_group(@group) },
+ s_('GroupSettings|Prevent members from sending invitations to groups outside of %{group} and its subgroups').html_safe % { group: link_to_group(@group) },
help_text: prevent_sharing_groups_outside_hierarchy_help_text(@group),
checkbox_options: { disabled: !can_change_prevent_sharing_groups_outside_hierarchy?(@group) }
@@ -21,13 +21,13 @@
= f.gitlab_ui_checkbox_component :emails_disabled,
s_('GroupSettings|Disable email notifications'),
checkbox_options: { checked: @group.emails_disabled?, disabled: !can_disable_group_emails?(@group) },
- help_text: s_('GroupSettings|This setting will override user notification preferences for all members of the group, subgroups, and projects.')
+ help_text: s_('GroupSettings|Overrides user notification preferences for all members of the group, subgroups, and projects.')
.form-group.gl-mb-3
= f.gitlab_ui_checkbox_component :mentions_disabled,
s_('GroupSettings|Disable group mentions'),
checkbox_options: { checked: @group.mentions_disabled? },
- help_text: s_('GroupSettings|This setting will prevent group members from being notified if the group is mentioned.')
+ help_text: s_('GroupSettings|Prevents group members from being notified if the group is mentioned.')
= render 'groups/settings/project_access_token_creation', f: f, group: @group
= render_if_exists 'groups/settings/delayed_project_removal', f: f, group: @group
diff --git a/app/views/groups/settings/_two_factor_auth.html.haml b/app/views/groups/settings/_two_factor_auth.html.haml
index 8204cafcb4..f86bcb24e6 100644
--- a/app/views/groups/settings/_two_factor_auth.html.haml
+++ b/app/views/groups/settings/_two_factor_auth.html.haml
@@ -4,16 +4,16 @@
%h5= _('Two-factor authentication')
-%p= s_('Check the %{docs_link_start}documentation%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: ''.html_safe }
+%p= s_('%{docs_link_start}What is two-factor authentication?%{docs_link_end}').html_safe % { docs_link_start: docs_link_start, docs_link_end: ''.html_safe }
.form-group
= f.gitlab_ui_checkbox_component :require_two_factor_authentication,
- _('Require all users in this group to setup two-factor authentication'),
+ _('Require all users in this group to set up two-factor authentication'),
checkbox_options: { data: { qa_selector: 'require_2fa_checkbox' } }
.form-group
- = f.label :two_factor_grace_period, _('Time before enforced'), class: 'label-bold'
- = f.text_field :two_factor_grace_period, class: 'form-control form-control-sm w-auto'
- .form-text.text-muted= _('Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication')
+ = f.label :two_factor_grace_period, _('Time before enforced')
+ = f.text_field :two_factor_grace_period, class: 'form-control form-control-sm w-auto gl-form-input gl-mb-3'
+ .form-text.text-muted= _('Time (in hours) that users are allowed to skip forced configuration of two-factor authentication.')
- unless group.has_parent?
.form-group
= f.gitlab_ui_checkbox_component :allow_mfa_for_subgroups,
diff --git a/app/views/groups/settings/packages_and_registries/show.html.haml b/app/views/groups/settings/packages_and_registries/show.html.haml
index 1a12ad4902..7be6dc73c4 100644
--- a/app/views/groups/settings/packages_and_registries/show.html.haml
+++ b/app/views/groups/settings/packages_and_registries/show.html.haml
@@ -1,5 +1,8 @@
- breadcrumb_title _('Packages & Registries')
- page_title _('Packages & Registries')
- @content_class = 'limit-container-width' unless fluid_layout
+- dependency_proxy_available = Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: true) || @group.public?
-%section#js-packages-and-registries-settings{ data: { default_expanded: expanded_by_default?.to_s, group_path: @group.full_path } }
+%section#js-packages-and-registries-settings{ data: { default_expanded: expanded_by_default?.to_s,
+ group_path: @group.full_path,
+ dependency_proxy_available: dependency_proxy_available.to_s } }
diff --git a/app/views/help/instance_configuration/_gitlab_pages.html.haml b/app/views/help/instance_configuration/_gitlab_pages.html.haml
index 51835c202d..1d8958c93e 100644
--- a/app/views/help/instance_configuration/_gitlab_pages.html.haml
+++ b/app/views/help/instance_configuration/_gitlab_pages.html.haml
@@ -7,7 +7,7 @@
= _('GitLab Pages')
%p
- - link_to_gitlab_pages = link_to(_('GitLab Pages'), gitlab_pages[:url], target: '_blank')
+ - link_to_gitlab_pages = link_to(_('GitLab Pages'), gitlab_pages[:url], target: '_blank', rel: 'noopener noreferrer')
= _('Below are the settings for %{link_to_gitlab_pages}.').html_safe % { link_to_gitlab_pages: link_to_gitlab_pages }
.table-responsive
%table
diff --git a/app/views/help/instance_configuration/_rate_limits.html.haml b/app/views/help/instance_configuration/_rate_limits.html.haml
index d72bd845c5..ed71b5a609 100644
--- a/app/views/help/instance_configuration/_rate_limits.html.haml
+++ b/app/views/help/instance_configuration/_rate_limits.html.haml
@@ -24,6 +24,7 @@
= render 'help/instance_configuration/rate_limit_row', title: _('Protected Paths: requests'), rate_limit: rate_limits[:protected_paths]
= render 'help/instance_configuration/rate_limit_row', title: _('Package Registry: unauthenticated API requests'), rate_limit: rate_limits[:unauthenticated_packages_api], public_visible: true
= render 'help/instance_configuration/rate_limit_row', title: _('Package Registry: authenticated API requests'), rate_limit: rate_limits[:authenticated_packages_api]
+ = render 'help/instance_configuration/rate_limit_row', title: _('Authenticated Git LFS requests'), rate_limit: rate_limits[:authenticated_git_lfs_api]
= render 'help/instance_configuration/rate_limit_row', title: _('Issue creation requests'), rate_limit: rate_limits[:issue_creation]
= render 'help/instance_configuration/rate_limit_row', title: _('Note creation requests'), rate_limit: rate_limits[:note_creation]
= render 'help/instance_configuration/rate_limit_row', title: _('Project export requests'), rate_limit: rate_limits[:project_export]
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index 02a8f3142c..8f18d68fd5 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -6,7 +6,7 @@
- provider_title = Gitlab::ImportSources.title(provider)
- header_title _("New project"), new_project_path
-- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
+- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project')
#import-projects-mount-element{ data: { provider: provider, provider_title: provider_title,
can_select_namespace: current_user.can_select_namespace?.to_s,
diff --git a/app/views/import/bitbucket_server/new.html.haml b/app/views/import/bitbucket_server/new.html.haml
index ce6bdd7a2f..721447186a 100644
--- a/app/views/import/bitbucket_server/new.html.haml
+++ b/app/views/import/bitbucket_server/new.html.haml
@@ -1,6 +1,6 @@
- page_title _('Bitbucket Server Import')
- header_title _("New project"), new_project_path
-- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
+- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project')
%h3.page-title.d-flex
.gl-display-flex.gl-align-items-center.gl-justify-content-center
diff --git a/app/views/import/bulk_imports/history.html.haml b/app/views/import/bulk_imports/history.html.haml
new file mode 100644
index 0000000000..80eb0c7a76
--- /dev/null
+++ b/app/views/import/bulk_imports/history.html.haml
@@ -0,0 +1,6 @@
+- add_to_breadcrumbs _('New group'), new_group_path
+- add_to_breadcrumbs _('Import group'), new_group_path(anchor: 'import-group-pane')
+- add_page_specific_style 'page_bundles/import'
+- page_title _('Import history')
+
+#import-history-mount-element
diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml
index 5115679727..d716d08529 100644
--- a/app/views/import/fogbugz/new.html.haml
+++ b/app/views/import/fogbugz/new.html.haml
@@ -1,6 +1,6 @@
- page_title _("FogBugz Import")
- header_title _("New project"), new_project_path
-- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
+- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project')
%h3.page-title.d-flex
.gl-display-flex.gl-align-items-center.gl-justify-content-center
diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml
index 4281d77e83..93572e14a6 100644
--- a/app/views/import/fogbugz/new_user_map.html.haml
+++ b/app/views/import/fogbugz/new_user_map.html.haml
@@ -1,6 +1,6 @@
- page_title _('User map'), _('FogBugz import')
- header_title _("New project"), new_project_path
-- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
+- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project')
%h3.page-title.d-flex
.gl-display-flex.gl-align-items-center.gl-justify-content-center
diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml
index 288ae5f1ce..de717ce87e 100644
--- a/app/views/import/gitea/new.html.haml
+++ b/app/views/import/gitea/new.html.haml
@@ -1,6 +1,6 @@
- page_title _("Gitea Import")
- header_title _("New project"), new_project_path
-- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
+- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project')
%h3.page-title
= custom_icon('gitea_logo')
diff --git a/app/views/import/github/new.html.haml b/app/views/import/github/new.html.haml
index 3407f9202b..3f7f929f76 100644
--- a/app/views/import/github/new.html.haml
+++ b/app/views/import/github/new.html.haml
@@ -1,7 +1,7 @@
- title = _('Authenticate with GitHub')
- page_title title
- header_title _("New project"), new_project_path
-- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
+- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project')
%h3.page-title
= title
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index 028268482c..533d0d13be 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -1,6 +1,6 @@
- page_title _("GitLab Import")
- header_title _("New project"), new_project_path
-- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
+- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project')
%h3.page-title.d-flex
.gl-display-flex.gl-align-items-center.gl-justify-content-center
diff --git a/app/views/import/manifest/new.html.haml b/app/views/import/manifest/new.html.haml
index bfaff3bb30..a949e14e27 100644
--- a/app/views/import/manifest/new.html.haml
+++ b/app/views/import/manifest/new.html.haml
@@ -1,6 +1,6 @@
- page_title _("Manifest file import")
- header_title _("New project"), new_project_path
-- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
+- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project')
%h3.page-title
diff --git a/app/views/import/phabricator/new.html.haml b/app/views/import/phabricator/new.html.haml
index 9596fdb615..0249dc446e 100644
--- a/app/views/import/phabricator/new.html.haml
+++ b/app/views/import/phabricator/new.html.haml
@@ -1,6 +1,6 @@
- page_title _('Phabricator Server Import')
- header_title _("New project"), new_project_path
-- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_projects_path(anchor: 'import_project')
+- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project')
%h3.page-title.d-flex
.gl-display-flex.gl-align-items-center.gl-justify-content-center
diff --git a/app/views/layouts/_one_trust.html.haml b/app/views/layouts/_one_trust.html.haml
new file mode 100644
index 0000000000..4fab017d27
--- /dev/null
+++ b/app/views/layouts/_one_trust.html.haml
@@ -0,0 +1,16 @@
+- if one_trust_enabled?
+ - one_trust_id = sanitize(extra_config.one_trust_id, scrubber: Rails::Html::TextOnlyScrubber.new)
+
+
+ = javascript_include_tag "https://cdn.cookielaw.org/consent/#{one_trust_id}/OtAutoBlock.js"
+ = javascript_tag nonce: content_security_policy_nonce do
+ :plain
+ const oneTrustScript = document.createElement('script');
+ oneTrustScript.src = 'https://cdn.cookielaw.org/scripttemplates/otSDKStub.js';
+ oneTrustScript.dataset.domainScript = '#{one_trust_id}';
+ oneTrustScript.nonce = '#{content_security_policy_nonce}'
+ oneTrustScript.charset = 'UTF-8';
+ oneTrustScript.defer = true;
+ document.head.appendChild(oneTrustScript);
+
+ function OptanonWrapper() { }
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index ec2904245d..dff1b5e3d0 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -17,6 +17,7 @@
= render_two_factor_auth_recovery_settings_check
= render_if_exists "layouts/header/ee_subscribable_banner"
= render_if_exists "shared/namespace_storage_limit_alert"
+ = render_if_exists "shared/namespace_user_cap_reached_alert"
= render_if_exists "shared/new_user_signups_cap_reached_alert"
= yield :page_level_alert
= yield :customize_homepage_banner
diff --git a/app/views/layouts/_startup_js.html.haml b/app/views/layouts/_startup_js.html.haml
index 35cd191c60..0bf9c16b0d 100644
--- a/app/views/layouts/_startup_js.html.haml
+++ b/app/views/layouts/_startup_js.html.haml
@@ -8,20 +8,30 @@
if (gl.startup_calls && window.fetch) {
Object.keys(gl.startup_calls).forEach(apiCall => {
- // fetch won’t send cookies in older browsers, unless you set the credentials init option.
- // We set to `same-origin` which is default value in modern browsers.
- // See https://github.com/whatwg/fetch/pull/585 for more information.
- gl.startup_calls[apiCall] = {
- fetchCall: fetch(apiCall, { credentials: 'same-origin' })
+ gl.startup_calls[apiCall] = {
+ fetchCall: fetch(apiCall, {
+ // Emulate XHR for Rails AJAX request checks
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest'
+ },
+ // fetch won’t send cookies in older browsers, unless you set the credentials init option.
+ // We set to `same-origin` which is default value in modern browsers.
+ // See https://github.com/whatwg/fetch/pull/585 for more information.
+ credentials: 'same-origin'
+ })
};
});
}
if (gl.startup_graphql_calls && window.fetch) {
+ const headers = #{page_startup_graphql_headers.to_json};
const url = `#{api_graphql_url}`
const opts = {
method: "POST",
- headers: { "Content-Type": "application/json", 'X-CSRF-Token': "#{form_authenticity_token}" },
+ headers: {
+ "Content-Type": "application/json",
+ ...headers,
+ }
};
gl.startup_graphql_calls = gl.startup_graphql_calls.map(call => ({
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 3e7155b2c0..8d28823bfa 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -34,7 +34,8 @@
#js-header-search.header-search{ data: { 'search-context' => search_context.to_json,
'search-path' => search_path,
'issues-path' => issues_dashboard_path,
- 'mr-path' => merge_requests_dashboard_path } }
+ 'mr-path' => merge_requests_dashboard_path,
+ 'autocomplete-path' => search_autocomplete_path } }
%input{ type: "text", placeholder: _('Search or jump to...'), class: 'form-control gl-form-input' }
- else
= render 'layouts/search'
diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml
index c111714f55..02a37dac15 100644
--- a/app/views/layouts/nav/_breadcrumbs.html.haml
+++ b/app/views/layouts/nav/_breadcrumbs.html.haml
@@ -19,8 +19,9 @@
= render "layouts/nav/breadcrumbs/collapsed_dropdown", location: :after
- unless @skip_current_level_breadcrumb
%li
- %h2.breadcrumbs-sub-title
+ %h2.breadcrumbs-sub-title{ data: { qa_selector: 'breadcrumb_sub_title_content' } }
= link_to @breadcrumb_title, breadcrumb_title_link
+ -# haml-lint:disable InlineJavaScript
%script{ type: 'application/ld+json' }
:plain
#{schema_breadcrumb_json}
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index d0b73a3364..842fb23d24 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -1,13 +1,13 @@
%aside.nav-sidebar.qa-admin-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), 'aria-label': _('Admin navigation') }
.nav-sidebar-inner-scroll
.context-header
- = link_to admin_root_path, title: _('Admin Overview') do
+ = link_to admin_root_path, title: _('Admin Overview'), class: 'has-tooltip', data: { container: 'body', placement: 'right' } do
%span{ class: ['avatar-container', 'settings-avatar', 'rect-avatar', 's32'] }
= sprite_icon('admin', size: 18)
%span.sidebar-context-title
= _('Admin Area')
%ul.sidebar-top-level-items{ data: { qa_selector: 'admin_sidebar_overview_submenu_content' } }
- = nav_link(controller: %w(dashboard admin admin/projects users groups jobs runners gitaly_servers cohorts), html_options: {class: 'home'}) do
+ = nav_link(controller: %w(dashboard admin admin/projects users groups admin/topics jobs runners gitaly_servers cohorts), html_options: {class: 'home'}) do
= link_to admin_root_path, class: 'has-sub-items' do
.nav-icon-container
= sprite_icon('overview')
@@ -35,6 +35,10 @@
= link_to admin_groups_path, title: _('Groups'), data: { qa_selector: 'groups_overview_link' } do
%span
= _('Groups')
+ = nav_link(controller: [:admin, 'admin/topics']) do
+ = link_to admin_topics_path, title: _('Topics'), data: { qa_selector: 'topics_overview_link' } do
+ %span
+ = _('Topics')
= nav_link path: 'jobs#index' do
= link_to admin_jobs_path, title: _('Jobs') do
%span
@@ -257,11 +261,6 @@
= link_to ci_cd_admin_application_settings_path, title: _('CI/CD') do
%span
= _('CI/CD')
- - if Feature.enabled?(:serverless_domain)
- = nav_link(path: 'application_settings#operations') do
- = link_to admin_serverless_domains_path, title: _('Operations') do
- %span
- = _('Operations')
= nav_link(path: 'application_settings#reporting') do
= link_to reporting_admin_application_settings_path, title: _('Reporting') do
%span
diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml
index 4db1e532ba..16c0c00ad3 100644
--- a/app/views/layouts/nav/sidebar/_profile.html.haml
+++ b/app/views/layouts/nav/sidebar/_profile.html.haml
@@ -1,7 +1,7 @@
%aside.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?), **sidebar_tracking_attributes_by_object(current_user), 'aria-label': _('User settings') }
.nav-sidebar-inner-scroll
.context-header
- = link_to profile_path, title: _('Profile Settings') do
+ = link_to profile_path, title: _('Profile Settings'), class: 'has-tooltip', data: { container: 'body', placement: 'right' } do
%span{ class: ['avatar-container', 'settings-avatar', 's32'] }
= image_tag avatar_icon_for_user(current_user, 32), class: ['avatar', 'avatar-tile', 'js-sidebar-user-avatar', 's32'], alt: current_user.name, data: { testid: 'sidebar-user-avatar' }
%span.sidebar-context-title= _('User Settings')
diff --git a/app/views/profiles/_email_settings.html.haml b/app/views/profiles/_email_settings.html.haml
index bc678c2c42..1057e96f44 100644
--- a/app/views/profiles/_email_settings.html.haml
+++ b/app/views/profiles/_email_settings.html.haml
@@ -3,8 +3,11 @@
- email_change_disabled = local_assigns.fetch(:email_change_disabled, nil)
- read_only_help_text = readonly ? s_("Profiles|Your email address was automatically set based on your %{provider_label} account") % { provider_label: attribute_provider_label(:email) } : user_email_help_text(@user)
- help_text = email_change_disabled ? s_("Your account uses dedicated credentials for the \"%{group_name}\" group and can only be updated through SSO.") % { group_name: @user.managing_group.name } : read_only_help_text
+- password_automatically_set = @user.password_automatically_set?
= form.text_field :email, required: true, class: 'input-lg gl-form-input', value: (@user.email unless @user.temp_oauth_email?), help: help_text.html_safe, readonly: readonly || email_change_disabled
+- unless password_automatically_set
+ = hidden_field_tag 'user[validation_password]', :validation_password, class: 'js-password-prompt-field', help: s_("Profiles|Enter your password to confirm the email change")
= form.select :public_email, options_for_select(@user.public_verified_emails, selected: @user.public_email),
{ help: s_("Profiles|This email will be displayed on your public profile"), include_blank: s_("Profiles|Do not show on profile") },
control_class: 'select2 input-lg', disabled: email_change_disabled
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 2cc919fc70..5d3e072017 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -19,16 +19,16 @@
- unless @user.password_automatically_set?
.form-group
- = f.label :current_password, _('Current password'), class: 'label-bold'
- = f.password_field :current_password, required: true, class: 'form-control gl-form-input', data: { qa_selector: 'current_password_field' }
+ = f.label :password, _('Current password'), class: 'label-bold'
+ = f.password_field :password, required: true, autocomplete: 'current-password', class: 'form-control gl-form-input', data: { qa_selector: 'current_password_field' }
%p.form-text.text-muted
= _('You must provide your current password in order to change it.')
.form-group
- = f.label :password, _('New password'), class: 'label-bold'
- = f.password_field :password, required: true, class: 'form-control gl-form-input', data: { qa_selector: 'new_password_field' }
+ = f.label :new_password, _('New password'), class: 'label-bold'
+ = f.password_field :new_password, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input', data: { qa_selector: 'new_password_field' }
.form-group
= f.label :password_confirmation, _('Password confirmation'), class: 'label-bold'
- = f.password_field :password_confirmation, required: true, class: 'form-control gl-form-input', data: { qa_selector: 'confirm_password_field' }
+ = f.password_field :password_confirmation, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input', data: { qa_selector: 'confirm_password_field' }
.gl-mt-3.gl-mb-3
= f.submit _('Save password'), class: "gl-button btn btn-confirm gl-mr-3", data: { qa_selector: 'save_password_button' }
- unless @user.password_automatically_set?
diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml
index 7780ffe0cb..9154c94abb 100644
--- a/app/views/profiles/passwords/new.html.haml
+++ b/app/views/profiles/passwords/new.html.haml
@@ -14,18 +14,18 @@
- unless @user.password_automatically_set?
.form-group.row
.col-sm-2.col-form-label
- = f.label :current_password, _('Current password')
+ = f.label :password, _('Current password')
.col-sm-10
- = f.password_field :current_password, required: true, class: 'form-control gl-form-input', data: { qa_selector: 'current_password_field' }
+ = f.password_field :password, required: true, autocomplete: 'current-password', class: 'form-control gl-form-input', data: { qa_selector: 'current_password_field' }
.form-group.row
.col-sm-2.col-form-label
- = f.label :password, _('New password')
+ = f.label :new_password, _('New password')
.col-sm-10
- = f.password_field :password, required: true, class: 'form-control gl-form-input', data: { qa_selector: 'new_password_field' }
+ = f.password_field :new_password, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input', data: { qa_selector: 'new_password_field' }
.form-group.row
.col-sm-2.col-form-label
= f.label :password_confirmation, _('Password confirmation')
.col-sm-10
- = f.password_field :password_confirmation, required: true, class: 'form-control gl-form-input', data: { qa_selector: 'confirm_password_field' }
+ = f.password_field :password_confirmation, required: true, autocomplete: 'new-password', class: 'form-control gl-form-input', data: { qa_selector: 'confirm_password_field' }
.form-actions
= f.submit _('Set new password'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'set_new_password_button' }
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 0bb4859dd1..3e41f107e0 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -5,7 +5,7 @@
- availability = availability_values
- custom_emoji = show_status_emoji?(@user.status)
-= bootstrap_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user gl-mt-3 js-quick-submit gl-show-field-errors' }, authenticity_token: true do |f|
+= bootstrap_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user gl-mt-3 js-quick-submit gl-show-field-errors js-password-prompt-form', remote: true }, authenticity_token: true do |f|
= form_errors(@user)
.row.js-search-settings-section
@@ -80,7 +80,7 @@
%p= s_("Profiles|Set your local time zone")
.col-lg-8
%h5= _("Time zone")
- = dropdown_tag(_("Select a time zone"), options: { toggle_class: 'gl-button btn js-timezone-dropdown input-lg', title: _("Select a time zone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
+ = dropdown_tag(_("Select a time zone"), options: { toggle_class: 'gl-button btn js-timezone-dropdown input-lg gl-w-full!', title: _("Select a time zone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
%input.hidden{ :type => 'hidden', :id => 'user_timezone', :name => 'user[timezone]', value: @user.timezone }
.col-lg-12
%hr
@@ -124,9 +124,11 @@
.help-block
= s_("Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information")
%hr
- = f.submit s_("Profiles|Update profile settings"), class: 'gl-button btn btn-confirm gl-mr-3'
+ = f.submit s_("Profiles|Update profile settings"), class: 'gl-button btn btn-confirm gl-mr-3 js-password-prompt-btn'
= link_to _("Cancel"), user_path(current_user), class: 'gl-button btn btn-default btn-cancel'
+#password-prompt-modal
+
.modal.modal-profile-crop{ data: { cropper_css_path: ActionController::Base.helpers.stylesheet_path('lazy_bundles/cropper.css') } }
.modal-dialog
.modal-content
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index bd3cb7e60f..00df860895 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -50,7 +50,7 @@
- if current_password_required?
.form-group
= label_tag :current_password, _('Current password'), class: 'label-bold'
- = password_field_tag :current_password, nil, required: true, class: 'form-control gl-form-input', data: { qa_selector: 'current_password_field' }
+ = password_field_tag :current_password, nil, autocomplete: 'current-password', required: true, class: 'form-control gl-form-input', data: { qa_selector: 'current_password_field' }
%p.form-text.text-muted
= _('Your current password is required to register a two-factor authenticator app.')
.gl-mt-3
diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml
index 597a22bf34..cdcc98552f 100644
--- a/app/views/projects/_files.html.haml
+++ b/app/views/projects/_files.html.haml
@@ -20,5 +20,6 @@
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout), project_buttons: true
#js-tree-list{ data: vue_file_list_data(project, ref) }
- - if can_edit_tree?
+ - if !Feature.enabled?(:new_dir_modal, default_enabled: :yaml) && can_edit_tree?
= render 'projects/blob/new_dir'
+
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index f2cee61884..1f2c16324f 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -45,11 +45,10 @@
- if can?(current_user, :download_code, @project)
= cache_if(cache_enabled, [@project, :download_code], expires_in: 1.minute) do
%nav.project-stats
- .nav-links.quick-links
- - if @project.empty_repo?
- = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors
- - else
- = render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
+ - if @project.empty_repo?
+ = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors
+ - else
+ = render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
.home-panel-home-desc.mt-1
- if @project.description.present?
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index 815a3cf696..81d9726fcd 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -83,7 +83,7 @@
.js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') }
- = form_for @project, html: { class: 'new_project' } do |f|
+ = form_for @project, html: { class: 'new_project gl-show-field-errors' } do |f|
%hr
= render "shared/import_form", f: f
= render 'projects/new_project_fields', f: f, project_name_id: "import-url-name", hide_init_with_readme: true, track_label: track_label
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index fb7a7ef898..256c3ebad0 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -16,7 +16,12 @@
- if current_user.can_select_namespace?
- namespace_id = namespace_id_from(params)
- if Feature.enabled?(:paginatable_namespace_drop_down_for_project_creation, current_user, default_enabled: :yaml)
- .js-vue-new-project-url-select{ data: { namespace_full_path: GroupFinder.new(current_user).execute(id: namespace_id)&.full_path, namespace_id: namespace_id, root_url: root_url, track_label: track_label } }
+ .js-vue-new-project-url-select{ data: { namespace_full_path: GroupFinder.new(current_user).execute(id: namespace_id)&.full_path,
+ namespace_id: namespace_id,
+ root_url: root_url,
+ track_label: track_label,
+ user_namespace_full_path: current_user.namespace.full_path,
+ user_namespace_id: current_user.namespace.id } }
- else
.input-group-prepend.flex-shrink-0.has-tooltip{ title: root_url }
.input-group-text
@@ -53,15 +58,36 @@
= render 'shared/visibility_level', f: f, visibility_level: visibility_level.to_i, can_change_visibility_level: true, form_model: @project, with_label: false
- if !hide_init_with_readme
- .form-group.row.initialize-with-readme-setting
- %div{ :class => "col-sm-12" }
- .form-check
- = check_box_tag 'project[initialize_with_readme]', '1', true, class: 'form-check-input', data: { qa_selector: "initialize_with_readme_checkbox", track_label: "#{track_label}", track_action: "activate_form_input", track_property: "init_with_readme", track_value: "" }
- = label_tag 'project[initialize_with_readme]', class: 'form-check-label' do
- .option-title
- %strong= s_('ProjectsNew|Initialize repository with a README')
- .option-description
- = s_('ProjectsNew|Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository.')
+ = f.label :project_configuration, class: 'label-bold' do
+ = s_('ProjectsNew|Project Configuration')
+
+ .form-group
+ .form-check.gl-mb-3
+ = check_box_tag 'project[initialize_with_readme]', '1', true, class: 'form-check-input', data: { qa_selector: 'initialize_with_readme_checkbox', track_label: track_label, track_action: 'activate_form_input', track_property: 'init_with_readme' }
+ = label_tag 'project[initialize_with_readme]', s_('ProjectsNew|Initialize repository with a README'), class: 'form-check-label'
+ .form-text.text-muted
+ = s_('ProjectsNew|Allows you to immediately clone this project’s repository. Skip this if you plan to push up an existing repository.')
+
+ - experiment(:new_project_sast_enabled, user: current_user) do |e|
+ - e.try do
+ .form-group
+ .form-check.gl-mb-3
+ = check_box_tag 'project[initialize_with_sast]', '1', true, class: 'form-check-input', data: { track_experiment: e.name, track_label: track_label, track_action: 'activate_form_input', track_property: 'init_with_sast' }
+ = label_tag 'project[initialize_with_sast]', class: 'form-check-label' do
+ = s_('ProjectsNew|Enable Static Application Security Testing (SAST)')
+ .form-text.text-muted
+ = s_('ProjectsNew|Analyze your source code for known security vulnerabilities.')
+ = link_to _('Learn more.'), help_page_path('user/application_security/sast/index'), target: '_blank', rel: 'noopener noreferrer', data: { track_action: 'followed', track_experiment: e.name }
+ - e.try(:free_indicator) do
+ .form-group
+ .form-check.gl-mb-3
+ = check_box_tag 'project[initialize_with_sast]', '1', true, class: 'form-check-input', data: { track_experiment: e.name, track_label: track_label, track_action: 'activate_form_input', track_property: 'init_with_sast' }
+ = label_tag 'project[initialize_with_sast]', class: 'form-check-label' do
+ = s_('ProjectsNew|Enable Static Application Security Testing (SAST)')
+ %span.badge.badge-info.badge-pill.gl-badge.sm= _('Free')
+ .form-text.text-muted
+ = s_('ProjectsNew|Analyze your source code for known security vulnerabilities.')
+ = link_to _('Learn more.'), help_page_path('user/application_security/sast/index'), target: '_blank', rel: 'noopener noreferrer', data: { track_action: 'followed', track_experiment: e.name }
= f.submit _('Create project'), class: "btn gl-button btn-confirm", data: { track_label: "#{track_label}", track_action: "click_button", track_property: "create_project", track_value: "" }
= link_to _('Cancel'), dashboard_projects_path, class: 'btn gl-button btn-default btn-cancel', data: { track_label: "#{track_label}", track_action: "click_button", track_property: "cancel", track_value: "" }
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index 66e9badbaf..168b240c65 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -18,3 +18,4 @@
= render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: project_update_blob_path(@project, @id), method: :put
= render partial: 'pipeline_tour_success' if show_suggest_pipeline_creation_celebration?
+= render 'shared/web_ide_path'
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index b1d465d0e7..6733db69c3 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -1,19 +1,12 @@
- page_title _('Branches')
- add_to_breadcrumbs(_('Repository'), project_tree_path(@project))
-.top-area.adjust
- %ul.nav-links.issues-state-filters.nav.nav-tabs
- %li{ class: active_when(@mode == 'overview') }>
- = link_to s_('Branches|Overview'), project_branches_path(@project), title: s_('Branches|Show overview of the branches')
-
- %li{ class: active_when(@mode == 'active') }>
- = link_to s_('Branches|Active'), project_branches_filtered_path(@project, state: 'active'), title: s_('Branches|Show active branches')
-
- %li{ class: active_when(@mode == 'stale') }>
- = link_to s_('Branches|Stale'), project_branches_filtered_path(@project, state: 'stale'), title: s_('Branches|Show stale branches')
-
- %li{ class: active_when(!%w[overview active stale].include?(@mode)) }>
- = link_to s_('Branches|All'), project_branches_filtered_path(@project, state: 'all'), title: s_('Branches|Show all branches')
+.top-area.gl-border-0
+ = gl_tabs_nav({ class: 'gl-flex-grow-1 gl-border-b-0' }) do
+ = gl_tab_link_to s_('Branches|Overview'), project_branches_path(@project), { item_active: @mode == 'overview', title: s_('Branches|Show overview of the branches') }
+ = gl_tab_link_to s_('Branches|Active'), project_branches_filtered_path(@project, state: 'active'), { title: s_('Branches|Show active branches') }
+ = gl_tab_link_to s_('Branches|Stale'), project_branches_filtered_path(@project, state: 'stale'), { title: s_('Branches|Show stale branches') }
+ = gl_tab_link_to s_('Branches|All'), project_branches_filtered_path(@project, state: 'all'), { item_active: !%w[overview active stale].include?(@mode), title: s_('Branches|Show all branches') }
.nav-controls
#js-branches-sort-dropdown{ data: { project_branches_filtered_path: project_branches_path(@project, state: 'all'), sort_options: branches_sort_options_hash.to_json, mode: @mode } }
@@ -38,7 +31,10 @@
%h5
= s_('Branches|Protected branches can be managed in %{project_settings_link}.').html_safe % { project_settings_link: project_settings_link }
-- if @mode == 'overview' && (@active_branches.any? || @stale_branches.any?)
+- if @gitaly_unavailable
+ = render 'shared/errors/gitaly_unavailable', reason: s_('Branches|Unable to load branches')
+
+- elsif @mode == 'overview' && (@active_branches.any? || @stale_branches.any?)
= render "projects/branches/panel", branches: @active_branches, state: 'active', panel_title: s_('Branches|Active branches'), show_more_text: s_('Branches|Show more active branches'), project: @project, overview_max_branches: @overview_max_branches
= render "projects/branches/panel", branches: @stale_branches, state: 'stale', panel_title: s_('Branches|Stale branches'), show_more_text: s_('Branches|Show more stale branches'), project: @project, overview_max_branches: @overview_max_branches
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index 27858932e5..8ee7910de4 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -31,4 +31,5 @@
.form-actions
= button_tag 'Create branch', class: 'gl-button btn btn-confirm'
= link_to _('Cancel'), project_branches_path(@project), class: 'gl-button btn btn-default btn-cancel'
+-# haml-lint:disable InlineJavaScript
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
diff --git a/app/views/projects/ci/pipeline_editor/show.html.haml b/app/views/projects/ci/pipeline_editor/show.html.haml
index 674765e9f8..ce6f7553ab 100644
--- a/app/views/projects/ci/pipeline_editor/show.html.haml
+++ b/app/views/projects/ci/pipeline_editor/show.html.haml
@@ -1,3 +1,5 @@
+- add_page_specific_style 'page_bundles/pipelines'
+
- page_title s_('Pipelines|Pipeline Editor')
- content_for :prefetch_asset_tags do
- webpack_preload_asset_tag('monaco')
diff --git a/app/views/projects/cluster_agents/show.html.haml b/app/views/projects/cluster_agents/show.html.haml
new file mode 100644
index 0000000000..a2d3426d99
--- /dev/null
+++ b/app/views/projects/cluster_agents/show.html.haml
@@ -0,0 +1,4 @@
+- add_to_breadcrumbs _('Kubernetes'), project_clusters_path(@project)
+- page_title @agent_name
+
+#js-cluster-agent-details{ data: js_cluster_agent_details_data(@agent_name, @project) }
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index bc0d14743b..62ed50f5a0 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -39,8 +39,14 @@
.committer
- commit_author_link = commit_author_link(commit, avatar: false, size: 24)
- - commit_timeago = time_ago_with_tooltip(commit.authored_date, placement: 'bottom')
- - commit_text = _('%{commit_author_link} authored %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago }
+ - commit_authored_timeago = time_ago_with_tooltip(commit.authored_date, placement: 'bottom')
+ - if commit.different_committer? && commit.committer
+ - commit_committer_link = commit_committer_link(commit)
+ - commit_committer_timeago = time_ago_with_tooltip(commit.committed_date, placement: 'bottom')
+ - commit_committer_avatar = commit_committer_avatar(commit.committer, size: 18, has_tooltip: false)
+ - commit_text = _('%{commit_author_link} authored %{commit_authored_timeago} and %{commit_committer_avatar} %{commit_committer_link} committed %{commit_committer_timeago}') % { commit_author_link: commit_author_link, commit_authored_timeago: commit_authored_timeago, commit_committer_avatar: commit_committer_avatar, commit_committer_link: commit_committer_link, commit_committer_timeago: commit_committer_timeago }
+ - else
+ - commit_text = _('%{commit_author_link} authored %{commit_authored_timeago}') % { commit_author_link: commit_author_link, commit_authored_timeago: commit_authored_timeago }
#{ commit_text.html_safe }
= render_if_exists 'projects/commits/project_namespace', show_project_name: show_project_name, project: project
diff --git a/app/views/projects/deployments/_deployment.html.haml b/app/views/projects/deployments/_deployment.html.haml
index 8270477ed3..57dfcb8cf4 100644
--- a/app/views/projects/deployments/_deployment.html.haml
+++ b/app/views/projects/deployments/_deployment.html.haml
@@ -27,7 +27,7 @@
= link_to deployment_path(deployment), class: 'build-link' do
#{deployment.deployable.name} (##{deployment.deployable.id})
- else
- .badge.badge-info.suggestion-help-hover{ title: s_('Deployment|This deployment was created using the API') }
+ .badge.badge-info.gl-cursor-help{ title: s_('Deployment|This deployment was created using the API') }
= s_('Deployment|API')
.table-section.section-10{ role: 'gridcell' }
diff --git a/app/views/projects/feature_flags/edit.html.haml b/app/views/projects/feature_flags/edit.html.haml
index c1c9f58265..ac8c057507 100644
--- a/app/views/projects/feature_flags/edit.html.haml
+++ b/app/views/projects/feature_flags/edit.html.haml
@@ -4,10 +4,4 @@
- breadcrumb_title @feature_flag.name
- page_title s_('FeatureFlags|Edit Feature Flag')
-#js-edit-feature-flag{ data: { endpoint: project_feature_flag_path(@project, @feature_flag),
- project_id: @project.id,
- feature_flags_path: project_feature_flags_path(@project),
- environments_endpoint: search_project_environments_path(@project, format: :json),
- strategy_type_docs_page_path: help_page_path('operations/feature_flags', anchor: 'feature-flag-strategies'),
- environments_scope_docs_path: help_page_path('ci/environments/index.md', anchor: 'scope-environments-with-specs'),
- feature_flag_issues_endpoint: feature_flag_issues_links_endpoint(@project, @feature_flag, current_user) } }
+#js-edit-feature-flag{ data: edit_feature_flag_data }
diff --git a/app/views/projects/google_cloud/index.html.haml b/app/views/projects/google_cloud/index.html.haml
new file mode 100644
index 0000000000..4fc66e1781
--- /dev/null
+++ b/app/views/projects/google_cloud/index.html.haml
@@ -0,0 +1,83 @@
+- breadcrumb_title _('Google Cloud')
+- page_title _('Google Cloud')
+
+- @content_class = "limit-container-width" unless fluid_layout
+
+#js-google-cloud
+
+ %h1.gl-font-size-h1 Google Cloud
+
+ %section#js-section-google-cloud-service-accounts
+
+ %h2.gl-font-size-h2 Service Accounts
+
+ %p= _('Service Accounts keys are required to authorize GitLab to deploy your Google Cloud project.')
+
+ %table.table.b-table.gl-table
+
+ %thead
+ %tr
+ %th Environment
+ %th GCP Project ID
+ %th Service Account Key
+
+ %tbody
+
+ %tr
+ %td *
+ %td serving-salutes-453
+ %td .....
+
+ %tr
+ %td production
+ %td crimson-corey-234
+ %td .....
+
+ %tr
+ %td review/*
+ %td roving-river-379
+ %td .....
+
+ %a.gl-button.btn.btn-primary= _('Add new service account')
+
+ %br
+
+ %section#js-section-google-cloud-deployments
+
+ .row.row-fluid
+
+ .col-lg-4
+ %h2.gl-font-size-h2 Deployments
+ %p= _('Google Cloud offers several deployment targets. Select the one most suitable for your project.')
+ %p
+ = _('Deployments to Google Kubernetes Engine can be ')
+ %a{ href: '#' }= _('managed')
+ = _('in Infrastructure :: Kubernetes clusters')
+
+ .col-lg-8
+
+ %br
+
+ .gl-card.gl-mb-6
+ .gl-card-body
+ .gl-display-flex.gl-align-items-baseline
+ %strong.gl-font-lg App Engine
+ .gl-ml-auto.gl-text-gray-500 Disabled
+ %p= _('App Engine description and apps that are suitable for this deployment target')
+ %button.gl-button.btn.btn-default= _('Configure via Merge Request')
+
+ .gl-card.gl-mb-6
+ .gl-card-body
+ .gl-display-flex.gl-align-items-baseline
+ %strong.gl-font-lg Cloud Functions
+ .gl-ml-auto.gl-text-gray-500 Disabled
+ %p= _('Cloud Functions description and apps that are suitable for this deployment target')
+ %button.gl-button.btn.btn-default= _('Configure via Merge Request')
+
+ .gl-card.gl-mb-6
+ .gl-card-body
+ .gl-display-flex.gl-align-items-baseline
+ %strong.gl-font-lg Cloud Run
+ .gl-ml-auto.gl-text-gray-500 Disabled
+ %p= _('Cloud Run description and apps that are suitable for this deployment target')
+ %button.gl-button.btn.btn-default= _('Configure via Merge Request')
diff --git a/app/views/projects/hook_logs/_index.html.haml b/app/views/projects/hook_logs/_index.html.haml
index ee4dbf5c05..6a46b0b351 100644
--- a/app/views/projects/hook_logs/_index.html.haml
+++ b/app/views/projects/hook_logs/_index.html.haml
@@ -1,37 +1,11 @@
-.row.gl-mt-7.gl-mb-3
+- docs_link_url = help_page_path('user/project/integrations/webhooks', anchor: 'troubleshoot-webhooks')
+- link_start = ''.html_safe % { url: docs_link_url }
+- link_end = ''.html_safe
+
+.row.gl-mt-3.gl-mb-3
.col-lg-3
%h4.gl-mt-0
- Recent Deliveries
- %p When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong.
+ = _('Recent events')
+ %p= _('GitLab events trigger webhooks. Use the request details of a webhook to help troubleshoot problems. %{link_start}How do I troubleshoot?%{link_end}').html_safe % { link_start: link_start, link_end: link_end }
.col-lg-9
- - if hook_logs.present?
- %table.table
- %thead
- %tr
- %th Status
- %th Trigger
- %th URL
- %th Elapsed time
- %th Request time
- %th
- - hook_logs.each do |hook_log|
- %tr
- %td
- = render partial: 'shared/hook_logs/status_label', locals: { hook_log: hook_log }
- %td.d-none.d-sm-block
- %span.badge.badge-gray.deploy-project-label
- = hook_log.trigger.singularize.titleize
- %td
- = truncate(hook_log.url, length: 50)
- %td.light
- #{number_with_precision(hook_log.execution_duration, precision: 2)} sec
- %td.light
- = time_ago_with_tooltip(hook_log.created_at)
- %td
- = link_to 'View details', hook_log.present.details_path
-
- = paginate hook_logs, theme: 'gitlab'
-
- - else
- .settings-message.text-center
- You don't have any webhooks deliveries
+ = render partial: 'shared/hook_logs/recent_deliveries_table', locals: { hook: hook, hook_logs: hook_logs }
diff --git a/app/views/projects/hook_logs/show.html.haml b/app/views/projects/hook_logs/show.html.haml
index ebe179c345..86dfa1929d 100644
--- a/app/views/projects/hook_logs/show.html.haml
+++ b/app/views/projects/hook_logs/show.html.haml
@@ -5,8 +5,8 @@
.row.gl-mt-3.gl-mb-3
.col-lg-3
%h4.gl-mt-0
- Request details
+ = _("Request details")
.col-lg-9
- = link_to 'Resend Request', @hook_log.present.retry_path, method: :post, class: "btn gl-button btn-default float-right gl-ml-3"
+ = link_to _('Resend Request'), @hook_log.present.retry_path, method: :post, class: "btn gl-button btn-default float-right gl-ml-3"
= render partial: 'shared/hook_logs/content', locals: { hook_log: @hook_log }
diff --git a/app/views/projects/issues/_nav_btns.html.haml b/app/views/projects/issues/_nav_btns.html.haml
index 0d69f6f69a..8d16c3d978 100644
--- a/app/views/projects/issues/_nav_btns.html.haml
+++ b/app/views/projects/issues/_nav_btns.html.haml
@@ -5,11 +5,11 @@
- can_edit = can?(current_user, :admin_project, @project)
- notification_email = @current_user.present? ? @current_user.notification_email_or_default : nil
-.nav-controls.issues-nav-controls
+.nav-controls.issues-nav-controls.gl-font-size-0
- if show_feed_buttons
= render 'shared/issuable/feed_buttons'
- .js-csv-import-export-buttons{ data: { show_export_button: show_export_button.to_s, show_import_button: show_import_button.to_s, issuable_type: issuable_type, issuable_count: issuables_count_for_state(issuable_type.to_sym, params[:state]), email: notification_email, export_csv_path: export_csv_project_issues_path(@project, request.query_parameters), import_csv_issues_path: import_csv_namespace_project_issues_path, container_class: 'gl-mr-3', can_edit: can_edit.to_s, project_import_jira_path: project_import_jira_path(@project) } }
+ .js-csv-import-export-buttons{ data: { show_export_button: show_export_button.to_s, show_import_button: show_import_button.to_s, issuable_type: issuable_type, issuable_count: issuables_count_for_state(issuable_type.to_sym, params[:state]), email: notification_email, export_csv_path: export_csv_project_issues_path(@project, request.query_parameters), import_csv_issues_path: import_csv_namespace_project_issues_path, container_class: 'gl-mr-3', can_edit: can_edit.to_s, project_import_jira_path: project_import_jira_path(@project), max_attachment_size: number_to_human_size(Gitlab::CurrentSettings.max_attachment_size.megabytes) } }
- if @can_bulk_update
= button_tag _("Edit issues"), class: "gl-button btn btn-default gl-mr-3 js-bulk-update-toggle"
@@ -18,4 +18,3 @@
issue: { milestone_id: finder.milestones.first.try(:id) }),
class: "gl-button btn btn-confirm",
id: "new_issue_link"
-
diff --git a/app/views/projects/issues/_related_branches.html.haml b/app/views/projects/issues/_related_branches.html.haml
index 0604e89be6..c47257eec4 100644
--- a/app/views/projects/issues/_related_branches.html.haml
+++ b/app/views/projects/issues/_related_branches.html.haml
@@ -1,9 +1,9 @@
- if @related_branches.any?
- %h2.related-branches-title
+ %h2.gl-font-lg
= pluralize(@related_branches.size, 'Related Branch')
%ul.unstyled-list.related-merge-requests
- @related_branches.each do |branch|
- %li
+ %li.gl-display-flex.gl-align-items-center
- if branch[:pipeline_status].present?
%span.related-branch-ci-status
= render 'ci/status/icon', status: branch[:pipeline_status]
diff --git a/app/views/projects/issues/_related_issues.html.haml b/app/views/projects/issues/_related_issues.html.haml
index d131d20f07..bab37609c2 100644
--- a/app/views/projects/issues/_related_issues.html.haml
+++ b/app/views/projects/issues/_related_issues.html.haml
@@ -3,4 +3,3 @@
can_add_related_issues: "#{can?(current_user, :admin_issue_link, @issue)}",
help_path: help_page_path('user/project/issues/related_issues'),
show_categorized_issues: "false" } }
- - render('projects/issues/related_issues_block')
diff --git a/app/views/projects/issues/_related_issues_block.html.haml b/app/views/projects/issues/_related_issues_block.html.haml
deleted file mode 100644
index 8d986b64b1..0000000000
--- a/app/views/projects/issues/_related_issues_block.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-.related-issues-block
- .card.card-slim
- .card-header.panel-empty-heading.border-bottom-0
- %h3.card-title.mt-0.mb-0.h5
- = _('Linked issues')
diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml
index 5a983fb556..0e8de3c2bb 100644
--- a/app/views/projects/merge_requests/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/_mr_title.html.haml
@@ -32,20 +32,20 @@
.dropdown-menu.dropdown-menu-right
%ul
- if can_update_merge_request
- %li= link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
+ %li= link_to _('Edit'), edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
- if @merge_request.opened?
%li
= link_to @merge_request.work_in_progress? ? _('Mark as ready') : _('Mark as draft'), toggle_draft_merge_request_path(@merge_request), method: :put, class: "js-draft-toggle-button"
%li{ class: [merge_request_button_visibility(@merge_request, true), 'js-close-item'] }
- = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request'
+ = link_to _('Close'), merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request'
- if can_reopen_merge_request
%li{ class: merge_request_button_visibility(@merge_request, false) }
- = link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, title: 'Reopen merge request'
+ = link_to _('Reopen'), merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, title: 'Reopen merge request'
- unless @merge_request.merged? || current_user == @merge_request.author
- %li= link_to 'Report abuse', new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request))
+ %li= link_to _('Report abuse'), new_abuse_report_path(user_id: @merge_request.author.id, ref_url: merge_request_url(@merge_request))
- if can_update_merge_request
- = link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "d-none d-md-block btn gl-button btn-default btn-grouped js-issuable-edit", data: { qa_selector: "edit_button" }
+ = link_to _('Edit'), edit_project_merge_request_path(@project, @merge_request), class: "d-none d-md-block btn gl-button btn-default btn-grouped js-issuable-edit", data: { qa_selector: "edit_button" }
- if can_update_merge_request && !are_close_and_open_buttons_hidden
= render 'projects/merge_requests/close_reopen_draft_report_toggle'
diff --git a/app/views/projects/merge_requests/_nav_btns.html.haml b/app/views/projects/merge_requests/_nav_btns.html.haml
index b34cf23634..00d12423eb 100644
--- a/app/views/projects/merge_requests/_nav_btns.html.haml
+++ b/app/views/projects/merge_requests/_nav_btns.html.haml
@@ -5,7 +5,7 @@
.js-csv-import-export-buttons{ data: { show_export_button: "true", issuable_type: issuable_type, issuable_count: issuables_count_for_state(issuable_type.to_sym, params[:state]), email: notification_email, export_csv_path: export_csv_project_merge_requests_path(@project, request.query_parameters), container_class: 'gl-mr-3' } }
- if @can_bulk_update
- = button_tag "Edit merge requests", class: "gl-button btn btn-default gl-mr-3 js-bulk-update-toggle"
+ = button_tag _("Edit merge requests"), class: "gl-button btn btn-default gl-mr-3 js-bulk-update-toggle"
- if merge_project
- = link_to new_merge_request_path, class: "gl-button btn btn-confirm", title: "New merge request" do
- New merge request
+ = link_to new_merge_request_path, class: "gl-button btn btn-confirm", title: _("New merge request") do
+ = _('New merge request')
diff --git a/app/views/projects/merge_requests/_widget.html.haml b/app/views/projects/merge_requests/_widget.html.haml
index 47a0d05fc6..459742c3b8 100644
--- a/app/views/projects/merge_requests/_widget.html.haml
+++ b/app/views/projects/merge_requests/_widget.html.haml
@@ -19,6 +19,6 @@
window.gl.mrWidgetData.pipelines_empty_svg_path = '#{image_path('illustrations/pipelines_empty.svg')}';
window.gl.mrWidgetData.codequality_help_path = '#{help_page_path("user/project/merge_requests/code_quality", anchor: "code-quality-reports")}';
window.gl.mrWidgetData.false_positive_doc_url = '#{help_page_path('user/application_security/vulnerabilities/index')}';
- window.gl.mrWidgetData.can_view_false_positive = '#{(Feature.enabled?(:vulnerability_flags, default_enabled: :yaml) && @merge_request.project.licensed_feature_available?(:sast_fp_reduction)).to_s}';
+ window.gl.mrWidgetData.can_view_false_positive = '#{@merge_request.project.licensed_feature_available?(:sast_fp_reduction).to_s}';
#js-vue-mr-widget.mr-widget
diff --git a/app/views/projects/merge_requests/conflicts/show.html.haml b/app/views/projects/merge_requests/conflicts/show.html.haml
index ee296258d0..5ba42ca761 100644
--- a/app/views/projects/merge_requests/conflicts/show.html.haml
+++ b/app/views/projects/merge_requests/conflicts/show.html.haml
@@ -6,7 +6,7 @@
.merge-request-details.issuable-details
= render "projects/merge_requests/mr_box"
-= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, source_branch: @merge_request.source_branch
+= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @merge_request.assignees, reviewers: @merge_request.reviewers, source_branch: @merge_request.source_branch
#conflicts{ data: { conflicts_path: conflicts_project_merge_request_path(@merge_request.project, @merge_request, format: :json),
resolve_conflicts_path: resolve_conflicts_project_merge_request_path(@merge_request.project, @merge_request),
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 7e260a03c5..2154ef6b59 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -49,6 +49,7 @@
= render "projects/merge_requests/tabs/pane", id: "notes", class: "notes voting_notes" do
.row
%section.col-md-12
+ -# haml-lint:disable InlineJavaScript
%script.js-notes-data{ type: "application/json" }= initial_notes_data(true).to_json.html_safe
.issuable-discussion.js-vue-notes-event
- if @merge_request.description.present?
@@ -98,3 +99,4 @@
= render 'projects/invite_members_modal', project: @project
- if Gitlab::CurrentSettings.gitpod_enabled && !current_user&.gitpod_enabled
= render 'shared/gitpod/enable_gitpod_modal'
+= render 'shared/web_ide_path'
diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml
index b89aa9d402..d253ab8e32 100644
--- a/app/views/projects/mirrors/_mirror_repos.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos.html.haml
@@ -10,7 +10,7 @@
= expanded ? _('Collapse') : _('Expand')
%p
= _('Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically.')
- = link_to _('How do I mirror repositories?'), help_page_path('user/project/repository/repository_mirroring'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('How do I mirror repositories?'), help_page_path('user/project/repository/mirror/index.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
- if mirror_settings_enabled
@@ -32,7 +32,7 @@
= label_tag :only_protected_branches, _('Mirror only protected branches'), class: 'form-check-label'
.form-text.text-muted
= _('If enabled, only protected branches will be mirrored.')
- = link_to _('Learn more.'), help_page_path('user/project/repository/repository_mirroring', anchor: 'mirror-only-protected-branches'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('user/project/repository/mirror/index.md', anchor: 'mirror-only-protected-branches'), target: '_blank', rel: 'noopener noreferrer'
.panel-footer
= f.submit _('Mirror repository'), class: 'gl-button btn btn-confirm js-mirror-submit qa-mirror-repository-button', name: :update_remote_mirror
diff --git a/app/views/projects/mirrors/_mirror_repos_push.html.haml b/app/views/projects/mirrors/_mirror_repos_push.html.haml
index b3e0f71bf1..339c5d8291 100644
--- a/app/views/projects/mirrors/_mirror_repos_push.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos_push.html.haml
@@ -12,4 +12,4 @@
= label_tag :keep_divergent_refs, _('Keep divergent refs'), class: 'form-check-label'
.form-text.text-muted
- link_opening_tag = ''.html_safe
- = html_escape(_('Do not force push over diverged refs. After the mirror is created, this setting can only be modified using the API. %{mirroring_docs_link_start}Learn more about this option%{link_closing_tag} and %{mirroring_api_docs_link_start}the API.%{link_closing_tag}')) % { mirroring_docs_link_start: link_opening_tag % {url: help_page_path('user/project/repository/repository_mirroring', anchor: 'keep-divergent-refs')}, mirroring_api_docs_link_start: link_opening_tag % {url: help_page_path('api/remote_mirrors')}, link_closing_tag: ''.html_safe }
+ = html_escape(_('Do not force push over diverged refs. After the mirror is created, this setting can only be modified using the API. %{mirroring_docs_link_start}Learn more about this option%{link_closing_tag} and %{mirroring_api_docs_link_start}the API.%{link_closing_tag}')) % { mirroring_docs_link_start: link_opening_tag % {url: help_page_path('user/project/repository/mirror/push.md', anchor: 'keep-divergent-refs')}, mirroring_api_docs_link_start: link_opening_tag % {url: help_page_path('api/remote_mirrors')}, link_closing_tag: ''.html_safe }
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index 23606e2456..93afddce77 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -70,10 +70,10 @@
= link_to retry_project_job_path(build.project, build, return_to: request.original_url), method: :post, title: _('Retry'), class: 'gl-button btn btn-default btn-icon' do
= sprite_icon('repeat', css_class: 'gl-icon')
- if can?(current_user, :read_build, job)
- %tr.build-trace-row.responsive-table-border-end
+ %tr.build-log-row.responsive-table-border-end
%td
- %td.responsive-table-cell.build-trace-container{ colspan: 4 }
- %pre.build-trace.build-trace-rounded
+ %td.responsive-table-cell.build-log-container{ colspan: 4 }
+ %pre.build-log.build-log-rounded
%code.bash.js-build-output
= build_summary(build)
diff --git a/app/views/projects/product_analytics/test.html.haml b/app/views/projects/product_analytics/test.html.haml
index 60d897ee13..3204cd5fbb 100644
--- a/app/views/projects/product_analytics/test.html.haml
+++ b/app/views/projects/product_analytics/test.html.haml
@@ -12,5 +12,6 @@
%code
= @event.as_json_wo_empty
+-# haml-lint:disable InlineJavaScript
:javascript
#{render 'tracker'}
diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml
index bdb5f021b7..cfdbf3410b 100644
--- a/app/views/projects/registry/repositories/index.html.haml
+++ b/app/views/projects/registry/repositories/index.html.haml
@@ -20,7 +20,8 @@
"is_admin": current_user&.admin.to_s,
"show_cleanup_policy_on_alert": show_cleanup_policy_on_alert(@project).to_s,
"cleanup_policies_settings_path": project_settings_packages_and_registries_path(@project),
- character_error: @character_error.to_s,
+ connection_error: (!!@connection_error).to_s,
+ invalid_path_error: (!!@invalid_path_error).to_s,
user_callouts_path: user_callouts_path,
user_callout_id: UserCalloutsHelper::UNFINISHED_TAG_CLEANUP_CALLOUT,
show_unfinished_tag_cleanup_callout: show_unfinished_tag_cleanup_callout?.to_s, } }
diff --git a/app/views/projects/settings/operations/_error_tracking.html.haml b/app/views/projects/settings/operations/_error_tracking.html.haml
index 6b2a1468ee..23b1ec4dea 100644
--- a/app/views/projects/settings/operations/_error_tracking.html.haml
+++ b/app/views/projects/settings/operations/_error_tracking.html.haml
@@ -18,4 +18,5 @@
api_host: setting.api_host,
enabled: setting.enabled.to_json,
integrated: setting.integrated.to_json,
+ gitlab_dsn: setting.gitlab_dsn,
token: setting.token.present? ? '*' * 12 : nil } }
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index 8ef53c40b1..3e6acdb130 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -3,7 +3,7 @@
- breadcrumb_title @snippet.to_reference
- page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
-#js-snippet-view{ data: {'qa-selector': 'snippet_view', 'snippet-gid': @snippet.to_global_id, 'report-abuse-path': snippet_report_abuse_path(@snippet) } }
+#js-snippet-view{ data: {'qa-selector': 'snippet_view', 'snippet-gid': @snippet.to_global_id, 'report-abuse-path': snippet_report_abuse_path(@snippet), 'can-report-spam': @snippet.submittable_as_spam_by?(current_user).to_s } }
.row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true, api_awards_path: project_snippets_award_api_path(@snippet)
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index 79205a51d7..d3cc409df1 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -18,6 +18,9 @@
= render_if_exists 'projects/commits/mirror_status'
+ - if @tags_loading_error
+ = render 'shared/errors/gitaly_unavailable', reason: s_('TagsPage|Unable to load tags')
+
.tags
- if @tags.any?
%ul.flex-list.content-list
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index fe00772d1d..4281152225 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -54,4 +54,5 @@
.form-actions.gl-display-flex
= button_tag s_('TagsPage|Create tag'), class: 'gl-button btn btn-confirm gl-mr-3', data: { qa_selector: "create_tag_button" }
= link_to s_('TagsPage|Cancel'), project_tags_path(@project), class: 'gl-button btn btn-default btn-cancel'
+-# haml-lint:disable InlineJavaScript
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index 2d0c4cc20a..1553eda1cf 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -11,3 +11,4 @@
= render 'projects/last_push'
= render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id)
+= render 'shared/web_ide_path'
diff --git a/app/views/projects/usage_quotas/index.html.haml b/app/views/projects/usage_quotas/index.html.haml
index dfd46af049..6c7cccfb9b 100644
--- a/app/views/projects/usage_quotas/index.html.haml
+++ b/app/views/projects/usage_quotas/index.html.haml
@@ -6,7 +6,7 @@
.row
.col-sm-12
= s_('UsageQuota|Usage of project resources across the %{strong_start}%{project_name}%{strong_end} project').html_safe % { strong_start: ''.html_safe, strong_end: ''.html_safe, project_name: @project.name } + '.'
- %a{ href: help_page_path('user/usage_quotas.md') }
+ %a{ href: help_page_path('user/usage_quotas.md'), target: '_blank', rel: 'noopener noreferrer' }
= s_('UsageQuota|Learn more about usage quotas') + '.'
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
diff --git a/app/views/search/results/_issuable.html.haml b/app/views/search/results/_issuable.html.haml
index 5645fbfb23..41058034d6 100644
--- a/app/views/search/results/_issuable.html.haml
+++ b/app/views/search/results/_issuable.html.haml
@@ -13,7 +13,8 @@
= highlight_and_truncate_issuable(issuable, @search_term, @search_highlight)
.col-sm-3.gl-mt-3.gl-sm-mt-0.gl-text-right
- if issuable.respond_to?(:upvotes_count) && issuable.upvotes_count > 0
- %li.issuable-upvotes.gl-list-style-none.has-tooltip{ title: _('Upvotes') }
- = sprite_icon('thumb-up', css_class: "gl-vertical-align-middle")
- = issuable.upvotes_count
+ %li.issuable-upvotes.gl-list-style-none
+ %span.has-tooltip{ title: _('Upvotes') }
+ = sprite_icon('thumb-up', css_class: "gl-vertical-align-middle")
+ = issuable.upvotes_count
%span.gl-text-gray-500= sprintf(s_('updated %{time_ago}'), { time_ago: time_ago_with_tooltip(issuable.updated_at, placement: 'bottom') }).html_safe
diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml
index f03314563c..3ab2b969b7 100644
--- a/app/views/shared/_import_form.html.haml
+++ b/app/views/shared/_import_form.html.haml
@@ -9,17 +9,12 @@
= f.text_field :import_url, value: import_url.sanitized_url,
autocomplete: 'off', class: 'form-control gl-form-input', placeholder: 'https://gitlab.company.com/group/project.git', required: true
= render 'shared/global_alert',
- variant: :warning,
- alert_class: 'gl-mt-3 js-import-url-warning hide',
+ variant: :danger,
+ alert_class: 'gl-mt-3 js-import-url-error hide',
dismissible: false,
close_button_class: 'js-close-2fa-enabled-success-alert' do
.gl-alert-body
- = s_('Import|A repository URL usually ends in a .git suffix, although this is not required. Double check to make sure your repository URL is correct.')
-
- .gl-alert.gl-alert-not-dismissible.gl-alert-warning.gl-mt-3.hide#project_import_url_warning
- .gl-alert-container
- = sprite_icon('warning-solid', css_class: 'gl-icon s16 gl-alert-icon gl-alert-icon-no-title')
- .gl-alert-content{ role: 'alert' }
+ = s_('Import|There is not a valid Git repository at this URL. If your HTTP repository is not publicly accessible, verify your credentials.')
.row
.form-group.col-md-6
= f.label :import_url_user, class: 'label-bold' do
diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml
index eb50960202..117ed212fd 100644
--- a/app/views/shared/_milestones_filter.html.haml
+++ b/app/views/shared/_milestones_filter.html.haml
@@ -1,13 +1,15 @@
-%ul.nav-links.mobile-separator.nav.nav-tabs
- %li{ class: milestone_class_for_state(params[:state], 'opened', true) }>
- = link_to milestones_filter_path(state: 'opened') do
- = _('Open')
- %span.badge.badge-pill= counts[:opened]
- %li{ class: milestone_class_for_state(params[:state], 'closed') }>
- = link_to milestones_filter_path(state: 'closed', sort: 'due_date_desc') do
- = _('Closed')
- %span.badge.badge-pill= counts[:closed]
- %li{ class: milestone_class_for_state(params[:state], 'all') }>
- = link_to milestones_filter_path(state: 'all', sort: 'due_date_desc') do
- = _('All')
- %span.badge.badge-pill= counts[:all]
+- count_badge_classes = 'badge badge-muted badge-pill gl-badge gl-tab-counter-badge sm gl-display-none gl-sm-display-inline-flex'
+
+= gl_tabs_nav( {class: 'gl-border-b-0 gl-flex-grow-1', data: { testid: 'milestones-filter' } } ) do
+ = gl_tab_link_to milestones_filter_path(state: 'opened'), { item_active: params[:state].blank? || params[:state] == 'opened' } do
+ = _('Open')
+ %span{ class: count_badge_classes }
+ = counts[:opened]
+ = gl_tab_link_to milestones_filter_path(state: 'closed', sort: 'due_date_desc'), { item_active: params[:state] == 'closed' } do
+ = _('Closed')
+ %span{ class: count_badge_classes }
+ = counts[:closed]
+ = gl_tab_link_to milestones_filter_path(state: 'all', sort: 'due_date_desc'), { item_active: params[:state] == 'all' } do
+ = _('All')
+ %span{ class: count_badge_classes }
+ = counts[:all]
diff --git a/app/views/shared/_web_ide_path.html.haml b/app/views/shared/_web_ide_path.html.haml
new file mode 100644
index 0000000000..73d00bcd40
--- /dev/null
+++ b/app/views/shared/_web_ide_path.html.haml
@@ -0,0 +1,4 @@
+= javascript_tag do
+ :plain
+ window.gl = window.gl || {};
+ window.gl.webIDEPath = '#{web_ide_url}'
diff --git a/app/views/shared/builds/_build_output.html.haml b/app/views/shared/builds/_build_output.html.haml
index 380fac4d0e..a3b7d4926f 100644
--- a/app/views/shared/builds/_build_output.html.haml
+++ b/app/views/shared/builds/_build_output.html.haml
@@ -1,4 +1,4 @@
-%pre.build-trace#build-trace
+%pre.build-log
%code.bash.js-build-output
.build-loader-animation.js-build-refresh
.dot
diff --git a/app/views/shared/builds/_tabs.html.haml b/app/views/shared/builds/_tabs.html.haml
index 4973309edf..498e9cc33c 100644
--- a/app/views/shared/builds/_tabs.html.haml
+++ b/app/views/shared/builds/_tabs.html.haml
@@ -1,24 +1,19 @@
-%ul.nav-links.mobile-separator.nav.nav-tabs
- %li{ class: active_when(scope.nil?) }>
- = link_to build_path_proc.call(nil) do
- All
- %span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm.js-totalbuilds-count
- = limited_counter_with_delimiter(all_builds)
+- count_badge_classes = 'badge badge-muted badge-pill gl-badge gl-tab-counter-badge sm gl-display-none gl-sm-display-inline-flex'
- %li{ class: active_when(scope == 'pending') }>
- = link_to build_path_proc.call('pending') do
- Pending
- %span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm
- = limited_counter_with_delimiter(all_builds.pending)
-
- %li{ class: active_when(scope == 'running') }>
- = link_to build_path_proc.call('running') do
- Running
- %span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm
- = limited_counter_with_delimiter(all_builds.running)
-
- %li{ class: active_when(scope == 'finished') }>
- = link_to build_path_proc.call('finished') do
- Finished
- %span.badge.gl-tab-counter-badge.badge-muted.badge-pill.gl-badge.sm
- = limited_counter_with_delimiter(all_builds.finished)
+= gl_tabs_nav( {class: 'gl-border-b-0 gl-flex-grow-1', data: { testid: 'jobs-tabs' } } ) do
+ = gl_tab_link_to build_path_proc.call(nil), { item_active: scope.nil? } do
+ = _('All')
+ %span{ class: count_badge_classes }
+ = limited_counter_with_delimiter(all_builds)
+ = gl_tab_link_to build_path_proc.call('pending'), { item_active: scope == 'pending' } do
+ = _('Pending')
+ %span{ class: count_badge_classes }
+ = limited_counter_with_delimiter(all_builds.pending)
+ = gl_tab_link_to build_path_proc.call('running'), { item_active: scope == 'running' } do
+ = _('Running')
+ %span{ class: count_badge_classes }
+ = limited_counter_with_delimiter(all_builds.running)
+ = gl_tab_link_to build_path_proc.call('finished'), { item_active: scope == 'finished' } do
+ = _('Finished')
+ %span{ class: count_badge_classes }
+ = limited_counter_with_delimiter(all_builds.finished)
diff --git a/app/views/shared/deploy_tokens/_form.html.haml b/app/views/shared/deploy_tokens/_form.html.haml
index 652da4b396..e049afbc40 100644
--- a/app/views/shared/deploy_tokens/_form.html.haml
+++ b/app/views/shared/deploy_tokens/_form.html.haml
@@ -4,7 +4,6 @@
= s_('DeployTokens|Create a new deploy token for all projects in this group. %{link_start}What are deploy tokens?%{link_end}').html_safe % { link_start: group_deploy_tokens_help_link_start, link_end: ''.html_safe }
= form_for token, url: create_deploy_token_path(group_or_project, anchor: 'js-deploy-tokens'), method: :post, remote: Feature.enabled?(:ajax_new_deploy_token, group_or_project) do |f|
- = form_errors(token)
.form-group
= f.label :name, class: 'label-bold'
diff --git a/app/views/shared/empty_states/_topics.html.haml b/app/views/shared/empty_states/_topics.html.haml
new file mode 100644
index 0000000000..fd82a85303
--- /dev/null
+++ b/app/views/shared/empty_states/_topics.html.haml
@@ -0,0 +1,7 @@
+.row.empty-state
+ .col-12
+ .svg-content
+ = image_tag 'illustrations/labels.svg', data: { qa_selector: 'svg_content' }
+ .text-content.gl-text-center.gl-pt-0!
+ %h4= _('There are no topics to show.')
+ %p= _('Add topics to projects to help users find them.')
diff --git a/app/views/shared/errors/_gitaly_unavailable.html.haml b/app/views/shared/errors/_gitaly_unavailable.html.haml
new file mode 100644
index 0000000000..96a68cbcdc
--- /dev/null
+++ b/app/views/shared/errors/_gitaly_unavailable.html.haml
@@ -0,0 +1,8 @@
+.gl-alert.gl-alert-danger.gl-mb-5.gl-mt-5
+ .gl-alert-container
+ = sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
+ .gl-alert-content
+ .gl-alert-title
+ = reason
+ .gl-alert-body
+ = s_('The git server, Gitaly, is not available at this time. Please contact your administrator.')
diff --git a/app/views/shared/hook_logs/_recent_deliveries_table.html.haml b/app/views/shared/hook_logs/_recent_deliveries_table.html.haml
new file mode 100644
index 0000000000..31ef856078
--- /dev/null
+++ b/app/views/shared/hook_logs/_recent_deliveries_table.html.haml
@@ -0,0 +1,34 @@
+%table.gl-table.gl-w-full
+ %thead
+ %tr
+ %th= _('Status')
+ %th.d-none.d-sm-table-cell= _('Trigger')
+ %th= _('Elapsed time')
+ %th= _('Request time')
+ %th
+
+ - if hook_logs.present?
+ - hook_logs.each do |hook_log|
+ %tr
+ %td
+ = render partial: 'shared/hook_logs/status_label', locals: { hook_log: hook_log }
+ %td.d-none.d-sm-table-cell
+ %span.badge.badge-pill.gl-badge.badge-muted.sm
+ = hook_log.trigger.singularize.titleize
+ %td
+ #{number_with_precision(hook_log.execution_duration, precision: 2)} sec
+ %td
+ = time_ago_with_tooltip(hook_log.created_at)
+ %td
+ = link_to _('View details'), hook_log_path(hook, hook_log)
+
+
+- if hook_logs.present?
+ = paginate hook_logs, theme: 'gitlab'
+- else
+ .gl-text-center.gl-mt-7
+ %h4= _('No webhook events')
+ %p
+ %span.gl-display-block= _('Webhook events will be displayed here.')
+ %span= _('Use the %{strongStart}Test%{strongEnd} option above to create an event.').html_safe % { strongStart: ''.html_safe, strongEnd: ''.html_safe }
+
diff --git a/app/views/shared/hook_logs/_status_label.html.haml b/app/views/shared/hook_logs/_status_label.html.haml
index dfa5ecee44..b930074303 100644
--- a/app/views/shared/hook_logs/_status_label.html.haml
+++ b/app/views/shared/hook_logs/_status_label.html.haml
@@ -1,3 +1,3 @@
- label_status = hook_log.success? ? 'badge-success' : 'badge-danger'
-%span{ class: "badge #{label_status}" }
+%span{ class: "badge badge-pill gl-badge sm #{label_status}" }
= hook_log.internal_error? ? _('Error') : hook_log.response_status
diff --git a/app/views/shared/integrations/_tabs.html.haml b/app/views/shared/integrations/_tabs.html.haml
index 553401e47b..d6ca0bd7d1 100644
--- a/app/views/shared/integrations/_tabs.html.haml
+++ b/app/views/shared/integrations/_tabs.html.haml
@@ -1,18 +1,11 @@
-- active_tab = local_assigns.fetch(:active_tab, 'edit')
-- active_classes = 'gl-tab-nav-item-active gl-tab-nav-item-active-indigo active'
-- tabs = integration_tabs(integration: integration)
-
-- if tabs.length <= 1
- = yield
-- else
+- if integration.instance_level?
.tabs.gl-tabs
%div
- %ul.nav.gl-tabs-nav{ role: 'tablist' }
- - tabs.each do |tab|
- %li.nav-item{ role: 'presentation' }
- %a.nav-link.gl-tab-nav-item{ role: 'tab', class: (active_classes if tab[:key] == active_tab), href: tab[:href] }
- = tab[:text]
+ = gl_tabs_nav({ class: 'gl-mb-5' }) do
+ = gl_tab_link_to _('Settings'), scoped_edit_integration_path(integration)
+ = gl_tab_link_to s_('Integrations|Projects using custom settings'), scoped_overrides_integration_path(integration)
- .tab-content.gl-tab-content
- .tab-pane.gl-pt-3.active{ role: 'tabpanel' }
- = yield
+ = yield
+
+- else
+ = yield
diff --git a/app/views/shared/issuable/_nav.html.haml b/app/views/shared/issuable/_nav.html.haml
index cff50eef88..4a33f62534 100644
--- a/app/views/shared/issuable/_nav.html.haml
+++ b/app/views/shared/issuable/_nav.html.haml
@@ -2,22 +2,16 @@
- page_context_word = type.to_s.humanize(capitalize: false)
- display_count = local_assigns.fetch(:display_count, true)
-%ul.nav-links.issues-state-filters.mobile-separator.nav.nav-tabs
- %li{ class: active_when(params[:state] == 'opened') }>
- = link_to page_filter_path(state: 'opened'), id: 'state-opened', title: _("Filter by %{page_context_word} that are currently open.") % { page_context_word: page_context_word }, data: { state: 'opened' } do
- #{issuables_state_counter_text(type, :opened, display_count)}
-
+= gl_tabs_nav({ class: 'issues-state-filters gl-border-b-0 gl-flex-grow-1' }) do
+ = gl_tab_link_to page_filter_path(state: 'opened'), { item_active: params[:state] == 'opened', id: 'state-opened', title: _("Filter by %{page_context_word} that are currently open.") % { page_context_word: page_context_word }, data: { state: 'opened' } } do
+ #{issuables_state_counter_text(type, :opened, display_count)}
- if type == :merge_requests
- %li{ class: active_when(params[:state] == 'merged') }>
- = link_to page_filter_path(state: 'merged'), id: 'state-merged', title: _('Filter by merge requests that are currently merged.'), data: { state: 'merged' } do
- #{issuables_state_counter_text(type, :merged, display_count)}
-
- %li{ class: active_when(params[:state] == 'closed') }>
- = link_to page_filter_path(state: 'closed'), id: 'state-closed', title: _('Filter by merge requests that are currently closed and unmerged.'), data: { state: 'closed' } do
- #{issuables_state_counter_text(type, :closed, display_count)}
+ = gl_tab_link_to page_filter_path(state: 'merged'), item_active: params[:state] == 'merged', id: 'state-merged', title: _('Filter by merge requests that are currently merged.'), data: { state: 'merged' } do
+ #{issuables_state_counter_text(type, :merged, display_count)}
+ = gl_tab_link_to page_filter_path(state: 'closed'), item_active: params[:state] == 'closed', id: 'state-closed', title: _('Filter by merge requests that are currently closed and unmerged.'), data: { state: 'closed' } do
+ #{issuables_state_counter_text(type, :closed, display_count)}
- else
- %li{ class: active_when(params[:state] == 'closed') }>
- = link_to page_filter_path(state: 'closed'), id: 'state-closed', title: _('Filter by issues that are currently closed.'), data: { state: 'closed', qa_selector: 'closed_issues_link' } do
- #{issuables_state_counter_text(type, :closed, display_count)}
+ = gl_tab_link_to page_filter_path(state: 'closed'), item_active: params[:state] == 'closed', id: 'state-closed', title: _('Filter by issues that are currently closed.'), data: { state: 'closed', qa_selector: 'closed_issues_link' } do
+ #{issuables_state_counter_text(type, :closed, display_count)}
= render 'shared/issuable/nav_links/all', page_context_word: page_context_word, counter: issuables_state_counter_text(type, :all, display_count)
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index e6c4b3f481..81a7581d39 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -19,7 +19,7 @@
- if params[:search].present?
= hidden_field_tag :search, params[:search]
- if @can_bulk_update
- .check-all-holder.d-none.d-sm-block.hidden
+ .check-all-holder.gl-display-none.gl-sm-display-block.hidden.gl-float-left.gl-mr-5.gl-line-height-36
- checkbox_id = 'check-all-issues'
%label.gl-sr-only{ for: checkbox_id }= _('Select all')
= check_box_tag checkbox_id, nil, false, class: "check-all-issues left"
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 1e8724c344..62539bfeff 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -7,6 +7,7 @@
- can_edit_issuable = issuable_sidebar.dig(:current_user, :can_edit)
- add_page_startup_api_call "#{issuable_sidebar[:issuable_json_path]}?serializer=sidebar_extras"
- reviewers = local_assigns.fetch(:reviewers, nil)
+- in_group_context_with_iterations = @project.group.present? && issuable_sidebar[:supports_iterations]
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite', 'aria-label': issuable_type }
.issuable-sidebar
@@ -28,11 +29,11 @@
= render_if_exists 'shared/issuable/sidebar_item_epic', issuable_sidebar: issuable_sidebar, group_path: @project.group.full_path, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid], issuable_type: issuable_type
- if issuable_sidebar[:supports_milestone]
- .block.milestone{ :class => ("gl-border-b-0!" if issuable_sidebar[:supports_iterations]), data: { qa_selector: 'milestone_block' } }
+ .block.milestone{ :class => ("gl-border-b-0!" if in_group_context_with_iterations), data: { qa_selector: 'milestone_block' } }
.js-milestone-select{ data: { can_edit: can_edit_issuable.to_s, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid] } }
- - if @project.group.present? && issuable_sidebar[:supports_iterations]
- .block{ class: 'gl-pt-0!', data: { qa_selector: 'iteration_container' } }
+ - if in_group_context_with_iterations
+ .block{ class: 'gl-pt-0! gl-collapse-empty', data: { qa_selector: 'iteration_container', testid: 'iteration_container' } }<
= render_if_exists 'shared/issuable/iteration_select', can_edit: can_edit_issuable.to_s, group_path: @project.group.full_path, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid], issuable_type: issuable_type
- if issuable_sidebar[:supports_time_tracking]
@@ -55,11 +56,13 @@
.js-sidebar-status-entry-point{ data: sidebar_status_data(issuable_sidebar, @project) }
- if issuable_sidebar.has_key?(:confidential)
+ -# haml-lint:disable InlineJavaScript
%script#js-confidential-issue-data{ type: "application/json" }= { is_confidential: issuable_sidebar[:confidential], is_editable: can_edit_issuable }.to_json.html_safe
#js-confidential-entry-point
= render_if_exists 'shared/issuable/sidebar_cve_id_request', issuable_sidebar: issuable_sidebar
+ -# haml-lint:disable InlineJavaScript
%script#js-lock-issue-data{ type: "application/json" }= { is_locked: !!issuable_sidebar[:discussion_locked], is_editable: can_edit_issuable }.to_json.html_safe
#js-lock-entry-point
@@ -72,7 +75,7 @@
#js-reference-entry-point
- if issuable_type == 'merge_request'
.sub-block.js-sidebar-source-branch
- .sidebar-collapsed-icon.dont-change-state
+ .sidebar-collapsed-icon.js-dont-change-state
= clipboard_button(text: source_branch, title: _('Copy branch name'), placement: "left", boundary: 'viewport')
.gl-display-flex.gl-align-items-center.gl-justify-content-space-between.gl-mb-2.hide-collapsed
%span.gl-overflow-hidden.gl-text-overflow-ellipsis.gl-white-space-nowrap
diff --git a/app/views/shared/issuable/nav_links/_all.html.haml b/app/views/shared/issuable/nav_links/_all.html.haml
index c92a50bcb7..7afa194d5d 100644
--- a/app/views/shared/issuable/nav_links/_all.html.haml
+++ b/app/views/shared/issuable/nav_links/_all.html.haml
@@ -1,6 +1,5 @@
- page_context_word = local_assigns.fetch(:page_context_word)
- counter = local_assigns.fetch(:counter)
-%li{ class: active_when(params[:state] == 'all') }>
- = link_to page_filter_path(state: 'all'), id: 'state-all', title: "Show all #{page_context_word}.", data: { state: 'all' } do
- #{counter}
+= gl_tab_link_to page_filter_path(state: 'all'), item_active: params[:state] == 'all', id: 'state-all', title: _("Show all %{issuable_type}.") % { issuable_type: page_context_word }, data: { state: 'all' } do
+ #{counter}
diff --git a/app/views/shared/issue_type/_emoji_block.html.haml b/app/views/shared/issue_type/_emoji_block.html.haml
index 26d3034199..d2c851a4e4 100644
--- a/app/views/shared/issue_type/_emoji_block.html.haml
+++ b/app/views/shared/issue_type/_emoji_block.html.haml
@@ -4,7 +4,7 @@
.row.gl-m-0.gl-justify-content-space-between
.js-noteable-awards
= render 'award_emoji/awards_block', awardable: issuable, inline: true, api_awards_path: api_awards_path
- .new-branch-col.gl-my-2
+ .new-branch-col.gl-my-2.gl-font-size-0
= render_if_exists "projects/issues/timeline_toggle", issuable: issuable
#js-vue-sort-issue-discussions
#js-vue-discussion-filter{ data: { default_filter: current_user&.notes_filter_for(issuable), notes_filters: UserPreference.notes_filters.to_json } }
diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml
index 56b2b0d580..c66ba5ba2e 100644
--- a/app/views/shared/milestones/_sidebar.html.haml
+++ b/app/views/shared/milestones/_sidebar.html.haml
@@ -161,11 +161,11 @@
- milestone_ref = milestone.try(:to_reference, full: true)
- if milestone_ref.present?
.block.reference
- .sidebar-collapsed-icon.dont-change-state
+ .sidebar-collapsed-icon.js-dont-change-state
= clipboard_button(text: milestone_ref, title: s_('MilestoneSidebar|Copy reference'), placement: "left", boundary: 'viewport')
.cross-project-reference.hide-collapsed
- %span
+ %span.gl-display-inline-block.gl-text-truncate
= s_('MilestoneSidebar|Reference:')
%span{ title: milestone_ref }
= milestone_ref
- = clipboard_button(text: milestone_ref, title: s_('MilestoneSidebar|Copy reference'), placement: "left", boundary: 'viewport')
+ = clipboard_button(text: milestone_ref, title: s_('MilestoneSidebar|Copy reference'), placement: "left", boundary: 'viewport', class: 'btn-clipboard btn-transparent gl-float-right gl-bg-gray-10')
diff --git a/app/views/shared/notes/_comment_button.html.haml b/app/views/shared/notes/_comment_button.html.haml
index d0a2d97df0..3e880a36e2 100644
--- a/app/views/shared/notes/_comment_button.html.haml
+++ b/app/views/shared/notes/_comment_button.html.haml
@@ -1,31 +1,4 @@
- noteable_name = @note.noteable.human_class_name
-.float-left.btn-group.gl-sm-mr-3.droplab-dropdown.comment-type-dropdown.js-comment-type-dropdown
+.js-comment-type-dropdown.float-left.gl-sm-mr-3{ data: { noteable_name: noteable_name } }
%input.btn.gl-button.btn-confirm.js-comment-button.js-comment-submit-button{ type: 'submit', value: _('Comment'), data: { qa_selector: 'comment_button' } }
-
- - if @note.can_be_discussion_note?
- = button_tag type: 'button', class: 'gl-button btn dropdown-toggle btn-confirm js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => _('Open comment type dropdown') do
- = sprite_icon('chevron-down')
-
- %ul#resolvable-comment-menu.dropdown-menu.dropdown-open-top{ data: { dropdown: true } }
- %li#comment.droplab-item-selected{ data: { value: '', 'submit-text' => _('Comment'), 'close-text' => _("Comment & close %{noteable_name}") % { noteable_name: noteable_name }, 'reopen-text' => _("Comment & reopen %{noteable_name}") % { noteable_name: noteable_name } } }
- %button.btn.gl-button.btn-default-tertiary
- = sprite_icon('check', css_class: 'icon')
- .description
- %strong= _("Comment")
- %p
- = _("Add a general comment to this %{noteable_name}.") % { noteable_name: noteable_name }
-
- %li.divider.droplab-item-ignore
-
- %li#discussion{ data: { value: 'DiscussionNote', 'submit-text' => _('Start thread'), 'close-text' => _("Start thread & close %{noteable_name}") % { noteable_name: noteable_name }, 'reopen-text' => _("Start thread & reopen %{noteable_name}") % { noteable_name: noteable_name } } }
- %button.btn.gl-button.btn-default-tertiary
- = sprite_icon('check', css_class: 'icon')
- .description
- %strong= _("Start thread")
- %p
- = succeed '.' do
- - if @note.noteable.supports_resolvable_notes?
- = _('Discuss a specific suggestion or question that needs to be resolved')
- - else
- = _('Discuss a specific suggestion or question')
diff --git a/app/views/shared/notes/_hints.html.haml b/app/views/shared/notes/_hints.html.haml
index a03e8446f5..6231f81770 100644
--- a/app/views/shared/notes/_hints.html.haml
+++ b/app/views/shared/notes/_hints.html.haml
@@ -1,4 +1,5 @@
- supports_quick_actions = local_assigns.fetch(:supports_quick_actions, false)
+- supports_file_upload = local_assigns.fetch(:supports_file_upload, true)
.comment-toolbar.clearfix
.toolbar-text
= link_to _('Markdown'), help_page_path('user/markdown'), target: '_blank'
@@ -10,33 +11,34 @@
is
supported
- %span.uploading-container
- %span.uploading-progress-container.hide
- = sprite_icon('media', css_class: 'gl-icon gl-vertical-align-text-bottom')
- %span.attaching-file-message
- -# Populated by app/assets/javascripts/dropzone_input.js
- %span.uploading-progress 0%
- = loading_icon(css_class: 'align-text-bottom gl-mr-2')
-
- %span.uploading-error-container.hide
- %span.uploading-error-icon
+ - if supports_file_upload
+ %span.uploading-container
+ %span.uploading-progress-container.hide
= sprite_icon('media', css_class: 'gl-icon gl-vertical-align-text-bottom')
- %span.uploading-error-message
- -# Populated by app/assets/javascripts/dropzone_input.js
- %button.btn.gl-button.btn-link.gl-vertical-align-baseline.retry-uploading-link
- %span.gl-button-text
- = _("Try again")
- = _("or")
- %button.btn.gl-button.btn-link.attach-new-file.markdown-selector.gl-vertical-align-baseline
- %span.gl-button-text
- = _("attach a new file")
- = _(".")
+ %span.attaching-file-message
+ -# Populated by app/assets/javascripts/dropzone_input.js
+ %span.uploading-progress 0%
+ = loading_icon(css_class: 'align-text-bottom gl-mr-2')
- %button.btn.gl-button.btn-link.button-attach-file.markdown-selector.button-attach-file.gl-vertical-align-text-bottom
- = sprite_icon('media')
- %span.gl-button-text
- = _("Attach a file")
+ %span.uploading-error-container.hide
+ %span.uploading-error-icon
+ = sprite_icon('media', css_class: 'gl-icon gl-vertical-align-text-bottom')
+ %span.uploading-error-message
+ -# Populated by app/assets/javascripts/dropzone_input.js
+ %button.btn.gl-button.btn-link.gl-vertical-align-baseline.retry-uploading-link
+ %span.gl-button-text
+ = _("Try again")
+ = _("or")
+ %button.btn.gl-button.btn-link.attach-new-file.markdown-selector.gl-vertical-align-baseline
+ %span.gl-button-text
+ = _("attach a new file")
+ = _(".")
- %button.btn.gl-button.btn-link.button-cancel-uploading-files.gl-vertical-align-baseline.hide
- %span.gl-button-text
- = _("Cancel")
+ %button.btn.gl-button.btn-link.button-attach-file.markdown-selector.button-attach-file.gl-vertical-align-text-bottom
+ = sprite_icon('media')
+ %span.gl-button-text
+ = _("Attach a file")
+
+ %button.btn.gl-button.btn-link.button-cancel-uploading-files.gl-vertical-align-baseline.hide
+ %span.gl-button-text
+ = _("Cancel")
diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml
index f7f5c02370..e34f412baa 100644
--- a/app/views/shared/notes/_notes_with_form.html.haml
+++ b/app/views/shared/notes/_notes_with_form.html.haml
@@ -25,4 +25,5 @@
= sprite_icon('lock', css_class: 'icon')
%span
= html_escape(_("This %{issuable} is locked. Only %{strong_open}project members%{strong_close} can comment.")) % { issuable: issuable.class.to_s.titleize.downcase, strong_open: ''.html_safe, strong_close: ''.html_safe }
+-# haml-lint:disable InlineJavaScript
%script.js-notes-data{ type: "application/json" }= initial_notes_data(autocomplete).to_json.html_safe
diff --git a/app/views/shared/topics/_search_form.html.haml b/app/views/shared/topics/_search_form.html.haml
new file mode 100644
index 0000000000..97343983b3
--- /dev/null
+++ b/app/views/shared/topics/_search_form.html.haml
@@ -0,0 +1,7 @@
+= form_tag page_filter_path, method: :get, class: "topic-filter-form js-topic-filter-form", id: 'topic-filter-form' do |f|
+ = search_field_tag :search, params[:search],
+ placeholder: s_('Filter by name'),
+ class: 'topic-filter-form-field form-control input-short',
+ spellcheck: false,
+ id: 'topic-filter-form-field',
+ autofocus: local_assigns[:autofocus]
diff --git a/app/views/shared/web_hooks/_hook.html.haml b/app/views/shared/web_hooks/_hook.html.haml
index abe23d0be7..fd124c2967 100644
--- a/app/views/shared/web_hooks/_hook.html.haml
+++ b/app/views/shared/web_hooks/_hook.html.haml
@@ -5,12 +5,12 @@
%div
- hook.class.triggers.each_value do |trigger|
- if hook.public_send(trigger)
- %span.gl-badge.gl-bg-gray-10.gl-mt-2.rounded.deploy-project-label= trigger.to_s.titleize
- %span.gl-badge.gl-bg-gray-10.gl-mt-2.rounded
+ %span.gl-badge.badge-muted.badge-pill.sm.gl-mt-2.deploy-project-label= trigger.to_s.titleize
+ %span.gl-badge.badge-muted.badge-pill.sm.gl-mt-2
= _('SSL Verification:')
= hook.enable_ssl_verification ? _('enabled') : _('disabled')
.col-md-4.col-lg-5.text-right-md.gl-mt-2
%span>= render 'shared/web_hooks/test_button', hook: hook, button_class: 'btn-sm btn-default gl-mr-3'
%span>= link_to _('Edit'), edit_hook_path(hook), class: 'btn gl-button btn-default btn-sm gl-mr-3'
- = link_to _('Delete'), destroy_hook_path(hook), data: { confirm: _('Are you sure?') }, method: :delete, class: 'btn gl-button btn-default btn-sm'
+ = link_to _('Delete'), destroy_hook_path(hook), data: { confirm: _('Are you sure?') }, method: :delete, class: 'btn gl-button btn-secondary btn-danger-secondary btn-sm'
diff --git a/app/views/shared/wikis/edit.html.haml b/app/views/shared/wikis/edit.html.haml
index 15710f0df4..e0860bc473 100644
--- a/app/views/shared/wikis/edit.html.haml
+++ b/app/views/shared/wikis/edit.html.haml
@@ -4,7 +4,7 @@
- if @error
#js-wiki-error{ data: { error: @error, wiki_page_path: wiki_page_path(@wiki, @page) } }
-.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
+.js-wiki-edit-page.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
= wiki_sidebar_toggle_button
%h3.page-title.gl-flex-grow-1
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index ca52a1f8f4..f1093a3b73 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -12,7 +12,7 @@
- content_for :prefetch_asset_tags do
- webpack_preload_asset_tag('monaco', prefetch: true)
-#js-snippet-view{ data: {'qa-selector': 'snippet_view', 'snippet-gid': @snippet.to_global_id, 'report-abuse-path': snippet_report_abuse_path(@snippet) } }
+#js-snippet-view{ data: {'qa-selector': 'snippet_view', 'snippet-gid': @snippet.to_global_id, 'report-abuse-path': snippet_report_abuse_path(@snippet), 'can-report-spam': @snippet.submittable_as_spam_by?(current_user).to_s } }
.row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 20cbe08225..522f0f771c 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -84,10 +84,12 @@
= sprite_icon('location', css_class: 'fgray')
%span{ itemprop: 'addressLocality' }
= @user.location
- = render 'middle_dot_divider', stacking: true do
- = sprite_icon('clock', css_class: 'fgray')
- %span
- = local_time(@user.timezone)
+ - user_local_time = local_time(@user.timezone)
+ - unless user_local_time.nil?
+ = render 'middle_dot_divider', stacking: true, data: { testid: 'user-local-time' } do
+ = sprite_icon('clock', css_class: 'fgray')
+ %span
+ = user_local_time
- unless work_information(@user).blank?
= render 'middle_dot_divider', stacking: true do
= sprite_icon('work', css_class: 'fgray')
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 955674b52a..c7ce2eb8d0 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -30,6 +30,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: authorized_project_update:authorized_project_update_project_recalculate_per_user
+ :worker_name: AuthorizedProjectUpdate::ProjectRecalculatePerUserWorker
+ :feature_category: :authentication_and_authorization
+ :has_external_dependencies:
+ :urgency: :high
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: authorized_project_update:authorized_project_update_user_refresh_from_replica
:worker_name: AuthorizedProjectUpdate::UserRefreshFromReplicaWorker
:feature_category: :authentication_and_authorization
@@ -46,7 +55,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
- :idempotent:
+ :idempotent: true
:tags: []
- :name: authorized_project_update:authorized_project_update_user_refresh_with_low_urgency
:worker_name: AuthorizedProjectUpdate::UserRefreshWithLowUrgencyWorker
@@ -185,7 +194,7 @@
:tags: []
- :name: cronjob:ci_delete_unit_tests
:worker_name: Ci::DeleteUnitTestsWorker
- :feature_category: :continuous_integration
+ :feature_category: :code_testing
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -194,7 +203,7 @@
:tags: []
- :name: cronjob:ci_pipeline_artifacts_expire_artifacts
:worker_name: Ci::PipelineArtifacts::ExpireArtifactsWorker
- :feature_category: :continuous_integration
+ :feature_category: :build_artifacts
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -219,6 +228,24 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: cronjob:ci_stuck_builds_drop_running
+ :worker_name: Ci::StuckBuilds::DropRunningWorker
+ :feature_category: :continuous_integration
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
+- :name: cronjob:ci_stuck_builds_drop_scheduled
+ :worker_name: Ci::StuckBuilds::DropScheduledWorker
+ :feature_category: :continuous_integration
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: cronjob:container_expiration_policy
:worker_name: ContainerExpirationPolicyWorker
:feature_category: :container_registry
@@ -255,6 +282,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: cronjob:dependency_proxy_image_ttl_group_policy
+ :worker_name: DependencyProxy::ImageTtlGroupPolicyWorker
+ :feature_category: :dependency_proxy
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
- :name: cronjob:environments_auto_delete_cron
:worker_name: Environments::AutoDeleteCronWorker
:feature_category: :continuous_delivery
@@ -275,7 +311,7 @@
:tags: []
- :name: cronjob:expire_build_artifacts
:worker_name: ExpireBuildArtifactsWorker
- :feature_category: :continuous_integration
+ :feature_category: :build_artifacts
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -347,7 +383,7 @@
:tags: []
- :name: cronjob:namespaces_in_product_marketing_emails
:worker_name: Namespaces::InProductMarketingEmailsWorker
- :feature_category: :subgroups
+ :feature_category: :experimentation_activation
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -557,7 +593,7 @@
:feature_category: :continuous_integration
:has_external_dependencies:
:urgency: :low
- :resource_boundary: :cpu
+ :resource_boundary: :unknown
:weight: 1
:idempotent:
:tags: []
@@ -642,6 +678,24 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: dependency_proxy_blob:dependency_proxy_cleanup_blob
+ :worker_name: DependencyProxy::CleanupBlobWorker
+ :feature_category: :dependency_proxy
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
+- :name: dependency_proxy_manifest:dependency_proxy_cleanup_manifest
+ :worker_name: DependencyProxy::CleanupManifestWorker
+ :feature_category: :dependency_proxy
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: deployment:deployments_drop_older_deployments
:worker_name: Deployments::DropOlderDeploymentsWorker
:feature_category: :continuous_delivery
@@ -1418,7 +1472,7 @@
:urgency: :high
:resource_boundary: :unknown
:weight: 3
- :idempotent:
+ :idempotent: true
:tags: []
- :name: pipeline_cache:expire_pipeline_cache
:worker_name: ExpirePipelineCacheWorker
@@ -1474,6 +1528,15 @@
:weight: 3
:idempotent:
:tags: []
+- :name: pipeline_default:ci_create_downstream_pipeline
+ :worker_name: Ci::CreateDownstreamPipelineWorker
+ :feature_category: :continuous_integration
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :cpu
+ :weight: 3
+ :idempotent:
+ :tags: []
- :name: pipeline_default:ci_drop_pipeline
:worker_name: Ci::DropPipelineWorker
:feature_category: :continuous_integration
@@ -1890,7 +1953,7 @@
:tags: []
- :name: default
:worker_name:
- :feature_category:
+ :feature_category: :not_owned
:has_external_dependencies:
:urgency:
:resource_boundary:
@@ -2044,7 +2107,7 @@
:tags: []
- :name: expire_build_instance_artifacts
:worker_name: ExpireBuildInstanceArtifactsWorker
- :feature_category: :continuous_integration
+ :feature_category: :build_artifacts
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -2215,7 +2278,7 @@
:tags: []
- :name: mailers
:worker_name: ActionMailer::MailDeliveryJob
- :feature_category: :issue_tracking
+ :feature_category: :not_owned
:has_external_dependencies:
:urgency: low
:resource_boundary:
@@ -2314,7 +2377,7 @@
:tags: []
- :name: namespaces_onboarding_issue_created
:worker_name: Namespaces::OnboardingIssueCreatedWorker
- :feature_category: :issue_tracking
+ :feature_category: :onboarding
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -2323,7 +2386,7 @@
:tags: []
- :name: namespaces_onboarding_pipeline_created
:worker_name: Namespaces::OnboardingPipelineCreatedWorker
- :feature_category: :subgroups
+ :feature_category: :onboarding
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -2332,7 +2395,7 @@
:tags: []
- :name: namespaces_onboarding_progress
:worker_name: Namespaces::OnboardingProgressWorker
- :feature_category: :product_analytics
+ :feature_category: :onboarding
:has_external_dependencies:
:urgency: :low
:resource_boundary: :cpu
@@ -2341,7 +2404,7 @@
:tags: []
- :name: namespaces_onboarding_user_added
:worker_name: Namespaces::OnboardingUserAddedWorker
- :feature_category: :users
+ :feature_category: :onboarding
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -2411,15 +2474,6 @@
:weight: 1
:idempotent:
:tags: []
-- :name: pages_remove
- :worker_name: PagesRemoveWorker
- :feature_category: :pages
- :has_external_dependencies:
- :urgency: :low
- :resource_boundary: :unknown
- :weight: 1
- :idempotent:
- :tags: []
- :name: pages_transfer
:worker_name: PagesTransferWorker
:feature_category: :pages
diff --git a/app/workers/authorized_project_update/project_recalculate_per_user_worker.rb b/app/workers/authorized_project_update/project_recalculate_per_user_worker.rb
new file mode 100644
index 0000000000..352c82e502
--- /dev/null
+++ b/app/workers/authorized_project_update/project_recalculate_per_user_worker.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module AuthorizedProjectUpdate
+ class ProjectRecalculatePerUserWorker < ProjectRecalculateWorker
+ data_consistency :always
+
+ feature_category :authentication_and_authorization
+ urgency :high
+ queue_namespace :authorized_project_update
+
+ deduplicate :until_executing, including_scheduled: true
+ idempotent!
+
+ def perform(project_id, user_id)
+ project = Project.find_by_id(project_id)
+ user = User.find_by_id(user_id)
+
+ return unless project && user
+
+ in_lock(lock_key(project), ttl: 10.seconds) do
+ AuthorizedProjectUpdate::ProjectRecalculatePerUserService.new(project, user).execute
+ end
+ end
+ end
+end
diff --git a/app/workers/authorized_project_update/project_recalculate_worker.rb b/app/workers/authorized_project_update/project_recalculate_worker.rb
index 4d350d95e7..3d073f1862 100644
--- a/app/workers/authorized_project_update/project_recalculate_worker.rb
+++ b/app/workers/authorized_project_update/project_recalculate_worker.rb
@@ -26,7 +26,9 @@ module AuthorizedProjectUpdate
private
def lock_key(project)
- "#{self.class.name.underscore}/projects/#{project.id}"
+ # The self.class.name.underscore value is hardcoded here as the prefix, so that the same
+ # lock_key for this superclass will be used by the ProjectRecalculatePerUserWorker subclass.
+ "authorized_project_update/project_recalculate_worker/projects/#{project.id}"
end
end
end
diff --git a/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb b/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb
index 48e3d0837c..daebb23baa 100644
--- a/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb
+++ b/app/workers/authorized_project_update/user_refresh_from_replica_worker.rb
@@ -30,8 +30,6 @@ module AuthorizedProjectUpdate
# does not allow us to deduplicate these jobs.
# https://gitlab.com/gitlab-org/gitlab/-/issues/325291
def use_replica_if_available(&block)
- return yield unless ::Gitlab::Database::LoadBalancing.enable?
-
::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries(&block)
end
diff --git a/app/workers/authorized_project_update/user_refresh_over_user_range_worker.rb b/app/workers/authorized_project_update/user_refresh_over_user_range_worker.rb
index ab4d9c1342..f532744924 100644
--- a/app/workers/authorized_project_update/user_refresh_over_user_range_worker.rb
+++ b/app/workers/authorized_project_update/user_refresh_over_user_range_worker.rb
@@ -19,11 +19,10 @@ module AuthorizedProjectUpdate
feature_category :authentication_and_authorization
urgency :low
queue_namespace :authorized_project_update
- # This job will not be deduplicated since it is marked with
- # `data_consistency :delayed` and not `idempotent!`
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/325291
+
deduplicate :until_executing, including_scheduled: true
data_consistency :delayed
+ idempotent!
def perform(start_user_id, end_user_id)
User.where(id: start_user_id..end_user_id).find_each do |user| # rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/workers/bulk_import_worker.rb b/app/workers/bulk_import_worker.rb
index fa255d064c..d560ebcc6e 100644
--- a/app/workers/bulk_import_worker.rb
+++ b/app/workers/bulk_import_worker.rb
@@ -26,7 +26,7 @@ class BulkImportWorker # rubocop:disable Scalability/IdempotentWorker
created_entities.first(next_batch_size).each do |entity|
entity.create_pipeline_trackers!
- BulkImports::ExportRequestWorker.perform_async(entity.id) if entity.group_entity?
+ BulkImports::ExportRequestWorker.perform_async(entity.id)
BulkImports::EntityWorker.perform_async(entity.id)
entity.start!
diff --git a/app/workers/bulk_imports/export_request_worker.rb b/app/workers/bulk_imports/export_request_worker.rb
index d5f7215b08..8bc0acc9b2 100644
--- a/app/workers/bulk_imports/export_request_worker.rb
+++ b/app/workers/bulk_imports/export_request_worker.rb
@@ -10,8 +10,6 @@ module BulkImports
worker_has_external_dependencies!
feature_category :importers
- GROUP_EXPORTED_URL_PATH = "/groups/%s/export_relations"
-
def perform(entity_id)
entity = BulkImports::Entity.find(entity_id)
@@ -21,8 +19,7 @@ module BulkImports
private
def request_export(entity)
- http_client(entity.bulk_import.configuration)
- .post(GROUP_EXPORTED_URL_PATH % entity.encoded_source_full_path)
+ http_client(entity.bulk_import.configuration).post(entity.export_relations_url_path)
end
def http_client(configuration)
diff --git a/app/workers/bulk_imports/pipeline_worker.rb b/app/workers/bulk_imports/pipeline_worker.rb
index 760a309a38..35633b5548 100644
--- a/app/workers/bulk_imports/pipeline_worker.rb
+++ b/app/workers/bulk_imports/pipeline_worker.rb
@@ -16,7 +16,7 @@ module BulkImports
def perform(pipeline_tracker_id, stage, entity_id)
pipeline_tracker = ::BulkImports::Tracker
- .with_status(:created)
+ .with_status(:created, :started)
.find_by_id(pipeline_tracker_id)
if pipeline_tracker.present?
@@ -59,18 +59,35 @@ module BulkImports
pipeline_tracker.pipeline_class.new(context).run
pipeline_tracker.finish!
+ rescue BulkImports::NetworkError => e
+ if e.retriable?(pipeline_tracker)
+ logger.error(
+ worker: self.class.name,
+ entity_id: pipeline_tracker.entity.id,
+ pipeline_name: pipeline_tracker.pipeline_name,
+ message: "Retrying error: #{e.message}"
+ )
+
+ reenqueue(pipeline_tracker, delay: e.retry_delay)
+ else
+ fail_tracker(pipeline_tracker, e)
+ end
rescue StandardError => e
+ fail_tracker(pipeline_tracker, e)
+ end
+
+ def fail_tracker(pipeline_tracker, exception)
pipeline_tracker.update!(status_event: 'fail_op', jid: jid)
logger.error(
worker: self.class.name,
entity_id: pipeline_tracker.entity.id,
pipeline_name: pipeline_tracker.pipeline_name,
- message: e.message
+ message: exception.message
)
Gitlab::ErrorTracking.track_exception(
- e,
+ exception,
entity_id: pipeline_tracker.entity.id,
pipeline_name: pipeline_tracker.pipeline_name
)
@@ -88,8 +105,13 @@ module BulkImports
(Time.zone.now - pipeline_tracker.entity.created_at) > Pipeline::NDJSON_EXPORT_TIMEOUT
end
- def reenqueue(pipeline_tracker)
- self.class.perform_in(NDJSON_PIPELINE_PERFORM_DELAY, pipeline_tracker.id, pipeline_tracker.stage, pipeline_tracker.entity.id)
+ def reenqueue(pipeline_tracker, delay: NDJSON_PIPELINE_PERFORM_DELAY)
+ self.class.perform_in(
+ delay,
+ pipeline_tracker.id,
+ pipeline_tracker.stage,
+ pipeline_tracker.entity.id
+ )
end
end
end
diff --git a/app/workers/ci/build_finished_worker.rb b/app/workers/ci/build_finished_worker.rb
index 3bca301598..f047ba8fde 100644
--- a/app/workers/ci/build_finished_worker.rb
+++ b/app/workers/ci/build_finished_worker.rb
@@ -15,13 +15,13 @@ module Ci
ARCHIVE_TRACES_IN = 2.minutes.freeze
- # rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
- Ci::Build.find_by(id: build_id).try do |build|
- process_build(build)
- end
+ return unless build = Ci::Build.find_by(id: build_id) # rubocop: disable CodeReuse/ActiveRecord
+ return unless build.project
+ return if build.project.pending_delete?
+
+ process_build(build)
end
- # rubocop: enable CodeReuse/ActiveRecord
private
diff --git a/app/workers/ci/create_downstream_pipeline_worker.rb b/app/workers/ci/create_downstream_pipeline_worker.rb
new file mode 100644
index 0000000000..6d4cd2539c
--- /dev/null
+++ b/app/workers/ci/create_downstream_pipeline_worker.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Ci
+ class CreateDownstreamPipelineWorker # rubocop:disable Scalability/IdempotentWorker
+ include ::ApplicationWorker
+ include ::PipelineQueue
+
+ sidekiq_options retry: 3
+ worker_resource_boundary :cpu
+
+ def perform(bridge_id)
+ ::Ci::Bridge.find_by_id(bridge_id).try do |bridge|
+ ::Ci::CreateDownstreamPipelineService
+ .new(bridge.project, bridge.user)
+ .execute(bridge)
+ end
+ end
+ end
+end
diff --git a/app/workers/ci/delete_unit_tests_worker.rb b/app/workers/ci/delete_unit_tests_worker.rb
index d5bb72ce80..01d909773d 100644
--- a/app/workers/ci/delete_unit_tests_worker.rb
+++ b/app/workers/ci/delete_unit_tests_worker.rb
@@ -10,7 +10,7 @@ module Ci
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext
- feature_category :continuous_integration
+ feature_category :code_testing
idempotent!
def perform
diff --git a/app/workers/ci/pipeline_artifacts/expire_artifacts_worker.rb b/app/workers/ci/pipeline_artifacts/expire_artifacts_worker.rb
index 2af07cf6f9..cde3d71286 100644
--- a/app/workers/ci/pipeline_artifacts/expire_artifacts_worker.rb
+++ b/app/workers/ci/pipeline_artifacts/expire_artifacts_worker.rb
@@ -14,7 +14,7 @@ module Ci
deduplicate :until_executed, including_scheduled: true
idempotent!
- feature_category :continuous_integration
+ feature_category :build_artifacts
def perform
service = ::Ci::PipelineArtifacts::DestroyAllExpiredService.new
diff --git a/app/workers/ci/stuck_builds/drop_running_worker.rb b/app/workers/ci/stuck_builds/drop_running_worker.rb
new file mode 100644
index 0000000000..db571fdc38
--- /dev/null
+++ b/app/workers/ci/stuck_builds/drop_running_worker.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Ci
+ module StuckBuilds
+ class DropRunningWorker
+ include ApplicationWorker
+ include ExclusiveLeaseGuard
+
+ idempotent!
+
+ # rubocop:disable Scalability/CronWorkerContext
+ # This is an instance-wide cleanup query, so there's no meaningful
+ # scope to consider this in the context of.
+ include CronjobQueue
+ # rubocop:enable Scalability/CronWorkerContext
+
+ data_consistency :always
+
+ feature_category :continuous_integration
+
+ def perform
+ try_obtain_lease do
+ Ci::StuckBuilds::DropRunningService.new.execute
+ end
+ end
+
+ private
+
+ def lease_timeout
+ 30.minutes
+ end
+ end
+ end
+end
diff --git a/app/workers/ci/stuck_builds/drop_scheduled_worker.rb b/app/workers/ci/stuck_builds/drop_scheduled_worker.rb
new file mode 100644
index 0000000000..923841771c
--- /dev/null
+++ b/app/workers/ci/stuck_builds/drop_scheduled_worker.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Ci
+ module StuckBuilds
+ class DropScheduledWorker
+ include ApplicationWorker
+ include ExclusiveLeaseGuard
+
+ idempotent!
+
+ # rubocop:disable Scalability/CronWorkerContext
+ # This is an instance-wide cleanup query, so there's no meaningful
+ # scope to consider this in the context of.
+ include CronjobQueue
+ # rubocop:enable Scalability/CronWorkerContext
+
+ data_consistency :always
+
+ feature_category :continuous_integration
+
+ def perform
+ try_obtain_lease do
+ Ci::StuckBuilds::DropScheduledService.new.execute
+ end
+ end
+
+ private
+
+ def lease_timeout
+ 30.minutes
+ end
+ end
+ end
+end
diff --git a/app/workers/cleanup_container_repository_worker.rb b/app/workers/cleanup_container_repository_worker.rb
index 9adc026ced..7274ecf62f 100644
--- a/app/workers/cleanup_container_repository_worker.rb
+++ b/app/workers/cleanup_container_repository_worker.rb
@@ -28,8 +28,8 @@ class CleanupContainerRepositoryWorker
end
result = Projects::ContainerRepository::CleanupTagsService
- .new(project, current_user, params)
- .execute(container_repository)
+ .new(container_repository, current_user, params)
+ .execute
if run_by_container_expiration_policy? && result[:status] == :success
container_repository.reset_expiration_policy_started_at!
diff --git a/app/workers/concerns/dependency_proxy/cleanup_worker.rb b/app/workers/concerns/dependency_proxy/cleanup_worker.rb
new file mode 100644
index 0000000000..b668634f23
--- /dev/null
+++ b/app/workers/concerns/dependency_proxy/cleanup_worker.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ module CleanupWorker
+ extend ActiveSupport::Concern
+ include Gitlab::Utils::StrongMemoize
+
+ def perform_work
+ return unless artifact
+
+ log_metadata(artifact)
+
+ artifact.destroy!
+ rescue StandardError
+ artifact&.error!
+ end
+
+ def max_running_jobs
+ ::Gitlab::CurrentSettings.dependency_proxy_ttl_group_policy_worker_capacity
+ end
+
+ def remaining_work_count
+ expired_artifacts.limit(max_running_jobs + 1).count
+ end
+
+ private
+
+ def model
+ raise NotImplementedError
+ end
+
+ def log_metadata
+ raise NotImplementedError
+ end
+
+ def log_cleanup_item
+ raise NotImplementedError
+ end
+
+ def artifact
+ strong_memoize(:artifact) do
+ model.transaction do
+ to_delete = next_item
+
+ if to_delete
+ to_delete.processing!
+ log_cleanup_item(to_delete)
+ end
+
+ to_delete
+ end
+ end
+ end
+
+ def expired_artifacts
+ model.expired
+ end
+
+ def next_item
+ expired_artifacts.lock_next_by(:updated_at).first
+ end
+ end
+end
diff --git a/app/workers/concerns/gitlab/github_import/object_importer.rb b/app/workers/concerns/gitlab/github_import/object_importer.rb
index a377b7a200..e1f404b250 100644
--- a/app/workers/concerns/gitlab/github_import/object_importer.rb
+++ b/app/workers/concerns/gitlab/github_import/object_importer.rb
@@ -26,8 +26,7 @@ module Gitlab
object = representation_class.from_json_hash(hash)
# To better express in the logs what object is being imported.
- self.github_id = object.attributes.fetch(:github_id)
-
+ self.github_identifiers = object.github_identifiers
info(project.id, message: 'starting importer')
importer_class.new(object, project, client).execute
@@ -35,10 +34,10 @@ module Gitlab
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :imported)
info(project.id, message: 'importer finished')
- rescue KeyError => e
+ rescue NoMethodError => e
# This exception will be more useful in development when a new
# Representation is created but the developer forgot to add a
- # `:github_id` field.
+ # `:github_identifiers` field.
Gitlab::Import::ImportFailureService.track(
project_id: project.id,
error_source: importer_class.name,
@@ -72,7 +71,7 @@ module Gitlab
private
- attr_accessor :github_id
+ attr_accessor :github_identifiers
def info(project_id, extra = {})
Logger.info(log_attributes(project_id, extra))
@@ -82,7 +81,7 @@ module Gitlab
extra.merge(
project_id: project_id,
importer: importer_class.name,
- github_id: github_id
+ github_identifiers: github_identifiers
)
end
end
diff --git a/app/workers/concerns/worker_attributes.rb b/app/workers/concerns/worker_attributes.rb
index eebea30655..6f91418e38 100644
--- a/app/workers/concerns/worker_attributes.rb
+++ b/app/workers/concerns/worker_attributes.rb
@@ -46,8 +46,14 @@ module WorkerAttributes
set_class_attribute(:feature_category, :not_owned)
end
+ # Special case: if a worker is not owned, get the feature category
+ # (if present) from the calling context.
def get_feature_category
- get_class_attribute(:feature_category)
+ feature_category = get_class_attribute(:feature_category)
+
+ return feature_category unless feature_category == :not_owned
+
+ Gitlab::ApplicationContext.current_context_attribute('meta.feature_category') || feature_category
end
def feature_category_not_owned?
diff --git a/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb b/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
index 433ed5e0ea..69f5906f54 100644
--- a/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
+++ b/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
@@ -21,6 +21,7 @@ module ContainerExpirationPolicies
cleanup_tags_service_original_size
cleanup_tags_service_before_truncate_size
cleanup_tags_service_after_truncate_size
+ cleanup_tags_service_cached_tags_count
cleanup_tags_service_before_delete_size
cleanup_tags_service_deleted_size
].freeze
@@ -147,13 +148,27 @@ module ContainerExpirationPolicies
log_extra_metadata_on_done(field, value)
end
+ log_truncate(result)
+ log_cache_ratio(result)
+ log_extra_metadata_on_done(:running_jobs_count, running_jobs_count)
+ end
+
+ def log_cache_ratio(result)
+ tags_count = result.payload[:cleanup_tags_service_after_truncate_size]
+ cached_tags_count = result.payload[:cleanup_tags_service_cached_tags_count]
+
+ return unless tags_count && cached_tags_count && tags_count != 0
+
+ log_extra_metadata_on_done(:cleanup_tags_service_cache_hit_ratio, cached_tags_count / tags_count.to_f)
+ end
+
+ def log_truncate(result)
before_truncate_size = result.payload[:cleanup_tags_service_before_truncate_size]
after_truncate_size = result.payload[:cleanup_tags_service_after_truncate_size]
truncated = before_truncate_size &&
after_truncate_size &&
before_truncate_size != after_truncate_size
log_extra_metadata_on_done(:cleanup_tags_service_truncated, !!truncated)
- log_extra_metadata_on_done(:running_jobs_count, running_jobs_count)
end
def policy
diff --git a/app/workers/container_expiration_policy_worker.rb b/app/workers/container_expiration_policy_worker.rb
index a791fe5d35..5fcbd74dda 100644
--- a/app/workers/container_expiration_policy_worker.rb
+++ b/app/workers/container_expiration_policy_worker.rb
@@ -45,8 +45,6 @@ class ContainerExpirationPolicyWorker # rubocop:disable Scalability/IdempotentWo
# not perfomed with a delay
# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63635#note_603771207
def use_replica_if_available(&blk)
- return yield unless ::Gitlab::Database::LoadBalancing.enable?
-
::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries(&blk)
end
diff --git a/app/workers/create_note_diff_file_worker.rb b/app/workers/create_note_diff_file_worker.rb
index 4bea4fc872..8481fd0a2a 100644
--- a/app/workers/create_note_diff_file_worker.rb
+++ b/app/workers/create_note_diff_file_worker.rb
@@ -10,8 +10,10 @@ class CreateNoteDiffFileWorker # rubocop:disable Scalability/IdempotentWorker
feature_category :code_review
def perform(diff_note_id)
- diff_note = DiffNote.find(diff_note_id)
+ return unless diff_note_id.present?
- diff_note.create_diff_file
+ diff_note = DiffNote.find_by_id(diff_note_id) # rubocop: disable CodeReuse/ActiveRecord
+
+ diff_note&.create_diff_file
end
end
diff --git a/app/workers/database/drop_detached_partitions_worker.rb b/app/workers/database/drop_detached_partitions_worker.rb
index f9c8ce57a3..1e4dc20a0d 100644
--- a/app/workers/database/drop_detached_partitions_worker.rb
+++ b/app/workers/database/drop_detached_partitions_worker.rb
@@ -10,7 +10,7 @@ module Database
idempotent!
def perform
- Gitlab::Database::Partitioning::DetachedPartitionDropper.new.perform
+ Gitlab::Database::Partitioning.drop_detached_partitions
ensure
Gitlab::Database::Partitioning::PartitionMonitoring.new.report_metrics
end
diff --git a/app/workers/dependency_proxy/cleanup_blob_worker.rb b/app/workers/dependency_proxy/cleanup_blob_worker.rb
new file mode 100644
index 0000000000..054bc5854a
--- /dev/null
+++ b/app/workers/dependency_proxy/cleanup_blob_worker.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ class CleanupBlobWorker
+ include ApplicationWorker
+ include LimitedCapacity::Worker
+ include Gitlab::Utils::StrongMemoize
+ include DependencyProxy::CleanupWorker
+
+ data_consistency :always
+
+ sidekiq_options retry: 3
+
+ queue_namespace :dependency_proxy_blob
+ feature_category :dependency_proxy
+ urgency :low
+ worker_resource_boundary :unknown
+ idempotent!
+
+ private
+
+ def model
+ DependencyProxy::Blob
+ end
+
+ def log_metadata(blob)
+ log_extra_metadata_on_done(:dependency_proxy_blob_id, blob.id)
+ log_extra_metadata_on_done(:group_id, blob.group_id)
+ end
+
+ def log_cleanup_item(blob)
+ logger.info(
+ structured_payload(
+ group_id: blob.group_id,
+ dependency_proxy_blob_id: blob.id
+ )
+ )
+ end
+ end
+end
diff --git a/app/workers/dependency_proxy/cleanup_manifest_worker.rb b/app/workers/dependency_proxy/cleanup_manifest_worker.rb
new file mode 100644
index 0000000000..1186efa203
--- /dev/null
+++ b/app/workers/dependency_proxy/cleanup_manifest_worker.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ class CleanupManifestWorker
+ include ApplicationWorker
+ include LimitedCapacity::Worker
+ include Gitlab::Utils::StrongMemoize
+ include DependencyProxy::CleanupWorker
+
+ data_consistency :always
+
+ sidekiq_options retry: 3
+
+ queue_namespace :dependency_proxy_manifest
+ feature_category :dependency_proxy
+ urgency :low
+ worker_resource_boundary :unknown
+ idempotent!
+
+ private
+
+ def model
+ DependencyProxy::Manifest
+ end
+
+ def log_metadata(manifest)
+ log_extra_metadata_on_done(:dependency_proxy_manifest_id, manifest.id)
+ log_extra_metadata_on_done(:group_id, manifest.group_id)
+ end
+
+ def log_cleanup_item(manifest)
+ logger.info(
+ structured_payload(
+ group_id: manifest.group_id,
+ dependency_proxy_manifest_id: manifest.id
+ )
+ )
+ end
+ end
+end
diff --git a/app/workers/dependency_proxy/image_ttl_group_policy_worker.rb b/app/workers/dependency_proxy/image_ttl_group_policy_worker.rb
new file mode 100644
index 0000000000..fed469e6dc
--- /dev/null
+++ b/app/workers/dependency_proxy/image_ttl_group_policy_worker.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module DependencyProxy
+ class ImageTtlGroupPolicyWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+ include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
+
+ data_consistency :always
+
+ feature_category :dependency_proxy
+
+ UPDATE_BATCH_SIZE = 100
+
+ def perform
+ DependencyProxy::ImageTtlGroupPolicy.enabled.each do |policy|
+ # Technical Debt: change to read_before https://gitlab.com/gitlab-org/gitlab/-/issues/341536
+ qualified_blobs = policy.group.dependency_proxy_blobs.active.updated_before(policy.ttl)
+ qualified_manifests = policy.group.dependency_proxy_manifests.active.updated_before(policy.ttl)
+
+ enqueue_blob_cleanup_job if expire_artifacts(qualified_blobs, DependencyProxy::Blob)
+ enqueue_manifest_cleanup_job if expire_artifacts(qualified_manifests, DependencyProxy::Manifest)
+ end
+
+ log_counts
+ end
+
+ private
+
+ def expire_artifacts(artifacts, model)
+ rows_updated = false
+
+ artifacts.each_batch(of: UPDATE_BATCH_SIZE) do |batch|
+ rows = batch.update_all(status: :expired)
+ rows_updated ||= rows > 0
+ end
+
+ rows_updated
+ end
+
+ def enqueue_blob_cleanup_job
+ DependencyProxy::CleanupBlobWorker.perform_with_capacity
+ end
+
+ def enqueue_manifest_cleanup_job
+ DependencyProxy::CleanupManifestWorker.perform_with_capacity
+ end
+
+ def log_counts
+ use_replica_if_available do
+ expired_blob_count = DependencyProxy::Blob.expired.count
+ expired_manifest_count = DependencyProxy::Manifest.expired.count
+ processing_blob_count = DependencyProxy::Blob.processing.count
+ processing_manifest_count = DependencyProxy::Manifest.processing.count
+ error_blob_count = DependencyProxy::Blob.error.count
+ error_manifest_count = DependencyProxy::Manifest.error.count
+
+ log_extra_metadata_on_done(:expired_dependency_proxy_blob_count, expired_blob_count)
+ log_extra_metadata_on_done(:expired_dependency_proxy_manifest_count, expired_manifest_count)
+ log_extra_metadata_on_done(:processing_dependency_proxy_blob_count, processing_blob_count)
+ log_extra_metadata_on_done(:processing_dependency_proxy_manifest_count, processing_manifest_count)
+ log_extra_metadata_on_done(:error_dependency_proxy_blob_count, error_blob_count)
+ log_extra_metadata_on_done(:error_dependency_proxy_manifest_count, error_manifest_count)
+ end
+ end
+
+ def use_replica_if_available(&block)
+ ::Gitlab::Database::LoadBalancing::Session.current.use_replicas_for_read_queries(&block)
+ end
+ end
+end
diff --git a/app/workers/expire_build_artifacts_worker.rb b/app/workers/expire_build_artifacts_worker.rb
index 65d387f73e..295703cc1c 100644
--- a/app/workers/expire_build_artifacts_worker.rb
+++ b/app/workers/expire_build_artifacts_worker.rb
@@ -10,7 +10,7 @@ class ExpireBuildArtifactsWorker # rubocop:disable Scalability/IdempotentWorker
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext
- feature_category :continuous_integration
+ feature_category :build_artifacts
def perform
service = Ci::JobArtifacts::DestroyAllExpiredService.new
diff --git a/app/workers/expire_build_instance_artifacts_worker.rb b/app/workers/expire_build_instance_artifacts_worker.rb
index 96378acca0..77b8f59e36 100644
--- a/app/workers/expire_build_instance_artifacts_worker.rb
+++ b/app/workers/expire_build_instance_artifacts_worker.rb
@@ -7,7 +7,7 @@ class ExpireBuildInstanceArtifactsWorker # rubocop:disable Scalability/Idempoten
sidekiq_options retry: 3
- feature_category :continuous_integration
+ feature_category :build_artifacts
# rubocop: disable CodeReuse/ActiveRecord
def perform(build_id)
diff --git a/app/workers/expire_job_cache_worker.rb b/app/workers/expire_job_cache_worker.rb
index 401fe1dc1e..7374f65054 100644
--- a/app/workers/expire_job_cache_worker.rb
+++ b/app/workers/expire_job_cache_worker.rb
@@ -10,11 +10,9 @@ class ExpireJobCacheWorker # rubocop:disable Scalability/IdempotentWorker
queue_namespace :pipeline_cache
urgency :high
- # This worker should be idempotent, but we're switching to data_consistency
- # :sticky and there is an ongoing incompatibility, so it needs to be disabled for
- # now. The following line can be uncommented and this comment removed once
- # https://gitlab.com/gitlab-org/gitlab/-/issues/325291 is resolved.
- # idempotent!
+
+ deduplicate :until_executing, including_scheduled: true
+ idempotent!
# rubocop: disable CodeReuse/ActiveRecord
def perform(job_id)
diff --git a/app/workers/gitlab/github_import/stage/finish_import_worker.rb b/app/workers/gitlab/github_import/stage/finish_import_worker.rb
index 006b79dbff..5197c1e1e8 100644
--- a/app/workers/gitlab/github_import/stage/finish_import_worker.rb
+++ b/app/workers/gitlab/github_import/stage/finish_import_worker.rb
@@ -18,36 +18,28 @@ module Gitlab
# project - An instance of Project.
def import(_, project)
+ @project = project
project.after_import
- report_import_time(project)
+ report_import_time
end
- def report_import_time(project)
- duration = Time.zone.now - project.created_at
+ private
- histogram.observe({ project: project.full_path }, duration)
- counter.increment
+ attr_reader :project
+
+ def report_import_time
+ metrics.track_finished_import
info(
project.id,
message: "GitHub project import finished",
- duration_s: duration.round(2),
+ duration_s: metrics.duration.round(2),
object_counts: ::Gitlab::GithubImport::ObjectCounter.summary(project)
)
end
- def histogram
- @histogram ||= Gitlab::Metrics.histogram(
- :github_importer_total_duration_seconds,
- 'Total time spent importing GitHub projects, in seconds'
- )
- end
-
- def counter
- @counter ||= Gitlab::Metrics.counter(
- :github_importer_imported_projects,
- 'The number of imported GitHub projects'
- )
+ def metrics
+ @metrics ||= Gitlab::Import::Metrics.new(:github_importer, project)
end
end
end
diff --git a/app/workers/gitlab/github_import/stage/import_base_data_worker.rb b/app/workers/gitlab/github_import/stage/import_base_data_worker.rb
index 715c39caf4..cc6a225516 100644
--- a/app/workers/gitlab/github_import/stage/import_base_data_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_base_data_worker.rb
@@ -31,6 +31,22 @@ module Gitlab
project.import_state.refresh_jid_expiration
ImportPullRequestsWorker.perform_async(project.id)
+ rescue StandardError => e
+ Gitlab::Import::ImportFailureService.track(
+ project_id: project.id,
+ error_source: self.class.name,
+ exception: e,
+ fail_import: abort_on_failure,
+ metrics: true
+ )
+
+ raise(e)
+ end
+
+ private
+
+ def abort_on_failure
+ true
end
end
end
diff --git a/app/workers/gitlab/github_import/stage/import_pull_requests_worker.rb b/app/workers/gitlab/github_import/stage/import_pull_requests_worker.rb
index d76d36531d..71d0247bae 100644
--- a/app/workers/gitlab/github_import/stage/import_pull_requests_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_pull_requests_worker.rb
@@ -27,6 +27,22 @@ module Gitlab
{ waiter.key => waiter.jobs_remaining },
:pull_requests_merged_by
)
+ rescue StandardError => e
+ Gitlab::Import::ImportFailureService.track(
+ project_id: project.id,
+ error_source: self.class.name,
+ exception: e,
+ fail_import: abort_on_failure,
+ metrics: true
+ )
+
+ raise(e)
+ end
+
+ private
+
+ def abort_on_failure
+ true
end
end
end
diff --git a/app/workers/gitlab/github_import/stage/import_repository_worker.rb b/app/workers/gitlab/github_import/stage/import_repository_worker.rb
index 227b7c304b..3e914cc759 100644
--- a/app/workers/gitlab/github_import/stage/import_repository_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_repository_worker.rb
@@ -33,6 +33,17 @@ module Gitlab
counter.increment
ImportBaseDataWorker.perform_async(project.id)
+
+ rescue StandardError => e
+ Gitlab::Import::ImportFailureService.track(
+ project_id: project.id,
+ error_source: self.class.name,
+ exception: e,
+ fail_import: abort_on_failure,
+ metrics: true
+ )
+
+ raise(e)
end
def counter
diff --git a/app/workers/issue_placement_worker.rb b/app/workers/issue_placement_worker.rb
index e0c4502ed1..22e2a8e95f 100644
--- a/app/workers/issue_placement_worker.rb
+++ b/app/workers/issue_placement_worker.rb
@@ -31,7 +31,7 @@ class IssuePlacementWorker
# while preserving creation order.
to_place = Issue
.relative_positioning_query_base(issue)
- .where(relative_position: nil)
+ .with_null_relative_position
.order({ created_at: :asc }, { id: :asc })
.limit(QUERY_LIMIT + 1)
.to_a
diff --git a/app/workers/namespaces/in_product_marketing_emails_worker.rb b/app/workers/namespaces/in_product_marketing_emails_worker.rb
index 49e65d59e8..470fba1227 100644
--- a/app/workers/namespaces/in_product_marketing_emails_worker.rb
+++ b/app/workers/namespaces/in_product_marketing_emails_worker.rb
@@ -8,7 +8,7 @@ module Namespaces
include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
- feature_category :subgroups
+ feature_category :experimentation_activation
urgency :low
def perform
diff --git a/app/workers/namespaces/onboarding_issue_created_worker.rb b/app/workers/namespaces/onboarding_issue_created_worker.rb
index 81d105ab19..aab5767e0f 100644
--- a/app/workers/namespaces/onboarding_issue_created_worker.rb
+++ b/app/workers/namespaces/onboarding_issue_created_worker.rb
@@ -8,7 +8,7 @@ module Namespaces
sidekiq_options retry: 3
- feature_category :issue_tracking
+ feature_category :onboarding
urgency :low
deduplicate :until_executing
diff --git a/app/workers/namespaces/onboarding_pipeline_created_worker.rb b/app/workers/namespaces/onboarding_pipeline_created_worker.rb
index f9a6b73458..4172e28647 100644
--- a/app/workers/namespaces/onboarding_pipeline_created_worker.rb
+++ b/app/workers/namespaces/onboarding_pipeline_created_worker.rb
@@ -8,7 +8,7 @@ module Namespaces
sidekiq_options retry: 3
- feature_category :subgroups
+ feature_category :onboarding
urgency :low
deduplicate :until_executing
diff --git a/app/workers/namespaces/onboarding_progress_worker.rb b/app/workers/namespaces/onboarding_progress_worker.rb
index b77db1aec5..77a31d85a9 100644
--- a/app/workers/namespaces/onboarding_progress_worker.rb
+++ b/app/workers/namespaces/onboarding_progress_worker.rb
@@ -8,7 +8,7 @@ module Namespaces
sidekiq_options retry: 3
- feature_category :product_analytics
+ feature_category :onboarding
worker_resource_boundary :cpu
urgency :low
diff --git a/app/workers/namespaces/onboarding_user_added_worker.rb b/app/workers/namespaces/onboarding_user_added_worker.rb
index 6a189e81b9..4d17cf9a6e 100644
--- a/app/workers/namespaces/onboarding_user_added_worker.rb
+++ b/app/workers/namespaces/onboarding_user_added_worker.rb
@@ -8,7 +8,7 @@ module Namespaces
sidekiq_options retry: 3
- feature_category :users
+ feature_category :onboarding
urgency :low
idempotent!
diff --git a/app/workers/packages/composer/cache_cleanup_worker.rb b/app/workers/packages/composer/cache_cleanup_worker.rb
index 19babf6396..c80d6ea45d 100644
--- a/app/workers/packages/composer/cache_cleanup_worker.rb
+++ b/app/workers/packages/composer/cache_cleanup_worker.rb
@@ -14,19 +14,7 @@ module Packages
idempotent!
def perform
- ::Packages::Composer::CacheFile.without_namespace.find_in_batches do |cache_files|
- cache_files.each(&:destroy)
- rescue ActiveRecord::RecordNotFound
- # ignore. likely due to object already being deleted.
- end
-
- ::Packages::Composer::CacheFile.expired.find_in_batches do |cache_files|
- cache_files.each(&:destroy)
- rescue ActiveRecord::RecordNotFound
- # ignore. likely due to object already being deleted.
- end
- rescue StandardError => e
- Gitlab::ErrorTracking.log_exception(e)
+ # no-op: to be removed after 14.5 https://gitlab.com/gitlab-org/gitlab/-/issues/333694
end
end
end
diff --git a/app/workers/packages/composer/cache_update_worker.rb b/app/workers/packages/composer/cache_update_worker.rb
index 874993a132..5600af6ce2 100644
--- a/app/workers/packages/composer/cache_update_worker.rb
+++ b/app/workers/packages/composer/cache_update_worker.rb
@@ -7,20 +7,14 @@ module Packages
data_consistency :always
- sidekiq_options retry: 3
+ sidekiq_options retry: false
feature_category :package_registry
idempotent!
- def perform(project_id, package_name, last_page_sha)
- project = Project.find_by_id(project_id)
-
- return unless project
-
- Gitlab::Composer::Cache.new(project: project, name: package_name, last_page_sha: last_page_sha).execute
- rescue StandardError => e
- Gitlab::ErrorTracking.log_exception(e, project_id: project_id)
+ def perform(*args)
+ # no-op: to be removed after 14.5 https://gitlab.com/gitlab-org/gitlab/-/issues/333694
end
end
end
diff --git a/app/workers/packages/debian/generate_distribution_worker.rb b/app/workers/packages/debian/generate_distribution_worker.rb
index b9b157d25d..1eff3ea02d 100644
--- a/app/workers/packages/debian/generate_distribution_worker.rb
+++ b/app/workers/packages/debian/generate_distribution_worker.rb
@@ -2,7 +2,7 @@
module Packages
module Debian
- class GenerateDistributionWorker # rubocop:disable Scalability/IdempotentWorker
+ class GenerateDistributionWorker
include ApplicationWorker
data_consistency :always
diff --git a/app/workers/pages_remove_worker.rb b/app/workers/pages_remove_worker.rb
deleted file mode 100644
index 4de99b8654..0000000000
--- a/app/workers/pages_remove_worker.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-# TODO: remove this worker https://gitlab.com/gitlab-org/gitlab/-/issues/340641
-class PagesRemoveWorker # rubocop:disable Scalability/IdempotentWorker
- include ApplicationWorker
-
- data_consistency :always
-
- sidekiq_options retry: 3
- feature_category :pages
- loggable_arguments 0
-
- def perform(project_id)
- # no-op
- end
-end
diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb
index 322f92d376..c67f3860a5 100644
--- a/app/workers/pipeline_hooks_worker.rb
+++ b/app/workers/pipeline_hooks_worker.rb
@@ -12,9 +12,10 @@ class PipelineHooksWorker # rubocop:disable Scalability/IdempotentWorker
# rubocop: disable CodeReuse/ActiveRecord
def perform(pipeline_id)
- Ci::Pipeline
- .find_by(id: pipeline_id)
- .try(:execute_hooks)
+ pipeline = Ci::Pipeline.find_by(id: pipeline_id)
+ return unless pipeline
+
+ Ci::Pipelines::HookService.new(pipeline).execute
end
# rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/workers/pipeline_process_worker.rb b/app/workers/pipeline_process_worker.rb
index 9cd471a5ab..9370b36106 100644
--- a/app/workers/pipeline_process_worker.rb
+++ b/app/workers/pipeline_process_worker.rb
@@ -14,7 +14,7 @@ class PipelineProcessWorker
loggable_arguments 1
idempotent!
- deduplicate :until_executing, feature_flag: :ci_idempotent_pipeline_process_worker
+ deduplicate :until_executing
# rubocop: disable CodeReuse/ActiveRecord
def perform(pipeline_id)
diff --git a/app/workers/run_pipeline_schedule_worker.rb b/app/workers/run_pipeline_schedule_worker.rb
index dd0f14a5ca..12042ebc4f 100644
--- a/app/workers/run_pipeline_schedule_worker.rb
+++ b/app/workers/run_pipeline_schedule_worker.rb
@@ -27,8 +27,9 @@ class RunPipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
user,
ref: schedule.ref)
.execute!(:schedule, ignore_skip_ci: true, save_on_errors: false, schedule: schedule)
- rescue Ci::CreatePipelineService::CreateError
- # no-op. This is a user operation error such as corrupted .gitlab-ci.yml.
+ rescue Ci::CreatePipelineService::CreateError => e
+ # This is a user operation error such as corrupted .gitlab-ci.yml. Log the error for debugging purpose.
+ log_extra_metadata_on_done(:pipeline_creation_error, e)
rescue StandardError => e
error(schedule, e)
end
@@ -37,10 +38,16 @@ class RunPipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
def error(schedule, error)
failed_creation_counter.increment
+ log_error(schedule, error)
+ track_error(schedule, error)
+ end
+ def log_error(schedule, error)
Gitlab::AppLogger.error "Failed to create a scheduled pipeline. " \
"schedule_id: #{schedule.id} message: #{error.message}"
+ end
+ def track_error(schedule, error)
Gitlab::ErrorTracking
.track_and_raise_for_dev_exception(error,
issue_url: 'https://gitlab.com/gitlab-org/gitlab-foss/issues/41231',
diff --git a/app/workers/stuck_ci_jobs_worker.rb b/app/workers/stuck_ci_jobs_worker.rb
index a2b2686c8d..72004f7568 100644
--- a/app/workers/stuck_ci_jobs_worker.rb
+++ b/app/workers/stuck_ci_jobs_worker.rb
@@ -2,6 +2,7 @@
class StuckCiJobsWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
+ include ExclusiveLeaseGuard
# rubocop:disable Scalability/CronWorkerContext
# This is an instance-wide cleanup query, so there's no meaningful
@@ -12,25 +13,19 @@ class StuckCiJobsWorker # rubocop:disable Scalability/IdempotentWorker
data_consistency :always
feature_category :continuous_integration
- worker_resource_boundary :cpu
-
- EXCLUSIVE_LEASE_KEY = 'stuck_ci_builds_worker_lease'
def perform
- return unless try_obtain_lease
+ Ci::StuckBuilds::DropRunningWorker.perform_in(20.minutes)
+ Ci::StuckBuilds::DropScheduledWorker.perform_in(40.minutes)
- Ci::StuckBuilds::DropService.new.execute
-
- remove_lease
+ try_obtain_lease do
+ Ci::StuckBuilds::DropPendingService.new.execute
+ end
end
private
- def try_obtain_lease
- @uuid = Gitlab::ExclusiveLease.new(EXCLUSIVE_LEASE_KEY, timeout: 30.minutes).try_obtain
- end
-
- def remove_lease
- Gitlab::ExclusiveLease.cancel(EXCLUSIVE_LEASE_KEY, @uuid)
+ def lease_timeout
+ 30.minutes
end
end
diff --git a/bin/background_jobs b/bin/background_jobs
index 6aebc8126c..f9b42b97e0 100755
--- a/bin/background_jobs
+++ b/bin/background_jobs
@@ -3,6 +3,7 @@
cd $(dirname $0)/..
app_root=$(pwd)
sidekiq_workers=${SIDEKIQ_WORKERS:-1}
+sidekiq_queues=${SIDEKIQ_QUEUES:-*} # Queues to listen to; default to `*` (all)
sidekiq_pidfile="$app_root/tmp/pids/sidekiq-cluster.pid"
sidekiq_logfile="$app_root/log/sidekiq.log"
gitlab_user=$(ls -l config.ru | awk '{print $3}')
@@ -37,8 +38,7 @@ restart()
stop
fi
- warn "Sidekiq output will be written to $sidekiq_logfile"
- start_sidekiq "$@" >> $sidekiq_logfile 2>&1
+ start_sidekiq "$@"
}
start_sidekiq()
@@ -50,13 +50,13 @@ start_sidekiq()
cmd="${cmd} ${chpst} -P"
fi
- # sidekiq-cluster expects '*' '*' arguments (one wildcard for each process).
+ # sidekiq-cluster expects an argument per process.
for (( i=1; i<=$sidekiq_workers; i++ ))
do
- processes_args+=("*")
+ processes_args+=("${sidekiq_queues}")
done
- ${cmd} bin/sidekiq-cluster "${processes_args[@]}" -P $sidekiq_pidfile -e $RAILS_ENV "$@"
+ ${cmd} bin/sidekiq-cluster "${processes_args[@]}" -P $sidekiq_pidfile -e $RAILS_ENV "$@" 2>&1 | tee -a $sidekiq_logfile
}
action="$1"
diff --git a/config/README.md b/config/README.md
index be5bd442fd..f04758fcae 100644
--- a/config/README.md
+++ b/config/README.md
@@ -1,13 +1,13 @@
# Configuration files Documentation
Note that most configuration files (`config/*.*`) committed into
-[gitlab-ce](https://gitlab.com/gitlab-org/gitlab-foss) **will not be used** for
+[gitlab-foss](https://gitlab.com/gitlab-org/gitlab-foss) **will not be used** for
[omnibus-gitlab](https://gitlab.com/gitlab-org/omnibus-gitlab). Configuration
-files committed into gitlab-ce are only used for development.
+files committed into gitlab-foss are only used for development.
## gitlab.yml
-You can find most of GitLab configuration settings here.
+You can find most of the GitLab configuration settings here.
## mail_room.yml
@@ -21,7 +21,7 @@ This file is called `resque.yml` for historical reasons. We are **NOT**
using Resque at the moment. It is used to specify Redis configuration
values when a single database instance of Redis is desired.
-# Advanced Redis configuration files
+## Advanced Redis configuration files
In more advanced configurations of Redis key-value storage, it is desirable
to separate the keys by lifecycle and intended use to ease provisioning and
@@ -40,7 +40,7 @@ If desired, the routing URL provided by these settings can be used with:
2. TCP port number for each Redis instance desired
3. `database number` for each Redis instance desired
-## Example URL attribute formats for GitLab Redis `.yml` configuration files
+### Example URL attribute formats for GitLab Redis `.yml` configuration files
* Unix Socket, default Redis database (0)
* `url: unix:/path/to/redis.sock`
* `url: unix:/path/to/redis.sock?db=`
@@ -52,129 +52,38 @@ If desired, the routing URL provided by these settings can be used with:
* TCP Socket for Redis on remote host `myserver`, port 6379, database 33
* `url: redis://:mynewpassword@myserver:6379/33`
-## redis.cache.yml
+## Available configuration files
-If configured, `redis.cache.yml` overrides the
-`resque.yml` settings to configure the Redis database instance
-used for `Rails.cache` and other volatile non-persistent data which enhances
-the performance of GitLab.
-Settings here can be overridden by the environment variable
-`GITLAB_REDIS_CACHE_CONFIG_FILE` which provides
-an alternate location for configuration settings.
+The Redis instances that can be configured are described in the table below. The
+order of precedence for configuration is described below, where `$NAME` and
+`$FALLBACK_NAME` are the upper-cased instance names from the table, and `$name`
+and `$fallback_name` are the lower-cased versions:
-The order of precedence for the URL used to connect to the Redis instance
-used for `cache` is:
-1. URL from a configuration file pointed to by the
-`GITLAB_REDIS_CACHE_CONFIG_FILE` environment variable
-2. URL from `redis.cache.yml`
-3. URL from a configuration file pointed to by the
-`GITLAB_REDIS_CONFIG_FILE` environment variable
-4. URL from `resque.yml`
-5. `redis://localhost:6380`
+1. The configuration file pointed to by the `GITLAB_REDIS_$NAME_CONFIG_FILE`
+ environment variable.
+1. The configuration file `redis.$name.yml`.
+1. **If a fallback instance is available**, the configuration file
+ `redis.$fallback_name.yml`.
+1. The configuration file pointed to by the `GITLAB_REDIS_CONFIG_FILE`
+environment variable.
+1. The configuration file `resque.yml`.
-The order of precedence for all other configuration settings for `cache`
-are selected from only the first of the following files found (if a setting
-is not provided in an earlier file, the remainder of the files are not
-searched):
-1. the configuration file pointed to by the
-`GITLAB_REDIS_CACHE_CONFIG_FILE` environment variable
-2. the configuration file `redis.cache.yml`
-3. the configuration file pointed to by the
-`GITLAB_REDIS_CONFIG_FILE` environment variable
-4. the configuration file `resque.yml`
+An example configuration file for Redis is in this directory under the name
+`resque.yml.example`.
-## redis.queues.yml
+| Name | Fallback instance | Purpose |
+| --- | --- | --- |
+| `cache` | | Volatile non-persistent data |
+| `queues` | | Background job processing queues |
+| `shared_state` | | Persistent application state |
+| `trace_chunks` | `shared_state` | [CI trace chunks](https://docs.gitlab.com/ee/administration/job_logs.html#incremental-logging-architecture) |
+| `rate_limiting` | `cache` | [Rate limiting](https://docs.gitlab.com/ee/user/admin_area/settings/user_and_ip_rate_limits.html) state |
+| `sessions` | `shared_state` | [Sessions](https://docs.gitlab.com/ee/development/session.html#redis)|
-If configured, `redis.queues.yml` overrides the
-`resque.yml` settings to configure the Redis database instance
-used for clients of `::Gitlab::Redis::Queues`.
-These queues are intended to be the foundation
-of reliable inter-process communication between modules, whether on the same
-host node, or within a cluster. The primary clients of the queues are
-SideKiq, Mailroom, CI Runner, Workhorse, and push services. Settings here can
-be overridden by the environment variable
-`GITLAB_REDIS_QUEUES_CONFIG_FILE` which provides an alternate location for
-configuration settings.
+If no configuration is found, or no URL is found in the configuration
+file, the default URL used is:
-The order of precedence for the URL used to connect to the Redis instance
-used for `queues` is:
-1. URL from a configuration file pointed to by the
-`GITLAB_REDIS_QUEUES_CONFIG_FILE` environment variable
-2. URL from `redis.queues.yml`
-3. URL from a configuration file pointed to by the
-`GITLAB_REDIS_CONFIG_FILE` environment variable
-4. URL from `resque.yml`
-5. `redis://localhost:6381`
-
-The order of precedence for all other configuration settings for `queues`
-are selected from only the first of the following files found (if a setting
-is not provided in an earlier file, the remainder of the files are not
-searched):
-1. the configuration file pointed to by the
-`GITLAB_REDIS_QUEUES_CONFIG_FILE` environment variable
-2. the configuration file `redis.queues.yml`
-3. the configuration file pointed to by the
-`GITLAB_REDIS_CONFIG_FILE` environment variable
-4. the configuration file `resque.yml`
-
-## redis.shared_state.yml
-
-If configured, `redis.shared_state.yml` overrides the
-`resque.yml` settings to configure the Redis database instance
-used for clients of `::Gitlab::Redis::SharedState` such as session state,
-and rate limiting.
-Settings here can be overridden by the environment variable
-`GITLAB_REDIS_SHARED_STATE_CONFIG_FILE` which provides
-an alternate location for configuration settings.
-
-The order of precedence for the URL used to connect to the Redis instance
-used for `shared_state` is:
-1. URL from a configuration file pointed to by the
-`GITLAB_REDIS_SHARED_STATE_CONFIG_FILE` environment variable
-2. URL from `redis.shared_state.yml`
-3. URL from a configuration file pointed to by the
-`GITLAB_REDIS_CONFIG_FILE` environment variable
-4. URL from `resque.yml`
-5. `redis://localhost:6382`
-
-The order of precedence for all other configuration settings for `shared_state`
-are selected from only the first of the following files found (if a setting
-is not provided in an earlier file, the remainder of the files are not
-searched):
-1. the configuration file pointed to by the
-`GITLAB_REDIS_SHARED_STATE_CONFIG_FILE` environment variable
-2. the configuration file `redis.shared_state.yml`
-3. the configuration file pointed to by the
-`GITLAB_REDIS_CONFIG_FILE` environment variable
-4. the configuration file `resque.yml`
-
-## redis.trace_chunks.yml
-
-If configured, `redis.trace_chunks.yml` overrides the
-`resque.yml` settings to configure the Redis database instance
-used for clients of `::Gitlab::Redis::TraceChunks` which stores CI trace chunks.
-
-Settings here can be overridden by the environment variable
-`GITLAB_REDIS_TRACE_CHUNKS_CONFIG_FILE` which provides
-an alternate location for configuration settings.
-
-The order of precedence for the URL used to connect to the Redis instance
-used for `trace_chunks` is:
-1. URL from a configuration file pointed to by the
-`GITLAB_REDIS_TRACE_CHUNKS_CONFIG_FILE` environment variable
-2. URL from `redis.trace_chunks.yml`
-3. URL from a configuration file pointed to by the
-`GITLAB_REDIS_CONFIG_FILE` environment variable
-4. URL from `resque.yml`
-5. `redis://localhost:6383`
-
-The order of precedence for all other configuration settings for `trace_chunks`
-are selected from only the first of the following files found (if a setting
-is not provided in an earlier file, the remainder of the files are not
-searched):
-1. the configuration file pointed to by the
-`GITLAB_REDIS_TRACE_CHUNKS_CONFIG_FILE` environment variable
-2. the configuration file `redis.trace_chunks.yml`
-3. the configuration file pointed to by the
-`GITLAB_REDIS_CONFIG_FILE` environment variable
-4. the configuration file `resque.yml`
+1. `redis://localhost:6380` for `cache`.
+1. `redis://localhost:6381` for `queues`.
+1. `redis://localhost:6382` for `shared_state`.
+1. The URL from the fallback instance for all other instances.
diff --git a/config/application.rb b/config/application.rb
index 2349de4892..dba9550a3d 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -23,6 +23,9 @@ module Gitlab
require_dependency Rails.root.join('lib/gitlab/redis/cache')
require_dependency Rails.root.join('lib/gitlab/redis/queues')
require_dependency Rails.root.join('lib/gitlab/redis/shared_state')
+ require_dependency Rails.root.join('lib/gitlab/redis/trace_chunks')
+ require_dependency Rails.root.join('lib/gitlab/redis/rate_limiting')
+ require_dependency Rails.root.join('lib/gitlab/redis/sessions')
require_dependency Rails.root.join('lib/gitlab/current_settings')
require_dependency Rails.root.join('lib/gitlab/middleware/read_only')
require_dependency Rails.root.join('lib/gitlab/middleware/basic_health_check')
@@ -370,15 +373,7 @@ module Gitlab
end
# Use caching across all environments
- # Full list of options:
- # https://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html#method-c-new
- caching_config_hash = {}
- caching_config_hash[:redis] = Gitlab::Redis::Cache.pool
- caching_config_hash[:compress] = Gitlab::Utils.to_boolean(ENV.fetch('ENABLE_REDIS_CACHE_COMPRESSION', '1'))
- caching_config_hash[:namespace] = Gitlab::Redis::Cache::CACHE_NAMESPACE
- caching_config_hash[:expires_in] = 2.weeks # Cache should not grow forever
-
- config.cache_store = :redis_cache_store, caching_config_hash
+ config.cache_store = :redis_cache_store, Gitlab::Redis::Cache.active_support_config
config.active_job.queue_adapter = :sidekiq
diff --git a/config/feature_categories.yml b/config/feature_categories.yml
index 1ecf217dd9..e61048a642 100644
--- a/config/feature_categories.yml
+++ b/config/feature_categories.yml
@@ -33,6 +33,7 @@
- continuous_integration_scaling
- database
- dataops
+- delivery
- delivery_management
- dependency_firewall
- dependency_proxy
@@ -53,16 +54,15 @@
- five_minute_production_app
- foundations
- fuzz_testing
-- gdk
- geo_replication
- git_lfs
- gitaly
- gitlab_docs
- global_search
- helm_chart_registry
+- horse
- importers
- incident_management
-- infrastructure
- infrastructure_as_code
- insider_threat
- integrations
@@ -103,12 +103,12 @@
- roadmaps
- runbooks
- runner
+- scalability
- secret_detection
- secrets_management
- security_benchmarking
- security_orchestration
- self_monitoring
-- serverless
- service_desk
- service_ping
- sharding
diff --git a/config/feature_flags/development/add_actor_based_user_to_snowplow_tracking.yml b/config/feature_flags/development/add_actor_based_user_to_snowplow_tracking.yml
new file mode 100644
index 0000000000..9dc20148d5
--- /dev/null
+++ b/config/feature_flags/development/add_actor_based_user_to_snowplow_tracking.yml
@@ -0,0 +1,8 @@
+---
+name: add_actor_based_user_to_snowplow_tracking
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71353
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338150
+milestone: '14.4'
+type: development
+group: group::product intelligence
+default_enabled: false
diff --git a/config/feature_flags/development/advanced_search_multi_project_select.yml b/config/feature_flags/development/advanced_search_multi_project_select.yml
index 4f38955fa7..8f74c8990f 100644
--- a/config/feature_flags/development/advanced_search_multi_project_select.yml
+++ b/config/feature_flags/development/advanced_search_multi_project_select.yml
@@ -2,7 +2,7 @@
name: advanced_search_multi_project_select
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62606
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/333011
-milestone: '14.0'
+milestone: '14.4'
type: development
group: group::global search
default_enabled: false
diff --git a/config/feature_flags/development/avoid_cross_joins_environments_in_self_and_descendants.yml b/config/feature_flags/development/avoid_cross_joins_environments_in_self_and_descendants.yml
new file mode 100644
index 0000000000..25b714b2c6
--- /dev/null
+++ b/config/feature_flags/development/avoid_cross_joins_environments_in_self_and_descendants.yml
@@ -0,0 +1,8 @@
+---
+name: avoid_cross_joins_environments_in_self_and_descendants
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71894
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342991
+milestone: '14.4'
+type: development
+group: group::release
+default_enabled: false
diff --git a/config/feature_flags/development/ci_archived_build_trace_checksum.yml b/config/feature_flags/development/ci_archived_build_trace_checksum.yml
new file mode 100644
index 0000000000..95e641e0ef
--- /dev/null
+++ b/config/feature_flags/development/ci_archived_build_trace_checksum.yml
@@ -0,0 +1,8 @@
+---
+name: ci_archived_build_trace_checksum
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70072
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340737
+milestone: '14.4'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/ci_create_external_pr_pipeline_async.yml b/config/feature_flags/development/ci_create_external_pr_pipeline_async.yml
index 3935a818b1..48c7dbcf74 100644
--- a/config/feature_flags/development/ci_create_external_pr_pipeline_async.yml
+++ b/config/feature_flags/development/ci_create_external_pr_pipeline_async.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338908
milestone: '14.3'
type: development
group: group::pipeline authoring
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/ci_idempotent_pipeline_process_worker.yml b/config/feature_flags/development/ci_idempotent_pipeline_process_worker.yml
deleted file mode 100644
index 60104bd310..0000000000
--- a/config/feature_flags/development/ci_idempotent_pipeline_process_worker.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_idempotent_pipeline_process_worker
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62410
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332963
-milestone: '14.0'
-type: development
-group: group::pipeline authoring
-default_enabled: true
diff --git a/config/feature_flags/development/ci_include_rules.yml b/config/feature_flags/development/ci_include_rules.yml
deleted file mode 100644
index d8a3f0b245..0000000000
--- a/config/feature_flags/development/ci_include_rules.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_include_rules
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67409
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337507
-milestone: '14.2'
-type: development
-group: group::pipeline authoring
-default_enabled: true
diff --git a/config/feature_flags/development/ci_minutes_track_live_consumption.yml b/config/feature_flags/development/ci_minutes_track_live_consumption.yml
deleted file mode 100644
index d94dfc4120..0000000000
--- a/config/feature_flags/development/ci_minutes_track_live_consumption.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_minutes_track_live_consumption
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59263
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/329197
-milestone: '13.12'
-type: development
-group: group::pipeline execution
-default_enabled: false
diff --git a/config/feature_flags/development/ci_new_query_for_running_stuck_jobs.yml b/config/feature_flags/development/ci_new_query_for_running_stuck_jobs.yml
new file mode 100644
index 0000000000..345e9b4c3a
--- /dev/null
+++ b/config/feature_flags/development/ci_new_query_for_running_stuck_jobs.yml
@@ -0,0 +1,8 @@
+---
+name: ci_new_query_for_running_stuck_jobs
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71013
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339264
+milestone: '14.4'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/ci_optimize_project_records_destruction.yml b/config/feature_flags/development/ci_optimize_project_records_destruction.yml
new file mode 100644
index 0000000000..73ad4ae995
--- /dev/null
+++ b/config/feature_flags/development/ci_optimize_project_records_destruction.yml
@@ -0,0 +1,8 @@
+---
+name: ci_optimize_project_records_destruction
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71342
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341936
+milestone: '14.4'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/ci_pipeline_add_job_with_lock.yml b/config/feature_flags/development/ci_pipeline_add_job_with_lock.yml
deleted file mode 100644
index 6a708013ca..0000000000
--- a/config/feature_flags/development/ci_pipeline_add_job_with_lock.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_pipeline_add_job_with_lock
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65754
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337628
-milestone: '14.2'
-type: development
-group: group::pipeline authoring
-default_enabled: true
diff --git a/config/feature_flags/development/ci_remove_update_retried_from_process_pipeline.yml b/config/feature_flags/development/ci_remove_update_retried_from_process_pipeline.yml
deleted file mode 100644
index 932ee76634..0000000000
--- a/config/feature_flags/development/ci_remove_update_retried_from_process_pipeline.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_remove_update_retried_from_process_pipeline
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54300
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321630
-milestone: '13.9'
-type: development
-group: group::pipeline authoring
-default_enabled: true
diff --git a/config/feature_flags/development/ci_runner_limits.yml b/config/feature_flags/development/ci_runner_limits.yml
deleted file mode 100644
index e7d30dd086..0000000000
--- a/config/feature_flags/development/ci_runner_limits.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_runner_limits
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60157
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/329438
-milestone: '13.12'
-type: development
-group: group::runner
-default_enabled: false
diff --git a/config/feature_flags/development/ci_scoped_job_token.yml b/config/feature_flags/development/ci_scoped_job_token.yml
index a7fa024483..a885a1e639 100644
--- a/config/feature_flags/development/ci_scoped_job_token.yml
+++ b/config/feature_flags/development/ci_scoped_job_token.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332272
milestone: '14.0'
type: development
group: group::pipeline execution
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/container_registry_expiration_policies_caching.yml b/config/feature_flags/development/container_registry_expiration_policies_caching.yml
new file mode 100644
index 0000000000..6e8b0efe94
--- /dev/null
+++ b/config/feature_flags/development/container_registry_expiration_policies_caching.yml
@@ -0,0 +1,8 @@
+---
+name: container_registry_expiration_policies_caching
+introduced_by_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340606
+milestone: '14.3'
+type: development
+group: group::package
+default_enabled: false
diff --git a/config/feature_flags/development/create_vulnerabilities_via_api.yml b/config/feature_flags/development/create_vulnerabilities_via_api.yml
index 0a3f9fa73f..3f8af065dc 100644
--- a/config/feature_flags/development/create_vulnerabilities_via_api.yml
+++ b/config/feature_flags/development/create_vulnerabilities_via_api.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338694
milestone: '14.3'
type: development
group: group::threat insights
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/decomposed_ci_query_in_pipelines_for_merge_request_finder.yml b/config/feature_flags/development/decomposed_ci_query_in_pipelines_for_merge_request_finder.yml
new file mode 100644
index 0000000000..235b37dfb1
--- /dev/null
+++ b/config/feature_flags/development/decomposed_ci_query_in_pipelines_for_merge_request_finder.yml
@@ -0,0 +1,8 @@
+---
+name: decomposed_ci_query_in_pipelines_for_merge_request_finder
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68549
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341341
+milestone: '14.4'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/dependency_proxy_workhorse.yml b/config/feature_flags/development/dependency_proxy_workhorse.yml
new file mode 100644
index 0000000000..a3545d32cd
--- /dev/null
+++ b/config/feature_flags/development/dependency_proxy_workhorse.yml
@@ -0,0 +1,8 @@
+---
+name: dependency_proxy_workhorse
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68157
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339639
+milestone: '14.3'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/ensure_verified_primary_email_for_2fa.yml b/config/feature_flags/development/ensure_verified_primary_email_for_2fa.yml
deleted file mode 100644
index 7a52486d35..0000000000
--- a/config/feature_flags/development/ensure_verified_primary_email_for_2fa.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ensure_verified_primary_email_for_2fa
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69593
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340151
-milestone: '14.3'
-type: development
-group: group::access
-default_enabled: true
diff --git a/config/feature_flags/development/environment_last_visible_pipeline_disable_joins.yml b/config/feature_flags/development/environment_last_visible_pipeline_disable_joins.yml
deleted file mode 100644
index 7667542506..0000000000
--- a/config/feature_flags/development/environment_last_visible_pipeline_disable_joins.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: environment_last_visible_pipeline_disable_joins
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68870
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340283
-milestone: '14.3'
-type: development
-group: group::release
-default_enabled: true
diff --git a/config/feature_flags/development/finding_ci_pipeline_disable_joins.yml b/config/feature_flags/development/finding_ci_pipeline_disable_joins.yml
new file mode 100644
index 0000000000..8987b729ca
--- /dev/null
+++ b/config/feature_flags/development/finding_ci_pipeline_disable_joins.yml
@@ -0,0 +1,8 @@
+---
+name: finding_ci_pipeline_disable_joins
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70216
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338665
+milestone: '14.3'
+type: development
+group: group::threat insights
+default_enabled: true
diff --git a/config/feature_flags/development/gitaly_tags_finder.yml b/config/feature_flags/development/gitaly_tags_finder.yml
deleted file mode 100644
index a0a1791e58..0000000000
--- a/config/feature_flags/development/gitaly_tags_finder.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: gitaly_tags_finder
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69101
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339741
-milestone: '14.3'
-type: development
-group: group::source code
-default_enabled: false
diff --git a/config/feature_flags/development/gitaly_user_merge_branch_access_error.yml b/config/feature_flags/development/gitaly_user_merge_branch_access_error.yml
new file mode 100644
index 0000000000..6e52112b75
--- /dev/null
+++ b/config/feature_flags/development/gitaly_user_merge_branch_access_error.yml
@@ -0,0 +1,8 @@
+---
+name: gitaly_user_merge_branch_access_error
+introduced_by_url: https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3705
+rollout_issue_url: https://gitlab.com/gitlab-org/gitaly/-/issues/3757
+milestone: '14.3'
+type: development
+group: group::gitaly
+default_enabled: false
diff --git a/config/feature_flags/development/help_page_documentation_redirect.yml b/config/feature_flags/development/help_page_documentation_redirect.yml
deleted file mode 100644
index 8871160e42..0000000000
--- a/config/feature_flags/development/help_page_documentation_redirect.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: help_page_documentation_redirect
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42702
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/255328
-milestone: '13.5'
-type: development
-group: group::static site editor
-default_enabled: false
diff --git a/config/feature_flags/development/improved_mergeability_checks.yml b/config/feature_flags/development/improved_mergeability_checks.yml
new file mode 100644
index 0000000000..83450ffa16
--- /dev/null
+++ b/config/feature_flags/development/improved_mergeability_checks.yml
@@ -0,0 +1,8 @@
+---
+name: improved_mergeability_checks
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68312
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342386
+milestone: '14.4'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/incubation_5mp_google_cloud.yml b/config/feature_flags/development/incubation_5mp_google_cloud.yml
new file mode 100644
index 0000000000..b687a656b4
--- /dev/null
+++ b/config/feature_flags/development/incubation_5mp_google_cloud.yml
@@ -0,0 +1,8 @@
+---
+name: incubation_5mp_google_cloud
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70715
+rollout_issue_url:
+milestone: '14.3'
+type: development
+group: group::incubation
+default_enabled: false
diff --git a/config/feature_flags/development/infinitely_collapsible_sections.yml b/config/feature_flags/development/infinitely_collapsible_sections.yml
index d0bf063c6f..44f37c06d7 100644
--- a/config/feature_flags/development/infinitely_collapsible_sections.yml
+++ b/config/feature_flags/development/infinitely_collapsible_sections.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/335297
milestone: '14.1'
type: development
group: group::pipeline execution
-default_enabled: true
+default_enabled: false
diff --git a/config/feature_flags/development/jira_connect_asymmetric_jwt.yml b/config/feature_flags/development/jira_connect_asymmetric_jwt.yml
new file mode 100644
index 0000000000..e204a7d6fa
--- /dev/null
+++ b/config/feature_flags/development/jira_connect_asymmetric_jwt.yml
@@ -0,0 +1,8 @@
+---
+name: jira_connect_asymmetric_jwt
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71080
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342808
+milestone: '14.4'
+type: development
+group: group::integrations
+default_enabled: false
diff --git a/config/feature_flags/development/lazy_load_commits.yml b/config/feature_flags/development/lazy_load_commits.yml
new file mode 100644
index 0000000000..d476490721
--- /dev/null
+++ b/config/feature_flags/development/lazy_load_commits.yml
@@ -0,0 +1,8 @@
+---
+name: lazy_load_commits
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71633
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342497
+milestone: '14.4'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/linear_application_setting_ancestor_scopes.yml b/config/feature_flags/development/linear_application_setting_ancestor_scopes.yml
new file mode 100644
index 0000000000..18c64df78d
--- /dev/null
+++ b/config/feature_flags/development/linear_application_setting_ancestor_scopes.yml
@@ -0,0 +1,8 @@
+---
+name: linear_application_setting_ancestor_scopes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70579
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341346
+milestone: '14.4'
+type: development
+group: group::access
+default_enabled: false
diff --git a/config/feature_flags/development/linear_ee_group_ancestor_scopes.yml b/config/feature_flags/development/linear_ee_group_ancestor_scopes.yml
new file mode 100644
index 0000000000..46294b0aef
--- /dev/null
+++ b/config/feature_flags/development/linear_ee_group_ancestor_scopes.yml
@@ -0,0 +1,8 @@
+---
+name: linear_ee_group_ancestor_scopes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70708
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341350
+milestone: '14.4'
+type: development
+group: group::access
+default_enabled: false
diff --git a/config/feature_flags/development/linear_group_ancestor_scopes.yml b/config/feature_flags/development/linear_group_ancestor_scopes.yml
new file mode 100644
index 0000000000..f23399c1e6
--- /dev/null
+++ b/config/feature_flags/development/linear_group_ancestor_scopes.yml
@@ -0,0 +1,8 @@
+---
+name: linear_group_ancestor_scopes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70495
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341115
+milestone: '14.4'
+type: development
+group: group::access
+default_enabled: false
diff --git a/config/feature_flags/development/linear_group_plans_preloaded_ancestor_scopes.yml b/config/feature_flags/development/linear_group_plans_preloaded_ancestor_scopes.yml
new file mode 100644
index 0000000000..d45b8d71a2
--- /dev/null
+++ b/config/feature_flags/development/linear_group_plans_preloaded_ancestor_scopes.yml
@@ -0,0 +1,8 @@
+---
+name: linear_group_plans_preloaded_ancestor_scopes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70685
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341349
+milestone: '14.4'
+type: development
+group: group::access
+default_enabled: false
diff --git a/config/feature_flags/development/linear_group_tree_ancestor_scopes.yml b/config/feature_flags/development/linear_group_tree_ancestor_scopes.yml
new file mode 100644
index 0000000000..3a195242fa
--- /dev/null
+++ b/config/feature_flags/development/linear_group_tree_ancestor_scopes.yml
@@ -0,0 +1,8 @@
+---
+name: linear_group_tree_ancestor_scopes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70503
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341117
+milestone: '14.4'
+type: development
+group: group::access
+default_enabled: false
diff --git a/config/feature_flags/development/linear_members_finder_ancestor_scopes.yml b/config/feature_flags/development/linear_members_finder_ancestor_scopes.yml
new file mode 100644
index 0000000000..6bd5e16432
--- /dev/null
+++ b/config/feature_flags/development/linear_members_finder_ancestor_scopes.yml
@@ -0,0 +1,8 @@
+---
+name: linear_members_finder_ancestor_scopes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70583
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341347
+milestone: '14.4'
+type: development
+group: group::access
+default_enabled: false
diff --git a/config/feature_flags/development/linear_participants_service_ancestor_scopes.yml b/config/feature_flags/development/linear_participants_service_ancestor_scopes.yml
new file mode 100644
index 0000000000..41b6f3b32d
--- /dev/null
+++ b/config/feature_flags/development/linear_participants_service_ancestor_scopes.yml
@@ -0,0 +1,8 @@
+---
+name: linear_participants_service_ancestor_scopes
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70684
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341348
+milestone: '14.4'
+type: development
+group: group::access
+default_enabled: false
diff --git a/config/feature_flags/development/merge_request_discussion_cache.yml b/config/feature_flags/development/merge_request_discussion_cache.yml
deleted file mode 100644
index e90887fc2b..0000000000
--- a/config/feature_flags/development/merge_request_discussion_cache.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: merge_request_discussion_cache
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64688
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/335799
-milestone: '14.1'
-type: development
-group: group::code review
-default_enabled: false
diff --git a/config/feature_flags/development/mergeability_caching.yml b/config/feature_flags/development/mergeability_caching.yml
new file mode 100644
index 0000000000..b906329992
--- /dev/null
+++ b/config/feature_flags/development/mergeability_caching.yml
@@ -0,0 +1,8 @@
+---
+name: mergeability_caching
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68312
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340810
+milestone: '14.4'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/mr_changes_fluid_layout.yml b/config/feature_flags/development/mr_changes_fluid_layout.yml
new file mode 100644
index 0000000000..87f0c0c656
--- /dev/null
+++ b/config/feature_flags/development/mr_changes_fluid_layout.yml
@@ -0,0 +1,8 @@
+---
+name: mr_changes_fluid_layout
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70815
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341809
+milestone: '14.4'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/new_customersdot_staging_url.yml b/config/feature_flags/development/new_customersdot_staging_url.yml
new file mode 100644
index 0000000000..288d7f66f0
--- /dev/null
+++ b/config/feature_flags/development/new_customersdot_staging_url.yml
@@ -0,0 +1,8 @@
+---
+name: new_customersdot_staging_url
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71827
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342513
+milestone: '14.4'
+type: development
+group: group::fulfillment
+default_enabled: false
diff --git a/config/feature_flags/development/new_dir_modal.yml b/config/feature_flags/development/new_dir_modal.yml
new file mode 100644
index 0000000000..12d007209b
--- /dev/null
+++ b/config/feature_flags/development/new_dir_modal.yml
@@ -0,0 +1,8 @@
+---
+name: new_dir_modal
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71154
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341675
+milestone: '14.4'
+type: development
+group: group::source code
+default_enabled: true
diff --git a/config/feature_flags/development/operational_vulnerabilities.yml b/config/feature_flags/development/operational_vulnerabilities.yml
new file mode 100644
index 0000000000..f1e19a626f
--- /dev/null
+++ b/config/feature_flags/development/operational_vulnerabilities.yml
@@ -0,0 +1,8 @@
+---
+name: operational_vulnerabilities
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70732
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341423
+milestone: '14.4'
+type: development
+group: group::container security
+default_enabled: false
diff --git a/config/feature_flags/development/package_list_apollo.yml b/config/feature_flags/development/package_list_apollo.yml
new file mode 100644
index 0000000000..522b08594e
--- /dev/null
+++ b/config/feature_flags/development/package_list_apollo.yml
@@ -0,0 +1,8 @@
+---
+name: package_list_apollo
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70598
+rollout_issue_url:
+milestone: '14.3'
+type: development
+group: group::package
+default_enabled: false
diff --git a/config/feature_flags/development/pages_smart_check_outdated_sha.yml b/config/feature_flags/development/pages_smart_check_outdated_sha.yml
deleted file mode 100644
index 528d357f65..0000000000
--- a/config/feature_flags/development/pages_smart_check_outdated_sha.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: pages_smart_check_outdated_sha
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67303
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/336574
-milestone: '14.2'
-type: development
-group: group::release
-default_enabled: false
diff --git a/config/feature_flags/development/paginated_tree_graphql_query.yml b/config/feature_flags/development/paginated_tree_graphql_query.yml
index 13096412f2..d56d8fc336 100644
--- a/config/feature_flags/development/paginated_tree_graphql_query.yml
+++ b/config/feature_flags/development/paginated_tree_graphql_query.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337214
milestone: '14.2'
type: development
group: group::source code
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/pipeline_editor_drawer.yml b/config/feature_flags/development/pipeline_editor_drawer.yml
deleted file mode 100644
index df73c4be01..0000000000
--- a/config/feature_flags/development/pipeline_editor_drawer.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: pipeline_editor_drawer
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60856
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/329806
-milestone: '13.12'
-type: development
-group: group::pipeline authoring
-default_enabled: true
diff --git a/config/feature_flags/development/pipeline_editor_empty_state_action.yml b/config/feature_flags/development/pipeline_editor_empty_state_action.yml
deleted file mode 100644
index 870aeb1493..0000000000
--- a/config/feature_flags/development/pipeline_editor_empty_state_action.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: pipeline_editor_empty_state_action
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55414
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323229
-milestone: '13.10'
-type: development
-group: group::pipeline authoring
-default_enabled: true
diff --git a/config/feature_flags/development/pipeline_editor_mini_graph.yml b/config/feature_flags/development/pipeline_editor_mini_graph.yml
new file mode 100644
index 0000000000..6f31cb18d8
--- /dev/null
+++ b/config/feature_flags/development/pipeline_editor_mini_graph.yml
@@ -0,0 +1,8 @@
+---
+name: pipeline_editor_mini_graph
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71622
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342217
+milestone: '14.4'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/rate_limited_service_issues_create.yml b/config/feature_flags/development/rate_limited_service_issues_create.yml
new file mode 100644
index 0000000000..95ece10aa6
--- /dev/null
+++ b/config/feature_flags/development/rate_limited_service_issues_create.yml
@@ -0,0 +1,8 @@
+---
+name: rate_limited_service_issues_create
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68526
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342677
+milestone: '14.4'
+type: development
+group: group::project management
+default_enabled: false
diff --git a/config/feature_flags/development/redirect_to_latest_template_jobs_browser_performance_testing.yml b/config/feature_flags/development/redirect_to_latest_template_jobs_browser_performance_testing.yml
deleted file mode 100644
index 37c475067a..0000000000
--- a/config/feature_flags/development/redirect_to_latest_template_jobs_browser_performance_testing.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: redirect_to_latest_template_jobs_browser_performance_testing
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
-rollout_issue_url:
-milestone: '14.0'
-type: development
-group: group::pipeline authoring
-default_enabled: false
diff --git a/config/feature_flags/development/redirect_to_latest_template_security_api_fuzzing.yml b/config/feature_flags/development/redirect_to_latest_template_security_api_fuzzing.yml
deleted file mode 100644
index 96606515bd..0000000000
--- a/config/feature_flags/development/redirect_to_latest_template_security_api_fuzzing.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: redirect_to_latest_template_security_api_fuzzing
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
-rollout_issue_url:
-milestone: '14.0'
-type: development
-group: group::pipeline authoring
-default_enabled: false
diff --git a/config/feature_flags/development/redirect_to_latest_template_security_dast.yml b/config/feature_flags/development/redirect_to_latest_template_security_dast.yml
deleted file mode 100644
index a95c1e1a04..0000000000
--- a/config/feature_flags/development/redirect_to_latest_template_security_dast.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: redirect_to_latest_template_security_dast
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
-rollout_issue_url:
-milestone: '14.0'
-type: development
-group: group::pipeline authoring
-default_enabled: false
diff --git a/config/feature_flags/development/redirect_to_latest_template_terraform.yml b/config/feature_flags/development/redirect_to_latest_template_terraform.yml
deleted file mode 100644
index cb5d833fa2..0000000000
--- a/config/feature_flags/development/redirect_to_latest_template_terraform.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: redirect_to_latest_template_terraform
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
-rollout_issue_url:
-milestone: '14.0'
-type: development
-group: group::pipeline authoring
-default_enabled: false
diff --git a/config/feature_flags/development/redirect_to_latest_template_verify_browser_performance.yml b/config/feature_flags/development/redirect_to_latest_template_verify_browser_performance.yml
deleted file mode 100644
index 4df74a5b07..0000000000
--- a/config/feature_flags/development/redirect_to_latest_template_verify_browser_performance.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: redirect_to_latest_template_verify_browser_performance
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63144
-rollout_issue_url:
-milestone: '14.0'
-type: development
-group: group::pipeline authoring
-default_enabled: false
diff --git a/config/feature_flags/development/refactor_mr_widgets_extensions.yml b/config/feature_flags/development/refactor_mr_widgets_extensions.yml
new file mode 100644
index 0000000000..5b6ea22aaf
--- /dev/null
+++ b/config/feature_flags/development/refactor_mr_widgets_extensions.yml
@@ -0,0 +1,8 @@
+---
+name: refactor_mr_widgets_extensions
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70993
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341759
+milestone: '14.4'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/refactor_mr_widgets_extensions_user.yml b/config/feature_flags/development/refactor_mr_widgets_extensions_user.yml
new file mode 100644
index 0000000000..aa3c279910
--- /dev/null
+++ b/config/feature_flags/development/refactor_mr_widgets_extensions_user.yml
@@ -0,0 +1,8 @@
+---
+name: refactor_mr_widgets_extensions_user
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70993
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341759
+milestone: '14.4'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/refactor_text_viewer.yml b/config/feature_flags/development/refactor_text_viewer.yml
new file mode 100644
index 0000000000..427137773c
--- /dev/null
+++ b/config/feature_flags/development/refactor_text_viewer.yml
@@ -0,0 +1,8 @@
+---
+name: refactor_text_viewer
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70909
+rollout_issue_url:
+milestone: '14.4'
+type: development
+group: 'group::source code'
+default_enabled: false
diff --git a/config/feature_flags/development/reference_cache_memoization.yml b/config/feature_flags/development/reference_cache_memoization.yml
new file mode 100644
index 0000000000..7401220817
--- /dev/null
+++ b/config/feature_flags/development/reference_cache_memoization.yml
@@ -0,0 +1,8 @@
+---
+name: reference_cache_memoization
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71310
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341849
+milestone: '14.4'
+type: development
+group: group::source code
+default_enabled: true
diff --git a/config/feature_flags/development/remove_composer_v1_cache_code.yml b/config/feature_flags/development/remove_composer_v1_cache_code.yml
deleted file mode 100644
index 9654fc8dc5..0000000000
--- a/config/feature_flags/development/remove_composer_v1_cache_code.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: remove_composer_v1_cache_code
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67843
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338264
-milestone: '14.2'
-type: development
-group: group::package
-default_enabled: false
diff --git a/config/feature_flags/development/request_apdex_counters.yml b/config/feature_flags/development/request_apdex_counters.yml
new file mode 100644
index 0000000000..07d6cb7ac5
--- /dev/null
+++ b/config/feature_flags/development/request_apdex_counters.yml
@@ -0,0 +1,8 @@
+---
+name: request_apdex_counters
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69154
+rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1099
+milestone: '14.3'
+type: development
+group: team::Scalability
+default_enabled: false
diff --git a/config/feature_flags/development/search_blobs_language_aggregation.yml b/config/feature_flags/development/search_blobs_language_aggregation.yml
new file mode 100644
index 0000000000..da1b81dc52
--- /dev/null
+++ b/config/feature_flags/development/search_blobs_language_aggregation.yml
@@ -0,0 +1,8 @@
+---
+name: search_blobs_language_aggregation
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71937
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342621
+milestone: '14.4'
+type: development
+group: group::global search
+default_enabled: false
diff --git a/config/feature_flags/development/security_orchestration_policies_configuration.yml b/config/feature_flags/development/security_orchestration_policies_configuration.yml
deleted file mode 100644
index 2570743c10..0000000000
--- a/config/feature_flags/development/security_orchestration_policies_configuration.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: security_orchestration_policies_configuration
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54220
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321258
-milestone: '13.9'
-type: development
-group: group::container security
-default_enabled: true
diff --git a/config/feature_flags/development/security_report_ingestion_framework.yml b/config/feature_flags/development/security_report_ingestion_framework.yml
new file mode 100644
index 0000000000..490fd03c67
--- /dev/null
+++ b/config/feature_flags/development/security_report_ingestion_framework.yml
@@ -0,0 +1,8 @@
+---
+name: security_report_ingestion_framework
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66735
+rollout_issue_url:
+milestone: '14.4'
+type: development
+group: group::threat insights
+default_enabled: false
diff --git a/config/feature_flags/development/serverless_domain.yml b/config/feature_flags/development/serverless_domain.yml
deleted file mode 100644
index 67b2c6b8e1..0000000000
--- a/config/feature_flags/development/serverless_domain.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: serverless_domain
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21222
-rollout_issue_url:
-milestone: '12.8'
-type: development
-group: group::configure
-default_enabled: false
diff --git a/config/feature_flags/development/show_author_on_note.yml b/config/feature_flags/development/show_author_on_note.yml
deleted file mode 100644
index 7775bf5f27..0000000000
--- a/config/feature_flags/development/show_author_on_note.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: show_author_on_note
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40198
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/250282
-milestone: '13.4'
-type: development
-group: group::project management
-default_enabled: false
diff --git a/config/feature_flags/development/sort_by_project_users_by_project_authorizations_user_id.yml b/config/feature_flags/development/sort_by_project_users_by_project_authorizations_user_id.yml
deleted file mode 100644
index 88a4e0b047..0000000000
--- a/config/feature_flags/development/sort_by_project_users_by_project_authorizations_user_id.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: sort_by_project_users_by_project_authorizations_user_id
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64528
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334167
-milestone: '14.1'
-type: development
-group: group::optimize
-default_enabled: true
diff --git a/config/feature_flags/development/specialized_worker_for_project_share_update_auth_recalculation.yml b/config/feature_flags/development/specialized_worker_for_project_share_update_auth_recalculation.yml
deleted file mode 100644
index 5e7d3819a4..0000000000
--- a/config/feature_flags/development/specialized_worker_for_project_share_update_auth_recalculation.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: specialized_worker_for_project_share_update_auth_recalculation
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61964
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334234
-milestone: '14.1'
-type: development
-group: group::access
-default_enabled: false
diff --git a/config/feature_flags/development/specialized_worker_for_project_transfer_auth_recalculation.yml b/config/feature_flags/development/specialized_worker_for_project_transfer_auth_recalculation.yml
deleted file mode 100644
index b77ed60750..0000000000
--- a/config/feature_flags/development/specialized_worker_for_project_transfer_auth_recalculation.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: specialized_worker_for_project_transfer_auth_recalculation
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61967
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334237
-milestone: '14.1'
-type: development
-group: group::access
-default_enabled: false
diff --git a/config/feature_flags/development/suppress_apollo_errors_during_navigation.yml b/config/feature_flags/development/suppress_apollo_errors_during_navigation.yml
new file mode 100644
index 0000000000..21548fa4db
--- /dev/null
+++ b/config/feature_flags/development/suppress_apollo_errors_during_navigation.yml
@@ -0,0 +1,8 @@
+---
+name: suppress_apollo_errors_during_navigation
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72031
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342745
+milestone: '14.4'
+type: development
+group: group::foundations
+default_enabled: false
diff --git a/config/feature_flags/development/surface_environment_creation_failure.yml b/config/feature_flags/development/surface_environment_creation_failure.yml
new file mode 100644
index 0000000000..2c312d432e
--- /dev/null
+++ b/config/feature_flags/development/surface_environment_creation_failure.yml
@@ -0,0 +1,8 @@
+---
+name: surface_environment_creation_failure
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69537
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340169
+milestone: '14.4'
+type: development
+group: group::release
+default_enabled: false
diff --git a/config/feature_flags/development/surface_environment_creation_failure_override.yml b/config/feature_flags/development/surface_environment_creation_failure_override.yml
new file mode 100644
index 0000000000..566281bcb8
--- /dev/null
+++ b/config/feature_flags/development/surface_environment_creation_failure_override.yml
@@ -0,0 +1,8 @@
+---
+name: surface_environment_creation_failure_override
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69537
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340169
+milestone: '14.4'
+type: development
+group: group::release
+default_enabled: false
diff --git a/config/feature_flags/development/tags_finder_gitaly.yml b/config/feature_flags/development/tags_finder_gitaly.yml
new file mode 100644
index 0000000000..065a253a69
--- /dev/null
+++ b/config/feature_flags/development/tags_finder_gitaly.yml
@@ -0,0 +1,8 @@
+---
+name: tags_finder_gitaly
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69101
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339741
+milestone: '14.3'
+type: development
+group: group::source code
+default_enabled: true
diff --git a/config/feature_flags/development/track_epic_boards_activity.yml b/config/feature_flags/development/track_epic_boards_activity.yml
deleted file mode 100644
index df48cc5a85..0000000000
--- a/config/feature_flags/development/track_epic_boards_activity.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: track_epic_boards_activity
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60357
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/338038
-milestone: '13.12'
-type: development
-group: group::product planning
-default_enabled: true
diff --git a/config/feature_flags/development/track_importer_activity.yml b/config/feature_flags/development/track_importer_activity.yml
new file mode 100644
index 0000000000..9f20a14790
--- /dev/null
+++ b/config/feature_flags/development/track_importer_activity.yml
@@ -0,0 +1,8 @@
+---
+name: track_importer_activity
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70012
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339392
+milestone: '14.4'
+type: development
+group: group::import
+default_enabled: false
diff --git a/config/feature_flags/development/update_deployment_after_transaction_commit.yml b/config/feature_flags/development/update_deployment_after_transaction_commit.yml
new file mode 100644
index 0000000000..c07622fc9b
--- /dev/null
+++ b/config/feature_flags/development/update_deployment_after_transaction_commit.yml
@@ -0,0 +1,8 @@
+---
+name: update_deployment_after_transaction_commit
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71450
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342021
+milestone: '14.4'
+type: development
+group: group::release
+default_enabled: false
diff --git a/config/feature_flags/development/usage_data_i_testing_group_code_coverage_visit_total.yml b/config/feature_flags/development/usage_data_i_testing_group_code_coverage_visit_total.yml
deleted file mode 100644
index 720b94fcf6..0000000000
--- a/config/feature_flags/development/usage_data_i_testing_group_code_coverage_visit_total.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: usage_data_i_testing_group_code_coverage_visit_total
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51382
-rollout_issue_url:
-milestone: '13.8'
-type: development
-group: group::testing
-default_enabled: true
diff --git a/config/feature_flags/development/usage_data_i_testing_metrics_report_artifact_uploaders.yml b/config/feature_flags/development/usage_data_i_testing_metrics_report_artifact_uploaders.yml
deleted file mode 100644
index 968ab3e63f..0000000000
--- a/config/feature_flags/development/usage_data_i_testing_metrics_report_artifact_uploaders.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: usage_data_i_testing_metrics_report_artifact_uploaders
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51670
-rollout_issue_url:
-milestone: '13.9'
-type: development
-group: group::testing
-default_enabled: true
diff --git a/config/feature_flags/development/usage_data_i_testing_summary_widget_total.yml b/config/feature_flags/development/usage_data_i_testing_summary_widget_total.yml
deleted file mode 100644
index fb06ea9f58..0000000000
--- a/config/feature_flags/development/usage_data_i_testing_summary_widget_total.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: usage_data_i_testing_summary_widget_total
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57543
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326058
-milestone: '13.11'
-type: development
-group: group::testing
-default_enabled: true
diff --git a/config/feature_flags/development/usage_data_i_testing_test_case_parsed.yml b/config/feature_flags/development/usage_data_i_testing_test_case_parsed.yml
deleted file mode 100644
index e6e3cd09c2..0000000000
--- a/config/feature_flags/development/usage_data_i_testing_test_case_parsed.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: usage_data_i_testing_test_case_parsed
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41918
-rollout_issue_url:
-milestone: '13.5'
-type: development
-group: group::testing
-default_enabled: true
diff --git a/config/feature_flags/development/use_upsert_query_for_mr_metrics.yml b/config/feature_flags/development/use_upsert_query_for_mr_metrics.yml
index 14cc5d1a98..605bc54b78 100644
--- a/config/feature_flags/development/use_upsert_query_for_mr_metrics.yml
+++ b/config/feature_flags/development/use_upsert_query_for_mr_metrics.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339677
milestone: '14.3'
type: development
group: group::optimize
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/validate_namespace_parent_type.yml b/config/feature_flags/development/validate_namespace_parent_type.yml
index dc89c462f1..5c2e0add24 100644
--- a/config/feature_flags/development/validate_namespace_parent_type.yml
+++ b/config/feature_flags/development/validate_namespace_parent_type.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/322101
milestone: '13.10'
type: development
group: group::access
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/variable_inside_variable.yml b/config/feature_flags/development/variable_inside_variable.yml
index 2060958590..fee4897b3f 100644
--- a/config/feature_flags/development/variable_inside_variable.yml
+++ b/config/feature_flags/development/variable_inside_variable.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/297382
milestone: '13.11'
type: development
group: group::runner
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/vulnerability_flags.yml b/config/feature_flags/development/vulnerability_flags.yml
deleted file mode 100644
index 6ea7dd2e3f..0000000000
--- a/config/feature_flags/development/vulnerability_flags.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: vulnerability_flags
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66775
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340203
-milestone: '14.3'
-type: development
-group: group::static analysis
-default_enabled: true
diff --git a/config/feature_flags/development/vulnerability_location_image_filter.yml b/config/feature_flags/development/vulnerability_location_image_filter.yml
new file mode 100644
index 0000000000..4b373b76ff
--- /dev/null
+++ b/config/feature_flags/development/vulnerability_location_image_filter.yml
@@ -0,0 +1,8 @@
+---
+name: vulnerability_location_image_filter
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69867
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340915
+milestone: '14.4'
+type: development
+group: group::container security
+default_enabled: false
diff --git a/config/feature_flags/development/workhorse_use_sidechannel.yml b/config/feature_flags/development/workhorse_use_sidechannel.yml
new file mode 100644
index 0000000000..f39d313bf1
--- /dev/null
+++ b/config/feature_flags/development/workhorse_use_sidechannel.yml
@@ -0,0 +1,8 @@
+---
+name: workhorse_use_sidechannel
+introduced_by_url:
+rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1193
+milestone: '14.4'
+type: development
+group: 'group::scalability'
+default_enabled: false
diff --git a/config/feature_flags/experiment/jobs_to_be_done.yml b/config/feature_flags/experiment/jobs_to_be_done.yml
deleted file mode 100644
index 5589d33a3c..0000000000
--- a/config/feature_flags/experiment/jobs_to_be_done.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: jobs_to_be_done
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60038
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/285564
-milestone: '13.12'
-type: experiment
-group: group::adoption
-default_enabled: false
diff --git a/config/feature_flags/experiment/new_project_sast_enabled.yml b/config/feature_flags/experiment/new_project_sast_enabled.yml
new file mode 100644
index 0000000000..f47c01d26a
--- /dev/null
+++ b/config/feature_flags/experiment/new_project_sast_enabled.yml
@@ -0,0 +1,8 @@
+---
+name: new_project_sast_enabled
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70548
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340929
+milestone: '14.4'
+type: experiment
+group: group::adoption
+default_enabled: false
diff --git a/config/feature_flags/ops/ecomm_instrumentation.yml b/config/feature_flags/ops/ecomm_instrumentation.yml
new file mode 100644
index 0000000000..e35937fa34
--- /dev/null
+++ b/config/feature_flags/ops/ecomm_instrumentation.yml
@@ -0,0 +1,8 @@
+---
+name: ecomm_instrumentation
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71243
+rollout_issue_url:
+milestone: '14.4'
+type: ops
+group: group::product intelligence
+default_enabled: false
diff --git a/config/feature_flags/ops/show_terraform_banner.yml b/config/feature_flags/ops/show_terraform_banner.yml
new file mode 100644
index 0000000000..a4ec831f4e
--- /dev/null
+++ b/config/feature_flags/ops/show_terraform_banner.yml
@@ -0,0 +1,8 @@
+---
+name: show_terraform_banner
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71462
+rollout_issue_url:
+milestone: '14.4'
+type: ops
+group: group::configure
+default_enabled: true
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 55b165b192..bb69c215f8 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -1220,6 +1220,9 @@ production: &base
# The URL to the internal KAS API (used by the GitLab backend)
# internal_url: grpc://localhost:8153
+ # The URL to the Kubernetes API proxy (used by GitLab users)
+ # external_k8s_proxy_url: https://localhost:8154 # default: nil
+
## GitLab Elasticsearch settings
elasticsearch:
indexer_path: /home/git/gitlab-elasticsearch-indexer/
@@ -1294,6 +1297,9 @@ production: &base
## Google tag manager
# google_tag_manager_id: '_your_tracking_id'
+ ## OneTrust
+ # one_trust_id: '_your_one_trust_id'
+
## Matomo analytics.
# matomo_url: '_your_matomo_url'
# matomo_site_id: '_your_matomo_site_id'
diff --git a/config/initializers/00_active_record_disable_joins.rb b/config/initializers/00_active_record_disable_joins.rb
new file mode 100644
index 0000000000..6348fd7c53
--- /dev/null
+++ b/config/initializers/00_active_record_disable_joins.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module ActiveRecordRelationAllowCrossJoins
+ def allow_cross_joins_across_databases(url:)
+ # this method is implemented in:
+ # spec/support/database/prevent_cross_joins.rb
+ self
+ end
+end
+
+ActiveRecord::Relation.prepend(ActiveRecordRelationAllowCrossJoins)
diff --git a/config/initializers/0_acts_as_taggable.rb b/config/initializers/0_acts_as_taggable.rb
index 04619590e3..8dee3c52a5 100644
--- a/config/initializers/0_acts_as_taggable.rb
+++ b/config/initializers/0_acts_as_taggable.rb
@@ -11,8 +11,8 @@ raise "Counter cache is not disabled" if
ActsAsTaggableOn::Tagging.reflections["tag"].options[:counter_cache]
ActsAsTaggableOn::Tagging.include IgnorableColumns
-ActsAsTaggableOn::Tagging.ignore_column :id_convert_to_bigint, remove_with: '14.2', remove_after: '2021-08-22'
-ActsAsTaggableOn::Tagging.ignore_column :taggable_id_convert_to_bigint, remove_with: '14.2', remove_after: '2021-08-22'
+ActsAsTaggableOn::Tagging.ignore_column :id_convert_to_bigint, remove_with: '14.5', remove_after: '2021-10-22'
+ActsAsTaggableOn::Tagging.ignore_column :taggable_id_convert_to_bigint, remove_with: '14.5', remove_after: '2021-10-22'
# The tags and taggings are supposed to be part of `gitlab_ci`
ActsAsTaggableOn::Tag.gitlab_schema = :gitlab_ci
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index e13061655c..d6957491b1 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -465,9 +465,6 @@ Settings.cron_jobs['personal_access_tokens_expired_notification_worker']['job_cl
Settings.cron_jobs['repository_archive_cache_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['repository_archive_cache_worker']['cron'] ||= '0 * * * *'
Settings.cron_jobs['repository_archive_cache_worker']['job_class'] = 'RepositoryArchiveCacheWorker'
-Settings.cron_jobs['packages_composer_cache_cleanup_worker'] ||= Settingslogic.new({})
-Settings.cron_jobs['packages_composer_cache_cleanup_worker']['cron'] ||= '30 * * * *'
-Settings.cron_jobs['packages_composer_cache_cleanup_worker']['job_class'] = 'Packages::Composer::CacheCleanupWorker'
Settings.cron_jobs['import_export_project_cleanup_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['import_export_project_cleanup_worker']['cron'] ||= '0 * * * *'
Settings.cron_jobs['import_export_project_cleanup_worker']['job_class'] = 'ImportExportProjectCleanupWorker'
@@ -534,6 +531,9 @@ Settings.cron_jobs['namespaces_prune_aggregation_schedules_worker']['job_class']
Settings.cron_jobs['container_expiration_policy_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['container_expiration_policy_worker']['cron'] ||= '50 * * * *'
Settings.cron_jobs['container_expiration_policy_worker']['job_class'] = 'ContainerExpirationPolicyWorker'
+Settings.cron_jobs['image_ttl_group_policy_worker'] ||= Settingslogic.new({})
+Settings.cron_jobs['image_ttl_group_policy_worker']['cron'] ||= '40 0 * * *'
+Settings.cron_jobs['image_ttl_group_policy_worker']['job_class'] = 'DependencyProxy::ImageTtlGroupPolicyWorker'
Settings.cron_jobs['x509_issuer_crl_check_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['x509_issuer_crl_check_worker']['cron'] ||= '30 1 * * *'
Settings.cron_jobs['x509_issuer_crl_check_worker']['job_class'] = 'X509IssuerCrlCheckWorker'
@@ -756,6 +756,7 @@ Settings.gitlab_kas['enabled'] ||= false
Settings.gitlab_kas['secret_file'] ||= Rails.root.join('.gitlab_kas_secret')
Settings.gitlab_kas['external_url'] ||= 'wss://kas.example.com'
Settings.gitlab_kas['internal_url'] ||= 'grpc://localhost:8153'
+# Settings.gitlab_kas['external_k8s_proxy_url'] ||= 'grpc://localhost:8154' # NOTE: Do not set a default until all distributions have been updated with a correct value
#
# Repositories
diff --git a/config/initializers/7_prometheus_metrics.rb b/config/initializers/7_prometheus_metrics.rb
index d2d546a543..587d393fd7 100644
--- a/config/initializers/7_prometheus_metrics.rb
+++ b/config/initializers/7_prometheus_metrics.rb
@@ -48,7 +48,7 @@ if !Rails.env.test? && Gitlab::Metrics.prometheus_metrics_enabled?
Gitlab::Metrics.gauge(:deployments, 'GitLab Version', {}, :max).set({ version: Gitlab::VERSION, revision: Gitlab.revision }, 1)
- unless Gitlab::Runtime.sidekiq?
+ if Gitlab::Runtime.web_server?
Gitlab::Metrics::RequestsRackMiddleware.initialize_metrics
end
diff --git a/config/initializers/7_redis.rb b/config/initializers/7_redis.rb
index 84aa231089..50f0fb9231 100644
--- a/config/initializers/7_redis.rb
+++ b/config/initializers/7_redis.rb
@@ -1,5 +1,11 @@
# frozen_string_literal: true
+# We set the instance variable directly to suppress warnings.
+# We cannot switch to the new behavior until we change all existing `redis.exists` calls to `redis.exists?`.
+# Some gems also need to be updated
+# https://gitlab.com/gitlab-org/gitlab/-/issues/340602
+Redis.instance_variable_set(:@exists_returns_integer, false)
+
Redis::Client.prepend(Gitlab::Instrumentation::RedisInterceptor)
# Make sure we initialize a Redis connection pool before multi-threaded
@@ -11,3 +17,5 @@ Gitlab::Redis::Cache.with { nil }
Gitlab::Redis::Queues.with { nil }
Gitlab::Redis::SharedState.with { nil }
Gitlab::Redis::TraceChunks.with { nil }
+Gitlab::Redis::RateLimiting.with { nil }
+Gitlab::Redis::Sessions.with { nil }
diff --git a/config/initializers/action_cable.rb b/config/initializers/action_cable.rb
index 16d29f5910..a7ef5cc332 100644
--- a/config/initializers/action_cable.rb
+++ b/config/initializers/action_cable.rb
@@ -19,4 +19,4 @@ ActionCable::SubscriptionAdapter::Redis.redis_connector = lambda do |config|
end
Gitlab::ActionCable::RequestStoreCallbacks.install
-Gitlab::Database::LoadBalancing::ActionCableCallbacks.install if Gitlab::Database::LoadBalancing.enable?
+Gitlab::Database::LoadBalancing::ActionCableCallbacks.install
diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb
index f3e01d2344..4fd41bd4c7 100644
--- a/config/initializers/backtrace_silencers.rb
+++ b/config/initializers/backtrace_silencers.rb
@@ -4,7 +4,7 @@ Rails.backtrace_cleaner.remove_silencers!
# This allows us to see the proper caller of SQL calls in {development,test}.log
if (Rails.env.development? || Rails.env.test?) && Gitlab.ee?
- Rails.backtrace_cleaner.add_silencer { |line| %r(^ee/lib/gitlab/database/load_balancing).match?(line) }
+ Rails.backtrace_cleaner.add_silencer { |line| %r(^lib/gitlab/database/load_balancing).match?(line) }
end
Rails.backtrace_cleaner.add_silencer { |line| !Gitlab::APP_DIRS_PATTERN.match?(line) }
diff --git a/config/initializers/batch_loader.rb b/config/initializers/batch_loader.rb
index d88b43fbce..e542a26cb8 100644
--- a/config/initializers/batch_loader.rb
+++ b/config/initializers/batch_loader.rb
@@ -1,3 +1,13 @@
# frozen_string_literal: true
Rails.application.config.middleware.use(BatchLoader::Middleware)
+
+# Disables replace_methods by default.
+# See https://github.com/exAspArk/batch-loader#replacing-methods for more information.
+module BatchLoaderWithoutMethodReplacementByDefault
+ def batch(replace_methods: false, **kw_args, &batch_block)
+ super
+ end
+end
+
+BatchLoader.prepend(BatchLoaderWithoutMethodReplacementByDefault)
diff --git a/config/initializers/carrierwave_patch.rb b/config/initializers/carrierwave_patch.rb
index c8c6f75949..a9c7447854 100644
--- a/config/initializers/carrierwave_patch.rb
+++ b/config/initializers/carrierwave_patch.rb
@@ -49,14 +49,8 @@ module CarrierWave
local_file = local_directory.files.new(key: path)
expire_at = options[:expire_at] || ::Fog::Time.now + @uploader.fog_authenticated_url_expiration
case @uploader.fog_credentials[:provider]
- when 'AWS', 'Google'
- # Older versions of fog-google do not support options as a parameter
- if url_options_supported?(local_file)
- local_file.url(expire_at, options)
- else
- warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
- local_file.url(expire_at)
- end
+ when 'AWS', 'Google', 'AzureRM'
+ local_file.url(expire_at, options)
when 'Rackspace'
connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
when 'OpenStack'
diff --git a/config/initializers/database_config.rb b/config/initializers/database_config.rb
index e9f10abd0b..7aedf9013a 100644
--- a/config/initializers/database_config.rb
+++ b/config/initializers/database_config.rb
@@ -1,15 +1,5 @@
# frozen_string_literal: true
-def log_pool_size(db, previous_pool_size, current_pool_size)
- log_message = ["#{db} connection pool size: #{current_pool_size}"]
-
- if previous_pool_size && current_pool_size > previous_pool_size
- log_message << "(increased from #{previous_pool_size} to match thread count)"
- end
-
- Gitlab::AppLogger.debug(log_message.join(' '))
-end
-
Gitlab.ee do
# We need to initialize the Geo database before
# setting the Geo DB connection pool size.
diff --git a/config/initializers/gettext_rails_i18n_patch.rb b/config/initializers/gettext_rails_i18n_patch.rb
index c23049e93c..3c994516b2 100644
--- a/config/initializers/gettext_rails_i18n_patch.rb
+++ b/config/initializers/gettext_rails_i18n_patch.rb
@@ -1,30 +1,8 @@
# frozen_string_literal: true
-require 'gettext_i18n_rails/haml_parser'
require 'gettext_i18n_rails_js/parser/javascript'
require 'json'
-VUE_TRANSLATE_REGEX = /((%[\w.-]+)(?:\s))?{{ (N|n|s)?__\((.*)\) }}/.freeze
-
-module GettextI18nRails
- class HamlParser
- singleton_class.send(:alias_method, :old_convert_to_code, :convert_to_code)
-
- # We need to convert text in Mustache format
- # to a format that can be parsed by Gettext scripts.
- # If we found a content like "{{ __('Stage') }}"
- # in a HAML file we convert it to "= _('Stage')", that way
- # it can be processed by the "rake gettext:find" script.
- #
- # Overwrites: https://github.com/grosser/gettext_i18n_rails/blob/8396387a431e0f8ead72fc1cd425cad2fa4992f2/lib/gettext_i18n_rails/haml_parser.rb#L9
- def self.convert_to_code(text)
- text.gsub!(VUE_TRANSLATE_REGEX, "\\2= \\3_(\\4)")
-
- old_convert_to_code(text)
- end
- end
-end
-
module GettextI18nRailsJs
module Parser
module Javascript
diff --git a/config/initializers/grape_validators.rb b/config/initializers/grape_validators.rb
index 07dd70822a..1492894e1f 100644
--- a/config/initializers/grape_validators.rb
+++ b/config/initializers/grape_validators.rb
@@ -10,3 +10,4 @@ Grape::Validations.register_validator(:check_assignees_count, ::API::Validations
Grape::Validations.register_validator(:untrusted_regexp, ::API::Validations::Validators::UntrustedRegexp)
Grape::Validations.register_validator(:email_or_email_list, ::API::Validations::Validators::EmailOrEmailList)
Grape::Validations.register_validator(:iteration_id, ::API::Validations::Validators::IntegerOrCustomValue)
+Grape::Validations.register_validator(:project_portable, ::API::Validations::Validators::ProjectPortable)
diff --git a/config/initializers/load_balancing.rb b/config/initializers/load_balancing.rb
index 2b58ae0f64..a31b11bb2b 100644
--- a/config/initializers/load_balancing.rb
+++ b/config/initializers/load_balancing.rb
@@ -1,28 +1,33 @@
# frozen_string_literal: true
-ActiveRecord::Base.singleton_class.attr_accessor :load_balancing_proxy
+Gitlab::Application.configure do |config|
+ config.middleware.use(Gitlab::Database::LoadBalancing::RackMiddleware)
+end
-if Gitlab::Database::LoadBalancing.enable?
- Gitlab::Database.main.disable_prepared_statements
+Gitlab::Database::LoadBalancing.base_models.each do |model|
+ # The load balancer needs to be configured immediately, and re-configured
+ # after forking. This ensures queries that run before forking use the load
+ # balancer, and queries running after a fork don't run into any errors when
+ # using dead database connections.
+ #
+ # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63485 for more
+ # information.
+ Gitlab::Database::LoadBalancing::Setup.new(model).setup
- Gitlab::Application.configure do |config|
- config.middleware.use(Gitlab::Database::LoadBalancing::RackMiddleware)
+ # Database queries may be run before we fork, so we must set up the load
+ # balancer as early as possible. When we do fork, we need to make sure all the
+ # hosts are disconnected.
+ Gitlab::Cluster::LifecycleEvents.on_before_fork do
+ # When forking, we don't want to wait until the connections aren't in use
+ # any more, as this could delay the boot cycle.
+ model.connection.load_balancer.disconnect!(timeout: 0)
end
- # This hijacks the "connection" method to ensure both
- # `ActiveRecord::Base.connection` and all models use the same load
- # balancing proxy.
- ActiveRecord::Base.singleton_class.prepend(Gitlab::Database::LoadBalancing::ActiveRecordProxy)
-
- Gitlab::Database::LoadBalancing.configure_proxy
-
- # This needs to be executed after fork of clustered processes
+ # Service discovery only needs to run in the worker processes, as the main one
+ # won't be running many (if any) database queries.
Gitlab::Cluster::LifecycleEvents.on_worker_start do
- # For Host-based LB, we need to re-connect as Rails discards connections on fork
- Gitlab::Database::LoadBalancing.configure_proxy
-
- # Service discovery must be started after configuring the proxy, as service
- # discovery depends on this.
- Gitlab::Database::LoadBalancing.start_service_discovery
+ Gitlab::Database::LoadBalancing::Setup
+ .new(model, start_service_discovery: true)
+ .setup
end
end
diff --git a/config/initializers/mailer_retries.rb b/config/initializers/mailer_retries.rb
index 64fb0ffaa5..5980513af9 100644
--- a/config/initializers/mailer_retries.rb
+++ b/config/initializers/mailer_retries.rb
@@ -1,41 +1,5 @@
# frozen_string_literal: true
-class ActiveJob::QueueAdapters::SidekiqAdapter
- # With Sidekiq 6, we can do something like:
- # class ActionMailer::MailDeliveryJob
- # sidekiq_options retry: 3
- # end
- #
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/329430
- raise "Update this monkey patch: #{__FILE__}" unless Sidekiq::VERSION == '5.2.9'
-
- def enqueue(job) #:nodoc:
- # Sidekiq::Client does not support symbols as keys
- job.provider_job_id = Sidekiq::Client.push \
- "class" => JobWrapper,
- "wrapped" => job.class.to_s,
- "queue" => job.queue_name,
- "args" => [job.serialize],
- "retry" => retry_for(job)
- end
-
- def enqueue_at(job, timestamp) #:nodoc:
- job.provider_job_id = Sidekiq::Client.push \
- "class" => JobWrapper,
- "wrapped" => job.class.to_s,
- "queue" => job.queue_name,
- "args" => [job.serialize],
- "at" => timestamp,
- "retry" => retry_for(job)
- end
-
- private
-
- def retry_for(job)
- if job.queue_name == 'mailers'
- 3
- else
- true
- end
- end
+Rails.application.config.after_initialize do
+ ActionMailer::MailDeliveryJob.sidekiq_options retry: 3
end
diff --git a/config/initializers/postgres_partitioning.rb b/config/initializers/postgres_partitioning.rb
index f2e2fba155..49f382547d 100644
--- a/config/initializers/postgres_partitioning.rb
+++ b/config/initializers/postgres_partitioning.rb
@@ -2,8 +2,7 @@
Gitlab::Database::Partitioning.register_models([
AuditEvent,
- WebHookLog,
- LooseForeignKeys::DeletedRecord
+ WebHookLog
])
if Gitlab.ee?
diff --git a/config/initializers/postgresql_cte.rb b/config/initializers/postgresql_cte.rb
index 6a9af7b486..7d00776e46 100644
--- a/config/initializers/postgresql_cte.rb
+++ b/config/initializers/postgresql_cte.rb
@@ -96,7 +96,7 @@ module ActiveRecord
end
end
- def build_arel(aliases)
+ def build_arel(aliases = nil)
arel = super
build_with(arel) if @values[:with]
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 19c5e4df85..d33550b82d 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -27,8 +27,14 @@ use_sidekiq_daemon_memory_killer = ENV.fetch("SIDEKIQ_DAEMON_MEMORY_KILLER", 1).
use_sidekiq_legacy_memory_killer = !use_sidekiq_daemon_memory_killer
Sidekiq.configure_server do |config|
+ config.options[:strict] = false
+ config.options[:queues] = Gitlab::SidekiqConfig.expand_queues(config.options[:queues])
+ config.options[:scheduled_enq] = Gitlab::SidekiqEnq
+
+ Sidekiq.logger.info "Listening on queues #{config.options[:queues].uniq.sort}"
+
if enable_json_logs
- Sidekiq.logger.formatter = Gitlab::SidekiqLogging::JSONFormatter.new
+ config.log_formatter = Gitlab::SidekiqLogging::JSONFormatter.new
config.options[:job_logger] = Gitlab::SidekiqLogging::StructuredLogger
# Remove the default-provided handler. The exception is logged inside
@@ -38,11 +44,11 @@ Sidekiq.configure_server do |config|
config.redis = queues_config_hash
- config.server_middleware(&Gitlab::SidekiqMiddleware.server_configurator({
+ config.server_middleware(&Gitlab::SidekiqMiddleware.server_configurator(
metrics: Settings.monitoring.sidekiq_exporter,
arguments_logger: SidekiqLogArguments.enabled? && !enable_json_logs,
memory_killer: enable_sidekiq_memory_killer && use_sidekiq_legacy_memory_killer
- }))
+ ))
config.client_middleware(&Gitlab::SidekiqMiddleware.client_configurator)
diff --git a/config/initializers/zz_metrics.rb b/config/initializers/zz_metrics.rb
index 25e4ec0d48..8e69e1634f 100644
--- a/config/initializers/zz_metrics.rb
+++ b/config/initializers/zz_metrics.rb
@@ -3,128 +3,6 @@
# This file was prefixed with zz_ because we want to load it the last!
# See: https://gitlab.com/gitlab-org/gitlab-foss/issues/55611
-# Autoload all classes that we want to instrument, and instrument the methods we
-# need. This takes the Gitlab::Metrics::Instrumentation module as an argument so
-# that we can stub it for testing, as it is only called when metrics are
-# enabled.
-#
-# rubocop:disable Metrics/AbcSize
-def instrument_classes(instrumentation)
- return if ENV['STATIC_VERIFICATION']
-
- instrumentation.instrument_instance_methods(Gitlab::Shell)
-
- instrumentation.instrument_methods(Gitlab::Git)
-
- Gitlab::Git.constants.each do |name|
- const = Gitlab::Git.const_get(name, false)
-
- next unless const.is_a?(Module)
-
- instrumentation.instrument_methods(const)
- instrumentation.instrument_instance_methods(const)
- end
-
- # Path to search => prefix to strip from constant
- paths_to_instrument = {
- %w(app finders) => %w(app finders),
- %w(app mailers emails) => %w(app mailers),
- # Don't instrument `app/services/concerns`
- # It contains modules that are included in the services.
- # The services themselves are instrumented so the methods from the modules
- # are included.
- %w(app services [^concerns]**) => %w(app services),
- %w(lib gitlab conflicts) => ['lib'],
- %w(lib gitlab email message) => ['lib'],
- %w(lib gitlab checks) => ['lib']
- }
-
- paths_to_instrument.each do |(path, prefix)|
- prefix = Rails.root.join(*prefix)
-
- Dir[Rails.root.join(*path + ['*.rb'])].each do |file_path|
- path = Pathname.new(file_path).relative_path_from(prefix)
- const = path.to_s.sub('.rb', '').camelize.constantize
-
- instrumentation.instrument_methods(const)
- instrumentation.instrument_instance_methods(const)
- end
- end
-
- instrumentation.instrument_methods(Premailer::Adapter::Nokogiri)
- instrumentation.instrument_instance_methods(Premailer::Adapter::Nokogiri)
-
- instrumentation.instrument_methods(Banzai::Renderer)
- instrumentation.instrument_methods(Banzai::Querying)
-
- instrumentation.instrument_instance_methods(Banzai::ObjectRenderer)
- instrumentation.instrument_instance_methods(Banzai::ReferenceRedactor)
-
- [Issuable, Mentionable, Participable].each do |klass|
- instrumentation.instrument_instance_methods(klass)
- instrumentation.instrument_instance_methods(klass::ClassMethods)
- end
-
- instrumentation.instrument_methods(Gitlab::ReferenceExtractor)
- instrumentation.instrument_instance_methods(Gitlab::ReferenceExtractor)
-
- # Instrument the classes used for checking if somebody has push access.
- instrumentation.instrument_instance_methods(Gitlab::GitAccess)
- instrumentation.instrument_instance_methods(Gitlab::GitAccessWiki)
-
- instrumentation.instrument_instance_methods(API::Helpers)
-
- instrumentation.instrument_instance_methods(RepositoryCheck::SingleRepositoryWorker)
-
- instrumentation.instrument_instance_methods(Rouge::Formatters::HTMLGitlab)
-
- [:XML, :HTML].each do |namespace|
- namespace_mod = Nokogiri.const_get(namespace, false)
-
- instrumentation.instrument_methods(namespace_mod)
- instrumentation.instrument_methods(namespace_mod::Document)
- end
-
- instrumentation.instrument_methods(Rinku)
- instrumentation.instrument_instance_methods(Repository)
-
- instrumentation.instrument_methods(Gitlab::Highlight)
- instrumentation.instrument_instance_methods(Gitlab::Highlight)
- instrumentation.instrument_instance_method(Gitlab::Ci::Config::Yaml::Tags::Resolver, :to_hash)
-
- Gitlab.ee do
- instrumentation.instrument_instance_methods(Elastic::Latest::GitInstanceProxy)
- instrumentation.instrument_instance_methods(Elastic::Latest::GitClassProxy)
-
- instrumentation.instrument_instance_methods(Search::GlobalService)
- instrumentation.instrument_instance_methods(Search::ProjectService)
-
- instrumentation.instrument_instance_methods(Gitlab::Elastic::SearchResults)
- instrumentation.instrument_instance_methods(Gitlab::Elastic::ProjectSearchResults)
- instrumentation.instrument_instance_methods(Gitlab::Elastic::Indexer)
- instrumentation.instrument_instance_methods(Gitlab::Elastic::SnippetSearchResults)
- instrumentation.instrument_instance_methods(Gitlab::Elastic::Helper)
-
- instrumentation.instrument_instance_methods(Elastic::ApplicationVersionedSearch)
- instrumentation.instrument_instance_methods(Elastic::ProjectsSearch)
- instrumentation.instrument_instance_methods(Elastic::RepositoriesSearch)
- instrumentation.instrument_instance_methods(Elastic::SnippetsSearch)
- instrumentation.instrument_instance_methods(Elastic::WikiRepositoriesSearch)
-
- instrumentation.instrument_instance_methods(Gitlab::BitbucketImport::Importer)
- instrumentation.instrument_instance_methods(Bitbucket::Connection)
-
- instrumentation.instrument_instance_methods(Geo::RepositorySyncWorker)
- end
-
- # This is a Rails scope so we have to instrument it manually.
- instrumentation.instrument_method(Project, :visible_to_user)
-
- # Needed for https://gitlab.com/gitlab-org/gitlab-foss/issues/30224#note_32306159
- instrumentation.instrument_instance_method(MergeRequestDiff, :load_commits)
-end
-# rubocop:enable Metrics/AbcSize
-
# With prometheus enabled by default this breaks all specs
# that stubs methods using `any_instance_of` for the models reloaded here.
#
@@ -151,12 +29,8 @@ if Gitlab::Metrics.enabled? && !Rails.env.test? && !(Rails.env.development? && d
Gitlab::Application.configure do |config|
# We want to track certain metrics during the Load Balancing host resolving process.
# Because of that, we need to have metrics code available earlier for Load Balancing.
- if Gitlab::Database::LoadBalancing.enable?
- config.middleware.insert_before Gitlab::Database::LoadBalancing::RackMiddleware,
- Gitlab::Metrics::RackMiddleware
- else
- config.middleware.use(Gitlab::Metrics::RackMiddleware)
- end
+ config.middleware.insert_before Gitlab::Database::LoadBalancing::RackMiddleware,
+ Gitlab::Metrics::RackMiddleware
config.middleware.use(Gitlab::Middleware::RailsQueueDuration)
config.middleware.use(Gitlab::Metrics::ElasticsearchRackMiddleware)
diff --git a/config/initializers_before_autoloader/002_sidekiq.rb b/config/initializers_before_autoloader/002_sidekiq.rb
index 8e2def0827..9ffcf39d6f 100644
--- a/config/initializers_before_autoloader/002_sidekiq.rb
+++ b/config/initializers_before_autoloader/002_sidekiq.rb
@@ -8,10 +8,6 @@
require 'sidekiq/web'
-# Disable the Sidekiq Rack session since GitLab already has its own session store.
-# CSRF protection still works (https://github.com/mperham/sidekiq/commit/315504e766c4fd88a29b7772169060afc4c40329).
-Sidekiq::Web.set :sessions, false
-
if Rails.env.development?
Sidekiq.default_worker_options[:backtrace] = true
end
diff --git a/config/initializers_before_autoloader/grape_entity_patch.rb b/config/initializers_before_autoloader/grape_entity_patch.rb
deleted file mode 100644
index 2db5876e75..0000000000
--- a/config/initializers_before_autoloader/grape_entity_patch.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-# This can be removed after the problem gets fixed on upstream.
-# You can follow https://github.com/ruby-grape/grape-entity/pull/355 to see the progress.
-#
-# For more information about the issue;
-# https://github.com/ruby/did_you_mean/issues/158#issuecomment-906056018
-
-require 'grape-entity'
-
-module Grape
- class Entity
- # Upstream version: https://github.com/ruby-grape/grape-entity/blob/675d3c0e20dfc1d6cf6f5ba5b46741bd404c8be7/lib/grape_entity/entity.rb#L520
- def exec_with_object(options, &block)
- if block.parameters.count == 1
- instance_exec(object, &block)
- else
- instance_exec(object, options, &block)
- end
- rescue StandardError => e
- # it handles: https://github.com/ruby/ruby/blob/v3_0_0_preview1/NEWS.md#language-changes point 3, Proc
- raise Grape::Entity::Deprecated.new e.message, 'in ruby 3.0' if e.is_a?(ArgumentError)
-
- raise e
- end
- end
-end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 4615c9a739..233dca33bb 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -10,6 +10,9 @@ en:
target: Target issue
group:
path: Group URL
+ member:
+ user: "The member's email address"
+ invite_email: "The member's email address"
project/error_tracking_setting:
token: "Auth Token"
project: "Project"
diff --git a/config/mail_room.yml b/config/mail_room.yml
index 25bda294a1..895438dcc4 100644
--- a/config/mail_room.yml
+++ b/config/mail_room.yml
@@ -29,6 +29,7 @@
:delivery_method: sidekiq
:delivery_options:
:redis_url: <%= config[:redis_url].to_json %>
+ :redis_db: <%= config[:redis_db] %>
:namespace: <%= Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE %>
:queue: <%= config[:queue] %>
:worker: <%= config[:worker] %>
diff --git a/config/metrics/counts_28d/20210216175055_merge_requests.yml b/config/metrics/counts_28d/20210216175055_merge_requests.yml
index dca4f81eee..06c4696495 100644
--- a/config/metrics/counts_28d/20210216175055_merge_requests.yml
+++ b/config/metrics/counts_28d/20210216175055_merge_requests.yml
@@ -1,7 +1,7 @@
---
data_category: optional
key_path: usage_activity_by_stage_monthly.create.merge_requests
-description: Count of the number of users creating merge requests
+description: Count distinct authors of merge requests
product_section: dev
product_stage: create
product_group: group::code review
diff --git a/config/metrics/counts_28d/20210216175101_merge_requests_users.yml b/config/metrics/counts_28d/20210216175101_merge_requests_users.yml
index 04f0f124e8..2b43fbf813 100644
--- a/config/metrics/counts_28d/20210216175101_merge_requests_users.yml
+++ b/config/metrics/counts_28d/20210216175101_merge_requests_users.yml
@@ -1,7 +1,7 @@
---
data_category: optional
key_path: usage_activity_by_stage_monthly.create.merge_requests_users
-description: Monthly count of the number of merge request users
+description: Distinct count of users performing merge request actions like closed, merged, created, commented
product_section: dev
product_stage: create
product_group: group::code review
diff --git a/config/metrics/counts_28d/20210216175405_clusters_applications_cert_managers.yml b/config/metrics/counts_28d/20210216175405_clusters_applications_cert_managers.yml
index a34c679e91..76627a91d0 100644
--- a/config/metrics/counts_28d/20210216175405_clusters_applications_cert_managers.yml
+++ b/config/metrics/counts_28d/20210216175405_clusters_applications_cert_managers.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: 28d
data_source: database
distribution:
@@ -20,3 +20,4 @@ tier:
name: 'count_distinct_user_id_from_clusters_applications_cert_managers'
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_28d/20210216175407_clusters_applications_helm.yml b/config/metrics/counts_28d/20210216175407_clusters_applications_helm.yml
index 2cc0e937d9..ced2b45484 100644
--- a/config/metrics/counts_28d/20210216175407_clusters_applications_helm.yml
+++ b/config/metrics/counts_28d/20210216175407_clusters_applications_helm.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: 28d
data_source: database
distribution:
@@ -20,3 +20,4 @@ tier:
name: 'count_distinct_user_id_from_clusters_applications_helm'
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_28d/20210216175409_clusters_applications_ingress.yml b/config/metrics/counts_28d/20210216175409_clusters_applications_ingress.yml
index 2cdd680eae..eee5839794 100644
--- a/config/metrics/counts_28d/20210216175409_clusters_applications_ingress.yml
+++ b/config/metrics/counts_28d/20210216175409_clusters_applications_ingress.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: 28d
data_source: database
distribution:
@@ -20,3 +20,4 @@ tier:
name: 'count_distinct_user_id_from_clusters_applications_ingress'
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_28d/20210216175411_clusters_applications_knative.yml b/config/metrics/counts_28d/20210216175411_clusters_applications_knative.yml
index 23114b91d1..f12fe19122 100644
--- a/config/metrics/counts_28d/20210216175411_clusters_applications_knative.yml
+++ b/config/metrics/counts_28d/20210216175411_clusters_applications_knative.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: 28d
data_source: database
distribution:
@@ -20,3 +20,4 @@ tier:
name: 'count_distinct_user_id_from_clusters_applications_knative'
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_28d/20210216180958_clusters_applications_prometheus.yml b/config/metrics/counts_28d/20210216180958_clusters_applications_prometheus.yml
index b947a6ff7e..895746f512 100644
--- a/config/metrics/counts_28d/20210216180958_clusters_applications_prometheus.yml
+++ b/config/metrics/counts_28d/20210216180958_clusters_applications_prometheus.yml
@@ -7,7 +7,7 @@ product_stage: monitor
product_group: group::monitor
product_category: metrics
value_type: number
-status: active
+status: removed
time_frame: 28d
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_28d/20210216181951_clusters_applications_runner.yml b/config/metrics/counts_28d/20210216181951_clusters_applications_runner.yml
index 22702146bc..4d676e436b 100644
--- a/config/metrics/counts_28d/20210216181951_clusters_applications_runner.yml
+++ b/config/metrics/counts_28d/20210216181951_clusters_applications_runner.yml
@@ -7,7 +7,7 @@ product_stage: verify
product_group: group::runner
product_category: runner
value_type: number
-status: active
+status: removed
time_frame: 28d
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_28d/20210715094458_releases_with_milestones.yml b/config/metrics/counts_28d/20210715094458_releases_with_milestones.yml
new file mode 100644
index 0000000000..74f9406a13
--- /dev/null
+++ b/config/metrics/counts_28d/20210715094458_releases_with_milestones.yml
@@ -0,0 +1,23 @@
+---
+key_path: usage_activity_by_stage_monthly.release.releases_with_milestones
+description: Unique users creating releases with milestones associated
+performance_indicator_type: [smau]
+product_section: ops
+product_stage: release
+product_group: 'group::release'
+product_category: Release Orchestration
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71287'
+time_frame: 28d
+data_source: database
+instrumentation_class: 'CountUsersAssociatingMilestonesToReleasesMetric'
+data_category: Optional
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20210916080405_promoted_issues.yml b/config/metrics/counts_28d/20210916080405_promoted_issues.yml
index 106c3a6128..ec73682b1f 100644
--- a/config/metrics/counts_28d/20210916080405_promoted_issues.yml
+++ b/config/metrics/counts_28d/20210916080405_promoted_issues.yml
@@ -7,7 +7,7 @@ product_stage: growth
product_group: group::product intelligence
product_category: collection
value_type: number
-status: active
+status: deprecated
milestone: "14.3"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70485
time_frame: 28d
diff --git a/config/metrics/counts_28d/20210916201533_clusters_integrations_prometheus.yml b/config/metrics/counts_28d/20210916201533_clusters_integrations_prometheus.yml
new file mode 100644
index 0000000000..6ecc98dcad
--- /dev/null
+++ b/config/metrics/counts_28d/20210916201533_clusters_integrations_prometheus.yml
@@ -0,0 +1,21 @@
+---
+data_category: optional
+key_path: usage_activity_by_stage_monthly.monitor.clusters_integrations_prometheus
+description: Users creating clusters with Prometheus integration enabled in last 28 days.
+product_section: ops
+product_stage: monitor
+product_group: group::monitor
+product_category: metrics
+value_type: number
+status: active
+time_frame: 28d
+data_source: database
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+performance_indicator_type: []
+milestone: "14.4"
diff --git a/config/metrics/counts_28d/20210929102434_p_ci_templates_implicit_jobs_build_monthly.yml b/config/metrics/counts_28d/20210929102434_p_ci_templates_implicit_jobs_build_monthly.yml
new file mode 100644
index 0000000000..4fa6d3d884
--- /dev/null
+++ b/config/metrics/counts_28d/20210929102434_p_ci_templates_implicit_jobs_build_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_build_monthly
+description: ''
+product_section: ''
+product_stage: ''
+product_group: ''
+product_category: ''
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71157
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_build
diff --git a/config/metrics/counts_28d/20210929102736_p_ci_templates_implicit_jobs_deploy_latest_monthly.yml b/config/metrics/counts_28d/20210929102736_p_ci_templates_implicit_jobs_deploy_latest_monthly.yml
new file mode 100644
index 0000000000..9d19a6ffa7
--- /dev/null
+++ b/config/metrics/counts_28d/20210929102736_p_ci_templates_implicit_jobs_deploy_latest_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_deploy_latest_monthly
+description: ''
+product_section: ''
+product_stage: ''
+product_group: ''
+product_category: ''
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71157
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_deploy_latest
diff --git a/config/metrics/counts_28d/20210929103010_p_ci_templates_implicit_jobs_deploy_monthly.yml b/config/metrics/counts_28d/20210929103010_p_ci_templates_implicit_jobs_deploy_monthly.yml
new file mode 100644
index 0000000000..5b7b7924c4
--- /dev/null
+++ b/config/metrics/counts_28d/20210929103010_p_ci_templates_implicit_jobs_deploy_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_deploy_monthly
+description: ''
+product_section: ''
+product_stage: ''
+product_group: ''
+product_category: ''
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71157
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_deploy
diff --git a/config/metrics/counts_28d/20210930125418_github_import_project_start_monthly.yml b/config/metrics/counts_28d/20210930125418_github_import_project_start_monthly.yml
new file mode 100644
index 0000000000..2812aa73ca
--- /dev/null
+++ b/config/metrics/counts_28d/20210930125418_github_import_project_start_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.importer.github_import_project_start_monthly
+description: The number of github projects that were enqueued to start monthy
+product_section: dev
+product_stage: devops
+product_group: group::import
+product_category:
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70012
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - github_import_project_start
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20210930130531_github_import_project_success_monthly.yml b/config/metrics/counts_28d/20210930130531_github_import_project_success_monthly.yml
new file mode 100644
index 0000000000..ab599c6737
--- /dev/null
+++ b/config/metrics/counts_28d/20210930130531_github_import_project_success_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.importer.github_import_project_success_monthly
+description: The number of github projects that were successful monthly
+product_section: dev
+product_stage: devops
+product_group: group::import
+product_category:
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70012
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - github_import_project_success
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20210930163813_github_import_project_failure_monthly.yml b/config/metrics/counts_28d/20210930163813_github_import_project_failure_monthly.yml
new file mode 100644
index 0000000000..6651a77092
--- /dev/null
+++ b/config/metrics/counts_28d/20210930163813_github_import_project_failure_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.importer.github_import_project_failure_monthly
+description: The number of github projects that failed monthly
+product_section: dev
+product_stage: devops
+product_group: group::import
+product_category:
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70012
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - github_import_project_failure
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210916100524_groups_gitlab_slack_application_active.yml b/config/metrics/counts_7d/20210916100524_groups_gitlab_slack_application_active.yml
new file mode 100644
index 0000000000..9a23d73b13
--- /dev/null
+++ b/config/metrics/counts_7d/20210916100524_groups_gitlab_slack_application_active.yml
@@ -0,0 +1,23 @@
+---
+key_path: counts.groups_gitlab_slack_application_active
+name: count_groups_gitlab_slack_application_active
+description: Count groups with active slack application
+product_section: dev
+product_stage: ecosystem
+product_group: group::integrations
+product_category: integrations
+value_type: number
+status: active
+milestone: "14.3"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70496
+time_frame: 7d
+data_source: database
+data_category: optional
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210916101641_projects_gitlab_slack_application_active.yml b/config/metrics/counts_7d/20210916101641_projects_gitlab_slack_application_active.yml
new file mode 100644
index 0000000000..2d1124a597
--- /dev/null
+++ b/config/metrics/counts_7d/20210916101641_projects_gitlab_slack_application_active.yml
@@ -0,0 +1,23 @@
+---
+key_path: counts.projects_gitlab_slack_application_active
+name: count_project_gitlab_slack_application_active
+description: Count projects with active slack application
+product_section: dev
+product_stage: ecosystem
+product_group: group::integrations
+product_category: integrations
+value_type: number
+status: active
+milestone: "14.3"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70496
+time_frame: 7d
+data_source: database
+data_category: optional
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210916101837_instances_gitlab_slack_application_active.yml b/config/metrics/counts_7d/20210916101837_instances_gitlab_slack_application_active.yml
new file mode 100644
index 0000000000..8a04e024eb
--- /dev/null
+++ b/config/metrics/counts_7d/20210916101837_instances_gitlab_slack_application_active.yml
@@ -0,0 +1,23 @@
+---
+key_path: counts.instances_gitlab_slack_application_active
+name: count_instances_gitlab_slack_application_active
+description: Count instances with active slack application
+product_section: dev
+product_stage: ecosystem
+product_group: group::integrations
+product_category: integrations
+value_type: number
+status: active
+milestone: "14.3"
+introduced_by_url: https://gilab.com/gitlab-org/gitlab/-/merge_requests/70496
+time_frame: 7d
+data_source: database
+data_category: optional
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210916102312_templates_gitlab_slack_application_active.yml b/config/metrics/counts_7d/20210916102312_templates_gitlab_slack_application_active.yml
new file mode 100644
index 0000000000..d465e1b3d0
--- /dev/null
+++ b/config/metrics/counts_7d/20210916102312_templates_gitlab_slack_application_active.yml
@@ -0,0 +1,23 @@
+---
+key_path: counts.templates_gitlab_slack_application_active
+name: count_templates_gitlab_slack_application_active
+description: Count templates with active slack application
+product_section: dev
+product_stage: ecosystem
+product_group: group::integrations
+product_category: integrations
+value_type: number
+status: active
+milestone: "14.3"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70496
+time_frame: 7d
+data_source: database
+data_category: optional
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210917040700_groups_inheriting_gitlab_slack_application_active.yml b/config/metrics/counts_7d/20210917040700_groups_inheriting_gitlab_slack_application_active.yml
new file mode 100644
index 0000000000..2a62c45c54
--- /dev/null
+++ b/config/metrics/counts_7d/20210917040700_groups_inheriting_gitlab_slack_application_active.yml
@@ -0,0 +1,23 @@
+---
+key_path: counts.groups_inheriting_gitlab_slack_application_active
+name: count_groups_inheriting_gitlab_slack_application_active
+description: Count groups inheriting active slack application
+product_section: dev
+product_stage: ecosystem
+product_group: group::integrations
+product_category: integrations
+value_type: number
+status: active
+milestone: "14.3"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70496
+time_frame: 7d
+data_source: database
+data_category: optional
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210917040956_projects_inheriting_gitlab_slack_application_active.yml b/config/metrics/counts_7d/20210917040956_projects_inheriting_gitlab_slack_application_active.yml
new file mode 100644
index 0000000000..266670159b
--- /dev/null
+++ b/config/metrics/counts_7d/20210917040956_projects_inheriting_gitlab_slack_application_active.yml
@@ -0,0 +1,23 @@
+---
+key_path: counts.projects_inheriting_gitlab_slack_application_active
+name: count_project_inheriting_gitlab_slack_application_active
+description: Count projects inheriting active slack application
+product_section: dev
+product_stage: ecosystem
+product_group: group::integrations
+product_category: integrations
+value_type: number
+status: active
+milestone: "14.3"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70496
+time_frame: 7d
+data_source: database
+data_category: optional
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210929102429_p_ci_templates_implicit_jobs_build_weekly.yml b/config/metrics/counts_7d/20210929102429_p_ci_templates_implicit_jobs_build_weekly.yml
new file mode 100644
index 0000000000..f13174a4af
--- /dev/null
+++ b/config/metrics/counts_7d/20210929102429_p_ci_templates_implicit_jobs_build_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_build_weekly
+description: ''
+product_section: ''
+product_stage: ''
+product_group: ''
+product_category: ''
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71157
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_build
diff --git a/config/metrics/counts_7d/20210929102731_p_ci_templates_implicit_jobs_deploy_latest_weekly.yml b/config/metrics/counts_7d/20210929102731_p_ci_templates_implicit_jobs_deploy_latest_weekly.yml
new file mode 100644
index 0000000000..f4d0ac7ff9
--- /dev/null
+++ b/config/metrics/counts_7d/20210929102731_p_ci_templates_implicit_jobs_deploy_latest_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_deploy_latest_weekly
+description: ''
+product_section: ''
+product_stage: ''
+product_group: ''
+product_category: ''
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71157
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_deploy_latest
diff --git a/config/metrics/counts_7d/20210929103006_p_ci_templates_implicit_jobs_deploy_weekly.yml b/config/metrics/counts_7d/20210929103006_p_ci_templates_implicit_jobs_deploy_weekly.yml
new file mode 100644
index 0000000000..964b57c65b
--- /dev/null
+++ b/config/metrics/counts_7d/20210929103006_p_ci_templates_implicit_jobs_deploy_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_deploy_weekly
+description: ''
+product_section: ''
+product_stage: ''
+product_group: ''
+product_category: ''
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71157
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_deploy
diff --git a/config/metrics/counts_7d/20210930125411_github_import_project_start_weekly.yml b/config/metrics/counts_7d/20210930125411_github_import_project_start_weekly.yml
new file mode 100644
index 0000000000..2c5b7d46e1
--- /dev/null
+++ b/config/metrics/counts_7d/20210930125411_github_import_project_start_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.importer.github_import_project_start_weekly
+description: The number of github projects that were enqueued to start weekly
+product_section: dev
+product_stage: devops
+product_group: group::import
+product_category:
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70012
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - github_import_project_start
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210930130525_github_import_project_success_weekly.yml b/config/metrics/counts_7d/20210930130525_github_import_project_success_weekly.yml
new file mode 100644
index 0000000000..10147658dd
--- /dev/null
+++ b/config/metrics/counts_7d/20210930130525_github_import_project_success_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.importer.github_import_project_success_weekly
+description: The number of github projects that were successful weekly
+product_section: dev
+product_stage: devops
+product_group: group::import
+product_category:
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70012
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - github_import_project_success
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20210930163807_github_import_project_failure_weekly.yml b/config/metrics/counts_7d/20210930163807_github_import_project_failure_weekly.yml
new file mode 100644
index 0000000000..33a1902504
--- /dev/null
+++ b/config/metrics/counts_7d/20210930163807_github_import_project_failure_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.importer.github_import_project_failure_weekly
+description: The number of github projects that failed weekly
+product_section: dev
+product_stage: devops
+product_group: group::import
+product_category:
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70012
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - github_import_project_failure
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20210216175255_clusters_applications_helm.yml b/config/metrics/counts_all/20210216175255_clusters_applications_helm.yml
index 661d809ae6..e772f66951 100644
--- a/config/metrics/counts_all/20210216175255_clusters_applications_helm.yml
+++ b/config/metrics/counts_all/20210216175255_clusters_applications_helm.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175257_clusters_applications_ingress.yml b/config/metrics/counts_all/20210216175257_clusters_applications_ingress.yml
index 889e701a8d..1e31c28f54 100644
--- a/config/metrics/counts_all/20210216175257_clusters_applications_ingress.yml
+++ b/config/metrics/counts_all/20210216175257_clusters_applications_ingress.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175259_clusters_applications_cert_managers.yml b/config/metrics/counts_all/20210216175259_clusters_applications_cert_managers.yml
index a22cf9dd84..bc16040f14 100644
--- a/config/metrics/counts_all/20210216175259_clusters_applications_cert_managers.yml
+++ b/config/metrics/counts_all/20210216175259_clusters_applications_cert_managers.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175301_clusters_applications_crossplane.yml b/config/metrics/counts_all/20210216175301_clusters_applications_crossplane.yml
index 1fc50c89bd..2856a0e25a 100644
--- a/config/metrics/counts_all/20210216175301_clusters_applications_crossplane.yml
+++ b/config/metrics/counts_all/20210216175301_clusters_applications_crossplane.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175303_clusters_applications_prometheus.yml b/config/metrics/counts_all/20210216175303_clusters_applications_prometheus.yml
index b883f9cdd1..961c9adfdb 100644
--- a/config/metrics/counts_all/20210216175303_clusters_applications_prometheus.yml
+++ b/config/metrics/counts_all/20210216175303_clusters_applications_prometheus.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175305_clusters_applications_runner.yml b/config/metrics/counts_all/20210216175305_clusters_applications_runner.yml
index 5f9c9a9c0d..33f8d045d2 100644
--- a/config/metrics/counts_all/20210216175305_clusters_applications_runner.yml
+++ b/config/metrics/counts_all/20210216175305_clusters_applications_runner.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175307_clusters_applications_knative.yml b/config/metrics/counts_all/20210216175307_clusters_applications_knative.yml
index 8b5f50c6c7..e5a80ca638 100644
--- a/config/metrics/counts_all/20210216175307_clusters_applications_knative.yml
+++ b/config/metrics/counts_all/20210216175307_clusters_applications_knative.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175309_clusters_applications_elastic_stack.yml b/config/metrics/counts_all/20210216175309_clusters_applications_elastic_stack.yml
index fafbf8a7d4..4d5dbcd198 100644
--- a/config/metrics/counts_all/20210216175309_clusters_applications_elastic_stack.yml
+++ b/config/metrics/counts_all/20210216175309_clusters_applications_elastic_stack.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175310_clusters_applications_jupyter.yml b/config/metrics/counts_all/20210216175310_clusters_applications_jupyter.yml
index 89c8116f0d..85d62985fa 100644
--- a/config/metrics/counts_all/20210216175310_clusters_applications_jupyter.yml
+++ b/config/metrics/counts_all/20210216175310_clusters_applications_jupyter.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175312_clusters_applications_cilium.yml b/config/metrics/counts_all/20210216175312_clusters_applications_cilium.yml
index faf7f486d3..33a8a53a8a 100644
--- a/config/metrics/counts_all/20210216175312_clusters_applications_cilium.yml
+++ b/config/metrics/counts_all/20210216175312_clusters_applications_cilium.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -19,3 +19,4 @@ tier:
- ultimate
performance_indicator_type: []
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175329_clusters_applications_cert_managers.yml b/config/metrics/counts_all/20210216175329_clusters_applications_cert_managers.yml
index 185ab921fd..f97816e18c 100644
--- a/config/metrics/counts_all/20210216175329_clusters_applications_cert_managers.yml
+++ b/config/metrics/counts_all/20210216175329_clusters_applications_cert_managers.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -18,3 +18,4 @@ tier:
- premium
- ultimate
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175331_clusters_applications_helm.yml b/config/metrics/counts_all/20210216175331_clusters_applications_helm.yml
index 4561338a3c..28c58e1185 100644
--- a/config/metrics/counts_all/20210216175331_clusters_applications_helm.yml
+++ b/config/metrics/counts_all/20210216175331_clusters_applications_helm.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -18,3 +18,4 @@ tier:
- premium
- ultimate
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175333_clusters_applications_ingress.yml b/config/metrics/counts_all/20210216175333_clusters_applications_ingress.yml
index 8336b6a404..b93198b92a 100644
--- a/config/metrics/counts_all/20210216175333_clusters_applications_ingress.yml
+++ b/config/metrics/counts_all/20210216175333_clusters_applications_ingress.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -18,3 +18,4 @@ tier:
- premium
- ultimate
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175335_clusters_applications_knative.yml b/config/metrics/counts_all/20210216175335_clusters_applications_knative.yml
index c5979093a1..735f3ea8e0 100644
--- a/config/metrics/counts_all/20210216175335_clusters_applications_knative.yml
+++ b/config/metrics/counts_all/20210216175335_clusters_applications_knative.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -18,3 +18,4 @@ tier:
- premium
- ultimate
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216175627_templates_asana_active.yml b/config/metrics/counts_all/20210216175627_templates_asana_active.yml
index 48025516d3..3575f359f9 100644
--- a/config/metrics/counts_all/20210216175627_templates_asana_active.yml
+++ b/config/metrics/counts_all/20210216175627_templates_asana_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175638_templates_assembla_active.yml b/config/metrics/counts_all/20210216175638_templates_assembla_active.yml
index 1ab0d42a5e..d0f203e12c 100644
--- a/config/metrics/counts_all/20210216175638_templates_assembla_active.yml
+++ b/config/metrics/counts_all/20210216175638_templates_assembla_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175649_templates_bamboo_active.yml b/config/metrics/counts_all/20210216175649_templates_bamboo_active.yml
index 85b10732ae..2e97c4bf1b 100644
--- a/config/metrics/counts_all/20210216175649_templates_bamboo_active.yml
+++ b/config/metrics/counts_all/20210216175649_templates_bamboo_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175701_templates_bugzilla_active.yml b/config/metrics/counts_all/20210216175701_templates_bugzilla_active.yml
index bc5a123801..6d26fbfb8d 100644
--- a/config/metrics/counts_all/20210216175701_templates_bugzilla_active.yml
+++ b/config/metrics/counts_all/20210216175701_templates_bugzilla_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175712_templates_buildkite_active.yml b/config/metrics/counts_all/20210216175712_templates_buildkite_active.yml
index 16287a4e71..8218ae10ca 100644
--- a/config/metrics/counts_all/20210216175712_templates_buildkite_active.yml
+++ b/config/metrics/counts_all/20210216175712_templates_buildkite_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175723_templates_campfire_active.yml b/config/metrics/counts_all/20210216175723_templates_campfire_active.yml
index 728d3d0af4..2e8b09e499 100644
--- a/config/metrics/counts_all/20210216175723_templates_campfire_active.yml
+++ b/config/metrics/counts_all/20210216175723_templates_campfire_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175734_templates_confluence_active.yml b/config/metrics/counts_all/20210216175734_templates_confluence_active.yml
index bb2d997611..2287415e83 100644
--- a/config/metrics/counts_all/20210216175734_templates_confluence_active.yml
+++ b/config/metrics/counts_all/20210216175734_templates_confluence_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175745_templates_custom_issue_tracker_active.yml b/config/metrics/counts_all/20210216175745_templates_custom_issue_tracker_active.yml
index c1c63d0529..343c4c887a 100644
--- a/config/metrics/counts_all/20210216175745_templates_custom_issue_tracker_active.yml
+++ b/config/metrics/counts_all/20210216175745_templates_custom_issue_tracker_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175756_templates_discord_active.yml b/config/metrics/counts_all/20210216175756_templates_discord_active.yml
index d0663da787..9f057665e3 100644
--- a/config/metrics/counts_all/20210216175756_templates_discord_active.yml
+++ b/config/metrics/counts_all/20210216175756_templates_discord_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175807_templates_drone_ci_active.yml b/config/metrics/counts_all/20210216175807_templates_drone_ci_active.yml
index 6a764f64ab..edec715a03 100644
--- a/config/metrics/counts_all/20210216175807_templates_drone_ci_active.yml
+++ b/config/metrics/counts_all/20210216175807_templates_drone_ci_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175818_templates_emails_on_push_active.yml b/config/metrics/counts_all/20210216175818_templates_emails_on_push_active.yml
index f709aff9f0..057e3b3056 100644
--- a/config/metrics/counts_all/20210216175818_templates_emails_on_push_active.yml
+++ b/config/metrics/counts_all/20210216175818_templates_emails_on_push_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175829_templates_external_wiki_active.yml b/config/metrics/counts_all/20210216175829_templates_external_wiki_active.yml
index 35bbce9538..31a270395f 100644
--- a/config/metrics/counts_all/20210216175829_templates_external_wiki_active.yml
+++ b/config/metrics/counts_all/20210216175829_templates_external_wiki_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175840_templates_flowdock_active.yml b/config/metrics/counts_all/20210216175840_templates_flowdock_active.yml
index dc1466d2be..74d085a444 100644
--- a/config/metrics/counts_all/20210216175840_templates_flowdock_active.yml
+++ b/config/metrics/counts_all/20210216175840_templates_flowdock_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175902_templates_hangouts_chat_active.yml b/config/metrics/counts_all/20210216175902_templates_hangouts_chat_active.yml
index b676501db1..fb007335b2 100644
--- a/config/metrics/counts_all/20210216175902_templates_hangouts_chat_active.yml
+++ b/config/metrics/counts_all/20210216175902_templates_hangouts_chat_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175924_templates_irker_active.yml b/config/metrics/counts_all/20210216175924_templates_irker_active.yml
index fc3ed89b6e..c3491169a7 100644
--- a/config/metrics/counts_all/20210216175924_templates_irker_active.yml
+++ b/config/metrics/counts_all/20210216175924_templates_irker_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175935_templates_jenkins_active.yml b/config/metrics/counts_all/20210216175935_templates_jenkins_active.yml
index 542671304b..96938d84ae 100644
--- a/config/metrics/counts_all/20210216175935_templates_jenkins_active.yml
+++ b/config/metrics/counts_all/20210216175935_templates_jenkins_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175946_templates_jira_active.yml b/config/metrics/counts_all/20210216175946_templates_jira_active.yml
index 83d3669b7f..964aef061d 100644
--- a/config/metrics/counts_all/20210216175946_templates_jira_active.yml
+++ b/config/metrics/counts_all/20210216175946_templates_jira_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216175957_templates_mattermost_active.yml b/config/metrics/counts_all/20210216175957_templates_mattermost_active.yml
index 460c5e808d..9e472a1cb5 100644
--- a/config/metrics/counts_all/20210216175957_templates_mattermost_active.yml
+++ b/config/metrics/counts_all/20210216175957_templates_mattermost_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180008_templates_mattermost_slash_commands_active.yml b/config/metrics/counts_all/20210216180008_templates_mattermost_slash_commands_active.yml
index 927710d918..97a8dccd83 100644
--- a/config/metrics/counts_all/20210216180008_templates_mattermost_slash_commands_active.yml
+++ b/config/metrics/counts_all/20210216180008_templates_mattermost_slash_commands_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180019_templates_microsoft_teams_active.yml b/config/metrics/counts_all/20210216180019_templates_microsoft_teams_active.yml
index fb35802c2b..1fed89e793 100644
--- a/config/metrics/counts_all/20210216180019_templates_microsoft_teams_active.yml
+++ b/config/metrics/counts_all/20210216180019_templates_microsoft_teams_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180030_templates_packagist_active.yml b/config/metrics/counts_all/20210216180030_templates_packagist_active.yml
index aa56cf0802..75c4f0f242 100644
--- a/config/metrics/counts_all/20210216180030_templates_packagist_active.yml
+++ b/config/metrics/counts_all/20210216180030_templates_packagist_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180041_templates_pipelines_email_active.yml b/config/metrics/counts_all/20210216180041_templates_pipelines_email_active.yml
index 2d81cf582e..7462974968 100644
--- a/config/metrics/counts_all/20210216180041_templates_pipelines_email_active.yml
+++ b/config/metrics/counts_all/20210216180041_templates_pipelines_email_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180052_templates_pivotaltracker_active.yml b/config/metrics/counts_all/20210216180052_templates_pivotaltracker_active.yml
index 7c869361d0..abc6227f43 100644
--- a/config/metrics/counts_all/20210216180052_templates_pivotaltracker_active.yml
+++ b/config/metrics/counts_all/20210216180052_templates_pivotaltracker_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180104_templates_pushover_active.yml b/config/metrics/counts_all/20210216180104_templates_pushover_active.yml
index 572b5da07d..9580fe0106 100644
--- a/config/metrics/counts_all/20210216180104_templates_pushover_active.yml
+++ b/config/metrics/counts_all/20210216180104_templates_pushover_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180115_templates_redmine_active.yml b/config/metrics/counts_all/20210216180115_templates_redmine_active.yml
index a85417cbc2..47ba5ed946 100644
--- a/config/metrics/counts_all/20210216180115_templates_redmine_active.yml
+++ b/config/metrics/counts_all/20210216180115_templates_redmine_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180126_templates_slack_active.yml b/config/metrics/counts_all/20210216180126_templates_slack_active.yml
index dffa673cd4..792c965216 100644
--- a/config/metrics/counts_all/20210216180126_templates_slack_active.yml
+++ b/config/metrics/counts_all/20210216180126_templates_slack_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180137_templates_slack_slash_commands_active.yml b/config/metrics/counts_all/20210216180137_templates_slack_slash_commands_active.yml
index e044d6cf1d..38bc695931 100644
--- a/config/metrics/counts_all/20210216180137_templates_slack_slash_commands_active.yml
+++ b/config/metrics/counts_all/20210216180137_templates_slack_slash_commands_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180148_templates_teamcity_active.yml b/config/metrics/counts_all/20210216180148_templates_teamcity_active.yml
index b3a37ebc57..63e8285e22 100644
--- a/config/metrics/counts_all/20210216180148_templates_teamcity_active.yml
+++ b/config/metrics/counts_all/20210216180148_templates_teamcity_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180159_templates_unify_circuit_active.yml b/config/metrics/counts_all/20210216180159_templates_unify_circuit_active.yml
index 39facfb237..10c7b23a30 100644
--- a/config/metrics/counts_all/20210216180159_templates_unify_circuit_active.yml
+++ b/config/metrics/counts_all/20210216180159_templates_unify_circuit_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180210_templates_webex_teams_active.yml b/config/metrics/counts_all/20210216180210_templates_webex_teams_active.yml
index 185b538a22..1e41a8170d 100644
--- a/config/metrics/counts_all/20210216180210_templates_webex_teams_active.yml
+++ b/config/metrics/counts_all/20210216180210_templates_webex_teams_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180221_templates_youtrack_active.yml b/config/metrics/counts_all/20210216180221_templates_youtrack_active.yml
index a6c11918ce..edd5aa07f5 100644
--- a/config/metrics/counts_all/20210216180221_templates_youtrack_active.yml
+++ b/config/metrics/counts_all/20210216180221_templates_youtrack_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180934_templates_prometheus_active.yml b/config/metrics/counts_all/20210216180934_templates_prometheus_active.yml
index 4bf8911c66..9f8a24488a 100644
--- a/config/metrics/counts_all/20210216180934_templates_prometheus_active.yml
+++ b/config/metrics/counts_all/20210216180934_templates_prometheus_active.yml
@@ -7,7 +7,8 @@ product_stage: monitor
product_group: group::monitor
product_category: metrics
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216180947_clusters_applications_prometheus.yml b/config/metrics/counts_all/20210216180947_clusters_applications_prometheus.yml
index a43dc103f9..bf56733738 100644
--- a/config/metrics/counts_all/20210216180947_clusters_applications_prometheus.yml
+++ b/config/metrics/counts_all/20210216180947_clusters_applications_prometheus.yml
@@ -7,7 +7,7 @@ product_stage: monitor
product_group: group::monitor
product_category: metrics
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -18,3 +18,4 @@ tier:
- premium
- ultimate
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216181949_clusters_applications_runner.yml b/config/metrics/counts_all/20210216181949_clusters_applications_runner.yml
index 96f16043e6..3c3fc60069 100644
--- a/config/metrics/counts_all/20210216181949_clusters_applications_runner.yml
+++ b/config/metrics/counts_all/20210216181949_clusters_applications_runner.yml
@@ -7,7 +7,7 @@ product_stage: configure
product_group: group::configure
product_category: kubernetes_management
value_type: number
-status: active
+status: removed
time_frame: all
data_source: database
distribution:
@@ -18,3 +18,4 @@ tier:
- premium
- ultimate
milestone: "<13.9"
+milestone_removed: "14.4"
diff --git a/config/metrics/counts_all/20210216182112_sast_jobs.yml b/config/metrics/counts_all/20210216182112_sast_jobs.yml
deleted file mode 100644
index 1012910675..0000000000
--- a/config/metrics/counts_all/20210216182112_sast_jobs.yml
+++ /dev/null
@@ -1,21 +0,0 @@
----
-data_category: operational
-key_path: counts.sast_jobs
-description: Count of SAST CI jobs for the month. Job names ending in '-sast'
-product_section: sec
-product_stage: secure
-product_group: group::static analysis
-product_category: static_application_security_testing
-value_type: number
-status: active
-time_frame: all
-data_source: database
-distribution:
-- ce
-- ee
-tier:
-- free
-- premium
-- ultimate
-performance_indicator_type: []
-milestone: "<13.9"
diff --git a/config/metrics/counts_all/20210216182114_secret_detection_jobs.yml b/config/metrics/counts_all/20210216182114_secret_detection_jobs.yml
deleted file mode 100644
index 8a3d1ef15f..0000000000
--- a/config/metrics/counts_all/20210216182114_secret_detection_jobs.yml
+++ /dev/null
@@ -1,21 +0,0 @@
----
-data_category: operational
-key_path: counts.secret_detection_jobs
-description: Count of all 'secret-detection' CI jobs.
-product_section: sec
-product_stage: secure
-product_group: group::static analysis
-product_category: secret_detection
-value_type: number
-status: active
-time_frame: all
-data_source: database
-distribution:
-- ce
-- ee
-tier:
-- free
-- premium
-- ultimate
-performance_indicator_type: []
-milestone: "<13.9"
diff --git a/config/metrics/counts_all/20210216182551_templates_datadog_active.yml b/config/metrics/counts_all/20210216182551_templates_datadog_active.yml
index d2df07b8d2..975bc2164b 100644
--- a/config/metrics/counts_all/20210216182551_templates_datadog_active.yml
+++ b/config/metrics/counts_all/20210216182551_templates_datadog_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216182618_templates_ewm_active.yml b/config/metrics/counts_all/20210216182618_templates_ewm_active.yml
index 1ace369980..be61177d81 100644
--- a/config/metrics/counts_all/20210216182618_templates_ewm_active.yml
+++ b/config/metrics/counts_all/20210216182618_templates_ewm_active.yml
@@ -7,7 +7,8 @@ product_stage: ecosystem
product_group: group::integrations
product_category: integrations
value_type: number
-status: active
+status: removed
+milestone_removed: '14.4'
time_frame: all
data_source: database
distribution:
diff --git a/config/metrics/counts_all/20210216182907_package_events_i_package_container_delete_package.yml b/config/metrics/counts_all/20210216182907_package_events_i_package_container_delete_package.yml
index e36411a81e..c20a79a2f2 100644
--- a/config/metrics/counts_all/20210216182907_package_events_i_package_container_delete_package.yml
+++ b/config/metrics/counts_all/20210216182907_package_events_i_package_container_delete_package.yml
@@ -7,7 +7,8 @@ product_stage: package
product_group: group::package
product_category: container registry
value_type: number
-status: deprecated
+status: removed
+milestone_removed: "14.4"
time_frame: all
data_source: redis
distribution:
diff --git a/config/metrics/counts_all/20210216182909_package_events_i_package_container_pull_package.yml b/config/metrics/counts_all/20210216182909_package_events_i_package_container_pull_package.yml
index c9c448b642..ff3ab7b40c 100644
--- a/config/metrics/counts_all/20210216182909_package_events_i_package_container_pull_package.yml
+++ b/config/metrics/counts_all/20210216182909_package_events_i_package_container_pull_package.yml
@@ -7,7 +7,8 @@ product_stage: package
product_group: group::package
product_category: container registry
value_type: number
-status: deprecated
+status: removed
+milestone_removed: "14.4"
time_frame: all
data_source: redis
distribution:
diff --git a/config/metrics/counts_all/20210216182911_package_events_i_package_container_push_package.yml b/config/metrics/counts_all/20210216182911_package_events_i_package_container_push_package.yml
index 9ca8e62f68..2c9d9a391a 100644
--- a/config/metrics/counts_all/20210216182911_package_events_i_package_container_push_package.yml
+++ b/config/metrics/counts_all/20210216182911_package_events_i_package_container_push_package.yml
@@ -7,7 +7,8 @@ product_stage: package
product_group: group::package
product_category: container registry
value_type: number
-status: deprecated
+status: removed
+milestone_removed: "14.4"
time_frame: all
data_source: redis
distribution:
diff --git a/config/metrics/counts_all/20210216182917_package_events_i_package_debian_push_package.yml b/config/metrics/counts_all/20210216182917_package_events_i_package_debian_push_package.yml
index 4fe173115c..c6f23fe0c8 100644
--- a/config/metrics/counts_all/20210216182917_package_events_i_package_debian_push_package.yml
+++ b/config/metrics/counts_all/20210216182917_package_events_i_package_debian_push_package.yml
@@ -7,7 +7,8 @@ product_stage: package
product_group: group::package
product_category: package registry
value_type: number
-status: deprecated
+status: removed
+milestone_removed: "14.4"
time_frame: all
data_source: redis
distribution:
diff --git a/config/metrics/counts_all/20210715094459_releases_with_milestones.yml b/config/metrics/counts_all/20210715094459_releases_with_milestones.yml
new file mode 100644
index 0000000000..5d85360458
--- /dev/null
+++ b/config/metrics/counts_all/20210715094459_releases_with_milestones.yml
@@ -0,0 +1,23 @@
+---
+key_path: usage_activity_by_stage.release.releases_with_milestones
+description: Unique users creating releases with milestones associated
+performance_indicator_type: []
+product_section: ops
+product_stage: release
+product_group: 'group::release'
+product_category: Release Orchestration
+value_type: number
+status: active
+milestone: "14.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71287'
+time_frame: 28d
+data_source: database
+instrumentation_class: 'CountUsersAssociatingMilestonesToReleasesMetric'
+data_category: Optional
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20210915082040_projects_with_expiration_policy_enabled_with_older_than_set_to_60d.yml b/config/metrics/counts_all/20210915082040_projects_with_expiration_policy_enabled_with_older_than_set_to_60d.yml
new file mode 100644
index 0000000000..1a9fe349a9
--- /dev/null
+++ b/config/metrics/counts_all/20210915082040_projects_with_expiration_policy_enabled_with_older_than_set_to_60d.yml
@@ -0,0 +1,22 @@
+---
+data_category: optional
+key_path: counts.projects_with_expiration_policy_enabled_with_older_than_set_to_60d
+description: A count of projects with the cleanup policy set delete tags older than
+ 60 days
+product_section: ops
+product_stage: package
+product_group: group::package
+product_category: container registry
+value_type: number
+status: active
+time_frame: all
+data_source: database
+distribution:
+- ee
+- ce
+tier:
+- free
+- premium
+- ultimate
+performance_indicator_type: []
+milestone: "14.4"
diff --git a/config/metrics/counts_all/20210916200930_clusters_integrations_prometheus.yml b/config/metrics/counts_all/20210916200930_clusters_integrations_prometheus.yml
new file mode 100644
index 0000000000..2e17c5d261
--- /dev/null
+++ b/config/metrics/counts_all/20210916200930_clusters_integrations_prometheus.yml
@@ -0,0 +1,21 @@
+---
+data_category: optional
+key_path: counts.clusters_integrations_prometheus
+description: Total clusters with Clusters::Integrations::Prometheus enabled
+product_section: ops
+product_stage: configure
+product_group: group::configure
+product_category: kubernetes_management
+value_type: number
+status: active
+time_frame: all
+data_source: database
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+performance_indicator_type: []
+milestone: "14.4"
diff --git a/config/metrics/counts_all/20210916200931_clusters_integrations_elastic_stack.yml b/config/metrics/counts_all/20210916200931_clusters_integrations_elastic_stack.yml
new file mode 100644
index 0000000000..54437cfbf3
--- /dev/null
+++ b/config/metrics/counts_all/20210916200931_clusters_integrations_elastic_stack.yml
@@ -0,0 +1,21 @@
+---
+data_category: optional
+key_path: counts.clusters_integrations_elastic_stack
+description: Total clusters with Clusters::Integrations::ElasticStack enabled
+product_section: ops
+product_stage: configure
+product_group: group::configure
+product_category: kubernetes_management
+value_type: number
+status: active
+time_frame: all
+data_source: database
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+performance_indicator_type: []
+milestone: "14.4"
diff --git a/config/metrics/counts_all/20210916202342_clusters_integrations_prometheus.yml b/config/metrics/counts_all/20210916202342_clusters_integrations_prometheus.yml
new file mode 100644
index 0000000000..d0baa4d075
--- /dev/null
+++ b/config/metrics/counts_all/20210916202342_clusters_integrations_prometheus.yml
@@ -0,0 +1,21 @@
+---
+data_category: optional
+key_path: usage_activity_by_stage.monitor.clusters_integrations_prometheus
+description: Users creating clusters with Prometheus integration enabled in last 28 days.
+product_section: ops
+product_stage: monitor
+product_group: group::monitor
+product_category: metrics
+value_type: number
+status: active
+time_frame: all
+data_source: database
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+performance_indicator_type: []
+milestone: "14.4"
diff --git a/config/metrics/license/20210204124829_active_user_count.yml b/config/metrics/license/20210204124829_active_user_count.yml
index 871a75bd97..c6e0895a91 100644
--- a/config/metrics/license/20210204124829_active_user_count.yml
+++ b/config/metrics/license/20210204124829_active_user_count.yml
@@ -10,6 +10,7 @@ status: active
milestone: "<13.9"
data_category: subscription
time_frame: none
+instrumentation_class: ActiveUserCountMetric
data_source: database
distribution:
- ce
diff --git a/config/redis.cache.yml.example b/config/redis.cache.yml.example
deleted file mode 100644
index 44d9f7e863..0000000000
--- a/config/redis.cache.yml.example
+++ /dev/null
@@ -1,38 +0,0 @@
-# If you change this file in a merge request, please also create
-# a merge request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
-#
-development:
- url: redis://localhost:6379/10
- #
- # url: redis://localhost:6380
- # sentinels:
- # -
- # host: localhost
- # port: 26380 # point to sentinel, not to redis port
- # -
- # host: replica2
- # port: 26380 # point to sentinel, not to redis port
-test:
- url: redis://localhost:6379/10
- #
- # url: redis://localhost:6380
-production:
- # Redis (single instance)
- url: unix:/var/run/redis/redis.cache.sock
- ##
- # Redis + Sentinel (for HA)
- #
- # Please read instructions carefully before using it as you may lose data:
- # http://redis.io/topics/sentinel
- #
- # You must specify a list of a few sentinels that will handle client connection
- # please read here for more information: https://docs.gitlab.com/ee/administration/redis/index.html
- ##
- # url: redis://master:6380
- # sentinels:
- # -
- # host: replica1
- # port: 26380 # point to sentinel, not to redis port
- # -
- # host: replica2
- # port: 26380 # point to sentinel, not to redis port
diff --git a/config/redis.queues.yml.example b/config/redis.queues.yml.example
deleted file mode 100644
index 4194b44cb8..0000000000
--- a/config/redis.queues.yml.example
+++ /dev/null
@@ -1,38 +0,0 @@
-# If you change this file in a merge request, please also create
-# a merge request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
-#
-development:
- url: redis://localhost:6379/11
- #
- # url: redis://localhost:6381
- # sentinels:
- # -
- # host: localhost
- # port: 26381 # point to sentinel, not to redis port
- # -
- # host: replica2
- # port: 26381 # point to sentinel, not to redis port
-test:
- url: redis://localhost:6379/11
- #
- # url: redis://localhost:6381
-production:
- # Redis (single instance)
- url: unix:/var/run/redis/redis.queues.sock
- ##
- # Redis + Sentinel (for HA)
- #
- # Please read instructions carefully before using it as you may lose data:
- # http://redis.io/topics/sentinel
- #
- # You must specify a list of a few sentinels that will handle client connection
- # please read here for more information: https://docs.gitlab.com/ee/administration/redis/index.html
- ##
- # url: redis://master:6381
- # sentinels:
- # -
- # host: replica1
- # port: 26381 # point to sentinel, not to redis port
- # -
- # host: replica2
- # port: 26381 # point to sentinel, not to redis port
diff --git a/config/redis.shared_state.yml.example b/config/redis.shared_state.yml.example
deleted file mode 100644
index b3e0c7a8fa..0000000000
--- a/config/redis.shared_state.yml.example
+++ /dev/null
@@ -1,38 +0,0 @@
-# If you change this file in a merge request, please also create
-# a merge request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
-#
-development:
- url: redis://localhost:6379/12
- #
- # url: redis://localhost:6382
- # sentinels:
- # -
- # host: localhost
- # port: 26382 # point to sentinel, not to redis port
- # -
- # host: replica2
- # port: 26382 # point to sentinel, not to redis port
-test:
- url: redis://localhost:6379/12
- #
- # url: redis://localhost:6382
-production:
- # Redis (single instance)
- url: unix:/var/run/redis/redis.shared_state.sock
- ##
- # Redis + Sentinel (for HA)
- #
- # Please read instructions carefully before using it as you may lose data:
- # http://redis.io/topics/sentinel
- #
- # You must specify a list of a few sentinels that will handle client connection
- # please read here for more information: https://docs.gitlab.com/ee/administration/redis/index.html
- ##
- # url: redis://master:6382
- # sentinels:
- # -
- # host: replica1
- # port: 26382 # point to sentinel, not to redis port
- # -
- # host: replica2
- # port: 26382 # point to sentinel, not to redis port
diff --git a/config/redis.trace_chunks.yml.example b/config/redis.trace_chunks.yml.example
deleted file mode 100644
index d38b9ba496..0000000000
--- a/config/redis.trace_chunks.yml.example
+++ /dev/null
@@ -1,38 +0,0 @@
-# If you change this file in a merge request, please also create
-# a merge request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
-#
-development:
- url: redis://localhost:6379/13
- #
- # url: redis://localhost:6382
- # sentinels:
- # -
- # host: localhost
- # port: 26382 # point to sentinel, not to redis port
- # -
- # host: replica2
- # port: 26382 # point to sentinel, not to redis port
-test:
- url: redis://localhost:6379/13
- #
- # url: redis://localhost:6382
-production:
- # Redis (single instance)
- url: unix:/var/run/redis/redis.trace_chunks.sock
- ##
- # Redis + Sentinel (for HA)
- #
- # Please read instructions carefully before using it as you may lose data:
- # http://redis.io/topics/sentinel
- #
- # You must specify a list of a few sentinels that will handle client connection
- # please read here for more information: https://docs.gitlab.com/ee/administration/redis/index.html
- ##
- # url: redis://master:6382
- # sentinels:
- # -
- # host: replica1
- # port: 26382 # point to sentinel, not to redis port
- # -
- # host: replica2
- # port: 26382 # point to sentinel, not to redis port
diff --git a/config/routes.rb b/config/routes.rb
index 8f4c3886e8..01e57a0135 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -161,8 +161,6 @@ Rails.application.routes.draw do
end
end
- resource :projects
-
draw :operations
draw :jira_connect
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index e3b365ad27..dac1937b76 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -38,14 +38,6 @@ namespace :admin do
resources :abuse_reports, only: [:index, :destroy]
resources :gitaly_servers, only: [:index]
- namespace :serverless do
- resources :domains, only: [:index, :create, :update, :destroy] do
- member do
- post '/verify', to: 'domains#verify'
- end
- end
- end
-
resources :spam_logs, only: [:index, :destroy] do
member do
post :mark_as_ham
@@ -69,6 +61,13 @@ namespace :admin do
end
end
+ resources :topics, only: [:index, :new, :create, :edit, :update] do
+ resource :avatar, controller: 'topics/avatars', only: [:destroy]
+ collection do
+ post :preview_markdown
+ end
+ end
+
resources :deploy_keys, only: [:index, :new, :create, :edit, :update, :destroy]
resources :hooks, only: [:index, :create, :edit, :update, :destroy] do
diff --git a/config/routes/group.rb b/config/routes/group.rb
index ef31b639d3..803249f886 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -146,5 +146,7 @@ scope format: false do
constraints image: Gitlab::PathRegex.container_image_regex, sha: Gitlab::PathRegex.container_image_blob_sha_regex do
get 'v2/*group_id/dependency_proxy/containers/*image/manifests/*tag' => 'groups/dependency_proxy_for_containers#manifest' # rubocop:todo Cop/PutGroupRoutesUnderScope
get 'v2/*group_id/dependency_proxy/containers/*image/blobs/:sha' => 'groups/dependency_proxy_for_containers#blob' # rubocop:todo Cop/PutGroupRoutesUnderScope
+ post 'v2/*group_id/dependency_proxy/containers/*image/blobs/:sha/upload/authorize' => 'groups/dependency_proxy_for_containers#authorize_upload_blob' # rubocop:todo Cop/PutGroupRoutesUnderScope
+ post 'v2/*group_id/dependency_proxy/containers/*image/blobs/:sha/upload' => 'groups/dependency_proxy_for_containers#upload_blob' # rubocop:todo Cop/PutGroupRoutesUnderScope
end
end
diff --git a/config/routes/import.rb b/config/routes/import.rb
index 64830ef1e5..9c76c4435f 100644
--- a/config/routes/import.rb
+++ b/config/routes/import.rb
@@ -12,6 +12,10 @@ end
namespace :import do
resources :available_namespaces, only: [:index], controller: :available_namespaces
+ namespace :url do
+ post :validate
+ end
+
resource :github, only: [:create, :new], controller: :github do
post :personal_access_token
get :status
@@ -66,6 +70,7 @@ namespace :import do
post :configure
get :status
get :realtime_changes
+ get :history
end
resource :manifest, only: [:create, :new], controller: :manifest do
diff --git a/config/routes/project.rb b/config/routes/project.rb
index cbd2f5ac83..b1be9ad2ad 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -298,6 +298,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
+ resources :cluster_agents, only: [:show], param: :name
+
concerns :clusterable
namespace :serverless do
@@ -311,6 +313,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :terraform, only: [:index]
+ resources :google_cloud, only: [:index]
+
resources :environments, except: [:destroy] do
member do
post :stop
diff --git a/config/routes/uploads.rb b/config/routes/uploads.rb
index 71a868175a..e2cdf8ba60 100644
--- a/config/routes/uploads.rb
+++ b/config/routes/uploads.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
scope path: :uploads do
- # Note attachments and User/Group/Project avatars
+ # Note attachments and User/Group/Project/Topic avatars
get "-/system/:model/:mounted_as/:id/:filename",
to: "uploads#show",
- constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: %r{[^/]+} }
+ constraints: { model: %r{note|user|group|project|projects\/topic}, mounted_as: /avatar|attachment/, filename: %r{[^/]+} }
# show uploads for models, snippets (notes) available for now
get '-/system/:model/:id/:secret/:filename',
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index d7c6e6031a..56183d167b 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -29,6 +29,8 @@
- 1
- - analytics_code_review_metrics
- 1
+- - analytics_cycle_analytics_group_data_loader
+ - 1
- - analytics_devops_adoption_create_snapshot
- 1
- - analytics_usage_trends_counter_job
@@ -91,6 +93,10 @@
- 1
- - dependency_proxy
- 1
+- - dependency_proxy_blob
+ - 1
+- - dependency_proxy_manifest
+ - 1
- - deployment
- 3
- - design_management_copy_design_collection
@@ -273,8 +279,6 @@
- 1
- - pages_domain_verification
- 1
-- - pages_remove
- - 1
- - pages_transfer
- 1
- - pages_update_configuration
diff --git a/config/webpack.config.js b/config/webpack.config.js
index adb11548a8..e1a48ee2b4 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -342,6 +342,14 @@ module.exports = {
esModule: false,
},
},
+ {
+ test: /editor\/schema\/.+\.json$/,
+ type: 'javascript/auto',
+ loader: 'file-loader',
+ options: {
+ name: '[name].[contenthash:8].[ext]',
+ },
+ },
],
},
diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile
index 3018196ddb..693c03b9da 100644
--- a/danger/database/Dangerfile
+++ b/danger/database/Dangerfile
@@ -33,7 +33,7 @@ MSG
DATABASE_APPROVED_LABEL = 'database::approved'
non_geo_db_schema_updated = !git.modified_files.grep(%r{\Adb/structure\.sql}).empty?
-geo_db_schema_updated = !git.modified_files.grep(%r{\Aee/db/geo/schema\.rb}).empty?
+geo_db_schema_updated = !git.modified_files.grep(%r{\Aee/db/geo/structure\.sql}).empty?
non_geo_migration_created = !git.added_files.grep(%r{\A(db/(post_)?migrate)/}).empty?
geo_migration_created = !git.added_files.grep(%r{\Aee/db/geo/(post_)?migrate/}).empty?
@@ -45,7 +45,7 @@ if non_geo_migration_created && !non_geo_db_schema_updated
end
if geo_migration_created && !geo_db_schema_updated
- warn format(format_str, migrations: 'Geo migrations', schema: helper.html_link("ee/db/geo/schema.rb"))
+ warn format(format_str, migrations: 'Geo migrations', schema: helper.html_link("ee/db/geo/structure.sql"))
end
return unless helper.ci?
diff --git a/danger/pajamas/Dangerfile b/danger/pajamas/Dangerfile
index a3ff1126bd..fde12c08b3 100644
--- a/danger/pajamas/Dangerfile
+++ b/danger/pajamas/Dangerfile
@@ -59,7 +59,7 @@ MARKDOWN
if blocking_components_in_mr.any?
markdown(<<~MARKDOWN)
- These deprecated components have already been migrated and can no longer be used. Please use [Pajamas components](https://design.gitlab.com/components/status/) instead.
+ These deprecated components have already been migrated and can no longer be used. Please use [Pajamas components](https://design.gitlab.com/components/overview) instead.
* #{blocking_components_in_mr.join("\n* ")}
@@ -70,7 +70,7 @@ end
if deprecated_components_in_mr.any?
markdown(<<~MARKDOWN)
- These deprecated components are in the process of being migrated. Please consider using [Pajamas components](https://design.gitlab.com/components/status/) instead.
+ These deprecated components are in the process of being migrated. Please consider using [Pajamas components](https://design.gitlab.com/components/overview) instead.
* #{deprecated_components_in_mr.join("\n* ")}
diff --git a/danger/plugins/specs.rb b/danger/plugins/specs.rb
new file mode 100644
index 0000000000..3188785487
--- /dev/null
+++ b/danger/plugins/specs.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require_relative '../../tooling/danger/specs'
+
+module Danger
+ class Specs < ::Danger::Plugin
+ # Put the helper code somewhere it can be tested
+ include Tooling::Danger::Specs
+ end
+end
diff --git a/danger/product_intelligence/Dangerfile b/danger/product_intelligence/Dangerfile
index ae58cf4588..fd6ae76b4f 100644
--- a/danger/product_intelligence/Dangerfile
+++ b/danger/product_intelligence/Dangerfile
@@ -5,20 +5,20 @@ CHANGED_FILES_MESSAGE = <<~MSG
For the following files, a review from the [Data team and Product Intelligence team](https://gitlab.com/groups/gitlab-org/growth/product-intelligence/engineers/-/group_members?with_inherited_permissions=exclude) is recommended
Please check the ~"product intelligence" [guide](https://docs.gitlab.com/ee/development/usage_ping.html).
+For MR review guidelines, see the [Service Ping review guidelines](https://docs.gitlab.com/ee/development/usage_ping/review_guidelines.html) or the [Snowplow review guidelines](https://docs.gitlab.com/ee/development/snowplow/review_guidelines.html).
%s
MSG
-# exit if not matching files
+# exit if not matching files or if no product intelligence labels
matching_changed_files = product_intelligence.matching_changed_files
-return unless matching_changed_files.any?
+labels = product_intelligence.missing_labels
+
+return if matching_changed_files.empty? || labels.empty?
warn format(CHANGED_FILES_MESSAGE, changed_files: helper.markdown_list(matching_changed_files))
-labels = product_intelligence.missing_labels
-return unless labels.any?
-
gitlab.api.update_merge_request(gitlab.mr_json['project_id'],
gitlab.mr_json['iid'],
add_labels: labels)
diff --git a/danger/roulette/Dangerfile b/danger/roulette/Dangerfile
index cd23028a37..54b4680724 100644
--- a/danger/roulette/Dangerfile
+++ b/danger/roulette/Dangerfile
@@ -99,6 +99,9 @@ categories << :database if helper.mr_labels.include?('database')
# Ensure to spin for Product Intelligence reviewer when ~"product intelligence::review pending" is applied
categories << :product_intelligence if helper.mr_labels.include?("product intelligence::review pending")
+# Skip Product intelligence reviews for growth experiment MRs
+categories.delete(:product_intelligence) unless helper.mr_labels.include?("growth experiment")
+
if changes.any?
project = project_helper.project_name
diff --git a/danger/specs/Dangerfile b/danger/specs/Dangerfile
index 35476ae645..117eaf6106 100644
--- a/danger/specs/Dangerfile
+++ b/danger/specs/Dangerfile
@@ -20,7 +20,7 @@ Please make sure the spec files pass in AS-IF-FOSS mode either:
1. Locally with `FOSS_ONLY=1 bin/rspec -- %s`.
1. In the MR pipeline by verifying that the `rspec foss-impact` job has passed.
-1. In the MR pipelines by including `RUN AS-IF-FOSS` in the MR title (you can do it with the ``/title %s [RUN AS-IF-FOSS]`` quick action) and start a new MR pipeline.
+1. In the MR pipelines by setting the ~"pipeline:run-as-if-foss" label on the MR (you can do it with the `/label ~"pipeline:run-as-if-foss"` quick action) and start a new MR pipeline.
MSG
@@ -32,11 +32,12 @@ request specs (and/or feature specs). Please add request specs under
See https://gitlab.com/groups/gitlab-org/-/epics/5076 for information.
MSG
-has_app_changes = helper.all_changed_files.grep(%r{\A(app|lib|db/(geo/)?(post_)?migrate)/}).any?
-has_ee_app_changes = helper.all_changed_files.grep(%r{\Aee/(app|lib|db/(geo/)?(post_)?migrate)/}).any?
-spec_changes = helper.all_changed_files.grep(%r{\Aspec/})
+all_changed_files = helper.all_changed_files
+has_app_changes = all_changed_files.grep(%r{\A(app|lib|db/(geo/)?(post_)?migrate)/}).any?
+has_ee_app_changes = all_changed_files.grep(%r{\Aee/(app|lib|db/(geo/)?(post_)?migrate)/}).any?
+spec_changes = specs.changed_specs_files(ee: :exclude)
has_spec_changes = spec_changes.any?
-has_ee_spec_changes = helper.all_changed_files.grep(%r{\Aee/spec/}).any?
+has_ee_spec_changes = specs.changed_specs_files(ee: :only).any?
new_specs_needed = (gitlab.mr_labels & NO_SPECS_LABELS).empty?
if (has_app_changes || has_ee_app_changes) && !(has_spec_changes || has_ee_spec_changes) && new_specs_needed
@@ -45,10 +46,14 @@ end
# The only changes outside `ee/` are in `spec/`
if has_ee_app_changes && has_spec_changes && !(has_app_changes || has_ee_spec_changes)
- warn format(EE_CHANGE_WITH_FOSS_SPEC_CHANGE_MESSAGE, spec_files: spec_changes.join(" "), mr_title: gitlab.mr_json['title']), sticky: false
+ warn format(EE_CHANGE_WITH_FOSS_SPEC_CHANGE_MESSAGE, spec_files: spec_changes.join(" ")), sticky: false
end
# Forbidding a new file addition under `/spec/controllers` or `/ee/spec/controllers`
-if git.added_files.grep(%r{^(ee/)?spec/controllers/}).any?
+if project_helper.changes.added.files.grep(%r{^(ee/)?spec/controllers/}).any?
warn CONTROLLER_SPEC_DEPRECATION_MESSAGE
end
+
+specs.changed_specs_files.each do |filename|
+ specs.add_suggestions_for_match_with_array(filename)
+end
diff --git a/data/deprecations/14-0-nfs-fot-git-repository-storage.yml b/data/deprecations/14-0-nfs-fot-git-repository-storage.yml
new file mode 100644
index 0000000000..f38742439a
--- /dev/null
+++ b/data/deprecations/14-0-nfs-fot-git-repository-storage.yml
@@ -0,0 +1,22 @@
+- name: "NFS for Git repository storage deprecated" # The name of the feature to be deprecated
+ announcement_milestone: "14.0" # The milestone when this feature was first announced as deprecated.
+ announcement_date: "2021-06-22" # The date of the milestone release when this feature was first announced as deprecated
+ removal_milestone: "15.2" # The milestone when this feature is planned to be removed
+ body: | # Do not modify this line, instead modify the lines below.
+ With the general availability of Gitaly Cluster ([introduced in GitLab 13.0](https://about.gitlab.com/releases/2020/05/22/gitlab-13-0-released/)), we have deprecated development (bugfixes, performance improvements, etc) for NFS for Git repository storage in GitLab 14.0. We will continue to provide technical support for NFS for Git repositories throughout 14.x, but we will remove all support for NFS in GitLab 15.0. Please see our official [Statement of Support](https://about.gitlab.com/support/statement-of-support.html#gitaly-and-nfs) for further information.
+
+ Gitaly Cluster offers tremendous benefits for our customers such as:
+
+ - [Variable replication factors](https://docs.gitlab.com/ee/administration/gitaly/index.html#replication-factor).
+ - [Strong consistency](https://docs.gitlab.com/ee/administration/gitaly/index.html#strong-consistency).
+ - [Distributed read capabilities](https://docs.gitlab.com/ee/administration/gitaly/index.html#distributed-reads).
+
+ We encourage customers currently using NFS for Git repositories to plan their migration by reviewing our documentation on [migrating to Gitaly Cluster](https://docs.gitlab.com/ee/administration/gitaly/index.html#migrate-to-gitaly-cluster).
+
+ stage: # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
+ tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ issue_url: # (optional) This is a link to the deprecation issue in GitLab
+ documentation_url: # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
+ removal_date: "2022-06-22" # (optional - may be required in the future) YYYY-MM-DD format - the date of the milestone release when this feature is planned to be removed
diff --git a/data/deprecations/14-2-deprecation-task-runner.yml b/data/deprecations/14-2-deprecation-task-runner.yml
new file mode 100644
index 0000000000..4d01a1969a
--- /dev/null
+++ b/data/deprecations/14-2-deprecation-task-runner.yml
@@ -0,0 +1,16 @@
+- name: "Rename Task Runner pod to Toolbox" # The name of the feature to be deprecated
+ announcement_milestone: "14.2" # The milestone when this feature was first announced as deprecated.
+ announcement_date: "2021-08-22" # The date of the milestone release when this feature was first announced as deprecated
+ removal_milestone: "14.4" # The milestone when this feature is planned to be removed
+ body: | # Do not modify this line, instead modify the lines below.
+ The Task Runner pod is used to execute periodic housekeeping tasks within the GitLab application and is often confused with the GitLab Runner. Thus, [Task Runner will be renamed to Toolbox](https://gitlab.com/groups/gitlab-org/charts/-/epics/25).
+
+ This will result in the rename of the sub-chart: `gitlab/task-runner` to `gitlab/toolbox`. Resulting pods will be named along the lines of `{{ .Release.Name }}-toolbox`, which will often be `gitlab-toolbox`. They will be locatable with the label `app=toolbox`.
+
+ stage: # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
+ tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ issue_url: # (optional) This is a link to the deprecation issue in GitLab
+ documentation_url: # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
+ removal_date: "2021-10-22" # (optional - may be required in the future) YYYY-MM-DD format - the date of the milestone release when this feature is planned to be removed
diff --git a/data/deprecations/14-3-deprecation-release-cli.yml b/data/deprecations/14-3-deprecation-release-cli.yml
new file mode 100644
index 0000000000..4273d00fc7
--- /dev/null
+++ b/data/deprecations/14-3-deprecation-release-cli.yml
@@ -0,0 +1,13 @@
+- name: "Release CLI be distributed as a generic package" # The name of the feature to be deprecated
+ announcement_milestone: "14.2" # The milestone when this feature was first announced as deprecated.
+ announcement_date: "2021-08-22" # The date of the milestone release when this feature was first announced as deprecated
+ removal_milestone: "14.6" # The milestone when this feature is planned to be removed
+ body: | # Do not modify this line, instead modify the lines below.
+ The [release-cli](https://gitlab.com/gitlab-org/release-cli) will be released as a [generic package](https://gitlab.com/gitlab-org/release-cli/-/packages) starting in GitLab 14.2. We will continue to deploy it as a binary to S3 until GitLab 14.5 and stop distributing it in S3 in GitLab 14.6.
+ stage: # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
+ tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ issue_url: # (optional) This is a link to the deprecation issue in GitLab
+ documentation_url: # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
+ removal_date: "2021-12-22" # (optional - may be required in the future) YYYY-MM-DD format - the date of the milestone release when this feature is planned to be removed
diff --git a/data/deprecations/14-3-repository-push-audit-events.yml b/data/deprecations/14-3-repository-push-audit-events.yml
index 3a39c1f430..4bca8751db 100644
--- a/data/deprecations/14-3-repository-push-audit-events.yml
+++ b/data/deprecations/14-3-repository-push-audit-events.yml
@@ -11,4 +11,4 @@
tiers: Premium
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337993
documentation_url: https://docs.gitlab.com/ee/administration/audit_events.html#repository-push
- announcement_date: "2021-09-02" # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69024
+ announcement_date: "2021-09-22" # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69024
diff --git a/data/deprecations/distribution_deprecations_14-3.yml b/data/deprecations/distribution_deprecations_14-3.yml
deleted file mode 100644
index 0526338315..0000000000
--- a/data/deprecations/distribution_deprecations_14-3.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-- name: "Rename Task Runner pod to Toolbox" # The name of the feature to be deprecated
- announcement_milestone: "14.2" # The milestone when this feature was first announced as deprecated.
- announcement_date: "2021-09-22"
- removal_milestone: "14.4" # the milestone when this feature is planned to be removed
- body: | # Do not modify this line, instead modify the lines below.
- The Task Runner pod is used to execute periodic housekeeping tasks within the GitLab application and is often confused with the GitLab Runner. Thus, [Task Runner will be renamed to Toolbox](https://gitlab.com/groups/gitlab-org/charts/-/epics/25).
-
- This will result in the rename of the sub-chart: `gitlab/task-runner` to `gitlab/toolbox`. Resulting pods will be named along the lines of `{{ .Release.Name }}-toolbox`, which will often be `gitlab-toolbox`. They will be locatable with the label `app=toolbox`.
diff --git a/data/deprecations/templates/_deprecation_template.md.erb b/data/deprecations/templates/_deprecation_template.md.erb
index 64bd6a75a5..a037151c6a 100644
--- a/data/deprecations/templates/_deprecation_template.md.erb
+++ b/data/deprecations/templates/_deprecation_template.md.erb
@@ -6,6 +6,16 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo
# Deprecated feature removal schedule
+DISCLAIMER:
+This page contains information related to upcoming products, features, and functionality.
+It is important to note that the information presented is for informational purposes only.
+Please do not rely on this information for purchasing or planning purposes.
+As with all projects, the items mentioned on this page are subject to change or delay.
+The development, release, and timing of any products, features, or functionality remain at the
+sole discretion of GitLab Inc.
+
+
+
-
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index 3ff5fb2635..572c341f2b 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -137,8 +137,6 @@ Project event queries are limited to a maximum of 30 days.
### Instance events **(PREMIUM SELF)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2336) in GitLab 9.3.
-
Server-wide audit events introduce the ability to observe user actions across
the entire instance of your GitLab server, making it easy to understand who
changed what and when for audit purposes.
diff --git a/doc/administration/auth/README.md b/doc/administration/auth/README.md
deleted file mode 100644
index 5ab8653dc3..0000000000
--- a/doc/administration/auth/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
----
-redirect_to: 'index.md'
----
-
-This document was moved to [another location](index.md).
-
-
-
diff --git a/doc/administration/auth/index.md b/doc/administration/auth/index.md
index a072cc73c4..959c521855 100644
--- a/doc/administration/auth/index.md
+++ b/doc/administration/auth/index.md
@@ -31,7 +31,7 @@ providers:
- [Salesforce](../../integration/salesforce.md)
- [SAML](../../integration/saml.md)
- [SAML for GitLab.com groups](../../user/group/saml_sso/index.md) **(PREMIUM SAAS)**
-- [Shibboleth](../../integration/shibboleth.md)
+- [Shibboleth](../../integration/saml.md)
- [Smartcard](smartcard.md) **(PREMIUM SELF)**
- [Twitter](../../integration/twitter.md)
@@ -45,7 +45,7 @@ For more information, see the links shown on this page for each external provide
| Capability | SaaS | Self-Managed |
|-------------------------------------------------|-----------------------------------------|------------------------------------|
-| **User Provisioning** | SCIM JIT Provisioning | LDAP Sync |
+| **User Provisioning** | SCIM Just-In-Time (JIT) Provisioning | LDAP Sync |
| **User Detail Updating** (not group management) | Not Available | LDAP Sync |
| **Authentication** | SAML at top-level group (1 provider) | LDAP (multiple providers) Generic OAuth2 SAML (only 1 permitted per unique provider) Kerberos JWT Smartcard OmniAuth Providers (only 1 permitted per unique provider) |
| **Provider-to-GitLab Role Sync** | SAML Group Sync | LDAP Group Sync |
diff --git a/doc/administration/auth/ldap/google_secure_ldap.md b/doc/administration/auth/ldap/google_secure_ldap.md
index 137f35986a..e5af8e8256 100644
--- a/doc/administration/auth/ldap/google_secure_ldap.md
+++ b/doc/administration/auth/ldap/google_secure_ldap.md
@@ -15,7 +15,7 @@ LDAP service that can be configured with GitLab for authentication and group syn
Secure LDAP requires a slightly different configuration than standard LDAP servers.
The steps below cover:
-- Configuring the Secure LDAP Client in the Google Admin console.
+- Configuring the Secure LDAP Client in the Google administrator console.
- Required GitLab configuration.
## Configuring Google LDAP client
diff --git a/doc/administration/auth/ldap/index.md b/doc/administration/auth/ldap/index.md
index 1992b45033..92815f10b9 100644
--- a/doc/administration/auth/ldap/index.md
+++ b/doc/administration/auth/ldap/index.md
@@ -5,7 +5,7 @@ group: Access
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/#assignments
---
-# General LDAP setup **(FREE SELF)**
+# Integrate LDAP with GitLab **(FREE SELF)**
GitLab integrates with [LDAP](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol)
to support user authentication.
@@ -39,7 +39,9 @@ the LDAP server, or share email addresses.
### User deletion
Users deleted from the LDAP server are immediately blocked from signing in
-to GitLab. However, there's an LDAP check cache time of one hour (which is
+to GitLab and [no longer consumes a
+license](../../../user/admin_area/moderate_users.md).
+However, there's an LDAP check cache time of one hour (which is
[configurable](#adjust-ldap-user-sync-schedule) for GitLab Premium users).
This means users already signed-in or who are using Git over SSH can access
GitLab for up to one hour. Manually block the user in the GitLab Admin Area
@@ -70,15 +72,15 @@ LDAP email address, and then sign into GitLab by using their LDAP credentials.
LDAP service that can be configured with GitLab for authentication and group sync.
See [Google Secure LDAP](google_secure_ldap.md) for detailed configuration instructions.
-## Configuration
+## Configure LDAP
-To enable LDAP integration you must add your LDAP server settings in
-`/etc/gitlab/gitlab.rb` or `/home/git/gitlab/config/gitlab.yml` for Omnibus
-GitLab and installations from source respectively.
+To configure LDAP integration, add your LDAP server settings in:
-There is a Rake task to check LDAP configuration. After configuring LDAP
-using the documentation below, see [LDAP check Rake task](../../raketasks/check.md#ldap-check)
-for information on the LDAP check Rake task.
+- `/etc/gitlab/gitlab.rb` for Omnibus GitLab instances.
+- `/home/git/gitlab/config/gitlab.yml` for source install instances.
+
+After configuring LDAP, to test the configuration, use the
+[LDAP check Rake task](../../raketasks/check.md#ldap-check).
NOTE:
The `encryption` value `simple_tls` corresponds to 'Simple TLS' in the LDAP
@@ -90,9 +92,9 @@ with `start_tls` and `ssl` was replaced with `simple_tls`.
LDAP users must have a set email address, regardless of whether or not it's used
to sign in.
-### Example Configurations
+### Example Omnibus GitLab configuration
-**Omnibus Configuration**
+This example shows configuration for Omnibus GitLab instances:
```ruby
gitlab_rails['ldap_enabled'] = true
@@ -139,7 +141,9 @@ gitlab_rails['ldap_servers'] = {
}
```
-**Source Configuration**
+### Example source install configuration
+
+This example shows configuration for source install instances:
```yaml
production:
@@ -155,6 +159,8 @@ production:
### Basic configuration settings
+These configuration settings are available:
+
| Setting | Description | Required | Examples |
|--------------------|-------------|----------|----------|
| `label` | A human-friendly name for your LDAP server. It is displayed on your sign-in page. | **{check-circle}** Yes | `'Paris'` or `'Acme, Ltd.'` |
@@ -183,6 +189,8 @@ Some examples of the `user_filter` field syntax:
### SSL configuration settings
+These SSL configuration settings are available:
+
| Setting | Description | Required | Examples |
|---------------|-------------|----------|----------|
| `ca_file` | Specifies the path to a file containing a PEM-format CA certificate, for example, if you need an internal CA. | **{dotted-circle}** No | `'/etc/ca.pem'` |
@@ -193,69 +201,72 @@ Some examples of the `user_filter` field syntax:
### Attribute configuration settings
-LDAP attributes that GitLab uses to create an account for the LDAP user. The specified
-attribute can either be the attribute name as a string (for example, `'mail'`), or an
-array of attribute names to try in order (for example, `['mail', 'email']`).
-The user's LDAP sign-in is the attribute specified as `uid` above.
+GitLab uses these LDAP attributes to create an account for the LDAP user. The specified
+attribute can be either:
+
+- The attribute name as a string. For example, `'mail'`.
+- An array of attribute names to try in order. For example, `['mail', 'email']`.
+
+The user's LDAP sign in is the LDAP attribute [specified as `uid`](#basic-configuration-settings).
| Setting | Description | Required | Examples |
|--------------|-------------|----------|----------|
-| `username` | The username is used in paths for the user's own projects (like `gitlab.example.com/username/project`) and when mentioning them in issues, merge request and comments (like `@username`). If the attribute specified for `username` contains an email address, the GitLab username is part of the email address before the `@`. | **{dotted-circle}** No | `['uid', 'userid', 'sAMAccountName']` |
+| `username` | Used in paths for the user's own projects (for example, `gitlab.example.com/username/project`) and when mentioning them in issues, merge request and comments (for example, `@username`). If the attribute specified for `username` contains an email address, the GitLab username is part of the email address before the `@`. | **{dotted-circle}** No | `['uid', 'userid', 'sAMAccountName']` |
| `email` | LDAP attribute for user email. | **{dotted-circle}** No | `['mail', 'email', 'userPrincipalName']` |
| `name` | LDAP attribute for user display name. If `name` is blank, the full name is taken from the `first_name` and `last_name`. | **{dotted-circle}** No | Attributes `'cn'`, or `'displayName'` commonly carry full names. Alternatively, you can force the use of `first_name` and `last_name` by specifying an absent attribute such as `'somethingNonExistent'`. |
| `first_name` | LDAP attribute for user first name. Used when the attribute configured for `name` does not exist. | **{dotted-circle}** No | `'givenName'` |
| `last_name` | LDAP attribute for user last name. Used when the attribute configured for `name` does not exist. | **{dotted-circle}** No | `'sn'` |
-### LDAP Sync configuration settings **(PREMIUM SELF)**
+### LDAP sync configuration settings **(PREMIUM SELF)**
+
+These LDAP sync configuration settings are available:
| Setting | Description | Required | Examples |
|-------------------|-------------|----------|----------|
| `group_base` | Base used to search for groups. | **{dotted-circle}** No | `'ou=groups,dc=gitlab,dc=example'` |
-| `admin_group` | The CN of a group containing GitLab administrators. Note: Not `cn=administrators` or the full DN. | **{dotted-circle}** No | `'administrators'` |
-| `external_groups` | An array of CNs of groups containing users that should be considered external. Note: Not `cn=interns` or the full DN. | **{dotted-circle}** No | `['interns', 'contractors']` |
+| `admin_group` | The CN of a group containing GitLab administrators. Not `cn=administrators` or the full DN. | **{dotted-circle}** No | `'administrators'` |
+| `external_groups` | An array of CNs of groups containing users that should be considered external. Not `cn=interns` or the full DN. | **{dotted-circle}** No | `['interns', 'contractors']` |
| `sync_ssh_keys` | The LDAP attribute containing a user's public SSH key. | **{dotted-circle}** No | `'sshPublicKey'` or false if not set |
### Set up LDAP user filter
-If you want to limit all GitLab access to a subset of the LDAP users on your
-LDAP server, the first step should be to narrow the configured `base`. However,
-it's sometimes necessary to further filter users. In this case, you can set
-up an LDAP user filter. The filter must comply with
-[RFC 4515](https://tools.ietf.org/search/rfc4515).
+To limit all GitLab access to a subset of the LDAP users on your LDAP server, first narrow the
+configured `base`. However, to further filter users if
+necessary, you can set up an LDAP user filter. The filter must comply with [RFC 4515](https://tools.ietf.org/search/rfc4515).
-**Omnibus configuration**
+- Example user filter for Omnibus GitLab instances:
-```ruby
-gitlab_rails['ldap_servers'] = {
-'main' => {
- # snip...
- 'user_filter' => '(employeeType=developer)'
+ ```ruby
+ gitlab_rails['ldap_servers'] = {
+ 'main' => {
+ # snip...
+ 'user_filter' => '(employeeType=developer)'
+ }
}
-}
-```
+ ```
-**Source configuration**
+- Example user filter for source install instances:
-```yaml
-production:
- ldap:
- servers:
- main:
- # snip...
- user_filter: '(employeeType=developer)'
-```
+ ```yaml
+ production:
+ ldap:
+ servers:
+ main:
+ # snip...
+ user_filter: '(employeeType=developer)'
+ ```
-If you want to limit access to the nested members of an Active Directory
-group, use the following syntax:
+To limit access to the nested members of an Active Directory group, use the following syntax:
```plaintext
(memberOf:1.2.840.113556.1.4.1941:=CN=My Group,DC=Example,DC=com)
```
-For more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter, see the following
-[Microsoft Search Filter Syntax](https://docs.microsoft.com/en-us/windows/win32/adsi/search-filter-syntax) document.
+For more information about `LDAP_MATCHING_RULE_IN_CHAIN` filters, see
+[Search Filter Syntax](https://docs.microsoft.com/en-us/windows/win32/adsi/search-filter-syntax).
+
Support for nested members in the user filter shouldn't be confused with
-[group sync nested groups support](#supported-ldap-group-typesattributes). **(PREMIUM SELF)**
+[group sync nested groups](#supported-ldap-group-typesattributes) support.
GitLab does not support the custom filter syntax used by OmniAuth LDAP.
@@ -451,7 +462,7 @@ If initially your LDAP configuration looked like:
### TLS server authentication
-There are two encryption methods, `simple_tls` and `start_tls`.
+`simple_tls` and `start_tls` are the two available encryption methods.
For either encryption method, if setting `verify_certificates: false`, TLS
encryption is established with the LDAP server before any LDAP-protocol data is
@@ -463,9 +474,9 @@ exchanged but no validation of the LDAP server's SSL certificate is performed.
Not implemented by `Net::LDAP`.
-You should disable anonymous LDAP authentication and enable simple or SASL
-authentication. The TLS client authentication setting in your LDAP server cannot
-be mandatory and clients cannot be authenticated with the TLS protocol.
+You should disable anonymous LDAP authentication and enable simple or Simple Authentication
+and Security Layer (SASL) authentication. The TLS client authentication setting in your LDAP server
+cannot be mandatory and clients cannot be authenticated with the TLS protocol.
## Multiple LDAP servers **(PREMIUM SELF)**
@@ -474,7 +485,7 @@ connects to.
To add another LDAP server:
-1. Duplicate the settings under [the main configuration](#configuration).
+1. Duplicate the settings under [the main configuration](#configure-ldap).
1. Edit them to match the additional LDAP server.
Be sure to choose a different provider ID made of letters a-z and numbers 0-9.
@@ -526,7 +537,7 @@ The process executes the following access checks:
- Ensure the user is still present in LDAP.
- If the LDAP server is Active Directory, ensure the user is active (not
- blocked/disabled state). This is checked only if
+ blocked/disabled state). This check is performed only if
`active_directory: true` is set in the LDAP configuration.
In Active Directory, a user is marked as disabled/blocked if the user
@@ -702,7 +713,7 @@ When enabled, the following applies:
To enable it, you must:
-1. [Enable LDAP](#configuration)
+1. [Configure LDAP](#configure-ldap).
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
@@ -716,7 +727,7 @@ The values shown are in cron format. If needed, you can use a
WARNING:
Do not start the sync process too frequently as this
-could lead to multiple syncs running concurrently. This is primarily a concern
+could lead to multiple syncs running concurrently. This concern is primarily
for installations with a large number of LDAP users. Review the
[LDAP group sync benchmark metrics](#benchmarks) to see how
your installation compares before proceeding.
@@ -850,7 +861,7 @@ LDAP group links each:
- Subsequent syncs (checking membership, no writes) took 15 minutes
These metrics are meant to provide a baseline and performance may vary based on
-any number of factors. This was an extreme benchmark and most instances don't
+any number of factors. This benchmark was extreme and most instances don't
have near this many users or groups. Disk speed, database performance,
network and LDAP server response time affects these metrics.
diff --git a/doc/administration/auth/ldap/ldap-troubleshooting.md b/doc/administration/auth/ldap/ldap-troubleshooting.md
index 1952e8afa9..4757725d0b 100644
--- a/doc/administration/auth/ldap/ldap-troubleshooting.md
+++ b/doc/administration/auth/ldap/ldap-troubleshooting.md
@@ -55,9 +55,8 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
#### Query LDAP **(PREMIUM SELF)**
The following allows you to perform a search in LDAP using the rails console.
-Depending on what you're trying to do, it may make more sense to query [a
-user](#query-a-user-in-ldap) or [a group](#query-a-group-in-ldap) directly, or
-even [use `ldapsearch`](#ldapsearch) instead.
+Depending on what you're trying to do, it may make more sense to query [a user](#query-a-user-in-ldap)
+or [a group](#query-a-group-in-ldap) directly, or even [use `ldapsearch`](#ldapsearch) instead.
```ruby
adapter = Gitlab::Auth::Ldap::Adapter.new('ldapmain')
@@ -90,7 +89,7 @@ established but GitLab doesn't show you LDAP users in the output, one of the
following is most likely true:
- The `bind_dn` user doesn't have enough permissions to traverse the user tree.
-- The user(s) don't fall under the [configured `base`](index.md#configuration).
+- The user(s) don't fall under the [configured `base`](index.md#configure-ldap).
- The [configured `user_filter`](index.md#set-up-ldap-user-filter) blocks access to the user(s).
In this case, you con confirm which of the above is true using
@@ -102,7 +101,7 @@ In this case, you con confirm which of the above is true using
A user can have trouble signing in for any number of reasons. To get started,
here are some questions to ask yourself:
-- Does the user fall under the [configured `base`](index.md#configuration) in
+- Does the user fall under the [configured `base`](index.md#configure-ldap) in
LDAP? The user must fall under this `base` to sign in.
- Does the user pass through the [configured `user_filter`](index.md#set-up-ldap-user-filter)?
If one is not configured, this question can be ignored. If it is, then the
@@ -355,11 +354,10 @@ things to check to debug the situation.
1. Select the **Identities** tab. There should be an LDAP identity with
an LDAP DN as the 'Identifier'. If not, this user hasn't signed in with
LDAP yet and must do so first.
-- You've waited an hour or [the configured
- interval](index.md#adjust-ldap-group-sync-schedule) for the group to
- sync. To speed up the process, either go to the GitLab group **Group information > Members**
- and press **Sync now** (sync one group) or [run the group sync Rake
- task](../../raketasks/ldap.md#run-a-group-sync) (sync all groups).
+- You've waited an hour or [the configured interval](index.md#adjust-ldap-group-sync-schedule) for
+ the group to sync. To speed up the process, either go to the GitLab group **Group information > Members**
+ and press **Sync now** (sync one group) or [run the group sync Rake task](../../raketasks/ldap.md#run-a-group-sync)
+ (sync all groups).
If all of the above looks good, jump in to a little more advanced debugging in
the rails console.
@@ -371,8 +369,8 @@ the rails console.
1. Look through the output of the sync. See [example log
output](#example-console-output-after-a-group-sync)
for how to read the output.
-1. If you still aren't able to see why the user isn't being added, [query the
- LDAP group directly](#query-a-group-in-ldap) to see what members are listed.
+1. If you still aren't able to see why the user isn't being added, [query the LDAP group directly](#query-a-group-in-ldap)
+ to see what members are listed.
1. Is the user's DN or UID in one of the lists from the above output? One of the DNs or
UIDs here should match the 'Identifier' from the LDAP identity checked earlier. If it doesn't,
the user does not appear to be in the LDAP group.
@@ -387,7 +385,7 @@ the following are true:
- The configured `admin_group` in the `gitlab.rb` is a CN, rather than a DN or an array.
- This CN falls under the scope of the configured `group_base`.
- The members of the `admin_group` have already signed into GitLab with their LDAP
- credentials. GitLab only grants this administrator access to the users whose
+ credentials. GitLab only grants the Administrator role to the users whose
accounts are already connected to LDAP.
If all the above are true and the users are still not getting access, [run a manual
@@ -398,8 +396,8 @@ GitLab syncs the `admin_group`.
#### Sync all groups
NOTE:
-To sync all groups manually when debugging is unnecessary, [use the Rake
-task](../../raketasks/ldap.md#run-a-group-sync) instead.
+To sync all groups manually when debugging is unnecessary,
+[use the Rake task](../../raketasks/ldap.md#run-a-group-sync) instead.
The output from a manual [group sync](index.md#group-sync) can show you what happens
when GitLab syncs its LDAP group memberships against LDAP.
diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md
index 6a037e75f5..12729f2050 100644
--- a/doc/administration/auth/oidc.md
+++ b/doc/administration/auth/oidc.md
@@ -228,8 +228,7 @@ Azure B2C [offers two ways of defining the business logic for logging in a user]
While cumbersome to configure, custom policies are required because
standard Azure B2C user flows [do not send the OpenID `email` claim](https://github.com/MicrosoftDocs/azure-docs/issues/16566). In
-other words, they do not work with the [`allow_single_sign_on` or `auto_link_user`
-parameters](../../integration/omniauth.md#initial-omniauth-configuration).
+other words, they do not work with the [`allow_single_sign_on` or `auto_link_user` parameters](../../integration/omniauth.md#initial-omniauth-configuration).
With a standard Azure B2C policy, GitLab cannot create a new account or
link to an existing one with an email address.
diff --git a/doc/administration/auth/smartcard.md b/doc/administration/auth/smartcard.md
index 7e2699d5eb..d79837776b 100644
--- a/doc/administration/auth/smartcard.md
+++ b/doc/administration/auth/smartcard.md
@@ -126,7 +126,7 @@ more information, see [the relevant issue](https://gitlab.com/gitlab-org/gitlab/
gitlab_rails['smartcard_client_certificate_required_port'] = 3444
```
- NOTE: **Note**
+ NOTE:
Assign a value to at least one of the following variables:
`gitlab_rails['smartcard_client_certificate_required_host']` or
`gitlab_rails['smartcard_client_certificate_required_port']`.
diff --git a/doc/administration/clusters/kas.md b/doc/administration/clusters/kas.md
index 6afaff7339..226710a891 100644
--- a/doc/administration/clusters/kas.md
+++ b/doc/administration/clusters/kas.md
@@ -104,7 +104,7 @@ In Omnibus GitLab, find the logs in `/var/log/gitlab/gitlab-kas/`.
See also the [user documentation](../../user/clusters/agent/index.md#troubleshooting)
for troubleshooting problems with individual agents.
-### KAS logs - GitOps: failed to get project info
+### KAS logs - GitOps: failed to get project information
If you get the following error message:
diff --git a/doc/administration/configure.md b/doc/administration/configure.md
index d3e37b4a0e..822acc1a74 100644
--- a/doc/administration/configure.md
+++ b/doc/administration/configure.md
@@ -7,10 +7,44 @@ type: reference
# Configure your GitLab installation **(FREE SELF)**
-Customize and configure your self-managed GitLab installation.
+Customize and configure your self-managed GitLab installation. Here are some quick links to get you started:
- [Authentication](auth/index.md)
- [Configuration](../user/admin_area/index.md)
- [Repository storage](repository_storage_paths.md)
- [Geo](geo/index.md)
- [Packages](packages/index.md)
+
+The following tables are intended to guide you to choose the right combination of capabilties based on your requirements. It is common to want the most
+available, quickly recoverable, highly performant and fully data resilient solution. However, there are tradeoffs.
+
+The tables lists features on the left and provides their capabilities to the right along with known trade-offs.
+
+## Gitaly Capabilities
+
+| | Availability | Recoverability | Data Resiliency | Performance | Risks/Trade-offs|
+|-|--------------|----------------|-----------------|-------------|-----------------|
+|Gitaly Cluster | Very high - tolerant of node failures | RTO for a single node of 10s with no manual intervention | Data is stored on multiple nodes | Good - While writes may take slightly longer due to voting, read distribution improves read speeds | **Trade-off** - Slight decrease in write speed for redundant, strongly-consistent storage solution. **Risks** - [Does not currently support snapshot backups](gitaly/index.md#snapshot-backup-and-recovery-limitations), GitLab backup task can be slow for large data sets |
+|Gitaly Shards | Single storage location is a single point of failure | Would need to restore only shards which failed | Single point of failure | Good - can allocate repositories to shards to spread load | **Trade-off** - Need to manually configure repositories into different shards to balance loads / storage space **Risks** - Single point of failure relies on recovery process when single-node failure occurs |
+|Gitaly + NFS | Single storage location is a single point of failure | Single node failure requires restoration from backup | Single point of failure | Average - NFS is not ideally suited to large quantities of small reads / writes which can have a detrimental impact on performance | **Trade-off** - Easy and familiar administration though NFS is not ideally suited to Git demands **Risks** - Many instances of NFS compatibility issues which provide very poor customer experiences |
+
+## Geo Capabilities
+
+If your availabity needs to span multiple zones or multiple locations, please read about [Geo](geo/index.md).
+
+| | Availability | Recoverability | Data Resiliency | Performance | Risks/Trade-offs|
+|-|--------------|----------------|-----------------|-------------|-----------------|
+|Geo| Depends on the architecture of the Geo site. It is possible to deploy secondaries in single and multiple node configurations. | Eventually consistent. Recovery point depends on replication lag, which depends on a number of factors such as network speeds. Geo supports failover from a primary to secondary site using manual commands that are scriptable. | Geo currently replicates 100% of planned data types and verifies 50%. See [limitations table](geo/replication/datatypes.md#limitations-on-replicationverification) for more detail. | Improves read/clone times for users of a secondary. | Geo is not intended to replace other backup/restore solutions. Because of replication lag and the possibility of replicating bad data from a primary, we recommend that customers also take regular backups of their primary site and test the restore process. |
+
+## Scenarios for failure modes and available mitigation paths
+
+The following table outlines failure modes and mitigation paths for the product offerings detailed in the tables above. Note - Gitaly Cluster install assumes an odd number replication factor of 3 or greater
+
+| Gitaly Mode | Loss of Single Gitaly Node | Application / Data Corruption | Regional Outage (Loss of Instance) | Notes |
+| ----------- | -------------------------- | ----------------------------- | ---------------------------------- | ----- |
+| Single Gitaly Node | Downtime - Must restore from backup | Downtime - Must restore from Backup | Downtime - Must wait for outage to end | |
+| Single Gitaly Node + Geo Secondary | Downtime - Must restore from backup, can perform a manual failover to secondary | Downtime - Must restore from Backup, errors could have propagated to secondary | Manual intervention - failover to Geo secondary | |
+| Sharded Gitaly Install | Partial Downtime - Only repos on impacted node affected, must restore from backup | Partial Downtime - Only repos on impacted node affected, must restore from backup | Downtime - Must wait for outage to end | |
+| Sharded Gitaly Install + Geo Secondary | Partial Downtime - Only repos on impacted node affected, must restore from backup, could perform manual failover to secondary for impacted repos | Partial Downtime - Only repos on impacted node affected, must restore from backup, errors could have propagated to secondary | Manual intervention - failover to Geo secondary | |
+| Gitaly Cluster Install* | No Downtime - will swap repository primary to another node after 10 seconds | N/A - All writes are voted on by multiple Gitaly Cluster nodes | Downtime - Must wait for outage to end | Snapshot backups for Gitaly Cluster nodes not supported at this time |
+| Gitaly Cluster Install* + Geo Secondary | No Downtime - will swap repository primary to another node after 10 seconds | N/A - All writes are voted on by multiple Gitaly Cluster nodes | Manual intervention - failover to Geo secondary | Snapshot backups for Gitaly Cluster nodes not supported at this time |
diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md
index 057abce0ed..3af8036391 100644
--- a/doc/administration/environment_variables.md
+++ b/doc/administration/environment_variables.md
@@ -21,6 +21,7 @@ You can use the following environment variables to override certain values:
|--------------------------------------------|---------|---------------------------------------------------------------------------------------------------------|
| `DATABASE_URL` | string | The database URL; is of the form: `postgresql://localhost/blog_development`. |
| `ENABLE_BOOTSNAP` | string | Enables Bootsnap for speeding up initial Rails boot (`1` to enable). |
+| `EXTERNAL_URL` | string | Specify the external URL at the [time of installation](https://docs.gitlab.com/omnibus/settings/configuration.html#specifying-the-external-url-at-the-time-of-installation). |
| `EXTERNAL_VALIDATION_SERVICE_TIMEOUT` | integer | Timeout, in seconds, for an [external CI/CD pipeline validation service](external_pipeline_validation.md). Default is `5`. |
| `EXTERNAL_VALIDATION_SERVICE_URL` | string | URL to an [external CI/CD pipeline validation service](external_pipeline_validation.md). |
| `EXTERNAL_VALIDATION_SERVICE_TOKEN` | string | The `X-Gitlab-Token` for authentication with an [external CI/CD pipeline validation service](external_pipeline_validation.md). |
diff --git a/doc/administration/external_pipeline_validation.md b/doc/administration/external_pipeline_validation.md
index 738cf59121..a4ed287cc3 100644
--- a/doc/administration/external_pipeline_validation.md
+++ b/doc/administration/external_pipeline_validation.md
@@ -76,7 +76,8 @@ required number of seconds.
"email": { "type": "string" },
"created_at": { "type": ["string", "null"], "format": "date-time" },
"current_sign_in_ip": { "type": ["string", "null"] },
- "last_sign_in_ip": { "type": ["string", "null"] }
+ "last_sign_in_ip": { "type": ["string", "null"] },
+ "sign_in_count": { "type": "integer" }
}
},
"pipeline": {
diff --git a/doc/administration/feature_flags.md b/doc/administration/feature_flags.md
index 575fa9eb22..f2067e7a2d 100644
--- a/doc/administration/feature_flags.md
+++ b/doc/administration/feature_flags.md
@@ -1,8 +1,7 @@
---
-stage: none
-group: Development
-info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines"
-type: reference
+stage: Enablement
+group: Distribution
+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/#assignments
description: "GitLab administrator: enable and disable GitLab features deployed behind feature flags"
---
diff --git a/doc/administration/file_hooks.md b/doc/administration/file_hooks.md
index f73c961f54..e041f1e11c 100644
--- a/doc/administration/file_hooks.md
+++ b/doc/administration/file_hooks.md
@@ -7,16 +7,15 @@ type: reference
# File hooks **(FREE SELF)**
-> - Introduced in GitLab 10.6.
-> - Until GitLab 12.8, the feature name was Plugins.
+> Renamed feature from Plugins to File hooks in GitLab 12.8.
With custom file hooks, GitLab administrators can introduce custom integrations
without modifying the GitLab source code.
-NOTE:
-Instead of writing and supporting your own file hook you can make changes
-directly to the GitLab source code and contribute back upstream. This way we can
-ensure functionality is preserved across versions and covered by tests.
+A file hook runs on each event. You can filter events or projects
+in a file hook's code, and create many file hooks as you need. Each file hook is
+triggered by GitLab asynchronously in case of an event. For a list of events
+see the [system hooks](../system_hooks/system_hooks.md) documentation.
NOTE:
File hooks must be configured on the file system of the GitLab server. Only GitLab
@@ -24,10 +23,9 @@ server administrators can complete these tasks. Explore
[system hooks](../system_hooks/system_hooks.md) or [webhooks](../user/project/integrations/webhooks.md)
as an option if you do not have file system access.
-A file hook runs on each event. You can filter events or projects
-in a file hook's code, and create many file hooks as you need. Each file hook is
-triggered by GitLab asynchronously in case of an event. For a list of events
-see the [system hooks](../system_hooks/system_hooks.md) documentation.
+Instead of writing and supporting your own file hook, you can also make changes
+directly to the GitLab source code and contribute back upstream. In this way, we can
+ensure functionality is preserved across versions and covered by tests.
## Setup
@@ -67,7 +65,7 @@ message is logged to:
- `log/file_hook.log` in a source installation.
NOTE:
-Before 14.0 release, the file name was `plugin.log`
+In GitLab 13.12 and earlier, the filename was `plugin.log`
## Creating file hooks
diff --git a/doc/administration/geo/disaster_recovery/planned_failover.md b/doc/administration/geo/disaster_recovery/planned_failover.md
index a7a64701cb..6312ed669a 100644
--- a/doc/administration/geo/disaster_recovery/planned_failover.md
+++ b/doc/administration/geo/disaster_recovery/planned_failover.md
@@ -162,6 +162,9 @@ be disabled on the **primary** site:
## Finish replicating and verifying all data
+NOTE:
+GitLab 13.9 through GitLab 14.3 are affected by a bug in which the Geo secondary site statuses will appear to stop updating and become unhealthy. For more information, see [Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode](../replication/troubleshooting.md#geo-admin-area-shows-unhealthy-after-enabling-maintenance-mode).
+
1. If you are manually replicating any data not managed by Geo, trigger the
final replication process now.
1. On the **primary** node:
@@ -192,12 +195,13 @@ At this point, your **secondary** node contains an up-to-date copy of everything
## Promote the **secondary** node
-Finally, follow the [Disaster Recovery docs](index.md) to promote the
-**secondary** node to a **primary** node. This process causes a brief outage on the **secondary** node, and users may need to log in again.
+After the replication is finished, [promote the **secondary** node to a **primary** node](index.md). This process causes a brief outage on the **secondary** node, and users may need to log in again. If you follow the steps correctly, the old primary Geo site should still be disabled and user traffic should go to the newly-promoted site instead.
-Once it is completed, the maintenance window is over! Your new **primary** node, now
-begin to diverge from the old one. If problems do arise at this point, failing
+When the promotion is completed, the maintenance window is over, and your new **primary** node now
+begins to diverge from the old one. If problems do arise at this point, failing
back to the old **primary** node [is possible](bring_primary_back.md), but likely to result
in the loss of any data uploaded to the new **primary** in the meantime.
-Don't forget to remove the broadcast message after failover is complete.
+Don't forget to remove the broadcast message after the failover is complete.
+
+Finally, you can bring the [old site back as a secondary](bring_primary_back.md#configure-the-former-primary-node-to-be-a-secondary-node).
diff --git a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
index 4255fba83f..3eb7bc2a8e 100644
--- a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
+++ b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
@@ -63,6 +63,9 @@ Before following any of those steps, make sure you have `root` access to the
**secondary** to promote it, since there isn't provided an automated way to
promote a Geo replica and perform a failover.
+NOTE:
+GitLab 13.9 through GitLab 14.3 are affected by a bug in which the Geo secondary site statuses will appear to stop updating and become unhealthy. For more information, see [Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode](../../replication/troubleshooting.md#geo-admin-area-shows-unhealthy-after-enabling-maintenance-mode).
+
On the **secondary** node:
1. On the top bar, select **Menu > Admin**.
diff --git a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
index 18923da105..d4782144df 100644
--- a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
+++ b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
@@ -51,6 +51,9 @@ Before following any of those steps, make sure you have `root` access to the
**secondary** to promote it, since there isn't provided an automated way to
promote a Geo replica and perform a failover.
+NOTE:
+GitLab 13.9 through GitLab 14.3 are affected by a bug in which the Geo secondary site statuses will appear to stop updating and become unhealthy. For more information, see [Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode](../../replication/troubleshooting.md#geo-admin-area-shows-unhealthy-after-enabling-maintenance-mode).
+
On the **secondary** node, navigate to the **Admin Area > Geo** dashboard to
review its status. Replicated objects (shown in green) should be close to 100%,
and there should be no failures (shown in red). If a large proportion of
diff --git a/doc/administration/geo/replication/datatypes.md b/doc/administration/geo/replication/datatypes.md
index 3f38436429..e8e87f9248 100644
--- a/doc/administration/geo/replication/datatypes.md
+++ b/doc/administration/geo/replication/datatypes.md
@@ -198,12 +198,12 @@ successfully, you must replicate their data using some other means.
|[Package Registry](../../../user/packages/package_registry/index.md) | **Yes** (13.2) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.10) | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_package_file_replication`, enabled by default. |
|[Versioned Terraform State](../../terraform_state.md) | **Yes** (13.5) | [**Yes**](#limitation-of-verification-for-files-in-object-storage) (13.12) | Via Object Storage provider if supported. Native Geo support (Beta). | Replication is behind the feature flag `geo_terraform_state_version_replication`, enabled by default. Verification was behind the feature flag `geo_terraform_state_version_verification`, which was removed in 14.0|
|[External merge request diffs](../../merge_request_diffs.md) | **Yes** (13.5) | No | Via Object Storage provider if supported. Native Geo support (Beta). | Replication is behind the feature flag `geo_merge_request_diff_replication`, enabled by default. Verification is under development, behind the feature flag `geo_merge_request_diff_verification`, introduced in 14.0.|
-|[Versioned snippets](../../../user/snippets.md#versioned-snippets) | [**Yes** (13.7)](https://gitlab.com/groups/gitlab-org/-/epics/2809) | [**Yes** (14.2)](https://gitlab.com/groups/gitlab-org/-/epics/2810) | No | Verification was implemented behind the feature flag `geo_snippet_repository_verification` in 13.11, and the feature flag was removed in 14.2. |
-|[Server-side Git hooks](../../server_hooks.md) | [No](https://gitlab.com/groups/gitlab-org/-/epics/1867) | No | No | |
-|[Elasticsearch integration](../../../integration/elasticsearch.md) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/1186) | No | No | |
+|[Versioned snippets](../../../user/snippets.md#versioned-snippets) | [**Yes** (13.7)](https://gitlab.com/groups/gitlab-org/-/epics/2809) | [**Yes** (14.2)](https://gitlab.com/groups/gitlab-org/-/epics/2810) | No | Verification was implemented behind the feature flag `geo_snippet_repository_verification` in 13.11, and the feature flag was removed in 14.2. |
|[GitLab Pages](../../pages/index.md) | [**Yes** (14.3)](https://gitlab.com/groups/gitlab-org/-/epics/589) | No | Via Object Storage provider if supported. Native Geo support (Beta). | Behind feature flag `geo_pages_deployment_replication`, enabled by default. |
-|[Dependency proxy images](../../../user/packages/dependency_proxy/index.md) | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/259694) | No | No | Blocked on [Geo: Secondary Mimicry](https://gitlab.com/groups/gitlab-org/-/epics/1528). Replication of this cache is not needed for Disaster Recovery purposes because it can be recreated from external sources. |
-|[Vulnerability Export](../../../user/application_security/vulnerability_report/#export-vulnerability-details) | [Not planned](https://gitlab.com/groups/gitlab-org/-/epics/3111) | No | | Not planned because they are ephemeral and sensitive. They can be regenerated on demand. |
+|[Server-side Git hooks](../../server_hooks.md) | [Not planned](https://gitlab.com/groups/gitlab-org/-/epics/1867) | No | No | Not planned because of current implementation complexity, low customer interest, and availability of alternatives to hooks. |
+|[Elasticsearch integration](../../../integration/elasticsearch.md) | [Not planned](https://gitlab.com/gitlab-org/gitlab/-/issues/1186) | No | No | Not planned because further product discovery is required and Elasticsearch (ES) clusters can be rebuilt. Secondaries currently use the same ES cluster as the primary. |
+|[Dependency proxy images](../../../user/packages/dependency_proxy/index.md) | [Not planned](https://gitlab.com/gitlab-org/gitlab/-/issues/259694) | No | No | Blocked by [Geo: Secondary Mimicry](https://gitlab.com/groups/gitlab-org/-/epics/1528). Replication of this cache is not needed for disaster recovery purposes because it can be recreated from external sources. |
+|[Vulnerability Export](../../../user/application_security/vulnerability_report/#export-vulnerability-details) | [Not planned](https://gitlab.com/groups/gitlab-org/-/epics/3111) | No | No | Not planned because they are ephemeral and sensitive information. They can be regenerated on demand. |
#### Limitation of verification for files in Object Storage
diff --git a/doc/administration/geo/replication/geo_validation_tests.md b/doc/administration/geo/replication/geo_validation_tests.md
index c6b1078ddf..a4c2f15621 100644
--- a/doc/administration/geo/replication/geo_validation_tests.md
+++ b/doc/administration/geo/replication/geo_validation_tests.md
@@ -114,6 +114,13 @@ The following are GitLab upgrade validation tests we performed.
The following are PostgreSQL upgrade validation tests we performed.
+### September 2021
+
+[Verify Geo installation with PostgreSQL 13](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6131):
+
+- Description: With PostgreSQL 13 available as an opt-in version in GitLab 14.1, we tested fresh installations of GitLab with Geo when PostgreSQL 13 is enabled.
+- Outcome: Successfully built an environment with Geo and PostgreSQL 13 using [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/quality/gitlab-environment-toolkit) and performed Geo QA tests against the environment without failures.
+
### September 2020
[Verify PostgreSQL 12 upgrade for Geo installations](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5454):
diff --git a/doc/administration/geo/replication/multiple_servers.md b/doc/administration/geo/replication/multiple_servers.md
index 7db210d31f..87b1aa7fc4 100644
--- a/doc/administration/geo/replication/multiple_servers.md
+++ b/doc/administration/geo/replication/multiple_servers.md
@@ -199,7 +199,7 @@ then make the following modifications:
## `application_role` already enables this. You only need this line if
## you selectively enable individual services that depend on Rails, like
- ## `puma`, `sidekiq`, `geo-logcursor`, etc.
+ ## `puma`, `sidekiq`, `geo-logcursor`, and so on.
gitlab_rails['enable'] = true
##
diff --git a/doc/administration/geo/replication/object_storage.md b/doc/administration/geo/replication/object_storage.md
index 1f799b3012..3a10d3bad5 100644
--- a/doc/administration/geo/replication/object_storage.md
+++ b/doc/administration/geo/replication/object_storage.md
@@ -73,7 +73,7 @@ GitLab does not currently support the case where both:
## Third-party replication services
When using Amazon S3, you can use
-[CRR](https://docs.aws.amazon.com/AmazonS3/latest/dev/crr.html) to
+[Cross-Region Replication (CRR)](https://docs.aws.amazon.com/AmazonS3/latest/dev/crr.html) to
have automatic replication between the bucket used by the **primary** site and
the bucket used by **secondary** sites.
diff --git a/doc/administration/geo/replication/security_review.md b/doc/administration/geo/replication/security_review.md
index 966902a3d7..df893298f8 100644
--- a/doc/administration/geo/replication/security_review.md
+++ b/doc/administration/geo/replication/security_review.md
@@ -26,7 +26,7 @@ from [owasp.org](https://owasp.org/).
- Geo streams almost all data held by a GitLab instance between sites. This
includes full database replication, most files (user-uploaded attachments,
- etc) and repository + wiki data. In a typical configuration, this will
+ and so on) and repository + wiki data. In a typical configuration, this will
happen across the public Internet, and be TLS-encrypted.
- PostgreSQL replication is TLS-encrypted.
- See also: [only TLSv1.2 should be supported](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/2948)
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index 7b82d742bd..b7370d3205 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -83,7 +83,7 @@ Checking Geo ... Finished
#### Sync status Rake task
Current sync information can be found manually by running this Rake task on any
-**secondary** app node:
+node running Rails (Puma, Sidekiq, or Geo Log Cursor) on the Geo **secondary** site:
```shell
sudo gitlab-rake geo:status
@@ -292,9 +292,8 @@ be set on the **primary** database. In GitLab 9.4, we have made this setting
default to 1. You may need to increase this value if you have more
**secondary** nodes.
-Be sure to restart PostgreSQL for this to take
-effect. See the [PostgreSQL replication
-setup](../setup/database.md#postgresql-replication) guide for more details.
+Be sure to restart PostgreSQL for this to take effect. See the
+[PostgreSQL replication setup](../setup/database.md#postgresql-replication) guide for more details.
### Message: `FATAL: could not start WAL streaming: ERROR: replication slot "geo_secondary_my_domain_com" does not exist`?
@@ -430,7 +429,7 @@ their resync may take a long time and cause significant load on your Geo nodes,
storage and network systems.
If you get the error `Synchronization failed - Error syncing repository` along with the following log messages, this indicates that the expected `geo` remote is not present in the `.git/config` file
-of a repository on the secondary Geo node's filesystem:
+of a repository on the secondary Geo node's file system:
```json
{
@@ -803,7 +802,7 @@ get_ctl_options': invalid option: --skip-preflight-checks (OptionParser::Invalid
get_ctl_options': invalid option: --force (OptionParser::InvalidOption)
```
-This can happen with XFS or filesystems that list files in lexical order, because the
+This can happen with XFS or file systems that list files in lexical order, because the
load order of the Omnibus command files can be different than expected, and a global function would get redefined.
More details can be found in [the related issue](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6076).
@@ -923,6 +922,14 @@ To resolve this issue:
If using a load balancer, ensure that the load balancer's URL is set as the `external_url` in the
`/etc/gitlab/gitlab.rb` of the nodes behind the load balancer.
+### Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode
+
+In GitLab 13.9 through GitLab 14.3, when [GitLab Maintenance Mode](../../maintenance_mode/index.md) is enabled, the status of Geo secondary sites will stop getting updated. After 10 minutes, the status will become `Unhealthy`.
+
+Geo secondary sites will continue to replicate and verify data, and the secondary sites should still be usable. You can use the [Sync status Rake task](#sync-status-rake-task) to determine the actual status of a secondary site during Maintenance Mode.
+
+This bug was [fixed in GitLab 14.4](https://gitlab.com/gitlab-org/gitlab/-/issues/292983).
+
### GitLab Pages return 404 errors after promoting
This is due to [Pages data not being managed by Geo](datatypes.md#limitations-on-replicationverification).
diff --git a/doc/administration/geo/replication/version_specific_updates.md b/doc/administration/geo/replication/version_specific_updates.md
index 84193e6baa..1b22a5f099 100644
--- a/doc/administration/geo/replication/version_specific_updates.md
+++ b/doc/administration/geo/replication/version_specific_updates.md
@@ -13,6 +13,8 @@ for updating Geo nodes.
## Updating to 14.1, 14.2, 14.3
+### Multi-arch images
+
We found an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/336013) where the Container Registry replication wasn't fully working if you used multi-arch images. In case of a multi-arch image, only the primary architecture (for example `amd64`) would be replicated to the secondary node. This has been [fixed in GitLab 14.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67624) and was backported to 14.2 and 14.1, but manual steps are required to force a re-sync.
You can check if you are affected by running:
@@ -46,18 +48,28 @@ Otherwise, on all your **secondary** nodes, in a [Rails console](../../operation
If you are running a version prior to 14.1 and are using Geo and multi-arch containers in your Container Registry, we recommend [upgrading](updating_the_geo_sites.md) to at least GitLab 14.1.
+### Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode
+
+GitLab 13.9 through GitLab 14.3 are affected by a bug in which enabling [GitLab Maintenance Mode](../../maintenance_mode/index.md) will cause Geo secondary site statuses to appear to stop updating and become unhealthy. For more information, see [Troubleshooting - Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode](troubleshooting.md#geo-admin-area-shows-unhealthy-after-enabling-maintenance-mode).
+
## Updating to GitLab 14.0/14.1
+### Primary sites can not be removed from the UI
+
We found an issue where [Primary sites can not be removed from the UI](https://gitlab.com/gitlab-org/gitlab/-/issues/338231).
This bug only exists in the UI and does not block the removal of Primary sites using any other method.
-### If you have already updated to an affected version and need to remove your Primary site
+If you are running an affected version and need to remove your Primary site, you can manually remove the Primary site by using the [Geo Nodes API](../../../api/geo_nodes.md#delete-a-geo-node).
-You can manually remove the Primary site by using the [Geo Nodes API](../../../api/geo_nodes.md#delete-a-geo-node).
+### Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode
+
+GitLab 13.9 through GitLab 14.3 are affected by a bug in which enabling [GitLab Maintenance Mode](../../maintenance_mode/index.md) will cause Geo secondary site statuses to appear to stop updating and become unhealthy. For more information, see [Troubleshooting - Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode](troubleshooting.md#geo-admin-area-shows-unhealthy-after-enabling-maintenance-mode).
## Updating to GitLab 13.12
+### Secondary nodes re-download all LFS files upon update
+
We found an issue where [secondary nodes re-download all LFS files](https://gitlab.com/gitlab-org/gitlab/-/issues/334550) upon update. This bug:
- Only applies to Geo secondary sites that have replicated LFS objects.
@@ -68,7 +80,7 @@ We found an issue where [secondary nodes re-download all LFS files](https://gitl
If you don't have many LFS objects or can stand a bit of churn, then it is safe to let the secondary sites re-download LFS objects.
If you do have many LFS objects, or many Geo secondary sites, or limited bandwidth, or a combination of them all, then we recommend you skip GitLab 13.12.0 through 13.12.6 and update to GitLab 13.12.7 or newer.
-### If you have already updated to an affected version, and the re-sync is ongoing
+#### If you have already updated to an affected version, and the re-sync is ongoing
You can manually migrate the legacy sync state to the new state column by running the following command in a [Rails console](../../operations/rails_console.md). It should take under a minute:
@@ -76,15 +88,31 @@ You can manually migrate the legacy sync state to the new state column by runnin
Geo::LfsObjectRegistry.where(state: 0, success: true).update_all(state: 2)
```
+### Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode
+
+GitLab 13.9 through GitLab 14.3 are affected by a bug in which enabling [GitLab Maintenance Mode](../../maintenance_mode/index.md) will cause Geo secondary site statuses to appear to stop updating and become unhealthy. For more information, see [Troubleshooting - Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode](troubleshooting.md#geo-admin-area-shows-unhealthy-after-enabling-maintenance-mode).
+
## Updating to GitLab 13.11
We found an [issue with Git clone/pull through HTTP(s)](https://gitlab.com/gitlab-org/gitlab/-/issues/330787) on Geo secondaries and on any GitLab instance if maintenance mode is enabled. This was caused by a regression in GitLab Workhorse. This is fixed in the [GitLab 13.11.4 patch release](https://about.gitlab.com/releases/2021/05/14/gitlab-13-11-4-released/). To avoid this issue, upgrade to GitLab 13.11.4 or later.
+### Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode
+
+GitLab 13.9 through GitLab 14.3 are affected by a bug in which enabling [GitLab Maintenance Mode](../../maintenance_mode/index.md) will cause Geo secondary site statuses to appear to stop updating and become unhealthy. For more information, see [Troubleshooting - Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode](troubleshooting.md#geo-admin-area-shows-unhealthy-after-enabling-maintenance-mode).
+
+## Updating to GitLab 13.10
+
+### Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode
+
+GitLab 13.9 through GitLab 14.3 are affected by a bug in which enabling [GitLab Maintenance Mode](../../maintenance_mode/index.md) will cause Geo secondary site statuses to appear to stop updating and become unhealthy. For more information, see [Troubleshooting - Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode](troubleshooting.md#geo-admin-area-shows-unhealthy-after-enabling-maintenance-mode).
+
## Updating to GitLab 13.9
+### Error during zero-downtime update: "cannot drop column asset_proxy_whitelist"
+
We've detected an issue [with a column rename](https://gitlab.com/gitlab-org/gitlab/-/issues/324160)
that will prevent upgrades to GitLab 13.9.0, 13.9.1, 13.9.2 and 13.9.3 when following the zero-downtime steps. It is necessary
-to perform the following additional steps for the zero-downtime upgrade:
+to perform the following additional steps for the zero-downtime update:
1. Before running the final `sudo gitlab-rake db:migrate` command on the deploy node,
execute the following queries using the PostgreSQL console (or `sudo gitlab-psql`)
@@ -118,6 +146,10 @@ DETAIL: trigger trigger_0d588df444c8 on table application_settings depends on co
To work around this bug, follow the previous steps to complete the update.
More details are available [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/324160).
+### Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode
+
+GitLab 13.9 through GitLab 14.3 are affected by a bug in which enabling [GitLab Maintenance Mode](../../maintenance_mode/index.md) will cause Geo secondary site statuses to appear to stop updating and become unhealthy. For more information, see [Troubleshooting - Geo Admin Area shows 'Unhealthy' after enabling Maintenance Mode](troubleshooting.md#geo-admin-area-shows-unhealthy-after-enabling-maintenance-mode).
+
## Updating to GitLab 13.7
We've detected an issue with the `FetchRemove` call used by Geo secondaries.
diff --git a/doc/administration/get_started.md b/doc/administration/get_started.md
index c0d5a45d8d..2455aecafe 100644
--- a/doc/administration/get_started.md
+++ b/doc/administration/get_started.md
@@ -54,7 +54,7 @@ Get started:
You may need to import projects from external sources like GitHub, Bitbucket, or another instance of GitLab. Many external sources can be imported into GitLab.
- Review the [GitLab projects documentation](../user/project/index.md#project-integrations).
-- Consider [repository mirroring](../user/project/repository/repository_mirroring.md)—an [alternative to project migrations](../ci/ci_cd_for_external_repos/index.md).
+- Consider [repository mirroring](../user/project/repository/mirror/index.md)—an [alternative to project migrations](../ci/ci_cd_for_external_repos/index.md).
- Check out our [migration index](../user/project/import/index.md) for documentation on common migration paths.
- Schedule your project exports with our [import/export API](../api/project_import_export.md#schedule-an-export).
@@ -128,7 +128,7 @@ The routine differs, depending on whether you deployed with Omnibus or the Helm
When you backing up an Omnibus (single node) GitLab server, you can use a single Rake task.
-Learn about [backing up Omnibus or Helm variations](../raketasks/backup_restore.md#back-up-gitlab).
+Learn about [backing up Omnibus or Helm variations](../raketasks/backup_restore.md).
This process backs up your entire instance, but does not back up the configuration files. Ensure those are backed up separately.
Keep your configuration files and backup archives in a separate location to ensure the encryption keys are not kept with the encrypted data.
@@ -214,7 +214,7 @@ If you use GitLab SaaS, you have several channels with which to get support and
To get assistance for GitLab SaaS:
-- Access [GitLab Docs](../README.md) for self-service support.
+- Access [GitLab Docs](../index.md) for self-service support.
- Join the [GitLab Forum](https://forum.gitlab.com/) for community support.
- Gather [your subscription information](https://about.gitlab.com/support/#for-self-managed-users) before submitting a ticket.
- Submit a support ticket for:
diff --git a/doc/administration/gitaly/faq.md b/doc/administration/gitaly/faq.md
index c7ecaa020e..f79b9555c1 100644
--- a/doc/administration/gitaly/faq.md
+++ b/doc/administration/gitaly/faq.md
@@ -35,7 +35,7 @@ For more information, see:
## Are there instructions for migrating to Gitaly Cluster?
-Yes! For more information, see [Migrate to Gitaly Cluster](index.md#migrate-to-gitaly-cluster).
+Yes! For more information, see [Migrating to Gitaly Cluster](index.md#migrating-to-gitaly-cluster).
## What are some repository storage recommendations?
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 7a7aac884e..c689530e12 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -10,14 +10,22 @@ type: reference
[Gitaly](https://gitlab.com/gitlab-org/gitaly) provides high-level RPC access to Git repositories.
It is used by GitLab to read and write Git data.
+Gitaly is present in every GitLab installation and coordinates Git repository
+storage and retrieval. Gitaly can be:
+
+- A simple background service operating on a single instance Omnibus GitLab (all of
+ GitLab on one machine).
+- Separated onto its own instance and configured in a full cluster configuration,
+ depending on scaling and availability requirements.
+
Gitaly implements a client-server architecture:
- A Gitaly server is any node that runs Gitaly itself.
-- A Gitaly client is any node that runs a process that makes requests of the Gitaly server. These
- include, but are not limited to:
- - [GitLab Rails application](https://gitlab.com/gitlab-org/gitlab).
- - [GitLab Shell](https://gitlab.com/gitlab-org/gitlab-shell).
- - [GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse).
+- A Gitaly client is any node that runs a process that makes requests of the Gitaly server. Gitaly clients are also known as _Gitaly consumers_ and include:
+ - [GitLab Rails application](https://gitlab.com/gitlab-org/gitlab)
+ - [GitLab Shell](https://gitlab.com/gitlab-org/gitlab-shell)
+ - [GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse)
+ - [GitLab Elasticsearch Indexer](https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer)
Gitaly manages only Git repository access for GitLab. Other types of GitLab data aren't accessed
using Gitaly.
@@ -35,9 +43,49 @@ repository storage is either:
- Read requests are distributed between multiple Gitaly nodes, which can improve performance.
- Write requests are broadcast to repository replicas.
-WARNING:
-Engineering support for NFS for Git repositories is deprecated. Read the
-[deprecation notice](#nfs-deprecation-notice).
+## Guidance regarding Gitaly Cluster
+
+Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please review existing technical limitations and considerations prior to deploying Gitaly Cluster.
+
+- [Known issues](#known-issues)
+- [Snapshot limitations](#snapshot-backup-and-recovery-limitations).
+
+Please also review the [configuration guidance](configure_gitaly.md) and [Repository storage options](../repository_storage_paths.md) to make sure that Gitaly Cluster is the best set-up for you. Finally, refer to the following guidance:
+
+- If you have not yet migrated to Gitaly Cluster and want to continue using NFS, remain on the
+ service you are using. NFS is supported in 14.x releases.
+- If you have not yet migrated to Gitaly Cluster but want to migrate away from NFS, you have two options - a sharded Gitaly instance or Gitaly Cluster.
+- If you have migrated to Gitaly Cluster and the limitations and tradeoffs are not suitable for your environment, your options are:
+ 1. [Migrate off Gitaly Cluster](#migrate-off-gitaly-cluster) back to your NFS solution
+ 1. [Migrate off Gitaly Cluster](#migrate-off-gitaly-cluster) to NFS solution or to a sharded Gitaly instance.
+
+Reach out to your Technical Account Manager or customer support if you have any questions.
+
+### Known issues
+
+The following table outlines current known issues impacting the use of Gitaly Cluster. For
+the current status of these issues, please refer to the referenced issues and epics.
+
+| Issue | Summary | How to avoid |
+|:--------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------|
+| Gitaly Cluster + Geo - Issues retrying failed syncs | If Gitaly Cluster is used on a Geo secondary site, repositories that have failed to sync could continue to fail when Geo tries to resync them. Recovering from this state requires assistance from support to run manual steps. Work is in-progress to update Gitaly Cluster to [identify repositories with a unique and persistent identifier](https://gitlab.com/gitlab-org/gitaly/-/issues/3485), which is expected to resolve the issue. | No known solution at this time. |
+| Database inconsistencies due to repository access outside of Gitaly Cluster's control | Operations that write to the repository storage that do not go through normal Gitaly Cluster methods can cause database inconsistencies. These can include (but are not limited to) snapshot restoration for cluster node disks, node upgrades which modify files under Git control, or any other disk operation that may touch repository storage external to GitLab. The Gitaly team is actively working to provide manual commands to [reconcile the Praefect database with the repository storage](https://gitlab.com/groups/gitlab-org/-/epics/6723). | Don't directly change repositories on any Gitaly Cluster node at this time. |
+| Praefect unable to insert data into the database due to migrations not being applied after an upgrade | If the database is not kept up to date with completed migrations, then the Praefect node is unable to perform normal operation. | Make sure the Praefect database is up and running with all migrations completed (For example: `/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate-status` should show a list of all applied migrations). Consider [requesting live upgrade assistance](https://about.gitlab.com/support/scheduling-live-upgrade-assistance.html) so your upgrade plan can be reviewed by support. |
+| Restoring a Gitaly Cluster node from a snapshot in a running cluster | Because the Gitaly Cluster runs with consistent state, introducing a single node that is behind will result in the cluster not being able to reconcile the nodes data and other nodes data | Don't restore a single Gitaly Cluster node from a backup snapshot. If you need to restore from backup, it's best to snapshot all Gitaly Cluster nodes at the same time and take a database dump of the Praefect database. |
+
+### Snapshot backup and recovery limitations
+
+Gitaly Cluster does not support snapshot backups because these can cause issues where the Praefect
+database becomes out of sync with the disk storage. Because of how Praefect rebuilds the replication
+metadata of Gitaly disk information during a restore, we recommend using the
+[official backup and restore Rake tasks](../../raketasks/backup_restore.md). If you are unable to use this method, please contact customer support for restoration help.
+
+To track progress on work on a solution for manually re-synchronizing the Praefect database with
+disk storage, see [this epic](https://gitlab.com/groups/gitlab-org/-/epics/6575).
+
+### What to do if you are on Gitaly Cluster experiencing an issue or limitation
+
+Please contact customer support for immediate help in restoration or recovery.
## Gitaly
@@ -156,54 +204,6 @@ WARNING:
If complete cluster failure occurs, disaster recovery plans should be executed. These can affect the
RPO and RTO discussed above.
-### Architecture and configuration recommendations
-
-The following table provides recommendations based on your
-requirements. Users means concurrent users actively performing
-simultaneous Git Operations.
-
-Gitaly services are present in every GitLab installation and always coordinates Git repository storage and
-retrieval. Gitaly can be as simple as a single background service when operating on a single instance Omnibus
-GitLab (All of GitLab on one machine). Gitaly can be separated into it's own instance and it can be configured in
-a full cluster configuration depending on scaling and availability requirements.
-
-The GitLab Reference Architectures provide guidance for what Gitaly configuration is advisable at each of the scales.
-The Gitaly configuration is noted by the architecture diagrams and the table of required resources.
-
-| User Scaling | Reference Architecture To Use | GitLab Instance Configuration | Gitaly Configuration | Git Repository Storage | Instances Dedicated to Gitaly Services |
-| ------------------------------------------------------------ | ------------------------------------------------------------ | -------------------------------------------- | ------------------------------- | ---------------------------------- | -------------------------------------- |
-| Up to 1000 Users | [1K](../reference_architectures/1k_users.md) | Single Instance for all of GitLab | Already Integrated as a Service | Local Disk | 0 |
-| Up to 2999 Users | [2K](../reference_architectures/2k_users.md) | Horizontally Scaled GitLab Instance (Non-HA) | Single Gitaly Server | Local Disk of Gitaly Instance | 1 |
-| 3000 Users and Over | [3K](../reference_architectures/1k_users.md) | Horizontally Scaled GitLab Instance with HA | Gitaly Cluster | Local Disk of Each Gitaly Instance | 8 |
-| RTO/RPO Requirements for AZ Failure Tolerance Regardless of User Scale | [3K (with downscaling)](../reference_architectures/3k_users.md) | Custom (1) | Gitaly Cluster | Local Disk of Each Gitaly Instance | 8 |
-
-1. If you feel that you need AZ Failure Tolerance for user scaling lower than 3K, please contact Customer Success
- to discuss your RTO and RPO needs and what options exist to meet those objectives.
-
-WARNING:
-At present, some [known database inconsistency issues](#known-issues-impacting-gitaly-cluster)
-exist in Gitaly Cluster. It is our recommendation that for now, you remain on your current service.
-We will adjust the date for NFS support removal if this applies to you.
-
-### Known issues impacting Gitaly Cluster
-
-The following table outlines current known issues impacting the use of Gitaly Cluster. For
-the most up to date status of these issues, please refer to the referenced issues / epics.
-
-| Issue | Summary |
-| Gitaly Cluster + Geo can cause database inconsistencies | There are some conditions during Geo replication that can cause database inconsistencies with Gitaly Cluster. These have been identified and are being resolved by updating Gitaly Cluster to [identify repositories with a unique and persistent identifier](https://gitlab.com/gitlab-org/gitaly/-/issues/3485). |
-| Database inconsistencies due to repository access outside of Gitaly Cluster's control | Operations that write to the repository storage which do not go through normal Gitaly Cluster methods can cause database inconsistencies. These can include (but are not limited to) snapshot restoration for cluster node disks, node upgrades which modify files under Git control, or any other disk operation that may touch repository storage external to GitLab. The Gitaly team is actively working to provide manual commands to [reconcile the Praefect database with the repository storage](https://gitlab.com/groups/gitlab-org/-/epics/6723). |
-
-### Snapshot backup and recovery limitations
-
-Gitaly Cluster does not support snapshot backups because these can cause issues where the
-Praefect database becomes out of sync with the disk storage. Because of how Praefect rebuilds
-the replication metadata of Gitaly disk information during a restore, we recommend using the
-[official backup and restore Rake tasks](../../raketasks/backup_restore.md).
-
-To track progress on work on a solution for manually re-synchronizing the Praefect database
-with disk storage, see [this epic](https://gitlab.com/groups/gitlab-org/-/epics/6575).
-
### Virtual storage
Virtual storage makes it viable to have a single repository storage in GitLab to simplify repository
@@ -232,9 +232,7 @@ As with normal Gitaly storages, virtual storages can be sharded.
### Moving beyond NFS
-WARNING:
-Engineering support for NFS for Git repositories is deprecated. Technical support is planned to be
-unavailable from GitLab 15.0. No further enhancements are planned for this feature.
+Engineering support for NFS for Git repositories is deprecated. Technical support is planned to be unavailable starting GitLab 15.0. Please see our [statement of support](https://about.gitlab.com/support/statement-of-support.html#gitaly-and-nfs) for more details.
[Network File System (NFS)](https://en.wikipedia.org/wiki/Network_File_System)
is not well suited to Git workloads which are CPU and IOPS sensitive.
@@ -355,13 +353,9 @@ For configuration information, see [Configure replication factor](praefect.md#co
For more information on configuring Gitaly Cluster, see [Configure Gitaly Cluster](praefect.md).
-## Migrate to Gitaly Cluster
+## Migrating to Gitaly Cluster
-We recommend you migrate to Gitaly Cluster if your
-[requirements recommend](#architecture-and-configuration-recommendations) Gitaly Cluster.
-
-Whether migrating to Gitaly Cluster because of [NFS support deprecation](index.md#nfs-deprecation-notice)
-or to move from single Gitaly nodes, the basic process involves:
+Please see [current guidance on Gitaly Cluster](#guidance-regarding-gitaly-cluster). The basic process for migrating to Gitaly Cluster involves:
1. Create the required storage. Refer to
[repository storage recommendations](faq.md#what-are-some-repository-storage-recommendations).
@@ -371,9 +365,20 @@ or to move from single Gitaly nodes, the basic process involves:
automatic migration but the moves can be scheduled with the GitLab API.
WARNING:
-At present, some [known database inconsistency issues](#known-issues-impacting-gitaly-cluster)
-exist in Gitaly Cluster. It is our recommendation that for now, you remain on your current service.
-We will adjust the date for NFS support removal if this applies to you.
+Some [known database inconsistency issues](#known-issues) exist in Gitaly Cluster. We recommend you
+remain on your current service for now.
+
+### Migrate off Gitaly Cluster
+
+If you have repositories stored on a Gitaly Cluster, but you'd like to migrate
+them back to direct Gitaly storage:
+
+1. Create and configure a new
+ [Gitaly server](configure_gitaly.md#run-gitaly-on-its-own-server).
+1. [Move the repositories](../operations/moving_repositories.md#move-repositories)
+ to the newly created storage. There are different possibilities to move them
+ by shard or by group, this gives you the opportunity to spread them over
+ multiple Gitaly servers.
## Monitor Gitaly and Gitaly Cluster
@@ -615,20 +620,4 @@ The second facet presents the only real solution. For this, we developed
## NFS deprecation notice
Engineering support for NFS for Git repositories is deprecated. Technical support is planned to be
-unavailable from GitLab 15.0. No further enhancements are planned for this feature.
-
-Additional information:
-
-- [Recommended NFS mount options and known issues with Gitaly and NFS](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
-- [GitLab statement of support](https://about.gitlab.com/support/statement-of-support.html#gitaly-and-nfs).
-
-GitLab recommends:
-
-- Creating a [Gitaly Cluster](#gitaly-cluster) as soon as possible.
-- [Moving your repositories](#migrate-to-gitaly-cluster) from NFS-based storage to Gitaly
- Cluster.
-
-We welcome your feedback on this process. You can:
-
-- Raise a support ticket.
-- [Comment on the epic](https://gitlab.com/groups/gitlab-org/-/epics/4916).
+unavailable from GitLab 15.0. For further information, please see our [NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation) documentation.
diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md
index eb666f1caf..d3ea71bc8d 100644
--- a/doc/administration/gitaly/praefect.md
+++ b/doc/administration/gitaly/praefect.md
@@ -265,8 +265,8 @@ praefect['database_direct_dbname'] = 'praefect_production'
#praefect['database_direct_sslrootcert'] = '...'
```
-We recommend using PgBouncer with `session` pool mode instead. You can use the [bundled
-PgBouncer](../postgresql/pgbouncer.md) or use an external PgBouncer and [configure it
+We recommend using PgBouncer with `session` pool mode instead. You can use the
+[bundled PgBouncer](../postgresql/pgbouncer.md) or use an external PgBouncer and [configure it
manually](https://www.pgbouncer.org/config.html).
The following example uses the bundled PgBouncer and sets up two separate connection pools,
@@ -429,7 +429,7 @@ On the **Praefect** node:
WARNING:
If you have data on an already existing storage called
`default`, you should configure the virtual storage with another name and
- [migrate the data to the Gitaly Cluster storage](index.md#migrate-to-gitaly-cluster)
+ [migrate the data to the Gitaly Cluster storage](index.md#migrating-to-gitaly-cluster)
afterwards.
Replace `PRAEFECT_INTERNAL_TOKEN` with a strong secret, which is used by
@@ -475,8 +475,8 @@ On the **Praefect** node:
1. [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2013) in GitLab 13.1 and later, enable [distribution of reads](index.md#distributed-reads).
-1. Save the changes to `/etc/gitlab/gitlab.rb` and [reconfigure
- Praefect](../restart_gitlab.md#omnibus-gitlab-reconfigure):
+1. Save the changes to `/etc/gitlab/gitlab.rb` and
+ [reconfigure Praefect](../restart_gitlab.md#omnibus-gitlab-reconfigure):
```shell
gitlab-ctl reconfigure
@@ -499,16 +499,16 @@ On the **Praefect** node:
running reconfigure automatically when running commands such as `apt-get update`. This way any
additional configuration changes can be done and then reconfigure can be run manually.
-1. Save the changes to `/etc/gitlab/gitlab.rb` and [reconfigure
- Praefect](../restart_gitlab.md#omnibus-gitlab-reconfigure):
+1. Save the changes to `/etc/gitlab/gitlab.rb` and
+ [reconfigure Praefect](../restart_gitlab.md#omnibus-gitlab-reconfigure):
```shell
gitlab-ctl reconfigure
```
1. To ensure that Praefect [has updated its Prometheus listen
- address](https://gitlab.com/gitlab-org/gitaly/-/issues/2734), [restart
- Praefect](../restart_gitlab.md#omnibus-gitlab-restart):
+ address](https://gitlab.com/gitlab-org/gitaly/-/issues/2734),
+ [restart Praefect](../restart_gitlab.md#omnibus-gitlab-restart):
```shell
gitlab-ctl restart praefect
@@ -695,8 +695,8 @@ Particular attention should be shown to:
was set in the [previous section](#praefect). This document uses `gitaly-1`,
`gitaly-2`, and `gitaly-3` as Gitaly storage names.
-For more information on Gitaly server configuration, see our [Gitaly
-documentation](configure_gitaly.md#configure-gitaly-servers).
+For more information on Gitaly server configuration, see our
+[Gitaly documentation](configure_gitaly.md#configure-gitaly-servers).
1. SSH into the **Gitaly** node and login as root:
@@ -803,16 +803,16 @@ documentation](configure_gitaly.md#configure-gitaly-servers).
})
```
-1. Save the changes to `/etc/gitlab/gitlab.rb` and [reconfigure
- Gitaly](../restart_gitlab.md#omnibus-gitlab-reconfigure):
+1. Save the changes to `/etc/gitlab/gitlab.rb` and
+ [reconfigure Gitaly](../restart_gitlab.md#omnibus-gitlab-reconfigure):
```shell
gitlab-ctl reconfigure
```
1. To ensure that Gitaly [has updated its Prometheus listen
- address](https://gitlab.com/gitlab-org/gitaly/-/issues/2734), [restart
- Gitaly](../restart_gitlab.md#omnibus-gitlab-restart):
+ address](https://gitlab.com/gitlab-org/gitaly/-/issues/2734),
+ [restart Gitaly](../restart_gitlab.md#omnibus-gitlab-restart):
```shell
gitlab-ctl restart gitaly
@@ -893,7 +893,7 @@ Particular attention should be shown to:
WARNING:
If you have existing data stored on the default Gitaly storage,
- you should [migrate the data your Gitaly Cluster storage](index.md#migrate-to-gitaly-cluster)
+ you should [migrate the data your Gitaly Cluster storage](index.md#migrating-to-gitaly-cluster)
first.
```ruby
@@ -1044,8 +1044,8 @@ To get started quickly:
grafana['disable_login_form'] = false
```
-1. Save the changes to `/etc/gitlab/gitlab.rb` and [reconfigure
- GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure):
+1. Save the changes to `/etc/gitlab/gitlab.rb` and
+ [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure):
```shell
gitlab-ctl reconfigure
@@ -1309,12 +1309,7 @@ To minimize data loss in GitLab 13.0 to 14.0, Gitaly Cluster:
new primary. If the failed primary contained unreplicated writes, [data loss can occur](#check-for-data-loss).
> - Removed in GitLab 14.1. Instead, repositories [become unavailable](#unavailable-repositories).
-In GitLab 13.0 to 14.0, when Gitaly Cluster switches to a new primary, repositories enter
-read-only mode if they are out of date. This can happen after failing over to an outdated
-secondary. Read-only mode eases data recovery efforts by preventing writes that may conflict
-with the unreplicated writes on other nodes.
-
-When Gitaly Cluster switches to a new primary In GitLab 13.0 to 14.0, repositories enter
+When Gitaly Cluster switches to a new primary in GitLab 13.0 to 14.0, repositories enter
read-only mode if they are out of date. This can happen after failing over to an outdated
secondary. Read-only mode eases data recovery efforts by preventing writes that may conflict
with the unreplicated writes on other nodes.
@@ -1586,13 +1581,99 @@ all state associated with a given repository including:
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml remove-repository -virtual-storage -repository
```
-- `-virtual-storage` is the virtual storage the repository is located in.
-- `-repository` is the repository's relative path in the storage.
+- `-virtual-storage` is the virtual storage the repository is located in. Virtual storages are configured in `/etc/gitlab/gitlab.rb` under `praefect['virtual_storages]` and looks like the following:
-Sometimes parts of the repository continue to exist after running `remove-repository`. This can be caused
-because of:
+ ```ruby
+ praefect['virtual_storages'] = {
+ 'default' => {
+ ...
+ },
+ 'storage-1' => {
+ ...
+ }
+ }
+ ```
+
+ In this example, the virtual storage to specify is `default` or `storage-1`.
+
+- `-repository` is the repository's relative path in the storage [beginning with `@hashed`](../repository_storage_types.md#hashed-storage).
+ For example:
+
+ ```plaintext
+ @hashed/f5/ca/f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b.git
+ ```
+
+Parts of the repository can continue to exist after running `remove-repository`. This can be because of:
- A deletion error.
- An in-flight RPC call targeting the repository.
If this occurs, run `remove-repository` again.
+
+### Manually list untracked repositories
+
+> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3926) in GitLab 14.4.
+
+The `list-untracked-repositories` Praefect sub-command lists repositories of the Gitaly Cluster that both:
+
+- Exist for at least one Gitaly storage.
+- Aren't tracked in the Praefect database.
+
+The command outputs:
+
+- Result to `STDOUT` and the command's logs.
+- Errors to `STDERR`.
+
+Each entry is a complete JSON string with a newline at the end (configurable using the
+`-delimiter` flag). For example:
+
+```plaintext
+sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml list-untracked-repositories
+{"virtual_storage":"default","storage":"gitaly-1","relative_path":"@hashed/ab/cd/abcd123456789012345678901234567890123456789012345678901234567890.git"}
+{"virtual_storage":"default","storage":"gitaly-1","relative_path":"@hashed/ab/cd/abcd123456789012345678901234567890123456789012345678901234567891.git"}
+```
+
+### Manually track repositories
+
+> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/5658) in GitLab 14.4.
+
+The `track-repository` Praefect sub-command adds repositories on disk to the Praefect database to be tracked.
+
+```shell
+sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml track-repository -virtual-storage -repository
+```
+
+- `-virtual-storage` is the virtual storage the repository is located in. Virtual storages are configured in `/etc/gitlab/gitlab.rb` under `praefect['virtual_storages]` and looks like the following:
+
+ ```ruby
+ praefect['virtual_storages'] = {
+ 'default' => {
+ ...
+ },
+ 'storage-1' => {
+ ...
+ }
+ }
+ ```
+
+ In this example, the virtual storage to specify is `default` or `storage-1`.
+
+- `-repository` is the repository's relative path in the storage [beginning with `@hashed`](../repository_storage_types.md#hashed-storage).
+ For example:
+
+ ```plaintext
+ @hashed/f5/ca/f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b.git
+ ```
+
+- `-authoritative-storage` is the storage we want Praefect to treat as the primary. Required if
+ [per-repository replication](#configure-replication-factor) is set as the replication strategy.
+
+The command outputs:
+
+- Results to `STDOUT` and the command's logs.
+- Errors to `STDERR`.
+
+This command fails if:
+
+- The repository is already being tracked by the Praefect database.
+- The repository does not exist on disk.
diff --git a/doc/administration/gitaly/troubleshooting.md b/doc/administration/gitaly/troubleshooting.md
index 1b53a0994f..1f90ebb756 100644
--- a/doc/administration/gitaly/troubleshooting.md
+++ b/doc/administration/gitaly/troubleshooting.md
@@ -243,7 +243,7 @@ To mirror-push branches and tags only, and avoid attempting to mirror-push prote
git push origin +refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/*
```
-Any other namespaces that the admin wants to push can be included there as well via additional patterns.
+Any other namespaces that the administrator wants to push can be included there as well via additional patterns.
### Command line tools cannot connect to Gitaly
@@ -365,6 +365,15 @@ To determine the current primary Gitaly node for a specific Praefect node:
curl localhost:9652/metrics | grep gitaly_praefect_primaries`
```
+### Check that repositories are in sync
+
+Is [some cases](index.md#known-issues) the Praefect database can get out of sync with the underlying Gitaly nodes. To check that
+a given repository is fully synced on all nodes, run the [`gitlab:praefect:replicas` Rake task](../raketasks/praefect.md#replica-checksums)
+that checksums the repository on all Gitaly nodes.
+
+The [Praefect dataloss](praefect.md#check-for-data-loss) command only checks the state of the repo in the Praefect database, and cannot
+be relied to detect sync problems in this scenario.
+
### Relation does not exist errors
By default Praefect database tables are created automatically by `gitlab-ctl reconfigure` task.
@@ -393,7 +402,7 @@ $ sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config
praefect sql-migrate: OK (applied 21 migrations)
```
-### Requests fail with 'repo scoped: invalid Repository' errors
+### Requests fail with 'repository scoped: invalid Repository' errors
This indicates that the virtual storage name used in the
[Praefect configuration](praefect.md#praefect) does not match the storage name used in
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 9412994edb..ee17edc35f 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -41,7 +41,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
### Configuring GitLab
-- [Adjust your instance's timezone](timezone.md): Customize the default time zone of GitLab.
+- [Adjust your instance's time zone](timezone.md): Customize the default time zone of GitLab.
- [System hooks](../system_hooks/system_hooks.md): Notifications when users, projects and keys are changed.
- [Security](../security/index.md): Learn what you can do to further secure your GitLab instance.
- [Usage statistics, version check, and Service Ping](../user/admin_area/settings/usage_statistics.md): Enable or disable information about your instance to be sent to GitLab, Inc.
diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md
index 24ffee088f..a2729e6054 100644
--- a/doc/administration/instance_limits.md
+++ b/doc/administration/instance_limits.md
@@ -88,6 +88,29 @@ requests per user. For more information, read
- **Default rate limit**: Disabled by default.
+### Files API
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68561) in GitLab 14.3.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available,
+ask an administrator to [enable the `files_api_throttling` flag](../administration/feature_flags.md). On GitLab.com, this feature is available but can be configured by GitLab.com administrators only.
+The feature is not ready for production use.
+
+This setting limits the request rate on the Packages API per user or IP address. For more information, read
+[Files API rate limits](../user/admin_area/settings/files_api_rate_limits.md).
+
+- **Default rate limit**: Disabled by default.
+
+### Deprecated API endpoints
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68645) in GitLab 14.4.
+
+This setting limits the request rate on deprecated API endpoints per user or IP address. For more information, read
+[Deprecated API rate limits](../user/admin_area/settings/deprecated_api_rate_limits.md).
+
+- **Default rate limit**: Disabled by default.
+
### Import/Export
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35728) in GitLab 13.2.
@@ -212,7 +235,7 @@ Activity history for projects and individuals' profiles was limited to one year
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14939) in GitLab 12.7.
-There is a limit when embedding metrics in GFM for performance reasons.
+There is a limit when embedding metrics in GitLab Flavored Markdown (GFM) for performance reasons.
- **Max limit**: 100 embeds.
@@ -240,10 +263,10 @@ Set the limit to `0` to disable it.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/237891) in GitLab 13.7.
-The [minimum wait time between pull refreshes](../user/project/repository/repository_mirroring.md)
+The [minimum wait time between pull refreshes](../user/project/repository/mirror/index.md)
defaults to 300 seconds (5 minutes). For example, by default a pull refresh will only run once in a given 300 second period regardless of how many times you try to trigger it.
-This setting applies in the context of pull refreshes invoked via the [projects API](../api/projects.md#start-the-pull-mirroring-process-for-a-project), or when forcing an update by selecting the **Update now** (**{retry}**) button within **Settings > Repository > Mirroring repositories**. This setting has no effect on the automatic 30 minute interval schedule used by Sidekiq for [pull mirroring](../user/project/repository/repository_mirroring.md#how-it-works).
+This setting applies in the context of pull refreshes invoked via the [projects API](../api/projects.md#start-the-pull-mirroring-process-for-a-project), or when forcing an update by selecting the **Update now** (**{retry}**) button within **Settings > Repository > Mirroring repositories**. This setting has no effect on the automatic 30 minute interval schedule used by Sidekiq for [pull mirroring](../user/project/repository/mirror/pull.md).
To change this limit for a self-managed installation, run the following in the
[GitLab Rails console](operations/rails_console.md#starting-a-rails-console-session):
@@ -511,7 +534,11 @@ Plan.default.actual_limits.update!(pages_file_entries: 100)
### Number of registered runners per scope
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321368) in GitLab 13.12.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321368) in GitLab 13.12. Disabled by default.
+> - Enabled on GitLab.com in GitLab 14.3.
+> - Enabled on self-managed in GitLab 14.4.
+> - Feature flag `ci_runner_limits` removed in GitLab 14.4. You can still use `ci_runner_limits_override`
+ to remove limits for a given scope.
The total number of registered runners is limited at the group and project levels. Each time a new runner is registered,
GitLab checks these limits against runners that have been active in the last 3 months.
@@ -749,7 +776,7 @@ than the specified limit, hooks won't be executed.
More information can be found in these docs:
-- [Webhooks push events](../user/project/integrations/webhooks.md#push-events)
+- [Webhooks push events](../user/project/integrations/webhook_events.md#push-events)
- [Project services push hooks limit](../user/project/integrations/overview.md#push-hooks-limit)
### Activities
diff --git a/doc/administration/instance_review.md b/doc/administration/instance_review.md
index b166bb32aa..6289765116 100644
--- a/doc/administration/instance_review.md
+++ b/doc/administration/instance_review.md
@@ -12,7 +12,7 @@ If you run a medium-sized self-managed instance (50+ users) of a free version of
[either Community Edition or unlicensed Enterprise Edition](https://about.gitlab.com/install/ce-or-ee/),
you qualify for a free Instance Review.
-1. Sign in as a user with administrator [permissions](../user/permissions.md).
+1. Sign in as a user with Administrator [role](../user/permissions.md).
1. In the top menu, click your user icon, and select
**Get a free instance review**:
diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md
index 882580b35b..45b94781ad 100644
--- a/doc/administration/integration/terminal.md
+++ b/doc/administration/integration/terminal.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Web terminals **(FREE)**
-With the introduction of the [Kubernetes integration](../../user/project/clusters/index.md),
+With the introduction of the [Kubernetes integration](../../user/infrastructure/clusters/index.md),
GitLab can store and use credentials for a Kubernetes cluster.
GitLab uses these credentials to provide access to
[web terminals](../../ci/environments/index.md#web-terminals) for environments.
@@ -50,8 +50,8 @@ detail below.
## Enabling and disabling terminal support
NOTE:
-AWS Elastic Load Balancers (ELBs) do not support web sockets.
-If you want web terminals to work, use AWS Application Load Balancers (ALBs).
+AWS Classic Load Balancers (CLBs) do not support web sockets.
+If you want web terminals to work, use AWS Network Load Balancers (NLBs).
Read [AWS Elastic Load Balancing Product Comparison](https://aws.amazon.com/elasticloadbalancing/features/#compare)
for more information.
diff --git a/doc/administration/invalidate_markdown_cache.md b/doc/administration/invalidate_markdown_cache.md
index 7a880c8184..855910fec6 100644
--- a/doc/administration/invalidate_markdown_cache.md
+++ b/doc/administration/invalidate_markdown_cache.md
@@ -15,8 +15,8 @@ in the cached text would still refer to the old URL.
To avoid this problem, the administrator can invalidate the existing cache by
increasing the `local_markdown_version` setting in application settings. This can
-be done by [changing the application settings through
-the API](../api/settings.md#change-application-settings):
+be done by changing the application settings
+[through the API](../api/settings.md#change-application-settings):
```shell
curl --request PUT --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/application/settings?local_markdown_version="
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index b4c16e007c..46a9ee1167 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -446,10 +446,10 @@ List the artifacts for a single project, sorted by artifact size. The output inc
- on-disk location of the artifact
```ruby
-p = Project.find_by_id(:project ID)
+p = Project.find_by_id()
arts = Ci::JobArtifact.where(project: p)
-list = arts.order('sort DESC').limit(50).each do |art|
+list = arts.order(size: :desc).limit(50).each do |art|
puts "Job ID: #{art.job_id} - Size: #{art.size}b - Type: #{art.file_type} - Created: #{art.created_at} - File loc: #{art.file}"
end
```
diff --git a/doc/administration/job_logs.md b/doc/administration/job_logs.md
index 64d9248cb1..f2748305c2 100644
--- a/doc/administration/job_logs.md
+++ b/doc/administration/job_logs.md
@@ -146,9 +146,9 @@ a background job archives the job log. The log is moved to `/var/opt/gitlab/gitl
by default, or to object storage if configured.
In a [scaled-out architecture](reference_architectures/index.md) with Rails and Sidekiq running on more than one
-server, these two locations on the filesystem have to be shared using NFS.
+server, these two locations on the file system have to be shared using NFS.
-To eliminate both filesystem requirements:
+To eliminate both file system requirements:
- [Enable the incremental logging feature](#enable-or-disable-incremental-logging), which uses Redis instead of disk space for temporary caching of job logs.
- Configure [object storage](job_artifacts.md#object-storage-settings) for storing archived job logs.
diff --git a/doc/administration/lfs/index.md b/doc/administration/lfs/index.md
index 682352d8f5..d2f220e379 100644
--- a/doc/administration/lfs/index.md
+++ b/doc/administration/lfs/index.md
@@ -2,18 +2,15 @@
stage: Create
group: Source Code
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/#assignments"
-type: reference, howto
disqus_identifier: 'https://docs.gitlab.com/ee/workflow/lfs/lfs_administration.html'
---
# GitLab Git Large File Storage (LFS) Administration **(FREE SELF)**
-> - Git LFS is supported in GitLab starting with version 8.2.
-> - Support for object storage, such as AWS S3, was introduced in 10.0.
-> - LFS is enabled in GitLab self-managed instances by default.
-
Documentation about how to use Git LFS are under [Managing large binary files with Git LFS doc](../../topics/git/lfs/index.md).
+LFS is enabled in GitLab self-managed instances by default.
+
## Requirements
- Users need to install [Git LFS client](https://git-lfs.github.com) version 1.0.1 or later.
@@ -346,8 +343,6 @@ git lfs version
## Known limitations
-- Support for removing unreferenced LFS objects was added in 8.14 onward.
-- LFS authentications via SSH was added with GitLab 8.12.
- Only compatible with the Git LFS client versions 1.1.0 and later, or 1.0.2.
- The storage statistics count each LFS object for
every project linking to it.
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index 990287e390..a9fd698a52 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -1069,6 +1069,14 @@ For Omnibus GitLab installations, Praefect logs are in `/var/log/gitlab/praefect
GitLab also tracks [Prometheus metrics for Praefect](gitaly/#monitor-gitaly-cluster).
+## Backup log
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63832) in GitLab 14.1.
+
+For Omnibus installations, the backup log is located at `/var/log/gitlab/gitlab-rails/backup_json.log`.
+
+This log is populated when a [GitLab backup is created](../raketasks/backup_restore.md). You can use this log to understand how the backup process performed.
+
## Performance bar stats
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48149) in GitLab 13.7.
@@ -1082,7 +1090,7 @@ Performance bar statistics (currently only duration of SQL queries) are recorded
in that file. For example:
```json
-{"severity":"INFO","time":"2020-12-04T09:29:44.592Z","correlation_id":"33680b1490ccd35981b03639c406a697","filename":"app/models/ci/pipeline.rb","method_path":"app/models/ci/pipeline.rb:each_with_object","request_id":"rYHomD0VJS4","duration_ms":26.889,"count":2,"type": "sql"}
+{"severity":"INFO","time":"2020-12-04T09:29:44.592Z","correlation_id":"33680b1490ccd35981b03639c406a697","filename":"app/models/ci/pipeline.rb","method_path":"app/models/ci/pipeline.rb:each_with_object","request_id":"rYHomD0VJS4","duration_ms":26.889,"count":2,"query_type": "active-record"}
```
These statistics are logged on .com only, disabled on self-deployments.
diff --git a/doc/administration/maintenance_mode/index.md b/doc/administration/maintenance_mode/index.md
index 39ee357cc2..d5bcd13266 100644
--- a/doc/administration/maintenance_mode/index.md
+++ b/doc/administration/maintenance_mode/index.md
@@ -75,7 +75,7 @@ An error is displayed when a user tries to perform a write operation that isn't
NOTE:
In some cases, the visual feedback from an action could be misleading, for example when starring a project, the **Star** button changes to show the **Unstar** action, however, this is only the frontend update, and it doesn't take into account the failed status of the POST request. These visual bugs are to be fixed [in follow-up iterations](https://gitlab.com/gitlab-org/gitlab/-/issues/295197).
-### Admin functions
+### Administrator functions
Systems administrators can edit the application settings. This allows
them to disable Maintenance Mode after it's been enabled.
@@ -111,18 +111,18 @@ For most JSON requests, POST, PUT, PATCH, and DELETE are blocked, and the API re
|HTTP request | Allowed routes | Notes |
|:----:|:--------------------------------------:|:----:|
-| POST | `/admin/application_settings/general` | To allow updating application settings in the admin UI |
+| POST | `/admin/application_settings/general` | To allow updating application settings in the administrator UI |
| PUT | `/api/v4/application/settings` | To allow updating application settings with the API |
| POST | `/users/sign_in` | To allow users to log in. |
| POST | `/users/sign_out`| To allow users to log out. |
| POST | `/oauth/token` | To allow users to log in to a Geo secondary for the first time. |
-| POST | `/admin/session`, `/admin/session/destroy` | To allow [Admin mode for GitLab administrators](https://gitlab.com/groups/gitlab-org/-/epics/2158) |
+| POST | `/admin/session`, `/admin/session/destroy` | To allow [Administrator mode for GitLab administrators](https://gitlab.com/groups/gitlab-org/-/epics/2158) |
| POST | Paths ending with `/compare`| Git revision routes. |
| POST | `.git/git-upload-pack` | To allow Git pull/clone. |
| POST | `/api/v4/internal` | [internal API routes](../../development/internal_api.md) |
-| POST | `/admin/sidekiq` | To allow management of background jobs in the admin UI |
-| POST | `/admin/geo` | To allow updating Geo Nodes in the admin UI |
-| POST | `/api/v4/geo_replication`| To allow certain Geo-specific admin UI actions on secondary sites |
+| POST | `/admin/sidekiq` | To allow management of background jobs in the Admin UI |
+| POST | `/admin/geo` | To allow updating Geo Nodes in the administrator UI |
+| POST | `/api/v4/geo_replication`| To allow certain Geo-specific administrator UI actions on secondary sites |
### GraphQL API
diff --git a/doc/administration/monitoring/performance/img/performance_bar_v14_0.png b/doc/administration/monitoring/performance/img/performance_bar_v14_0.png
deleted file mode 100644
index 42261ddd72..0000000000
Binary files a/doc/administration/monitoring/performance/img/performance_bar_v14_0.png and /dev/null differ
diff --git a/doc/administration/monitoring/performance/img/performance_bar_v14_4.png b/doc/administration/monitoring/performance/img/performance_bar_v14_4.png
new file mode 100644
index 0000000000..388d628c7c
Binary files /dev/null and b/doc/administration/monitoring/performance/img/performance_bar_v14_4.png differ
diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md
index ef4db93d5f..0befd9eac5 100644
--- a/doc/administration/monitoring/performance/performance_bar.md
+++ b/doc/administration/monitoring/performance/performance_bar.md
@@ -8,11 +8,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - The **Stats** field [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/271551) in GitLab 13.9.
> - The **Memory** field [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/330736) in GitLab 14.0.
+> - The **Flamegraph** field [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30275) in GitLab 14.4.
You can display the performance bar to see statistics for the performance of a GitLab UI page.
For example:
-![Performance bar](img/performance_bar_v14_0.png)
+![Performance bar](img/performance_bar_v14_4.png)
## Available information
@@ -64,6 +65,11 @@ From left to right, the performance bar displays:
can be added by its full URL (authenticated as the current user), or by the value of
its `X-Request-Id` header.
- **Download**: a link to download the raw JSON used to generate the Performance Bar reports.
+- **Flamegraph** with mode: a link to generate a [flamegraph](../../../development/profiling.md#speedscope-flamegraphs)
+ of the current URL with the selected [Stackprof mode](https://github.com/tmm1/stackprof#sampling):
+ - The **Wall** mode samples every *interval* of the time on a clock on a wall. The interval is set to `10100` microseconds.
+ - The **CPU** mode samples every *interval* of CPU activity. The interval is set to `10100` microseconds.
+ - The **Object** mode samples every *interval*. The interval is set to `100` allocations.
- **Request Selector**: a select box displayed on the right-hand side of the
Performance Bar which enables you to view these metrics for any requests made while
the current page was open. Only the first two requests per unique URL are captured.
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index c36d2b0f7a..1d27501055 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -119,12 +119,15 @@ The following metrics are available:
| `action_cable_pool_largest_size` | Gauge | 13.4 | Largest number of worker threads observed so far in ActionCable thread pool | `server_mode` |
| `action_cable_pool_pending_tasks` | Gauge | 13.4 | Number of tasks waiting to be executed in ActionCable thread pool | `server_mode` |
| `action_cable_pool_tasks_total` | Gauge | 13.4 | Total number of tasks executed in ActionCable thread pool | `server_mode` |
+| `gitlab_ci_trace_operations_total` | Counter | 13.4 | Total amount of different operations on a build trace | `operation` |
+| `gitlab_ci_trace_bytes_total` | Counter | 13.4 | Total amount of build trace bytes transferred | |
| `action_cable_single_client_transmissions_total` | Counter | 13.10 | The number of ActionCable messages transmitted to any client in any channel | `server_mode` |
| `action_cable_subscription_confirmations_total` | Counter | 13.10 | The number of ActionCable subscriptions from clients confirmed | `server_mode` |
| `action_cable_subscription_rejections_total` | Counter | 13.10 | The number of ActionCable subscriptions from clients rejected | `server_mode` |
-| `action_cable_transmitted_bytes` | Histogram | 14.1 | Message size, in bytes, transmitted over action cable | `operation`, `channel` |
+| `action_cable_transmitted_bytes` | Histogram | 14.1 | Message size, in bytes, transmitted over action cable | `operation`, `channel` |
| `gitlab_issuable_fast_count_by_state_total` | Counter | 13.5 | Total number of row count operations on issue/merge request list pages | |
| `gitlab_issuable_fast_count_by_state_failures_total` | Counter | 13.5 | Number of soft-failed row count operations on issue/merge request list pages | |
+| `gitlab_ci_trace_finalize_duration_seconds` | Histogram | 13.6 | Duration of build trace chunks migration to object storage | |
| `gitlab_external_http_total` | Counter | 13.8 | Total number of HTTP calls to external systems | `controller`, `action` |
| `gitlab_external_http_duration_seconds` | Counter | 13.8 | Duration in seconds spent on each HTTP call to external systems | |
| `gitlab_external_http_exception_total` | Counter | 13.8 | Total number of exceptions raised when making external HTTP calls | |
@@ -132,15 +135,15 @@ The following metrics are available:
| `pipeline_graph_link_calculation_duration_seconds` | Histogram | 13.9 | Total time spent calculating links, in seconds | |
| `pipeline_graph_links_total` | Histogram | 13.9 | Number of links per graph | |
| `pipeline_graph_links_per_job_ratio` | Histogram | 13.9 | Ratio of links to job per graph | |
-| `gitlab_ci_pipeline_security_orchestration_policy_processing_duration_seconds` | Histogram | 13.12 | Time in seconds it takes to process Security Policies in CI/CD pipeline | |
-| `gitlab_ci_difference_live_vs_actual_minutes` | Histogram | 13.12 | Difference between CI minute consumption counted while jobs were running (live) vs when jobs are complete (actual). Used to enforce CI minute consumption limits on long running jobs. | `plan` |
-| `gitlab_spamcheck_request_duration_seconds` | Histogram | 13.12 | The duration for requests between Rails and the anti-spam engine | |
+| `gitlab_ci_pipeline_security_orchestration_policy_processing_duration_seconds` | Histogram | 13.12 | Time in seconds it takes to process Security Policies in CI/CD pipeline | |
+| `gitlab_spamcheck_request_duration_seconds` | Histogram | 13.12 | The duration for requests between Rails and the anti-spam engine | |
| `service_desk_thank_you_email` | Counter | 14.0 | Total number of email responses to new service desk emails | |
| `service_desk_new_note_email` | Counter | 14.0 | Total number of email notifications on new service desk comment | |
| `email_receiver_error` | Counter | 14.1 | Total number of errors when processing incoming emails | |
| `gitlab_snowplow_events_total` | Counter | 14.1 | Total number of GitLab Snowplow product intelligence events emitted | |
| `gitlab_snowplow_failed_events_total` | Counter | 14.1 | Total number of GitLab Snowplow product intelligence events emission failures | |
| `gitlab_snowplow_successful_events_total` | Counter | 14.1 | Total number of GitLab Snowplow product intelligence events emission successes | |
+| `gitlab_ci_build_trace_errors_total` | Counter | 14.4 | Total amount of different error types on a build trace | `type` |
## Metrics controlled by a feature flag
diff --git a/doc/administration/monitoring/prometheus/puma_exporter.md b/doc/administration/monitoring/prometheus/puma_exporter.md
new file mode 100644
index 0000000000..c348e74afa
--- /dev/null
+++ b/doc/administration/monitoring/prometheus/puma_exporter.md
@@ -0,0 +1,27 @@
+---
+stage: Monitor
+group: Monitor
+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/#assignments
+---
+
+# Puma exporter **(FREE SELF)**
+
+You can use the [Puma exporter](https://github.com/sapcc/puma-exporter)
+to measure various Puma metrics.
+
+To enable the Puma exporter:
+
+1. [Enable Prometheus](index.md#configuring-prometheus).
+1. Edit `/etc/gitlab/gitlab.rb` to add (or find and uncomment) the following lines. Make sure
+ `puma['exporter_enabled']` is set to `true`:
+
+ ```ruby
+ puma['exporter_enabled'] = true
+ puma['exporter_address'] = "127.0.0.1"
+ puma['exporter_port'] = 8083
+ ```
+
+1. Save the file and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure)
+ for the changes to take effect.
+
+Prometheus begins collecting performance data from the Puma exporter exposed at `localhost:8083`.
diff --git a/doc/administration/nfs.md b/doc/administration/nfs.md
index 0cab46a95c..f18c39af24 100644
--- a/doc/administration/nfs.md
+++ b/doc/administration/nfs.md
@@ -20,12 +20,46 @@ file system performance, see
## Gitaly and NFS deprecation
+Starting with GitLab version 14.0, support for NFS to store Git repository data will be deprecated. Technical customer support and engineering support will be available for the 14.x releases. Engineering will fix bugs and security vulnerabilities consistent with our [release and maintenance policy](../policy/maintenance.md#security-releases).
+
+At the end of the 14.12 milestone (tenatively June 22nd, 2022) technical and engineering support for using NFS to store Git repository data will be officially at end-of-life. There will be no product changes or troubleshooting provided via Engineering, Security or Paid Support channels.
+
+For those customers still running earlier versions of GitLab, [our support eligibility and maintenance policy applies](https://about.gitlab.com/support/statement-of-support.html#version-support).
+
+For the 14.x releases, we will continue to help with Git related tickets from customers running one or more Gitaly servers with its data stored on NFS. Examples may include:
+
+- Performance issues or timeouts accessing Git data
+- Commits or branches vanish
+- GitLab intermittently returns the wrong Git data (such as reporting that a repository has no branches)
+
+Assistance will be limited to activities like:
+
+- Verifying developers' workflow uses features like protected branches
+- Reviewing GitLab event data from the database to advise if it looks like a force push over-wrote branches
+- Verifying that NFS client mount options match our [documented recommendations](#mount-options)
+- Analyzing the GitLab Workhorse and Rails logs, and determining that `500` errors being seen in the environment are caused by slow responses from Gitaly
+
+GitLab support will be unable to continue with the investigation if:
+
+- The date of the request is on or after the release of GitLab version 15.0, and
+- Support Engineers and Management determine that all reasonable non-NFS root causes have been exhausted
+
+If the issue is reproducible, or if it happens intermittently but regularly, GitLab Support will investigate providing the issue reproduces without the use of NFS. In order to reproduce without NFS, the affected repositories should be migrated to a different Gitaly shard, such as Gitaly cluster or a standalone Gitaly VM, backed with block storage.
+
+### Why remove NFS for Git repository data
+
+{:.no-toc}
+
+NFS is not well-suited to a workload consisting of many small files, like Git repositories. NFS does provide a number of configuration options designed to improve performance. However, over time, a number of these mount options have proven to result in inconsistencies across multiple nodes mounting the NFS volume, up to and including data loss. Addressing these inconsistencies consume extraordinary development and support engineer time that hamper our ability to develop [Gitaly Cluster](gitaly/praefect.md), our purpose-built solution to addressing the deficiencies of NFS in this environment.
+
+Please note that Gitaly Cluster provides highly-available Git repository storage. If this is not a requirement, single-node Gitaly backed by block storage is a suitable substitute.
+
Engineering support for NFS for Git repositories is deprecated. Technical support is planned to be
unavailable from GitLab 15.0. No further enhancements are planned for this feature.
Read:
-- The [Gitaly and NFS deprecation notice](gitaly/index.md#nfs-deprecation-notice).
+- [Moving beyond NFS](gitaly/index.md#moving-beyond-nfs).
- About the [correct mount options to use](#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
## Known kernel version incompatibilities
@@ -153,7 +187,7 @@ If the Rugged feature flag is explicitly set to either `true` or `false`, GitLab
NOTE:
From GitLab 12.7, Rugged is not automatically enabled if Puma thread count is greater than `1`.
-If you want to use Rugged with Puma, [set Puma thread count to `1`](https://docs.gitlab.com/omnibus/settings/puma.html#puma-settings).
+If you want to use Rugged with Puma, [set Puma thread count to `1`](../install/requirements.md#puma-settings).
If you want to use Rugged with Puma thread count more than `1`, Rugged can be enabled using the [feature flag](../development/gitaly.md#legacy-rugged-code).
@@ -217,7 +251,7 @@ use the `hard` option, because (from the man page):
> use the soft option only when client responsiveness is more important than data integrity
Other vendors make similar recommendations, including
-[SAP](http://wiki.scn.sap.com/wiki/x/PARnFQ) and NetApp's
+[System Applications and Products in Data Processing (SAP)](http://wiki.scn.sap.com/wiki/x/PARnFQ) and NetApp's
[knowledge base](https://kb.netapp.com/Advice_and_Troubleshooting/Data_Storage_Software/ONTAP_OS/What_are_the_differences_between_hard_mount_and_soft_mount),
they highlight that if the NFS client driver caches data, `soft` means there is no certainty if
writes by GitLab are actually on disk.
@@ -351,7 +385,7 @@ Any `Operation not permitted` errors means you should investigate your NFS serve
If the traffic between your NFS server and NFS client(s) is subject to port filtering
by a firewall, then you need to reconfigure that firewall to allow NFS communication.
-[This guide from TDLP](https://tldp.org/HOWTO/NFS-HOWTO/security.html#FIREWALLS)
+[This guide from The Linux Documentation Project (TDLP)](https://tldp.org/HOWTO/NFS-HOWTO/security.html#FIREWALLS)
covers the basics of using NFS in a firewalled environment. Additionally, we encourage you to
search for and review the specific documentation for your operating system or distribution and your firewall software.
@@ -370,8 +404,8 @@ sudo ufw allow from to any port nfs
### Upgrade to Gitaly Cluster or disable caching if experiencing data loss
WARNING:
-Engineering support for NFS for Git repositories is deprecated. Read the
-[Gitaly and NFS deprecation notice](gitaly/index.md#nfs-deprecation-notice).
+Engineering support for NFS for Git repositories is deprecated. Read about
+[moving beyond NFS](gitaly/index.md#moving-beyond-nfs).
Customers and users have reported data loss on high-traffic repositories when using NFS for Git repositories.
For example, we have seen:
diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md
index e30ad4d824..8aa5af4c2b 100644
--- a/doc/administration/operations/fast_ssh_key_lookup.md
+++ b/doc/administration/operations/fast_ssh_key_lookup.md
@@ -104,12 +104,12 @@ In the case of lookup failures (which are common), the `authorized_keys`
file is still scanned. So Git SSH performance would still be slow for many
users as long as a large file exists.
-To disable any more writes to the `authorized_keys` file:
+To disable writes to the `authorized_keys` file:
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Performance optimization**.
-1. Clear the **Write to "authorized_keys" file** checkbox.
+1. Clear the **Use authorized_keys file to authenticate SSH keys** checkbox.
1. Select **Save changes**.
Again, confirm that SSH is working by removing your user's SSH key in the UI,
@@ -123,10 +123,14 @@ or for asking users to re-add their keys.
This is a brief overview. Please refer to the above instructions for more context.
-1. [Rebuild the `authorized_keys` file](../raketasks/maintenance.md#rebuild-authorized_keys-file)
-1. Enable writes to the `authorized_keys` file in Application Settings
+1. [Rebuild the `authorized_keys` file](../raketasks/maintenance.md#rebuild-authorized_keys-file).
+1. Enable writes to the `authorized_keys` file.
+ 1. On the top bar, select **Menu > Admin**.
+ 1. On the left sidebar, select **Settings > Network**.
+ 1. Expand **Performance optimization**.
+ 1. Select the **Use authorized_keys file to authenticate SSH keys** checkbox.
1. Remove the `AuthorizedKeysCommand` lines from `/etc/ssh/sshd_config` or from `/assets/sshd_config` if you are using Omnibus Docker.
-1. Reload `sshd`: `sudo service sshd reload`
+1. Reload `sshd`: `sudo service sshd reload`.
## Compiling a custom version of OpenSSH for CentOS 6
diff --git a/doc/administration/operations/moving_repositories.md b/doc/administration/operations/moving_repositories.md
index 61a9ec8e7d..8aeaadc17e 100644
--- a/doc/administration/operations/moving_repositories.md
+++ b/doc/administration/operations/moving_repositories.md
@@ -27,7 +27,7 @@ For more information, see:
querying and scheduling snippet repository moves.
- [The API documentation](../../api/group_repository_storage_moves.md) details the endpoints for
querying and scheduling group repository moves **(PREMIUM SELF)**.
-- [Migrate to Gitaly Cluster](../gitaly/index.md#migrate-to-gitaly-cluster).
+- [Migrating to Gitaly Cluster](../gitaly/index.md#migrating-to-gitaly-cluster).
### Move Repositories
diff --git a/doc/administration/operations/ssh_certificates.md b/doc/administration/operations/ssh_certificates.md
index 814e742b02..77dc4eb180 100644
--- a/doc/administration/operations/ssh_certificates.md
+++ b/doc/administration/operations/ssh_certificates.md
@@ -21,8 +21,7 @@ upload the new keys to GitLab.
WARNING:
OpenSSH version 6.9+ is required because that version
introduced the `AuthorizedPrincipalsCommand` configuration option. If
-using CentOS 6, you can [follow these
-instructions](fast_ssh_key_lookup.html#compiling-a-custom-version-of-openssh-for-centos-6)
+using CentOS 6, you can [follow these instructions](fast_ssh_key_lookup.md#compiling-a-custom-version-of-openssh-for-centos-6)
to compile an up-to-date version.
## Why use OpenSSH certificates?
@@ -132,8 +131,8 @@ requirement for it, we effectively only care about the "key ID" being
correct. Once that's extracted GitLab enforces its own ACLs for
that user (for example, what projects the user can access).
-So it's OK to e.g. be overly generous in what you accept, since if the
-user e.g. has no access to GitLab at all it just errors out with a
+It's therefore fine to be overly generous in what you accept. For example, if the user has no access
+to GitLab, an error is produced with a message about an invalid user.
message about this being an invalid user.
## Interaction with the `authorized_keys` file
diff --git a/doc/administration/package_information/defaults.md b/doc/administration/package_information/defaults.md
index 45bea06599..95d6135c28 100644
--- a/doc/administration/package_information/defaults.md
+++ b/doc/administration/package_information/defaults.md
@@ -4,7 +4,7 @@ group: Distribution
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
---
-# Package defaults
+# Package defaults **(FREE SELF)**
Unless configuration is specified in the `/etc/gitlab/gitlab.rb` file,
the package will assume the defaults as noted below.
@@ -14,42 +14,42 @@ the package will assume the defaults as noted below.
See the table below for the list of ports that the Omnibus GitLab assigns
by default:
-| Component | On by default | Communicates via | Alternative | Connection port |
-| :----------------------------------------------------: | :------------: | :--------------: | :---------: | :------------------------------------: |
-| GitLab Rails | Yes | Port | X | 80 or 443 |
-| GitLab Shell | Yes | Port | X | 22 |
-| PostgreSQL | Yes | Socket | Port (5432) | X |
-| Redis | Yes | Socket | Port (6379) | X |
-| Puma | Yes | Socket | Port (8080) | X |
-| GitLab Workhorse | Yes | Socket | Port (8181) | X |
-| NGINX status | Yes | Port | X | 8060 |
-| Prometheus | Yes | Port | X | 9090 |
-| Node exporter | Yes | Port | X | 9100 |
-| Redis exporter | Yes | Port | X | 9121 |
-| PostgreSQL exporter | Yes | Port | X | 9187 |
-| PgBouncer exporter | No | Port | X | 9188 |
-| GitLab Exporter | Yes | Port | X | 9168 |
-| Sidekiq exporter | Yes | Port | X | 8082 |
-| Puma exporter | No | Port | X | 8083 |
-| Geo PostgreSQL | No | Socket | Port (5431) | X |
-| Redis Sentinel | No | Port | X | 26379 |
-| Incoming email | No | Port | X | 143 |
-| Elastic search | No | Port | X | 9200 |
-| GitLab Pages | No | Port | X | 80 or 443 |
-| GitLab Registry | No* | Port | X | 80, 443 or 5050 |
-| GitLab Registry | No | Port | X | 5000 |
-| LDAP | No | Port | X | Depends on the component configuration |
-| Kerberos | No | Port | X | 8443 or 8088 |
-| OmniAuth | Yes | Port | X | Depends on the component configuration |
-| SMTP | No | Port | X | 465 |
-| Remote syslog | No | Port | X | 514 |
-| Mattermost | No | Port | X | 8065 |
-| Mattermost | No | Port | X | 80 or 443 |
-| PgBouncer | No | Port | X | 6432 |
-| Consul | No | Port | X | 8300, 8301(UDP), 8500, 8600[^Consul-notes] |
-| Patroni | No | Port | X | 8008 |
-| GitLab KAS | No | Port | X | 8150 |
-| Gitaly | No | Port | X | 8075 |
+| Component | On by default | Communicates via | Alternative | Connection port |
+|:-------------------:|:-------------:|:----------------:|:-----------:|:------------------------------------------:|
+| GitLab Rails | Yes | Port | X | 80 or 443 |
+| GitLab Shell | Yes | Port | X | 22 |
+| PostgreSQL | Yes | Socket | Port (5432) | X |
+| Redis | Yes | Socket | Port (6379) | X |
+| Puma | Yes | Socket | Port (8080) | X |
+| GitLab Workhorse | Yes | Socket | Port (8181) | X |
+| NGINX status | Yes | Port | X | 8060 |
+| Prometheus | Yes | Port | X | 9090 |
+| Node exporter | Yes | Port | X | 9100 |
+| Redis exporter | Yes | Port | X | 9121 |
+| PostgreSQL exporter | Yes | Port | X | 9187 |
+| PgBouncer exporter | No | Port | X | 9188 |
+| GitLab Exporter | Yes | Port | X | 9168 |
+| Sidekiq exporter | Yes | Port | X | 8082 |
+| Puma exporter | No | Port | X | 8083 |
+| Geo PostgreSQL | No | Socket | Port (5431) | X |
+| Redis Sentinel | No | Port | X | 26379 |
+| Incoming email | No | Port | X | 143 |
+| Elastic search | No | Port | X | 9200 |
+| GitLab Pages | No | Port | X | 80 or 443 |
+| GitLab Registry | No* | Port | X | 80, 443 or 5050 |
+| GitLab Registry | No | Port | X | 5000 |
+| LDAP | No | Port | X | Depends on the component configuration |
+| Kerberos | No | Port | X | 8443 or 8088 |
+| OmniAuth | Yes | Port | X | Depends on the component configuration |
+| SMTP | No | Port | X | 465 |
+| Remote syslog | No | Port | X | 514 |
+| Mattermost | No | Port | X | 8065 |
+| Mattermost | No | Port | X | 80 or 443 |
+| PgBouncer | No | Port | X | 6432 |
+| Consul | No | Port | X | 8300, 8301(UDP), 8500, 8600[^Consul-notes] |
+| Patroni | No | Port | X | 8008 |
+| GitLab KAS | No | Port | X | 8150 |
+| Gitaly | No | Port | X | 8075 |
Legend:
@@ -59,7 +59,7 @@ Legend:
- `Alternative` - If it is possible to configure the component to use different type of communication. The type is listed with default port used in that case.
- `Connection port` - Port on which the component communicates.
-GitLab also expects a filesystem to be ready for the storage of Git repositories
+GitLab also expects a file system to be ready for the storage of Git repositories
and various other files.
Note that if you are using NFS (Network File System), files will be carried
diff --git a/doc/administration/package_information/deprecated_os.md b/doc/administration/package_information/deprecated_os.md
index 251dbe1e20..35b333241b 100644
--- a/doc/administration/package_information/deprecated_os.md
+++ b/doc/administration/package_information/deprecated_os.md
@@ -4,7 +4,7 @@ group: Distribution
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
---
-# OS Versions that are no longer supported
+# OS Versions that are no longer supported **(FREE SELF)**
GitLab provides omnibus packages for operating systems only until their
EOL (End-Of-Life). After the EOL date of the OS, GitLab will stop releasing
diff --git a/doc/administration/package_information/deprecation_policy.md b/doc/administration/package_information/deprecation_policy.md
index cc16661442..80ce72d54f 100644
--- a/doc/administration/package_information/deprecation_policy.md
+++ b/doc/administration/package_information/deprecation_policy.md
@@ -4,7 +4,7 @@ group: Distribution
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
---
-# Deprecation policy
+# Deprecation policy **(FREE SELF)**
The Omnibus GitLab packages come with number of different libraries and services which offers users plethora of configuration options.
@@ -60,7 +60,7 @@ We should aim to not remove sensitive configuration in the *next major* release
See the table below for some examples:
-| Config. type | Deprecation announced | Final minor release | Remove |
+| Configuration type | Deprecation announced | Final minor release | Remove |
| -------- | -------- | -------- | -------- |
| Sensitive | 10.1.0 | 10.9.0 | 11.0.0 |
| Sensitive | 10.7.0 | 10.9.0 | 12.0.0 |
@@ -90,6 +90,6 @@ the feature will continue working the same way as if you had `gitlab_rails['bett
However, setting the old version of configuration will print out a deprecation
notice at the end of installation/upgrade/reconfigure run.
-With GitLab 11, `gitlab_rails['configuration'] = true` will no longer work and you will have to manually change the configuration in `/etc/gitlab/gitlab.rb` to the new valid config.
+With GitLab 11, `gitlab_rails['configuration'] = true` will no longer work and you will have to manually change the configuration in `/etc/gitlab/gitlab.rb` to the new valid configuration.
**Note** If this configuration option is sensitive and can put integrity of the installation or
data in danger, installation/upgrade will be aborted.
diff --git a/doc/administration/package_information/index.md b/doc/administration/package_information/index.md
index e18fb621b8..12f3274eca 100644
--- a/doc/administration/package_information/index.md
+++ b/doc/administration/package_information/index.md
@@ -4,7 +4,7 @@ group: Distribution
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
---
-# Package information
+# Package information **(FREE SELF)**
The Omnibus GitLab package is bundled with all dependencies required for GitLab
to function correctly. More details can be found
@@ -15,10 +15,10 @@ at [bundling dependencies document](omnibus_packages.md).
The released package versions are in the format `MAJOR.MINOR.PATCH-EDITION.OMNIBUS_RELEASE`
| Component | Meaning | Example |
-| --------- | ------- | ------- |
-| MAJOR.MINOR.PATCH | The GitLab version this corresponds to | 13.3.0 |
-| EDITION | The edition of GitLab this corresponds to | ee |
-| OMNIBUS_RELEASE | The omnibus release. Usually, this will be 0. This will be incremented if we need to build a new package without changing the GitLab version. | 0 |
+|-------------------|---------|---------|
+| MAJOR.MINOR.PATCH | The GitLab version this corresponds to. | 13.3.0 |
+| EDITION | The edition of GitLab this corresponds to. | ee |
+| OMNIBUS_RELEASE | The Omnibus GitLab release. Usually, this will be 0. This is incremented if we need to build a new package without changing the GitLab version. | 0 |
## Licenses
@@ -26,23 +26,22 @@ See [licensing](licensing.md)
## Defaults
-The Omnibus GitLab package requires various configuration to get the
-components in working order.
-If the configuration is not provided, the package will use the default
-values assumed in the package.
+The Omnibus GitLab package requires various configuration to get the components
+in working order. If the configuration is not provided, the package will use
+the default values assumed in the package.
These defaults are noted in the package [defaults document](defaults.md).
## Checking the versions of bundled software
-Once the Omnibus GitLab package is installed, all versions of the bundled
+After the Omnibus GitLab package is installed, all versions of the bundled
libraries are located in `/opt/gitlab/version-manifest.txt`.
If you don't have the package installed, you can always check the Omnibus GitLab
[source repository](https://gitlab.com/gitlab-org/omnibus-gitlab/tree/master), specifically the
-[config directory](https://gitlab.com/gitlab-org/omnibus-gitlab/tree/master/config).
+[configuration directory](https://gitlab.com/gitlab-org/omnibus-gitlab/tree/master/config).
-For example, if you take a look at the `8-6-stable` branch, you can conclude that
+For example, if you examine the `8-6-stable` branch, you can conclude that
8.6 packages were running [Ruby 2.1.8](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/8-6-stable/config/projects/gitlab.rb#L48).
Or, that 8.5 packages were bundled with [NGINX 1.9.0](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/8-5-stable/config/software/nginx.rb#L20).
@@ -70,11 +69,10 @@ To view a diff between your configuration file and the latest version, run:
sudo gitlab-ctl diff-config
```
-_**Note:** This command is available from GitLab 8.17_
-
-**Important:** If you are copy-pasting the output of this command into your
-`/etc/gitlab/gitlab.rb` configuration file, make sure to omit leading `+` and `-`
-on each line.
+WARNING:
+If you are pasting the output of this command into your
+`/etc/gitlab/gitlab.rb` configuration file, omit any leading `+` and `-`
+characters on each line.
## Init system detection
diff --git a/doc/administration/package_information/licensing.md b/doc/administration/package_information/licensing.md
index 8557a94bf9..02358c6699 100644
--- a/doc/administration/package_information/licensing.md
+++ b/doc/administration/package_information/licensing.md
@@ -4,7 +4,7 @@ group: Distribution
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
---
-# Package Licensing
+# Package Licensing **(FREE SELF)**
## License
diff --git a/doc/administration/package_information/omnibus_packages.md b/doc/administration/package_information/omnibus_packages.md
index aa73534fc5..115d6c394a 100644
--- a/doc/administration/package_information/omnibus_packages.md
+++ b/doc/administration/package_information/omnibus_packages.md
@@ -4,7 +4,7 @@ group: Distribution
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
---
-# Omnibus based packages and images
+# Omnibus based packages and images **(FREE SELF)**
Below you can find some basic information on why GitLab provides packages and
a Docker image that come with bundled dependencies.
diff --git a/doc/administration/package_information/postgresql_versions.md b/doc/administration/package_information/postgresql_versions.md
index 89da486487..252e0cad76 100644
--- a/doc/administration/package_information/postgresql_versions.md
+++ b/doc/administration/package_information/postgresql_versions.md
@@ -4,7 +4,7 @@ group: Distribution
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
---
-# PostgreSQL versions shipped with Omnibus GitLab
+# PostgreSQL versions shipped with Omnibus GitLab **(FREE SELF)**
NOTE:
This table lists only GitLab versions where a significant change happened in the
diff --git a/doc/administration/package_information/signed_packages.md b/doc/administration/package_information/signed_packages.md
index fb99480946..fb605f8d5b 100644
--- a/doc/administration/package_information/signed_packages.md
+++ b/doc/administration/package_information/signed_packages.md
@@ -4,9 +4,9 @@ group: Distribution
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
---
-# Package Signatures
+# Package Signatures **(FREE SELF)**
-As of the release of GitLab 9.5 on August 22, 2017, GitLab provides signed Omnibus GitLab packages for RPM and DEB based distributions. This means that all packages provided on are signed, starting with `9.5.0`, and all future versions of supported branches (e.g. `9.3.x` and `9.4.x` after August 22, 2017). Any package version prior to August 22, 2017, will not be signed. Please pass the appropriate argument to your package manager. (Example: `yum --nogpgcheck`)
+As of the release of GitLab 9.5 on August 22, 2017, GitLab provides signed Omnibus GitLab packages for RPM and DEB based distributions. This means that all packages provided on are signed, starting with `9.5.0`, and all future versions of supported branches (for example `9.3.x` and `9.4.x` after August 22, 2017). Any package version prior to August 22, 2017, will not be signed. Please pass the appropriate argument to your package manager. (Example: `yum --nogpgcheck`)
Omnibus GitLab packages produced by GitLab are created via the [Omnibus](https://github.com/chef/omnibus) tool, for which GitLab has added DEB signing via `debsigs` in [our own fork](https://gitlab.com/gitlab-org/omnibus). This addition, combined with the existing functionality of RPM signing, allows GitLab to provide signed packages for all supported distributions using DEB or RPM.
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 1067474c8b..7e711bb574 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -119,6 +119,11 @@ GitLab from source respectively.
Ensure you choose a port different than the one that Registry listens to (`5000` by default),
otherwise conflicts occur.
+NOTE:
+Host and container firewall rules must be configured to allow traffic in through the port listed
+under the `registry_external_url` line, rather than the port listed under
+`gitlab_rails['registry_port']` (default `5000`).
+
**Omnibus GitLab installations**
1. Your `/etc/gitlab/gitlab.rb` should contain the Registry URL as well as the
@@ -151,6 +156,19 @@ otherwise conflicts occur.
If your certificate provider provides the CA Bundle certificates, append them to the TLS certificate file.
+An administrator may want the container registry listening on an arbitrary port such as `5678`.
+However, the registry and application server are behind an AWS application load balancer that only
+listens on ports `80` and `443`. The admin may simply remove the port number for
+`registry_external_url`, so HTTP or HTTPS is assumed. Then, the rules apply that map the load
+balancer to the registry from ports `80` or `443` to the arbitrary port. This is important if users
+rely on the `docker login` example in the container registry. Here's an example:
+
+```ruby
+registry_external_url 'https://registry-gitlab.example.com'
+registry_nginx['redirect_http_to_https'] = true
+registry_nginx['listen_port'] = 5678
+```
+
**Installations from source**
1. Open `/home/git/gitlab/config/gitlab.yml`, find the `registry` entry and
diff --git a/doc/administration/packages/dependency_proxy.md b/doc/administration/packages/dependency_proxy.md
index 32e7e19101..a394a32fc1 100644
--- a/doc/administration/packages/dependency_proxy.md
+++ b/doc/administration/packages/dependency_proxy.md
@@ -29,8 +29,8 @@ To enable the dependency proxy feature:
gitlab_rails['dependency_proxy_enabled'] = true
```
-1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab") for the changes to take effect.
-1. Enable the [Puma web server](https://docs.gitlab.com/omnibus/settings/puma.html).
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+1. Enable the [Puma web server](../operations/puma.md).
**Helm chart installations**
@@ -88,7 +88,7 @@ To change the local storage path:
gitlab_rails['dependency_proxy_storage_path'] = "/mnt/dependency_proxy"
```
-1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab") for the changes to take effect.
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
**Installations from source**
@@ -145,7 +145,7 @@ This section describes the earlier configuration format.
}
```
-1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab") for the changes to take effect.
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
**Installations from source**
diff --git a/doc/administration/packages/img/gitlab-registry-architecture.png b/doc/administration/packages/img/gitlab-registry-architecture.png
index 742678d5e1..d6e8f935ad 100644
Binary files a/doc/administration/packages/img/gitlab-registry-architecture.png and b/doc/administration/packages/img/gitlab-registry-architecture.png differ
diff --git a/doc/administration/packages/index.md b/doc/administration/packages/index.md
index 37473d3557..90f2d9127f 100644
--- a/doc/administration/packages/index.md
+++ b/doc/administration/packages/index.md
@@ -68,7 +68,7 @@ To enable the Packages feature:
gitlab_rails['packages_enabled'] = true
```
-1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab") for the changes to take effect.
+1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
**Installations from source**
@@ -80,7 +80,7 @@ To enable the Packages feature:
enabled: true
```
-1. [Restart GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab") for the changes to take effect.
+1. [Restart GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
**Helm Chart installations**
@@ -92,7 +92,7 @@ To enable the Packages feature:
enabled: true
```
-1. [Restart GitLab](../restart_gitlab.md#helm-chart-installations "How to reconfigure Helm GitLab") for the changes to take effect.
+1. [Restart GitLab](../restart_gitlab.md#helm-chart-installations) for the changes to take effect.
## Rate limits
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 8b7af5ee17..8a0d3f552b 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -7,12 +7,6 @@ description: 'Learn how to administer GitLab Pages.'
# GitLab Pages administration **(FREE SELF)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80) in GitLab EE 8.3.
-> - Custom CNAMEs with TLS support were [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/173) in GitLab EE 8.5.
-> - GitLab Pages [was ported](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/14605) to Community Edition in GitLab 8.17.
-> - Support for subgroup project's websites was
-> [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/30548) in GitLab 11.8.
-
GitLab Pages allows for hosting of static sites. It must be configured by an
administrator. Separate [user documentation](../../user/project/pages/index.md) is available.
@@ -23,10 +17,10 @@ GitLab from source, see
## Overview
-GitLab Pages makes use of the [GitLab Pages daemon](https://gitlab.com/gitlab-org/gitlab-pages), a simple HTTP server
+GitLab Pages makes use of the [GitLab Pages daemon](https://gitlab.com/gitlab-org/gitlab-pages), a basic HTTP server
written in Go that can listen on an external IP address and provide support for
custom domains and custom certificates. It supports dynamic certificates through
-SNI and exposes pages using HTTP2 by default.
+Server Name Indication (SNI) and exposes pages using HTTP2 by default.
You are encouraged to read its [README](https://gitlab.com/gitlab-org/gitlab-pages/blob/master/README.md) to fully understand how
it works.
@@ -89,7 +83,7 @@ added `gitlab.io` [in 2016](https://gitlab.com/gitlab-com/infrastructure/-/issue
### DNS configuration
GitLab Pages expect to run on their own virtual host. In your DNS server/provider
-you need to add a [wildcard DNS A record](https://en.wikipedia.org/wiki/Wildcard_DNS_record) pointing to the
+add a [wildcard DNS A record](https://en.wikipedia.org/wiki/Wildcard_DNS_record) pointing to the
host that GitLab runs. For example, an entry would look like this:
```plaintext
@@ -99,9 +93,9 @@ host that GitLab runs. For example, an entry would look like this:
Where `example.io` is the domain GitLab Pages is served from,
`192.0.2.1` is the IPv4 address of your GitLab instance, and `2001:db8::1` is the
-IPv6 address. If you don't have IPv6, you can omit the AAAA record.
+IPv6 address. If you don't have IPv6, you can omit the `AAAA` record.
-#### Custom domains
+#### DNS configuration for custom domains
If support for custom domains is needed, the Pages root domain and its subdomains should point to
the secondary IP (which is dedicated for the Pages daemon). `.` should
@@ -131,7 +125,7 @@ Depending on your needs, you can set up GitLab Pages in 4 different ways.
The following examples are listed from the easiest setup to the most
advanced one. The absolute minimum requirement is to set up the wildcard DNS
-since that is needed in all configurations.
+because that is needed in all configurations.
### Wildcard domains
@@ -143,7 +137,7 @@ since that is needed in all configurations.
URL scheme: `http://.example.io/`
-This is the minimum setup that you can use Pages with. It is the base for all
+The following is the minimum setup that you can use Pages with. It is the base for all
other setups as described below. NGINX proxies all requests to the daemon.
The Pages daemon doesn't listen to the outside world.
@@ -180,8 +174,8 @@ outside world.
pages_nginx['redirect_http_to_https'] = true
```
-1. If you haven't named your certificate and key `example.io.crt` and `example.io.key`
-then you'll need to also add the full paths as shown below:
+1. If you haven't named your certificate and key `example.io.crt` and `example.io.key`,
+you must also add the full paths as shown below:
```ruby
pages_nginx['ssl_certificate'] = "/etc/gitlab/ssl/pages-nginx.crt"
@@ -201,7 +195,7 @@ Multiple wildcards for one instance is not supported. Only one wildcard per inst
Below is a table of all configuration settings known to Pages in Omnibus GitLab,
and what they do. These options can be adjusted in `/etc/gitlab/gitlab.rb`,
and take effect after you [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
-Most of these settings don't need to be configured manually unless you need more granular
+Most of these settings don't have to be configured manually unless you need more granular
control over how the Pages daemon runs and serves content in your environment.
| Setting | Description |
@@ -382,8 +376,6 @@ To enable it:
### Access control
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/33422) in GitLab 11.5.
-
GitLab Pages access control can be configured per-project, and allows access to a Pages
site to be controlled based on a user's membership to that project.
@@ -444,7 +436,7 @@ You can enforce [Access Control](#access-control) for all GitLab Pages websites
on your GitLab instance. By doing so, only logged-in users have access to them.
This setting overrides Access Control set by users in individual projects.
-This can be useful to preserve information published with Pages websites to the users
+This can be helpful to restrict information published with Pages websites to the users
of your instance only.
To do that:
@@ -491,7 +483,7 @@ For Omnibus, this is fixed by [installing a custom CA in Omnibus GitLab](https:/
> [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/392) in GitLab 13.7.
WARNING:
-These are advanced settings. The recommended default values are set inside GitLab Pages. You should
+These instructions deal with some advanced settings of your GitLab instance. The recommended default values are set inside GitLab Pages. You should
change these settings only if absolutely necessary. Use extreme caution.
GitLab Pages can serve content from ZIP archives through object storage (an
@@ -524,9 +516,6 @@ After an archive reaches `zip_cache_expiration`, it's marked as expired and remo
## Activate verbose logging for daemon
-Verbose logging was [introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/2533) in
-Omnibus GitLab 11.1.
-
Follow the steps below to configure verbose logging of GitLab Pages daemon.
1. By default the daemon only logs with `INFO` level.
@@ -603,8 +592,6 @@ the below steps to do a no downtime transfer to a new storage location.
## Configure listener for reverse proxy requests
-> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/2533) in Omnibus GitLab 11.1.
-
Follow the steps below to configure the proxy listener of GitLab Pages.
1. By default the listener is configured to listen for requests on `localhost:8090`.
@@ -775,7 +762,7 @@ WARNING:
From [GitLab 13.3](https://gitlab.com/gitlab-org/gitlab/-/issues/217912) to [GitLab 13.12](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5993) GitLab Pages can either use `disk` or `gitlab` domain configuration source.
-We highly advise you to use `gitlab` configuration source as it will make transition to newer versions easier.
+We highly advise you to use `gitlab` configuration source as it makes transitions to newer versions easier.
To explicitly enable API source:
@@ -1019,14 +1006,13 @@ Starting from GitLab 13.12, this setting also disables the [legacy storage](#mig
In GitLab 14.0 a number of breaking changes were introduced which may require some user intervention.
The steps below describe the best way to migrate without causing any downtime for your GitLab instance.
-If you run GitLab on a single server, then most likely the upgrade process to 14.0 will go smoothly for you
-and you will not notice any problem after upgrading.
+A GitLab instance running on a single server typically upgrades to 14.0 smoothly, and there should be minimal issues after the upgrade is complete.
Regardless, we recommend everyone follow the migration steps to ensure a successful upgrade.
If at any point you run into issues, consult the [troubleshooting section](#troubleshooting).
-If your current GitLab version is lower than 13.12, then you first need to update to 13.12.
+If your current GitLab version is lower than 13.12, then you must first update to 13.12.
Updating directly to 14.0 is [not supported](../../update/index.md#upgrade-paths)
-and may cause downtime for some web-sites hosted on GitLab Pages. Once you update to 13.12,
+and may cause downtime for some web-sites hosted on GitLab Pages. After you update to 13.12,
migrate GitLab Pages to prepare them for GitLab 14.0:
1. Set [`domain_config_source` to `gitlab`](#domain-source-configuration-before-140), which
@@ -1077,7 +1063,7 @@ This issue is fixed in GitLab 14.3 and above, try upgrading GitLab first.
GitLab Pages runs inside a `chroot` jail, usually in a uniquely numbered directory like
`/tmp/gitlab-pages-*`.
-Within the jail, a bundle of trusted certificates is
+In the jail, a bundle of trusted certificates is
provided at `/etc/ssl/ca-bundle.pem`. It's
[copied there](https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/51)
from `/opt/gitlab/embedded/ssl/certs/cacert.pem`
@@ -1206,26 +1192,10 @@ To stop `systemd` from cleaning the Pages related content:
sudo gitlab-ctl restart gitlab-pages
```
-### 404 error after transferring the project to a different group or user, or changing project path
-
-If you encounter a `404 Not Found` error a Pages site after transferring a project to
-another group or user, or changing project path, you must trigger a domain configuration
-update for Pages. To do so, write something in the `.update` file. The Pages daemon
-monitors for changes to this file, and reloads the configuration when changes occur.
-
-Use this example to fix a `404 Not Found` error after transferring a project or changing
-a project path with Pages:
-
-```shell
-date > /var/opt/gitlab/gitlab-rails/shared/pages/.update
-```
-
-If you've customized the Pages storage path, adjust the command above to use your custom path.
-
### 404 error after promoting a Geo secondary to a primary node
-These are due to the Pages files not being among the
-[supported data types](../geo/replication/datatypes.md#limitations-on-replicationverification).
+Pages files are not among the
+[supported data types](../geo/replication/datatypes.md#limitations-on-replicationverification) for replication in Geo. After a secondary node is promoted to a primary node, attempts to access a Pages site result in a `404 Not Found` error.
It is possible to copy the subfolders and files in the [Pages path](#change-storage-path)
to the new primary node to resolve this.
@@ -1233,11 +1203,11 @@ For example, you can adapt the `rsync` strategy from the
[moving repositories documentation](../operations/moving_repositories.md).
Alternatively, run the CI pipelines of those projects that contain a `pages` job again.
-## 404 or 500 error when accessing GitLab Pages in a Geo setup
+### 404 or 500 error when accessing GitLab Pages in a Geo setup
Pages sites are only available on the primary Geo site, while the codebase of the project is available on all sites.
-If you try to access a Pages page on a secondary site, you will get a 404 or 500 HTTP code depending on the access control.
+If you try to access a Pages page on a secondary site, a 404 or 500 HTTP code is returned depending on the access control.
Read more which [features don't support Geo replication/verification](../geo/replication/datatypes.md#limitations-on-replicationverification).
diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md
index 2e0820b69c..dc569a81ab 100644
--- a/doc/administration/postgresql/replication_and_failover.md
+++ b/doc/administration/postgresql/replication_and_failover.md
@@ -23,10 +23,7 @@ replication and failover requires:
- A minimum of three database nodes.
- A minimum of three `Consul` server nodes.
-- A minimum of one `pgbouncer` service node, but it's recommended to have one
- per database node.
- - An internal load balancer (TCP) is required when there is more than one
- `pgbouncer` service node.
+- A minimum of one `pgbouncer` service node, but it's recommended to have one per database node. An internal load balancer (TCP) is required when there is more than one `pgbouncer` service node.
![PostgreSQL HA Architecture](img/pg_ha_architecture.png)
@@ -35,40 +32,31 @@ sure you have redundant connectivity between all Database and GitLab instances
to avoid the network becoming a single point of failure.
NOTE:
-As of GitLab 13.3, PostgreSQL 12 is shipped with Omnibus GitLab. Clustering for PostgreSQL 12 is only supported with
+As of GitLab 13.3, PostgreSQL 12 is shipped with Omnibus GitLab. Clustering for PostgreSQL 12 is supported only with
Patroni. See the [Patroni](#patroni) section for further details. Starting with GitLab 14.0, only PostgreSQL 12 is
-shipped with Omnibus GitLab and thus Patroni becomes mandatory for replication and failover.
+shipped with Omnibus GitLab, and thus Patroni becomes mandatory for replication and failover.
### Database node
Each database node runs three services:
-`PostgreSQL` - The database itself.
-
-`Patroni` - Communicates with other Patroni services in the cluster and handles
-failover when issues with the leader server occurs. The failover procedure
-consists of:
-
-- Selecting a new leader for the cluster.
-- Promoting the new node to leader.
-- Instructing remaining servers to follow the new leader node.
-
-`Consul` agent - To communicate with Consul cluster which stores the current Patroni state. The agent monitors the status of each node in the database cluster and tracks its health in a service definition on the Consul cluster.
+- `PostgreSQL`: The database itself.
+- `Patroni`: Communicates with other Patroni services in the cluster and handles failover when issues with the leader server occurs. The failover procedure consists of:
+ - Selecting a new leader for the cluster.
+ - Promoting the new node to leader.
+ - Instructing remaining servers to follow the new leader node.
+- `Consul` agent: To communicate with Consul cluster which stores the current Patroni state. The agent monitors the status of each node in the database cluster and tracks its health in a service definition on the Consul cluster.
### Consul server node
-The Consul server node runs the Consul server service. These nodes must have reached the quorum and elected a leader _before_ Patroni cluster bootstrap otherwise database nodes wait until such Consul leader is elected.
+The Consul server node runs the Consul server service. These nodes must have reached the quorum and elected a leader _before_ Patroni cluster bootstrap; otherwise, database nodes wait until such Consul leader is elected.
### PgBouncer node
Each PgBouncer node runs two services:
-`PgBouncer` - The database connection pooler itself.
-
-`Consul` agent - Watches the status of the PostgreSQL service definition on the
-Consul cluster. If that status changes, Consul runs a script which updates the
-PgBouncer configuration to point to the new PostgreSQL leader node and reloads
-the PgBouncer service.
+- `PgBouncer`: The database connection pooler itself.
+- `Consul` agent: Watches the status of the PostgreSQL service definition on the Consul cluster. If that status changes, Consul runs a script which updates the PgBouncer configuration to point to the new PostgreSQL leader node and reloads the PgBouncer service.
### Connection flow
@@ -106,8 +94,7 @@ When using default setup, minimum configuration requires:
- `CONSUL_USERNAME`. The default user for Omnibus GitLab is `gitlab-consul`
- `CONSUL_DATABASE_PASSWORD`. Password for the database user.
-- `CONSUL_PASSWORD_HASH`. This is a hash generated out of Consul username/password pair.
- Can be generated with:
+- `CONSUL_PASSWORD_HASH`. This is a hash generated out of Consul username/password pair. It can be generated with:
```shell
sudo gitlab-ctl pg-password-md5 CONSUL_USERNAME
@@ -118,8 +105,7 @@ When using default setup, minimum configuration requires:
Few notes on the service itself:
- The service runs under a system account, by default `gitlab-consul`.
- - If you are using a different username, you have to specify it through the
- `CONSUL_USERNAME` variable.
+- If you are using a different username, you have to specify it through the `CONSUL_USERNAME` variable.
- Passwords are stored in the following locations:
- `/etc/gitlab/gitlab.rb`: hashed
- `/var/opt/gitlab/pgbouncer/pg_auth`: hashed
@@ -129,10 +115,8 @@ Few notes on the service itself:
When configuring PostgreSQL, we do the following:
-- Set `max_replication_slots` to double the number of database nodes.
- Patroni uses one extra slot per node when initiating the replication.
-- Set `max_wal_senders` to one more than the allocated number of replication slots in the cluster.
- This prevents replication from using up all of the available database connections.
+- Set `max_replication_slots` to double the number of database nodes. Patroni uses one extra slot per node when initiating the replication.
+- Set `max_wal_senders` to one more than the allocated number of replication slots in the cluster. This prevents replication from using up all of the available database connections.
In this document we are assuming 3 database nodes, which makes this configuration:
@@ -151,7 +135,7 @@ You need the following password information for the application's database user:
- `POSTGRESQL_USERNAME`. The default user for Omnibus GitLab is `gitlab`
- `POSTGRESQL_USER_PASSWORD`. The password for the database user
- `POSTGRESQL_PASSWORD_HASH`. This is a hash generated out of the username/password pair.
- Can be generated with:
+ It can be generated with:
```shell
sudo gitlab-ctl pg-password-md5 POSTGRESQL_USERNAME
@@ -170,8 +154,7 @@ When using a default setup, the minimum configuration requires:
- `PGBOUNCER_USERNAME`. The default user for Omnibus GitLab is `pgbouncer`
- `PGBOUNCER_PASSWORD`. This is a password for PgBouncer service.
-- `PGBOUNCER_PASSWORD_HASH`. This is a hash generated out of PgBouncer username/password pair.
- Can be generated with:
+- `PGBOUNCER_PASSWORD_HASH`. This is a hash generated out of PgBouncer username/password pair. It can be generated with:
```shell
sudo gitlab-ctl pg-password-md5 PGBOUNCER_USERNAME
@@ -181,8 +164,7 @@ When using a default setup, the minimum configuration requires:
Few things to remember about the service itself:
-- The service runs as the same system account as the database
- - In the package, this is by default `gitlab-psql`
+- The service runs as the same system account as the database. In the package, this is by default `gitlab-psql`
- If you use a non-default user account for PgBouncer service (by default `pgbouncer`), you need to specify this username.
- Passwords are stored in the following locations:
- `/etc/gitlab/gitlab.rb`: hashed, and in plain text
@@ -206,7 +188,7 @@ When installing the GitLab package, do not supply `EXTERNAL_URL` value.
You must enable Patroni explicitly to be able to use it (with `patroni['enable'] = true`).
-Any PostgreSQL configuration item that controls replication, for example `wal_level`, `max_wal_senders`, etc, are strictly
+Any PostgreSQL configuration item that controls replication, for example `wal_level`, `max_wal_senders`, or others are strictly
controlled by Patroni. These configurations override the original settings that you make with the `postgresql[...]` configuration key.
Hence, they are all separated and placed under `patroni['postgresql'][...]`. This behavior is limited to replication.
Patroni honours any other PostgreSQL configuration that was made with the `postgresql[...]` configuration key. For example,
@@ -215,7 +197,7 @@ configuration key.
NOTE:
The configuration of a Patroni node is very similar to a repmgr but shorter. When Patroni is enabled, first you can ignore
-any replication setting of PostgreSQL (it is overwritten anyway). Then you can remove any `repmgr[...]` or
+any replication setting of PostgreSQL (which is overwritten). Then, you can remove any `repmgr[...]` or
repmgr-specific configuration as well. Especially, make sure that you remove `postgresql['shared_preload_libraries'] = 'repmgr_funcs'`.
Here is an example:
@@ -282,7 +264,7 @@ on each node for the changes to take effect.
Generally, when Consul cluster is ready, the first node that [reconfigures](../restart_gitlab.md#omnibus-gitlab-reconfigure)
becomes the leader. You do not need to sequence the nodes reconfiguration. You can run them in parallel or in any order.
-If you choose an arbitrary order you do not have any predetermined leader.
+If you choose an arbitrary order, you do not have any predetermined leader.
#### Enable Monitoring
@@ -296,7 +278,7 @@ If you enable Monitoring, it must be enabled on **all** database servers.
# Enable service discovery for Prometheus
consul['monitoring_service_discovery'] = true
- # Set the network addresses that the exporters will listen on
+ # Set the network addresses that the exporters must listen on
node_exporter['listen_address'] = '0.0.0.0:9100'
postgres_exporter['listen_address'] = '0.0.0.0:9187'
```
@@ -340,9 +322,9 @@ patroni['tls_ca_file'] = '/path/to/ca.pem'
When TLS is enabled, mutual authentication of the API server and client is possible for all endpoints, the extent of which depends on
the `patroni['tls_client_mode']` attribute:
-- `none` (default): the API will not check for any client certificates.
-- `optional`: client certificates are required for all [unsafe](https://patroni.readthedocs.io/en/latest/security.html#protecting-the-rest-api) API calls.
-- `required`: client certificates are required for all API calls.
+- `none` (default): The API does not check for any client certificates.
+- `optional`: Client certificates are required for all [unsafe](https://patroni.readthedocs.io/en/latest/security.html#protecting-the-rest-api) API calls.
+- `required`: Client certificates are required for all API calls.
The client certificates are verified against the CA certificate that is specified with the `patroni['tls_ca_file']` attribute. Therefore,
this attribute is required for mutual TLS authentication. You also need to specify PEM-formatted client certificate and private key files.
@@ -450,9 +432,9 @@ authentication mode (`patroni['tls_client_mode']`), must each have the same valu
#### Configure the internal load balancer
-If you're running more than one PgBouncer node as recommended, then you need to set up a TCP internal load balancer to serve each correctly. This can be accomplished with any reputable TCP load balancer.
+If you're running more than one PgBouncer node as recommended, you must set up a TCP internal load balancer to serve each correctly. This can be accomplished with any reputable TCP load balancer.
-As an example here's how you could do it with [HAProxy](https://www.haproxy.org/):
+As an example, here's how you could do it with [HAProxy](https://www.haproxy.org/):
```plaintext
global
@@ -554,7 +536,7 @@ Here is a list and description of each machine and the assigned IP:
- `10.6.0.33`: PostgreSQL 3
- `10.6.0.41`: GitLab application
-All passwords are set to `toomanysecrets`, please do not use this password or derived hashes and the `external_url` for GitLab is `http://gitlab.example.com`.
+All passwords are set to `toomanysecrets`. Please do not use this password or derived hashes and the `external_url` for GitLab is `http://gitlab.example.com`.
After the initial configuration, if a failover occurs, the PostgresSQL leader node changes to one of the available secondaries until it is failed back.
@@ -675,7 +657,7 @@ This example uses 3 PostgreSQL servers, and 1 application node (with PgBouncer s
It differs from the [recommended setup](#example-recommended-setup) by moving the Consul servers into the same servers we use for PostgreSQL.
The trade-off is between reducing server counts, against the increased operational complexity of needing to deal with PostgreSQL [failover](#manual-failover-procedure-for-patroni) procedures in addition to [Consul outage recovery](../consul.md#outage-recovery) on the same set of machines.
-In this example we start with all servers on the same 10.6.0.0/16 private network range, they can connect to each freely other on those addresses.
+In this example, we start with all servers on the same 10.6.0.0/16 private network range; they can connect to each freely other on those addresses.
Here is a list and description of each machine and the assigned IP:
@@ -684,7 +666,7 @@ Here is a list and description of each machine and the assigned IP:
- `10.6.0.23`: PostgreSQL 3
- `10.6.0.31`: GitLab application
-All passwords are set to `toomanysecrets`, please do not use this password or derived hashes.
+All passwords are set to `toomanysecrets`. Please do not use this password or derived hashes.
The `external_url` for GitLab is `http://gitlab.example.com`
@@ -787,7 +769,7 @@ Patroni is an opinionated solution for PostgreSQL high-availability. It takes th
The fundamental [architecture](#example-recommended-setup-manual-steps) (mentioned above) does not change for Patroni.
You do not need any special consideration for Patroni while provisioning your database nodes. Patroni heavily relies on Consul to store the state of the cluster and elect a leader. Any failure in Consul cluster and its leader election propagates to the Patroni cluster as well.
-Patroni monitors the cluster and handles any failover. When the primary node fails it works with Consul to notify PgBouncer. On failure, Patroni handles the transitioning of the old primary to a replica and rejoins it to the cluster automatically.
+Patroni monitors the cluster and handles any failover. When the primary node fails, it works with Consul to notify PgBouncer. On failure, Patroni handles the transitioning of the old primary to a replica and rejoins it to the cluster automatically.
With Patroni, the connection flow is slightly different. Patroni on each node connects to Consul agent to join the cluster. Only after this point it decides if the node is the primary or a replica. Based on this decision, it configures and starts PostgreSQL which it communicates with directly over a Unix socket. This means that if the Consul cluster is not functional or does not have a leader, Patroni and by extension PostgreSQL does not start. Patroni also exposes a REST API which can be accessed via its [default port](../package_information/defaults.md)
on each node.
@@ -847,7 +829,7 @@ Investigate further if:
- `reply_time` is not current.
The `lsn` fields relate to which write-ahead-log segments have been replicated.
-Run the following on the leader to find out the current LSN:
+Run the following on the leader to find out the current Log Sequence Number (LSN):
```shell
echo 'SELECT pg_current_wal_lsn();' | gitlab-psql
@@ -918,7 +900,7 @@ patroni['remove_data_directory_on_diverged_timelines'] = false
|-|-|
|`use_pg_rewind`|Try running `pg_rewind` on the former cluster leader before it rejoins the database cluster.|
|`remove_data_directory_on_rewind_failure`|If `pg_rewind` fails, remove the local PostgreSQL data directory and re-replicate from the current cluster leader.|
-|`remove_data_directory_on_diverged_timelines`|If `pg_rewind` cannot be used and the former leader's timeline has diverged from the current one, then delete the local data directory and re-replicate from the current cluster leader.|
+|`remove_data_directory_on_diverged_timelines`|If `pg_rewind` cannot be used and the former leader's timeline has diverged from the current one, delete the local data directory and re-replicate from the current cluster leader.|
### Database authorization for Patroni
@@ -936,7 +918,7 @@ You can use `gitlab-ctl patroni members` to check the status of the cluster memb
is the primary or a replica.
When Patroni is enabled, it exclusively controls PostgreSQL's startup,
-shutdown, and restart. This means, to shut down PostgreSQL on a certain node you must shutdown Patroni on the same node with:
+shutdown, and restart. This means, to shut down PostgreSQL on a certain node, you must shutdown Patroni on the same node with:
```shell
sudo gitlab-ctl stop patroni
@@ -974,7 +956,7 @@ When a Geo secondary site is replicating from a primary site that uses `Patroni`
sudo gitlab-ctl replicate-geo-database --host= --replication-slot=
```
-Otherwise, the replication will not happen, even if the original node gets re-added as a follower node. This re-syncs your secondary site database and may take a long time depending on the amount of data to sync. You may also need to run `gitlab-ctl reconfigure` if replication is still not working after re-syncing.
+Otherwise, the replication does not happen, even if the original node gets re-added as a follower node. This re-syncs your secondary site database and may take a long time depending on the amount of data to sync. You may also need to run `gitlab-ctl reconfigure` if replication is still not working after re-syncing.
### Recovering the Patroni cluster
@@ -1097,7 +1079,7 @@ Considering these, you should carefully plan your PostgreSQL upgrade:
sudo gitlab-ctl pg-upgrade -V 12
```
-1. Check the status of the leader and cluster. You can only proceed if you have a healthy leader:
+1. Check the status of the leader and cluster. You can proceed only if you have a healthy leader:
```shell
gitlab-ctl patroni check-leader
@@ -1192,7 +1174,7 @@ If replication is not occurring, it may be necessary to reinitialize a replica.
WARNING:
This is a destructive process and may lead the cluster into a bad state. Make sure that you have a healthy backup before running this process.
-As a last resort, if your Patroni cluster is in an unknown/bad state and no node can start, you can
+As a last resort, if your Patroni cluster is in an unknown or bad state and no node can start, you can
reset the Patroni state in Consul completely, resulting in a reinitialized Patroni cluster when
the first Patroni node starts.
@@ -1248,7 +1230,7 @@ To fix the problem, ensure the loopback interface is included in the CIDR addres
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
1. Check that [all the replicas are synchronized](#check-replication-status)
-### Errors in Patroni logs: the requested start point is ahead of the WAL flush position
+### Errors in Patroni logs: the requested start point is ahead of the Write Ahead Log (WAL) flush position
This error indicates that the database is not replicating:
diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md
index 7cd7ecc26f..1d60b8c6f5 100644
--- a/doc/administration/raketasks/check.md
+++ b/doc/administration/raketasks/check.md
@@ -14,7 +14,8 @@ Even though Git is very resilient and tries to prevent data integrity issues,
there are times when things go wrong. The following Rake tasks intend to
help GitLab administrators diagnose problem repositories so they can be fixed.
-There are 3 things that are checked to determine integrity.
+These Rake tasks use three different methods to determine the integrity of Git
+repositories.
1. Git repository file system check ([`git fsck`](https://git-scm.com/docs/git-fsck)).
This step verifies the connectivity and validity of objects in the repository.
@@ -37,7 +38,7 @@ exactly which repositories are causing the trouble.
### Check project code repositories
This task loops through the project code repositories and runs the integrity check
-described previously. If a project uses a pool repository, that will also be checked.
+described previously. If a project uses a pool repository, that is also checked.
Other types of Git repositories [are not checked](https://gitlab.com/gitlab-org/gitaly/-/issues/3643).
**Omnibus Installation**
@@ -67,7 +68,7 @@ source repository.
This task loops through all repositories on the GitLab server and outputs
checksums in the format `,`.
-- If a repository doesn't exist, the project ID will have a blank checksum.
+- If a repository doesn't exist, the project ID is a blank checksum.
- If a repository exists but is empty, the output checksum is `0000000000000000000000000000000000000000`.
- Projects which don't exist are skipped.
@@ -85,9 +86,9 @@ sudo -u git -H bundle exec rake gitlab:git:checksum_projects RAILS_ENV=productio
For example, if:
-- Project with ID#2 doesn't exist, it will be skipped.
-- Project with ID#4 doesn't have a repository, its checksum will be blank.
-- Project with ID#5 has an empty repository, its checksum will be `0000000000000000000000000000000000000000`.
+- Project with ID#2 doesn't exist, it is skipped.
+- Project with ID#4 doesn't have a repository, its checksum is blank.
+- Project with ID#5 has an empty repository, its checksum is `0000000000000000000000000000000000000000`.
The output would then look something like:
@@ -105,7 +106,7 @@ Optionally, specific project IDs can be checksummed by setting an environment
variable `CHECKSUM_PROJECT_IDS` with a list of comma-separated integers, for example:
```shell
-CHECKSUM_PROJECT_IDS="1,3" sudo gitlab-rake gitlab:git:checksum_projects
+sudo CHECKSUM_PROJECT_IDS="1,3" gitlab-rake gitlab:git:checksum_projects
```
## Uploaded files integrity
@@ -115,7 +116,7 @@ These integrity checks can detect missing files. Additionally, for locally
stored files, checksums are generated and stored in the database upon upload,
and these checks verify them against current files.
-Currently, integrity checks are supported for the following types of file:
+Integrity checks are supported for the following types of file:
- CI artifacts (Available from version 10.7.0)
- LFS objects (Available from version 10.6.0)
@@ -206,7 +207,7 @@ above.
### Dangling objects
-The `gitlab:git:fsck` task can find dangling objects such as:
+The `gitlab-rake gitlab:git:fsck` task can find dangling objects such as:
```plaintext
dangling blob a12...
diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md
index 5ddab999ef..d770361864 100644
--- a/doc/administration/raketasks/maintenance.md
+++ b/doc/administration/raketasks/maintenance.md
@@ -115,7 +115,7 @@ component servers like [Gitaly](../gitaly/configure_gitaly.md#run-gitaly-on-its-
You may also have a look at our troubleshooting guides for:
- [GitLab](../index.md#troubleshooting)
-- [Omnibus GitLab](https://docs.gitlab.com/omnibus/README.html#troubleshooting)
+- [Omnibus GitLab](https://docs.gitlab.com/omnibus/index.html#troubleshooting)
To run `gitlab:check`, run:
@@ -247,7 +247,7 @@ have been corrupted, you should reinstall the omnibus package.
Sometimes you need to know if your GitLab installation can connect to a TCP
service on another machine (for example a PostgreSQL or web server)
-in order to troubleshoot proxy issues.
+to troubleshoot proxy issues.
A Rake task is included to help you with this.
**Omnibus Installation**
@@ -334,13 +334,13 @@ This is an experimental feature that isn't enabled by default. It requires Postg
Database indexes can be rebuilt regularly to reclaim space and maintain healthy levels of index bloat over time.
-In order to rebuild the two indexes with the highest estimated bloat, use the following Rake task:
+To rebuild the two indexes with the highest estimated bloat, use the following Rake task:
```shell
sudo gitlab-rake gitlab:db:reindex
```
-In order to target a specific index, use the following Rake task:
+To target a specific index, use the following Rake task:
```shell
sudo gitlab-rake gitlab:db:reindex['public.a_specific_index']
@@ -352,7 +352,7 @@ The following index types are not supported:
1. Partitioned indexes
1. Expression indexes
-Optionally, this Rake task sends annotations to a Grafana (4.6 or later) endpoint. Use the following custom environment variables in order to enable annotations:
+Optionally, this Rake task sends annotations to a Grafana (4.6 or later) endpoint. Use the following custom environment variables to enable annotations:
1. `GRAFANA_API_URL` - Grafana's base URL, for example `http://some-host:3000`.
1. `GRAFANA_API_KEY` - Grafana API key with at least `Editor role`.
diff --git a/doc/administration/read_only_gitlab.md b/doc/administration/read_only_gitlab.md
index cf45d3e15c..2fbcb2a62e 100644
--- a/doc/administration/read_only_gitlab.md
+++ b/doc/administration/read_only_gitlab.md
@@ -66,7 +66,7 @@ sudo gitlab-ctl start puma
If you want to allow users to use the GitLab UI, then you'll need to ensure that
the database is read-only:
-1. Take a [GitLab backup](../raketasks/backup_restore.md#back-up-gitlab)
+1. Take a [GitLab backup](../raketasks/backup_restore.md)
in case things don't go as expected.
1. Enter PostgreSQL on the console as an administrator user:
diff --git a/doc/administration/redis/replication_and_failover.md b/doc/administration/redis/replication_and_failover.md
index 48db734b1a..db652a8078 100644
--- a/doc/administration/redis/replication_and_failover.md
+++ b/doc/administration/redis/replication_and_failover.md
@@ -647,6 +647,7 @@ persistence classes.
| `shared_state` | Store session-related and other persistent data. |
| `actioncable` | Pub/Sub queue backend for ActionCable. |
| `trace_chunks` | Store [CI trace chunks](../job_logs.md#enable-or-disable-incremental-logging) data. |
+| `rate_limiting` | Store [rate limiting](../../user/admin_area/settings/user_and_ip_rate_limits.md) state. |
To make this work with Sentinel:
@@ -659,6 +660,7 @@ To make this work with Sentinel:
gitlab_rails['redis_shared_state_instance'] = REDIS_SHARED_STATE_URL
gitlab_rails['redis_actioncable_instance'] = REDIS_ACTIONCABLE_URL
gitlab_rails['redis_trace_chunks_instance'] = REDIS_TRACE_CHUNKS_URL
+ gitlab_rails['redis_rate_limiting_instance'] = REDIS_RATE_LIMITING_URL
# Configure the Sentinels
gitlab_rails['redis_cache_sentinels'] = [
@@ -681,6 +683,10 @@ To make this work with Sentinel:
{ host: TRACE_CHUNKS_SENTINEL_HOST, port: 26379 },
{ host: TRACE_CHUNKS_SENTINEL_HOST2, port: 26379 }
]
+ gitlab_rails['redis_rate_limiting_sentinels'] = [
+ { host: RATE_LIMITING_SENTINEL_HOST, port: 26379 },
+ { host: RATE_LIMITING_SENTINEL_HOST2, port: 26379 }
+ ]
```
- Redis URLs should be in the format: `redis://:PASSWORD@SENTINEL_PRIMARY_NAME`, where:
diff --git a/doc/administration/redis/standalone.md b/doc/administration/redis/standalone.md
index 42482475d2..8a3b88780a 100644
--- a/doc/administration/redis/standalone.md
+++ b/doc/administration/redis/standalone.md
@@ -44,8 +44,7 @@ Omnibus GitLab:
1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
1. Note the Redis node's IP address or hostname, port, and
- Redis password. These will be necessary when [configuring the GitLab
- application servers](#set-up-the-gitlab-rails-application-instance).
+ Redis password. These will be necessary when [configuring the GitLab application servers](#set-up-the-gitlab-rails-application-instance).
[Advanced configuration options](https://docs.gitlab.com/omnibus/settings/redis.html)
are supported and can be added if needed.
diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md
index 0fd597e6a2..e731085b0c 100644
--- a/doc/administration/reference_architectures/10k_users.md
+++ b/doc/administration/reference_architectures/10k_users.md
@@ -12,6 +12,7 @@ full list of reference architectures, see
> - **Supported users (approximate):** 10,000
> - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA)
+> - **Cloud Native Hybrid:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
> - **Test requests per second (RPS) rates:** API: 200 RPS, Web: 20 RPS, Git (Pull): 20 RPS, Git (Push): 4 RPS
> - **[Latest 10k weekly performance testing results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/10k)**
@@ -22,18 +23,16 @@ full list of reference architectures, see
| PostgreSQL1 | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` | `D8s v3` |
| PgBouncer1 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Internal load balancing node3 | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Redis - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Redis - Queues / Shared State2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Redis Sentinel - Cache2 | 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | `c5.large` | `A1 v2` |
-| Redis Sentinel - Queues / Shared State2 | 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | `c5.large` | `A1 v2` |
-| Gitaly | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` | `m5.4xlarge` | `D16s v3` |
-| Praefect | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Redis/Sentinel - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Redis/Sentinel - Persistent2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Gitaly5 | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` | `m5.4xlarge` | `D16s v3` |
+| Praefect5 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Praefect PostgreSQL1 | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
| GitLab Rails | 3 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` | `F32s v2` |
| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
| Object storage4 | n/a | n/a | n/a | n/a | n/a |
-| NFS server (optional, not recommended) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
@@ -41,6 +40,7 @@ full list of reference architectures, see
2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster). If Gitaly Sharded is desired, the same specs listed above for `Gitaly` should be used.
NOTE:
@@ -82,11 +82,6 @@ card "Database" as database {
card "redis" as redis {
collections "**Redis Persistent** x3" as redis_persistent #FF6347
collections "**Redis Cache** x3" as redis_cache #FF6347
- collections "**Redis Persistent Sentinel** x3" as redis_persistent_sentinel #FF6347
- collections "**Redis Cache Sentinel** x3"as redis_cache_sentinel #FF6347
-
- redis_persistent <.[#FF6347]- redis_persistent_sentinel
- redis_cache <.[#FF6347]- redis_cache_sentinel
}
cloud "**Object Storage**" as object_storage #white
@@ -137,8 +132,7 @@ our [Sysbench](https://github.com/akopytov/sysbench)-based
Due to better performance and availability, for data objects (such as LFS,
uploads, or artifacts), using an [object storage service](#configure-the-object-storage)
-is recommended instead of using NFS. Using an object storage service also
-doesn't require you to provision and maintain a node.
+is recommended.
It's also worth noting that at this time [Praefect requires its own database server](../gitaly/praefect.md#postgresql) and
that to achieve full High Availability a third-party PostgreSQL database solution will be required.
@@ -169,10 +163,8 @@ To set up GitLab and its components to accommodate up to 10,000 users:
used for shared data objects.
1. [Configure Advanced Search](#configure-advanced-search) (optional) for faster,
more advanced code search across your entire GitLab instance.
-1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
- to have shared disk storage service as an alternative to Gitaly or object
- storage. You can skip this step if you're not using GitLab Pages (which
- requires NFS).
+1. [Configure NFS](#configure-nfs)
+ to have shared disk storage service for certain GitLab operations (non Gitaly or Object Storage).
The servers start on the same 10.6.0.0/24 private network range, and can
connect to each other freely on these addresses.
@@ -193,15 +185,9 @@ The following list includes descriptions of each server and its assigned IP:
- `10.6.0.51`: Redis - Cache Primary
- `10.6.0.52`: Redis - Cache Replica 1
- `10.6.0.53`: Redis - Cache Replica 2
-- `10.6.0.71`: Sentinel - Cache 1
-- `10.6.0.72`: Sentinel - Cache 2
-- `10.6.0.73`: Sentinel - Cache 3
-- `10.6.0.61`: Redis - Queues Primary
-- `10.6.0.62`: Redis - Queues Replica 1
-- `10.6.0.63`: Redis - Queues Replica 2
-- `10.6.0.81`: Sentinel - Queues 1
-- `10.6.0.82`: Sentinel - Queues 2
-- `10.6.0.83`: Sentinel - Queues 3
+- `10.6.0.61`: Redis - Persistent Primary
+- `10.6.0.62`: Redis - Persistent Replica 1
+- `10.6.0.63`: Redis - Persistent Replica 2
- `10.6.0.91`: Gitaly 1
- `10.6.0.92`: Gitaly 2
- `10.6.0.93`: Gitaly 3
@@ -792,15 +778,9 @@ to be used with GitLab. The following IPs will be used as an example:
- `10.6.0.51`: Redis - Cache Primary
- `10.6.0.52`: Redis - Cache Replica 1
- `10.6.0.53`: Redis - Cache Replica 2
-- `10.6.0.71`: Sentinel - Cache 1
-- `10.6.0.72`: Sentinel - Cache 2
-- `10.6.0.73`: Sentinel - Cache 3
-- `10.6.0.61`: Redis - Queues Primary
-- `10.6.0.62`: Redis - Queues Replica 1
-- `10.6.0.63`: Redis - Queues Replica 2
-- `10.6.0.81`: Sentinel - Queues 1
-- `10.6.0.82`: Sentinel - Queues 2
-- `10.6.0.83`: Sentinel - Queues 3
+- `10.6.0.61`: Redis - Persistent Primary
+- `10.6.0.62`: Redis - Persistent Replica 1
+- `10.6.0.63`: Redis - Persistent Replica 2
### Providing your own Redis instance
@@ -812,7 +792,7 @@ optional count argument to SPOP, which is required for [Merge Trains](../../ci/p
Note the Redis node's IP address or hostname, port, and password (if required).
These will be necessary later when configuring the [GitLab application servers](#configure-gitlab-rails).
-### Configure the Redis and Sentinel Cache cluster
+### Configure the Redis Cache cluster
This is the section where we install and set up the new Redis Cache instances.
@@ -830,8 +810,12 @@ a node and change its status from primary to replica (and vice versa).
1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
```ruby
- # Specify server role as 'redis_master_role' and enable Consul agent
- roles(['redis_master_role', 'consul_role'])
+ # Specify server roles as 'redis_master_role' with sentinel and the Consul agent
+ roles ['redis_sentinel_role', 'redis_master_role', 'consul_role']
+
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = '0.0.0.0'
+ sentinel['quorum'] = 2
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
@@ -843,8 +827,19 @@ a node and change its status from primary to replica (and vice versa).
# machines to connect to it.
redis['port'] = 6379
- # Set up password authentication for Redis (use the same password in all nodes).
+ ## Port of primary Redis server for Sentinel, uncomment to change to non default. Defaults
+ ## to `6379`.
+ #redis['master_port'] = 6379
+
+ # Set up password authentication for Redis and replicas (use the same password in all nodes).
redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+
+ ## Must be the same in every Redis node
+ redis['master_name'] = 'gitlab-redis-cache'
+
+ ## The IP of this primary Redis node.
+ redis['master_ip'] = '10.6.0.51'
# Set the Redis Cache instance as an LRU
# 90% of available RAM in MB
@@ -878,10 +873,6 @@ a node and change its status from primary to replica (and vice versa).
1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles(['redis_sentinel_role', 'redis_master_role'])`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
-
#### Configure the replica Redis Cache nodes
1. SSH in to the **replica** Redis server.
@@ -889,11 +880,15 @@ You can specify multiple roles, like sentinel and Redis, as:
package of your choice. Be sure to both follow _only_ installation steps 1 and 2
on the page, and to select the correct Omnibus GitLab package, with the same version
and type (Community or Enterprise editions) as your current install.
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
+1. Edit `/etc/gitlab/gitlab.rb` and add same contents as the primary node in the previous section replacing `redis_master_node` with `redis_replica_node`:
```ruby
- # Specify server role as 'redis_replica_role' and enable Consul agent
- roles(['redis_replica_role', 'consul_role'])
+ # Specify server roles as 'redis_sentinel_role' and 'redis_replica_role'
+ roles ['redis_sentinel_role', 'redis_replica_role', 'consul_role']
+
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = '0.0.0.0'
+ sentinel['quorum'] = 2
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
@@ -905,16 +900,20 @@ You can specify multiple roles, like sentinel and Redis, as:
# machines to connect to it.
redis['port'] = 6379
- # The same password for Redis authentication you set up for the primary node.
- redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
-
- # The IP of the primary Redis node.
- redis['master_ip'] = '10.6.0.51'
-
- # Port of primary Redis server, uncomment to change to non default. Defaults
- # to `6379`.
+ ## Port of primary Redis server for Sentinel, uncomment to change to non default. Defaults
+ ## to `6379`.
#redis['master_port'] = 6379
+ # Set up password authentication for Redis and replicas (use the same password in all nodes).
+ redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+
+ ## Must be the same in every Redis node
+ redis['master_name'] = 'gitlab-redis-cache'
+
+ ## The IP of the primary Redis node.
+ redis['master_ip'] = '10.6.0.51'
+
# Set the Redis Cache instance as an LRU
# 90% of available RAM in MB
redis['maxmemory'] = '13500mb'
@@ -949,15 +948,6 @@ You can specify multiple roles, like sentinel and Redis, as:
1. Go through the steps again for all the other replica nodes, and
make sure to set up the IPs correctly.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles(['redis_sentinel_role', 'redis_master_role'])`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
-
-These values don't have to be changed again in `/etc/gitlab/gitlab.rb` after
-a failover, as the nodes will be managed by the [Sentinels](#configure-the-sentinel-cache-nodes), and even after a
-`gitlab-ctl reconfigure`, they will get their configuration restored by
-the same Sentinels.
-
Advanced [configuration options](https://docs.gitlab.com/omnibus/settings/redis.html)
are supported and can be added if needed.
@@ -967,133 +957,15 @@ are supported and can be added if needed.
-#### Configure the Sentinel Cache nodes
+### Configure the Redis Persistent cluster
-Now that the Redis servers are all set up, let's configure the Sentinel
-servers. The following IPs will be used as an example:
-
-- `10.6.0.71`: Sentinel - Cache 1
-- `10.6.0.72`: Sentinel - Cache 2
-- `10.6.0.73`: Sentinel - Cache 3
-
-NOTE:
-If you're using an external Redis Sentinel instance, be sure to exclude the
-`requirepass` parameter from the Sentinel configuration. This parameter causes
-clients to report `NOAUTH Authentication required.`.
-[Redis Sentinel 3.2.x doesn't support password authentication](https://github.com/antirez/redis/issues/3279).
-
-To configure the Sentinel Cache server:
-
-1. SSH in to the server that will host Consul/Sentinel.
-1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
- package of your choice. Be sure to both follow _only_ installation steps 1 and 2
- on the page, and to select the correct Omnibus GitLab package, with the same version
- and type (Community or Enterprise editions) as your current install.
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
-
- ```ruby
- roles(['redis_sentinel_role', 'consul_role'])
-
- ## Must be the same in every sentinel node
- redis['master_name'] = 'gitlab-redis-cache'
-
- ## The same password for Redis authentication you set up for the primary node.
- redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
-
- ## The IP of the primary Redis node.
- redis['master_ip'] = '10.6.0.51'
-
- ## Define a port so Redis can listen for TCP requests which will allow other
- ## machines to connect to it.
- redis['port'] = 6379
-
- ## Port of primary Redis server, uncomment to change to non default. Defaults
- ## to `6379`.
- #redis['master_port'] = 6379
-
- ## Configure Sentinel's IP
- sentinel['bind'] = '10.6.0.71'
-
- ## Port that Sentinel listens on, uncomment to change to non default. Defaults
- ## to `26379`.
- #sentinel['port'] = 26379
-
- ## Quorum must reflect the amount of voting sentinels it take to start a failover.
- ## Value must NOT be greater then the amount of sentinels.
- ##
- ## The quorum can be used to tune Sentinel in two ways:
- ## 1. If a the quorum is set to a value smaller than the majority of Sentinels
- ## we deploy, we are basically making Sentinel more sensible to primary failures,
- ## triggering a failover as soon as even just a minority of Sentinels is no longer
- ## able to talk with the primary.
- ## 1. If a quorum is set to a value greater than the majority of Sentinels, we are
- ## making Sentinel able to failover only when there are a very large number (larger
- ## than majority) of well connected Sentinels which agree about the primary being down.s
- sentinel['quorum'] = 2
-
- ## Consider unresponsive server down after x amount of ms.
- #sentinel['down_after_milliseconds'] = 10000
-
- ## Specifies the failover timeout in milliseconds. It is used in many ways:
- ##
- ## - The time needed to re-start a failover after a previous failover was
- ## already tried against the same primary by a given Sentinel, is two
- ## times the failover timeout.
- ##
- ## - The time needed for a replica replicating to a wrong primary according
- ## to a Sentinel current configuration, to be forced to replicate
- ## with the right primary, is exactly the failover timeout (counting since
- ## the moment a Sentinel detected the misconfiguration).
- ##
- ## - The time needed to cancel a failover that is already in progress but
- ## did not produced any configuration change (REPLICAOF NO ONE yet not
- ## acknowledged by the promoted replica).
- ##
- ## - The maximum time a failover in progress waits for all the replica to be
- ## reconfigured as replicas of the new primary. However even after this time
- ## the replicas will be reconfigured by the Sentinels anyway, but not with
- ## the exact parallel-syncs progression as specified.
- #sentinel['failover_timeout'] = 60000
-
- ## Enable service discovery for Prometheus
- consul['monitoring_service_discovery'] = true
-
- ## The IPs of the Consul server nodes
- ## You can also use FQDNs and intermix them with IPs
- consul['configuration'] = {
- retry_join: %w(10.6.0.11 10.6.0.12 10.6.0.13),
- }
-
- # Set the network addresses that the exporters will listen on
- node_exporter['listen_address'] = '0.0.0.0:9100'
- redis_exporter['listen_address'] = '0.0.0.0:9121'
-
- # Prevent database migrations from running on upgrade automatically
- gitlab_rails['auto_migrate'] = false
- ```
-
-1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
- the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
-
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-1. Go through the steps again for all the other Consul/Sentinel nodes, and
- make sure you set up the correct IPs.
-
-
-
-### Configure the Redis and Sentinel Queues cluster
-
-This is the section where we install and set up the new Redis Queues instances.
+This is the section where we install and set up the new Redis Persistent instances.
Both the primary and replica Redis nodes need the same password defined in
`redis['password']`. At any time during a failover, the Sentinels can reconfigure
a node and change its status from primary to replica (and vice versa).
-#### Configure the primary Redis Queues node
+#### Configure the primary Redis Persistent node
1. SSH in to the **Primary** Redis server.
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
@@ -1103,8 +975,12 @@ a node and change its status from primary to replica (and vice versa).
1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
```ruby
- # Specify server role as 'redis_master_role' and enable Consul agent
- roles(['redis_master_role', 'consul_role'])
+ # Specify server roles as 'redis_master_role' with Sentinel and the Consul agent
+ roles ['redis_sentinel_role', 'redis_master_role', 'consul_role']
+
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = '0.0.0.0'
+ sentinel['quorum'] = 2
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
@@ -1116,8 +992,19 @@ a node and change its status from primary to replica (and vice versa).
# machines to connect to it.
redis['port'] = 6379
- # Set up password authentication for Redis (use the same password in all nodes).
- redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+ ## Port of primary Redis server for Sentinel, uncomment to change to non default. Defaults
+ ## to `6379`.
+ #redis['master_port'] = 6379
+
+ # Set up password authentication for Redis and replicas (use the same password in all nodes).
+ redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+
+ ## Must be the same in every Redis node
+ redis['master_name'] = 'gitlab-redis-persistent'
+
+ ## The IP of this primary Redis node.
+ redis['master_ip'] = '10.6.0.61'
## Enable service discovery for Prometheus
consul['monitoring_service_discovery'] = true
@@ -1141,13 +1028,9 @@ a node and change its status from primary to replica (and vice versa).
1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles(['redis_sentinel_role', 'redis_master_role'])`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
+#### Configure the replica Redis Persistent nodes
-#### Configure the replica Redis Queues nodes
-
-1. SSH in to the **replica** Redis Queue server.
+1. SSH in to the **replica** Redis Persistent server.
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to both follow _only_ installation steps 1 and 2
on the page, and to select the correct Omnibus GitLab package, with the same version
@@ -1155,8 +1038,12 @@ You can specify multiple roles, like sentinel and Redis, as:
1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
```ruby
- # Specify server role as 'redis_replica_role' and enable Consul agent
- roles(['redis_replica_role', 'consul_role'])
+ # Specify server roles as 'redis_sentinel_role' and 'redis_replica_role'
+ roles ['redis_sentinel_role', 'redis_replica_role', 'consul_role']
+
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = '0.0.0.0'
+ sentinel['quorum'] = 2
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
@@ -1168,16 +1055,20 @@ You can specify multiple roles, like sentinel and Redis, as:
# machines to connect to it.
redis['port'] = 6379
+ ## Port of primary Redis server for Sentinel, uncomment to change to non default. Defaults
+ ## to `6379`.
+ #redis['master_port'] = 6379
+
# The same password for Redis authentication you set up for the primary node.
redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+
+ ## Must be the same in every Redis node
+ redis['master_name'] = 'gitlab-redis-persistent'
# The IP of the primary Redis node.
redis['master_ip'] = '10.6.0.61'
- # Port of primary Redis server, uncomment to change to non default. Defaults
- # to `6379`.
- #redis['master_port'] = 6379
-
## Enable service discovery for Prometheus
consul['monitoring_service_discovery'] = true
@@ -1202,15 +1093,6 @@ You can specify multiple roles, like sentinel and Redis, as:
1. Go through the steps again for all the other replica nodes, and
make sure to set up the IPs correctly.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles(['redis_sentinel_role', 'redis_master_role'])`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
-
-These values don't have to be changed again in `/etc/gitlab/gitlab.rb` after
-a failover, as the nodes will be managed by the [Sentinels](#configure-the-sentinel-queues-nodes), and even after a
-`gitlab-ctl reconfigure`, they will get their configuration restored by
-the same Sentinels.
-
Advanced [configuration options](https://docs.gitlab.com/omnibus/settings/redis.html)
are supported and can be added if needed.
@@ -1220,129 +1102,15 @@ are supported and can be added if needed.
-#### Configure the Sentinel Queues nodes
-
-Now that the Redis servers are all set up, let's configure the Sentinel
-servers. The following IPs will be used as an example:
-
-- `10.6.0.81`: Sentinel - Queues 1
-- `10.6.0.82`: Sentinel - Queues 2
-- `10.6.0.83`: Sentinel - Queues 3
-
-NOTE:
-If you're using an external Redis Sentinel instance, be sure to exclude the
-`requirepass` parameter from the Sentinel configuration. This parameter causes
-clients to report `NOAUTH Authentication required.`.
-[Redis Sentinel 3.2.x doesn't support password authentication](https://github.com/antirez/redis/issues/3279).
-
-To configure the Sentinel Queues server:
-
-1. SSH in to the server that will host Sentinel.
-1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
- package of your choice. Be sure to both follow _only_ installation steps 1 and 2
- on the page, and to select the correct Omnibus GitLab package, with the same version
- and type (Community or Enterprise editions) as your current install.
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
-
- ```ruby
- roles(['redis_sentinel_role', 'consul_role'])
-
- ## Must be the same in every sentinel node
- redis['master_name'] = 'gitlab-redis-persistent'
-
- ## The same password for Redis authentication you set up for the primary node.
- redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
-
- ## The IP of the primary Redis node.
- redis['master_ip'] = '10.6.0.61'
-
- ## Define a port so Redis can listen for TCP requests which will allow other
- ## machines to connect to it.
- redis['port'] = 6379
-
- ## Port of primary Redis server, uncomment to change to non default. Defaults
- ## to `6379`.
- #redis['master_port'] = 6379
-
- ## Configure Sentinel's IP
- sentinel['bind'] = '10.6.0.81'
-
- ## Port that Sentinel listens on, uncomment to change to non default. Defaults
- ## to `26379`.
- #sentinel['port'] = 26379
-
- ## Quorum must reflect the amount of voting sentinels it take to start a failover.
- ## Value must NOT be greater then the amount of sentinels.
- ##
- ## The quorum can be used to tune Sentinel in two ways:
- ## 1. If a the quorum is set to a value smaller than the majority of Sentinels
- ## we deploy, we are basically making Sentinel more sensible to primary failures,
- ## triggering a failover as soon as even just a minority of Sentinels is no longer
- ## able to talk with the primary.
- ## 1. If a quorum is set to a value greater than the majority of Sentinels, we are
- ## making Sentinel able to failover only when there are a very large number (larger
- ## than majority) of well connected Sentinels which agree about the primary being down.s
- sentinel['quorum'] = 2
-
- ## Consider unresponsive server down after x amount of ms.
- #sentinel['down_after_milliseconds'] = 10000
-
- ## Specifies the failover timeout in milliseconds. It is used in many ways:
- ##
- ## - The time needed to re-start a failover after a previous failover was
- ## already tried against the same primary by a given Sentinel, is two
- ## times the failover timeout.
- ##
- ## - The time needed for a replica replicating to a wrong primary according
- ## to a Sentinel current configuration, to be forced to replicate
- ## with the right primary, is exactly the failover timeout (counting since
- ## the moment a Sentinel detected the misconfiguration).
- ##
- ## - The time needed to cancel a failover that is already in progress but
- ## did not produced any configuration change (REPLICAOF NO ONE yet not
- ## acknowledged by the promoted replica).
- ##
- ## - The maximum time a failover in progress waits for all the replica to be
- ## reconfigured as replicas of the new primary. However even after this time
- ## the replicas will be reconfigured by the Sentinels anyway, but not with
- ## the exact parallel-syncs progression as specified.
- #sentinel['failover_timeout'] = 60000
-
- ## Enable service discovery for Prometheus
- consul['monitoring_service_discovery'] = true
-
- ## The IPs of the Consul server nodes
- ## You can also use FQDNs and intermix them with IPs
- consul['configuration'] = {
- retry_join: %w(10.6.0.11 10.6.0.12 10.6.0.13),
- }
-
- # Set the network addresses that the exporters will listen on
- node_exporter['listen_address'] = '0.0.0.0:9100'
- redis_exporter['listen_address'] = '0.0.0.0:9121'
-
- # Prevent database migrations from running on upgrade automatically
- gitlab_rails['auto_migrate'] = false
- ```
-
-1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
- the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
-
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-1. Go through the steps again for all the other Sentinel nodes, and
- make sure you set up the correct IPs.
-
-
-
## Configure Gitaly Cluster
[Gitaly Cluster](../gitaly/praefect.md) is a GitLab provided and recommended fault tolerant solution for storing Git repositories.
In this configuration, every Git repository is stored on every Gitaly node in the cluster, with one being designated the primary, and failover occurs automatically if the primary node goes down.
+NOTE:
+Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster).
+For implementations with Gitaly Sharded, the same Gitaly specs should be used. Follow the [separate Gitaly documentation](../gitaly/configure_gitaly.md) instead of this section.
+
The recommended cluster setup includes the following components:
- 3 Gitaly nodes: Replicated storage of Git repositories.
@@ -1509,7 +1277,7 @@ the details of each Gitaly node that makes up the cluster. Each storage is also
and this name is used in several areas of the configuration. In this guide, the name of the storage will be
`default`. Also, this guide is geared towards new installs, if upgrading an existing environment
to use Gitaly Cluster, you may need to use a different name.
-Refer to the [Praefect documentation](../gitaly/praefect.md#praefect) for more info.
+Refer to the [Praefect documentation](../gitaly/praefect.md#praefect) for more information.
The following IPs will be used as an example:
@@ -1836,7 +1604,7 @@ To configure the Sidekiq nodes, on each one:
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to follow _only_ installation steps 1 and 2
on the page.
-1. Open `/etc/gitlab/gitlab.rb` with your editor:
+1. Create or edit `/etc/gitlab/gitlab.rb` and use the following configuration:
```ruby
# Avoid running unnecessary services on the Sidekiq server
@@ -1851,36 +1619,40 @@ To configure the Sidekiq nodes, on each one:
gitlab_exporter['enable'] = false
nginx['enable'] = false
+ # External URL
+ ## This should match the URL of the external load balancer
+ external_url 'https://gitlab.example.com'
+
# Redis
## Redis connection details
## First cluster that will host the cache
gitlab_rails['redis_cache_instance'] = 'redis://:@gitlab-redis-cache'
gitlab_rails['redis_cache_sentinels'] = [
- {host: '10.6.0.71', port: 26379},
- {host: '10.6.0.72', port: 26379},
- {host: '10.6.0.73', port: 26379},
+ {host: '10.6.0.51', port: 26379},
+ {host: '10.6.0.52', port: 26379},
+ {host: '10.6.0.53', port: 26379},
]
- ## Second cluster that will host the queues, shared state, and actioncable
+ ## Second cluster that will host the persistent queues, shared state, and actioncable
gitlab_rails['redis_queues_instance'] = 'redis://:@gitlab-redis-persistent'
gitlab_rails['redis_shared_state_instance'] = 'redis://:@gitlab-redis-persistent'
gitlab_rails['redis_actioncable_instance'] = 'redis://:@gitlab-redis-persistent'
gitlab_rails['redis_queues_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
gitlab_rails['redis_shared_state_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
gitlab_rails['redis_actioncable_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
# Gitaly Cluster
@@ -2032,30 +1804,30 @@ On each node perform the following:
gitlab_rails['redis_cache_instance'] = 'redis://:@gitlab-redis-cache'
gitlab_rails['redis_cache_sentinels'] = [
- {host: '10.6.0.71', port: 26379},
- {host: '10.6.0.72', port: 26379},
- {host: '10.6.0.73', port: 26379},
+ {host: '10.6.0.51', port: 26379},
+ {host: '10.6.0.52', port: 26379},
+ {host: '10.6.0.53', port: 26379},
]
- ## Second cluster that will host the queues, shared state, and actionable
+ ## Second cluster that will host the persistent queues, shared state, and actionable
gitlab_rails['redis_queues_instance'] = 'redis://:@gitlab-redis-persistent'
gitlab_rails['redis_shared_state_instance'] = 'redis://:@gitlab-redis-persistent'
gitlab_rails['redis_actioncable_instance'] = 'redis://:@gitlab-redis-persistent'
gitlab_rails['redis_queues_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
gitlab_rails['redis_shared_state_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
gitlab_rails['redis_actioncable_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
# Set the network addresses that the exporters used for monitoring will listen on
@@ -2127,7 +1899,7 @@ On each node perform the following:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-1. If you're [using NFS](#configure-nfs-optional):
+1. If you're [using NFS](#configure-nfs):
1. If necessary, install the NFS client utility packages using the following
commands:
@@ -2271,7 +2043,7 @@ To configure the Monitoring node:
## Configure the object storage
GitLab supports using an object storage service for holding numerous types of data.
-It's recommended over [NFS](#configure-nfs-optional) and in general it's better
+It's recommended over [NFS](#configure-nfs) and in general it's better
in larger setups as object storage is typically much more performant, reliable,
and scalable.
@@ -2295,6 +2067,9 @@ There are two ways of specifying object storage configuration in GitLab:
Starting with GitLab 13.2, consolidated object storage configuration is available. It simplifies your GitLab configuration since the connection details are shared across object types. Refer to [Consolidated object storage configuration](../object_storage.md#consolidated-object-storage-configuration) guide for instructions on how to set it up.
+GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared via NFS on any GitLab Rails and Sidekiq nodes.
+In GitLab 13.6 and later, it's recommended to switch to [Incremental logging](../job_logs.md#incremental-logging-architecture), which uses Redis instead of disk space for temporary caching of job logs.
+
For configuring object storage in GitLab 13.1 and earlier, or for storage types not
supported by consolidated configuration form, refer to the following guides based
on what features you intend to use:
@@ -2341,7 +2116,7 @@ cluster alongside your instance, read how to
-## Configure NFS (optional)
+## Configure NFS
[Object storage](#configure-the-object-storage), along with [Gitaly](#configure-gitaly)
are recommended over NFS wherever possible for improved performance. If you intend
@@ -2355,7 +2130,7 @@ unavailable from GitLab 15.0. No further enhancements are planned for this featu
Read:
-- The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice).
+- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
@@ -2415,12 +2190,10 @@ services where applicable):
| PostgreSQL1 | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` |
| PgBouncer1 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Internal load balancing node3 | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
-| Redis - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
-| Redis - Queues / Shared State2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
-| Redis Sentinel - Cache2 | 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` |
-| Redis Sentinel - Queues / Shared State2 | 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` |
-| Gitaly | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` |
-| Praefect | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
+| Redis/Sentinel - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
+| Redis/Sentinel - Persistent2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
+| Gitaly5 | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` |
+| Praefect5 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Praefect PostgreSQL1 | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Object storage4 | n/a | n/a | n/a |
@@ -2430,6 +2203,7 @@ services where applicable):
2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster). If Gitaly Sharded is desired, the same specs listed above for `Gitaly` should be used.
NOTE:
@@ -2474,11 +2248,6 @@ card "Database" as database {
card "redis" as redis {
collections "**Redis Persistent** x3" as redis_persistent #FF6347
collections "**Redis Cache** x3" as redis_cache #FF6347
- collections "**Redis Persistent Sentinel** x3" as redis_persistent_sentinel #FF6347
- collections "**Redis Cache Sentinel** x3"as redis_cache_sentinel #FF6347
-
- redis_persistent <.[#FF6347]- redis_persistent_sentinel
- redis_cache <.[#FF6347]- redis_cache_sentinel
}
cloud "**Object Storage**" as object_storage #white
diff --git a/doc/administration/reference_architectures/1k_users.md b/doc/administration/reference_architectures/1k_users.md
index ea40e150e5..ae832c2226 100644
--- a/doc/administration/reference_architectures/1k_users.md
+++ b/doc/administration/reference_architectures/1k_users.md
@@ -18,6 +18,8 @@ many organizations.
> - **Supported users (approximate):** 1,000
> - **High Availability:** No. For a highly-available environment, you can
> follow a modified [3K reference architecture](3k_users.md#supported-modifications-for-lower-user-counts-ha).
+> - **Cloud Native Hybrid:** No. For a cloud native hybrid environment, you
+> can follow a [modified hybrid reference architecture](#cloud-native-hybrid-reference-architecture-with-helm-charts).
> - **Test requests per second (RPS) rates:** API: 20 RPS, Web: 2 RPS, Git (Pull): 2 RPS, Git (Push): 1 RPS
> - **[Latest 1k weekly performance testing results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/1k)**
@@ -58,3 +60,12 @@ Elasticsearch cluster design and requirements are dependent on your specific
data. For recommended best practices about how to set up your Elasticsearch
cluster alongside your instance, read how to
[choose the optimal cluster configuration](../../integration/elasticsearch.md#guidance-on-choosing-optimal-cluster-configuration).
+
+## Cloud Native Hybrid reference architecture with Helm Charts
+
+Cloud Native Hybrid Reference Architecture is an alternative approach where select _stateless_
+components are deployed in Kubernetes via our official [Helm Charts](https://docs.gitlab.com/charts/),
+and _stateful_ components are deployed in compute VMs with Omnibus.
+
+The [2k GitLab Cloud Native Hybrid](2k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) (non HA) and [3k GitLab Cloud Native Hybrid](3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) (HA) reference architectures are the smallest we recommend in Kubernetes.
+For environments that need to serve less users, you can lower the node specs. Depending on your user count, you can lower all suggested node specs as desired. However, it's recommended that you don't go lower than the [general requirements](../../install/requirements.md).
diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md
index f500434d75..267f81efd9 100644
--- a/doc/administration/reference_architectures/25k_users.md
+++ b/doc/administration/reference_architectures/25k_users.md
@@ -12,6 +12,7 @@ full list of reference architectures, see
> - **Supported users (approximate):** 25,000
> - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA)
+> - **Cloud Native Hybrid:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
> - **Test requests per second (RPS) rates:** API: 500 RPS, Web: 50 RPS, Git (Pull): 50 RPS, Git (Push): 10 RPS
> - **[Latest 25k weekly performance testing results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/25k)**
@@ -22,18 +23,16 @@ full list of reference architectures, see
| PostgreSQL1 | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` | `m5.4xlarge` | `D16s v3` |
| PgBouncer1 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Internal load balancing node3 | 1 | 4 vCPU, 3.6GB memory | `n1-highcpu-4` | `c5.large` | `F2s v2` |
-| Redis - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Redis - Queues / Shared State2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Redis Sentinel - Cache2 | 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | `c5.large` | `A1 v2` |
-| Redis Sentinel - Queues / Shared State2| 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | `c5.large` | `A1 v2` |
-| Gitaly | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` | `D32s v3` |
-| Praefect | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Redis/Sentinel - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Redis/Sentinel - Persistent2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.large` | `D4s v3` |
+| Gitaly5 | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` | `D32s v3` |
+| Praefect5 | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
| Praefect PostgreSQL1 | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
| GitLab Rails | 5 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` | `F32s v2` |
| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
| Object storage4 | n/a | n/a | n/a | n/a | n/a |
-| NFS server (optional, not recommended) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
@@ -41,6 +40,7 @@ full list of reference architectures, see
2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster). If Gitaly Sharded is desired, the same specs listed above for `Gitaly` should be used.
NOTE:
@@ -82,11 +82,6 @@ card "Database" as database {
card "redis" as redis {
collections "**Redis Persistent** x3" as redis_persistent #FF6347
collections "**Redis Cache** x3" as redis_cache #FF6347
- collections "**Redis Persistent Sentinel** x3" as redis_persistent_sentinel #FF6347
- collections "**Redis Cache Sentinel** x3"as redis_cache_sentinel #FF6347
-
- redis_persistent <.[#FF6347]- redis_persistent_sentinel
- redis_cache <.[#FF6347]- redis_cache_sentinel
}
cloud "**Object Storage**" as object_storage #white
@@ -137,8 +132,7 @@ our [Sysbench](https://github.com/akopytov/sysbench)-based
Due to better performance and availability, for data objects (such as LFS,
uploads, or artifacts), using an [object storage service](#configure-the-object-storage)
-is recommended instead of using NFS. Using an object storage service also
-doesn't require you to provision and maintain a node.
+is recommended.
It's also worth noting that at this time [Praefect requires its own database server](../gitaly/praefect.md#postgresql) and
that to achieve full High Availability a third-party PostgreSQL database solution will be required.
@@ -169,10 +163,9 @@ To set up GitLab and its components to accommodate up to 25,000 users:
used for shared data objects.
1. [Configure Advanced Search](#configure-advanced-search) (optional) for faster,
more advanced code search across your entire GitLab instance.
-1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
- to have shared disk storage service as an alternative to Gitaly or object
- storage. You can skip this step if you're not using GitLab Pages (which
- requires NFS).
+1. [Configure NFS](#configure-nfs)
+ to have shared disk storage service for certain GitLab operations (non
+ Gitaly or Object Storage).
The servers start on the same 10.6.0.0/24 private network range, and can
connect to each other freely on these addresses.
@@ -193,15 +186,9 @@ The following list includes descriptions of each server and its assigned IP:
- `10.6.0.51`: Redis - Cache Primary
- `10.6.0.52`: Redis - Cache Replica 1
- `10.6.0.53`: Redis - Cache Replica 2
-- `10.6.0.71`: Sentinel - Cache 1
-- `10.6.0.72`: Sentinel - Cache 2
-- `10.6.0.73`: Sentinel - Cache 3
-- `10.6.0.61`: Redis - Queues Primary
-- `10.6.0.62`: Redis - Queues Replica 1
-- `10.6.0.63`: Redis - Queues Replica 2
-- `10.6.0.81`: Sentinel - Queues 1
-- `10.6.0.82`: Sentinel - Queues 2
-- `10.6.0.83`: Sentinel - Queues 3
+- `10.6.0.61`: Redis - Persistent Primary
+- `10.6.0.62`: Redis - Persistent Replica 1
+- `10.6.0.63`: Redis - Persistent Replica 2
- `10.6.0.91`: Gitaly 1
- `10.6.0.92`: Gitaly 2
- `10.6.0.93`: Gitaly 3
@@ -794,15 +781,9 @@ to be used with GitLab. The following IPs will be used as an example:
- `10.6.0.51`: Redis - Cache Primary
- `10.6.0.52`: Redis - Cache Replica 1
- `10.6.0.53`: Redis - Cache Replica 2
-- `10.6.0.71`: Sentinel - Cache 1
-- `10.6.0.72`: Sentinel - Cache 2
-- `10.6.0.73`: Sentinel - Cache 3
-- `10.6.0.61`: Redis - Queues Primary
-- `10.6.0.62`: Redis - Queues Replica 1
-- `10.6.0.63`: Redis - Queues Replica 2
-- `10.6.0.81`: Sentinel - Queues 1
-- `10.6.0.82`: Sentinel - Queues 2
-- `10.6.0.83`: Sentinel - Queues 3
+- `10.6.0.61`: Redis - Persistent Primary
+- `10.6.0.62`: Redis - Persistent Replica 1
+- `10.6.0.63`: Redis - Persistent Replica 2
### Providing your own Redis instance
@@ -814,7 +795,7 @@ optional count argument to SPOP, which is required for [Merge Trains](../../ci/p
Note the Redis node's IP address or hostname, port, and password (if required).
These will be necessary later when configuring the [GitLab application servers](#configure-gitlab-rails).
-### Configure the Redis and Sentinel Cache cluster
+### Configure the Redis Cache cluster
This is the section where we install and set up the new Redis Cache instances.
@@ -832,10 +813,14 @@ a node and change its status from primary to replica (and vice versa).
1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
```ruby
- # Specify server role as 'redis_master_role' and enable Consul agent
- roles(['redis_master_role', 'consul_role']
+ # Specify server role as 'redis_sentinel_role' 'redis_master_role'
+ roles ['redis_sentinel_role', 'redis_master_role', 'consul_role']
- # IP address pointing to a local IP that the other machines can reach to.
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = '0.0.0.0'
+ sentinel['quorum'] = 2
+
+ # IP address pointing to a local IP that the other machines can reach.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
# If you really need to bind to an external accessible IP, make
# sure you add extra firewall rules to prevent unauthorized access.
@@ -845,8 +830,18 @@ a node and change its status from primary to replica (and vice versa).
# machines to connect to it.
redis['port'] = 6379
+ # Port of primary Redis server for Sentinel, uncomment to change to non default.
+ # Defaults to `6379`.
+ # redis['master_port'] = 6379
+
# Set up password authentication for Redis (use the same password in all nodes).
- redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+
+ # Must be the same in every Redis node.
+ redis['master_name'] = 'gitlab-redis-cache'
+
+ # The IP of this primary Redis node.
+ redis['master_ip'] = '10.6.0.51'
# Set the Redis Cache instance as an LRU
# 90% of available RAM in MB
@@ -880,10 +875,6 @@ a node and change its status from primary to replica (and vice versa).
1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles(['redis_sentinel_role', 'redis_master_role'])`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
-
#### Configure the replica Redis Cache nodes
1. SSH in to the **replica** Redis server.
@@ -891,14 +882,18 @@ You can specify multiple roles, like sentinel and Redis, as:
package of your choice. Be sure to both follow _only_ installation steps 1 and 2
on the page, and to select the correct Omnibus GitLab package, with the same version
and type (Community or Enterprise editions) as your current install.
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
+1. Edit `/etc/gitlab/gitlab.rb` and add the same contents as the primary node in the previous section replacing `redis_master_node` with `redis_replica_node`:
```ruby
# Specify server role as 'redis_replica_role' and enable Consul agent
- roles(['redis_replica_role', 'consul_role']
+ roles ['redis_sentinel_role', 'redis_replica_role', 'consul_role']
- # IP address pointing to a local IP that the other machines can reach to.
- # You can also set bind to '0.0.0.0' which listen in all interfaces.
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = `0.0.0.0`
+ sentinel['quorum'] = 2
+
+ # IP address pointing to a local IP that the other machines can reach.
+ # Set bind to '0.0.0.0' to listen on all interfaces.
# If you really need to bind to an external accessible IP, make
# sure you add extra firewall rules to prevent unauthorized access.
redis['bind'] = '10.6.0.52'
@@ -907,16 +902,19 @@ You can specify multiple roles, like sentinel and Redis, as:
# machines to connect to it.
redis['port'] = 6379
- # The same password for Redis authentication you set up for the primary node.
- redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+ ## Port of primary Redis server for Sentinel, uncomment to change to non default. Defaults
+ ## to `6379`.
+ #redis['master_port'] = 6379
+
+ # Set up password authentication for Redis and replicas (use the same password in all nodes).
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+
+ # Must be the same in every Redis node
+ redis['master_name'] = 'gitlab-redis-cache'
# The IP of the primary Redis node.
redis['master_ip'] = '10.6.0.51'
- # Port of primary Redis server, uncomment to change to non default. Defaults
- # to `6379`.
- #redis['master_port'] = 6379
-
# Set the Redis Cache instance as an LRU
# 90% of available RAM in MB
redis['maxmemory'] = '13500mb'
@@ -952,17 +950,7 @@ You can specify multiple roles, like sentinel and Redis, as:
1. Go through the steps again for all the other replica nodes, and
make sure to set up the IPs correctly.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles(['redis_sentinel_role', 'redis_master_role'])`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
-
-These values don't have to be changed again in `/etc/gitlab/gitlab.rb` after
-a failover, as the nodes will be managed by the [Sentinels](#configure-the-sentinel-cache-nodes), and even after a
-`gitlab-ctl reconfigure`, they will get their configuration restored by
-the same Sentinels.
-
-Advanced [configuration options](https://docs.gitlab.com/omnibus/settings/redis.html)
-are supported and can be added if needed.
+ Advanced [configuration options](https://docs.gitlab.com/omnibus/settings/redis.html) are supported and can be added if needed.
-#### Configure the Sentinel Cache nodes
+### Configure the Redis Persistent cluster
-Now that the Redis servers are all set up, let's configure the Sentinel
-servers. The following IPs will be used as an example:
-
-- `10.6.0.71`: Sentinel - Cache 1
-- `10.6.0.72`: Sentinel - Cache 2
-- `10.6.0.73`: Sentinel - Cache 3
-
-NOTE:
-If you're using an external Redis Sentinel instance, be sure to exclude the
-`requirepass` parameter from the Sentinel configuration. This parameter causes
-clients to report `NOAUTH Authentication required.`.
-[Redis Sentinel 3.2.x doesn't support password authentication](https://github.com/antirez/redis/issues/3279).
-
-To configure the Sentinel Cache server:
-
-1. SSH in to the server that will host Consul/Sentinel.
-1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
- package of your choice. Be sure to both follow _only_ installation steps 1 and 2
- on the page, and to select the correct Omnibus GitLab package, with the same version
- and type (Community or Enterprise editions) as your current install.
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
-
- ```ruby
- roles(['redis_sentinel_role', 'consul_role'])
-
- ## Must be the same in every sentinel node
- redis['master_name'] = 'gitlab-redis-cache'
-
- ## The same password for Redis authentication you set up for the primary node.
- redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
-
- ## The IP of the primary Redis node.
- redis['master_ip'] = '10.6.0.51'
-
- ## Define a port so Redis can listen for TCP requests which will allow other
- ## machines to connect to it.
- redis['port'] = 6379
-
- ## Port of primary Redis server, uncomment to change to non default. Defaults
- ## to `6379`.
- #redis['master_port'] = 6379
-
- ## Configure Sentinel's IP
- sentinel['bind'] = '10.6.0.71'
-
- ## Port that Sentinel listens on, uncomment to change to non default. Defaults
- ## to `26379`.
- #sentinel['port'] = 26379
-
- ## Quorum must reflect the amount of voting sentinels it take to start a failover.
- ## Value must NOT be greater then the amount of sentinels.
- ##
- ## The quorum can be used to tune Sentinel in two ways:
- ## 1. If a the quorum is set to a value smaller than the majority of Sentinels
- ## we deploy, we are basically making Sentinel more sensible to primary failures,
- ## triggering a failover as soon as even just a minority of Sentinels is no longer
- ## able to talk with the primary.
- ## 1. If a quorum is set to a value greater than the majority of Sentinels, we are
- ## making Sentinel able to failover only when there are a very large number (larger
- ## than majority) of well connected Sentinels which agree about the primary being down.s
- sentinel['quorum'] = 2
-
- ## Consider unresponsive server down after x amount of ms.
- #sentinel['down_after_milliseconds'] = 10000
-
- ## Specifies the failover timeout in milliseconds. It is used in many ways:
- ##
- ## - The time needed to re-start a failover after a previous failover was
- ## already tried against the same primary by a given Sentinel, is two
- ## times the failover timeout.
- ##
- ## - The time needed for a replica replicating to a wrong primary according
- ## to a Sentinel current configuration, to be forced to replicate
- ## with the right primary, is exactly the failover timeout (counting since
- ## the moment a Sentinel detected the misconfiguration).
- ##
- ## - The time needed to cancel a failover that is already in progress but
- ## did not produced any configuration change (REPLICAOF NO ONE yet not
- ## acknowledged by the promoted replica).
- ##
- ## - The maximum time a failover in progress waits for all the replica to be
- ## reconfigured as replicas of the new primary. However even after this time
- ## the replicas will be reconfigured by the Sentinels anyway, but not with
- ## the exact parallel-syncs progression as specified.
- #sentinel['failover_timeout'] = 60000
-
- ## Enable service discovery for Prometheus
- consul['monitoring_service_discovery'] = true
-
- ## The IPs of the Consul server nodes
- ## You can also use FQDNs and intermix them with IPs
- consul['configuration'] = {
- retry_join: %w(10.6.0.11 10.6.0.12 10.6.0.13),
- }
-
- # Set the network addresses that the exporters will listen on
- node_exporter['listen_address'] = '0.0.0.0:9100'
- redis_exporter['listen_address'] = '0.0.0.0:9121'
-
- # Prevent database migrations from running on upgrade automatically
- gitlab_rails['auto_migrate'] = false
- ```
-
-1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
- the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
-
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-1. Go through the steps again for all the other Consul/Sentinel nodes, and
- make sure you set up the correct IPs.
-
-
-
-### Configure the Redis and Sentinel Queues cluster
-
-This is the section where we install and set up the new Redis Queues instances.
+This is the section where we install and set up the new Redis Persistent instances.
Both the primary and replica Redis nodes need the same password defined in
`redis['password']`. At any time during a failover, the Sentinels can reconfigure
a node and change its status from primary to replica (and vice versa).
-#### Configure the primary Redis Queues node
+#### Configure the primary Redis Persistent node
1. SSH in to the **Primary** Redis server.
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
@@ -1107,8 +976,12 @@ a node and change its status from primary to replica (and vice versa).
1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
```ruby
- # Specify server role as 'redis_master_role' and enable Consul agent
- roles(['redis_master_role', 'consul_role'])
+ # Specify server roles as 'redis_sentinel_role' and 'redis_master_role'
+ roles ['redis_sentinel_role', 'redis_master_role', 'consul_role']
+
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = '0.0.0.0'
+ sentinel['quorum'] = 2
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
@@ -1120,8 +993,19 @@ a node and change its status from primary to replica (and vice versa).
# machines to connect to it.
redis['port'] = 6379
- # Set up password authentication for Redis (use the same password in all nodes).
- redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+ ## Port of primary Redis server for Sentinel, uncomment to change to non default. Defaults
+ ## to `6379`.
+ #redis['master_port'] = 6379
+
+ # Set up password authentication for Redis and replicas (use the same password in all nodes).
+ redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+
+ ## Must be the same in every Redis node
+ redis['master_name'] = 'gitlab-redis-persistent'
+
+ ## The IP of this primary Redis node.
+ redis['master_ip'] = '10.6.0.61'
## Enable service discovery for Prometheus
consul['monitoring_service_discovery'] = true
@@ -1145,13 +1029,9 @@ a node and change its status from primary to replica (and vice versa).
1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles(['redis_sentinel_role', 'redis_master_role'])`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
+#### Configure the replica Redis Persistent nodes
-#### Configure the replica Redis Queues nodes
-
-1. SSH in to the **replica** Redis Queue server.
+1. SSH in to the **replica** Redis Persistent server.
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to both follow _only_ installation steps 1 and 2
on the page, and to select the correct Omnibus GitLab package, with the same version
@@ -1160,7 +1040,11 @@ You can specify multiple roles, like sentinel and Redis, as:
```ruby
# Specify server role as 'redis_replica_role' and enable Consul agent
- roles(['redis_replica_role', 'consul_role'])
+ roles ['redis_sentinel_role', 'redis_replica_role', 'consul_role']
+
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = '0.0.0.0'
+ sentinel['quorum'] = 2
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
@@ -1172,16 +1056,19 @@ You can specify multiple roles, like sentinel and Redis, as:
# machines to connect to it.
redis['port'] = 6379
+ ## Port of primary Redis server for Sentinel, uncomment to change to non default. Defaults
+ ## to `6379`.
+ #redis['master_port'] = 6379
+
# The same password for Redis authentication you set up for the primary node.
- redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+
+ ## Must be the same in every Redis node
+ redis['master_name'] = 'gitlab-redis-persistent'
# The IP of the primary Redis node.
redis['master_ip'] = '10.6.0.61'
- # Port of primary Redis server, uncomment to change to non default. Defaults
- # to `6379`.
- #redis['master_port'] = 6379
-
## Enable service discovery for Prometheus
consul['monitoring_service_discovery'] = true
@@ -1211,15 +1098,6 @@ You can specify multiple roles, like sentinel and Redis, as:
1. Go through the steps again for all the other replica nodes, and
make sure to set up the IPs correctly.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles(['redis_sentinel_role', 'redis_master_role'])`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
-
-These values don't have to be changed again in `/etc/gitlab/gitlab.rb` after
-a failover, as the nodes will be managed by the [Sentinels](#configure-the-sentinel-queues-nodes), and even after a
-`gitlab-ctl reconfigure`, they will get their configuration restored by
-the same Sentinels.
-
Advanced [configuration options](https://docs.gitlab.com/omnibus/settings/redis.html)
are supported and can be added if needed.
@@ -1229,138 +1107,16 @@ are supported and can be added if needed.
-#### Configure the Sentinel Queues nodes
-
-Now that the Redis servers are all set up, let's configure the Sentinel
-servers. The following IPs will be used as an example:
-
-- `10.6.0.81`: Sentinel - Queues 1
-- `10.6.0.82`: Sentinel - Queues 2
-- `10.6.0.83`: Sentinel - Queues 3
-
-NOTE:
-If you're using an external Redis Sentinel instance, be sure to exclude the
-`requirepass` parameter from the Sentinel configuration. This parameter causes
-clients to report `NOAUTH Authentication required.`.
-[Redis Sentinel 3.2.x doesn't support password authentication](https://github.com/antirez/redis/issues/3279).
-
-To configure the Sentinel Queues server:
-
-1. SSH in to the server that will host Sentinel.
-1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
- package of your choice. Be sure to both follow _only_ installation steps 1 and 2
- on the page, and to select the correct Omnibus GitLab package, with the same version
- and type (Community or Enterprise editions) as your current install.
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
-
- ```ruby
- roles(['redis_sentinel_role', 'consul_role'])
-
- ## Must be the same in every sentinel node
- redis['master_name'] = 'gitlab-redis-persistent'
-
- ## The same password for Redis authentication you set up for the primary node.
- redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
-
- ## The IP of the primary Redis node.
- redis['master_ip'] = '10.6.0.61'
-
- ## Define a port so Redis can listen for TCP requests which will allow other
- ## machines to connect to it.
- redis['port'] = 6379
-
- ## Port of primary Redis server, uncomment to change to non default. Defaults
- ## to `6379`.
- #redis['master_port'] = 6379
-
- ## Configure Sentinel's IP
- sentinel['bind'] = '10.6.0.81'
-
- ## Port that Sentinel listens on, uncomment to change to non default. Defaults
- ## to `26379`.
- #sentinel['port'] = 26379
-
- ## Quorum must reflect the amount of voting sentinels it take to start a failover.
- ## Value must NOT be greater then the amount of sentinels.
- ##
- ## The quorum can be used to tune Sentinel in two ways:
- ## 1. If a the quorum is set to a value smaller than the majority of Sentinels
- ## we deploy, we are basically making Sentinel more sensible to primary failures,
- ## triggering a failover as soon as even just a minority of Sentinels is no longer
- ## able to talk with the primary.
- ## 1. If a quorum is set to a value greater than the majority of Sentinels, we are
- ## making Sentinel able to failover only when there are a very large number (larger
- ## than majority) of well connected Sentinels which agree about the primary being down.s
- sentinel['quorum'] = 2
-
- ## Consider unresponsive server down after x amount of ms.
- #sentinel['down_after_milliseconds'] = 10000
-
- ## Specifies the failover timeout in milliseconds. It is used in many ways:
- ##
- ## - The time needed to re-start a failover after a previous failover was
- ## already tried against the same primary by a given Sentinel, is two
- ## times the failover timeout.
- ##
- ## - The time needed for a replica replicating to a wrong primary according
- ## to a Sentinel current configuration, to be forced to replicate
- ## with the right primary, is exactly the failover timeout (counting since
- ## the moment a Sentinel detected the misconfiguration).
- ##
- ## - The time needed to cancel a failover that is already in progress but
- ## did not produced any configuration change (REPLICAOF NO ONE yet not
- ## acknowledged by the promoted replica).
- ##
- ## - The maximum time a failover in progress waits for all the replica to be
- ## reconfigured as replicas of the new primary. However even after this time
- ## the replicas will be reconfigured by the Sentinels anyway, but not with
- ## the exact parallel-syncs progression as specified.
- #sentinel['failover_timeout'] = 60000
-
- ## Enable service discovery for Prometheus
- consul['monitoring_service_discovery'] = true
-
- ## The IPs of the Consul server nodes
- ## You can also use FQDNs and intermix them with IPs
- consul['configuration'] = {
- retry_join: %w(10.6.0.11 10.6.0.12 10.6.0.13),
- }
-
- # Set the network addresses that the exporters will listen on
- node_exporter['listen_address'] = '0.0.0.0:9100'
- redis_exporter['listen_address'] = '0.0.0.0:9121'
-
- # Prevent database migrations from running on upgrade automatically
- gitlab_rails['auto_migrate'] = false
- ```
-
-1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
- the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
-
-1. To ensure database migrations are only run during reconfigure and not automatically on upgrade, run:
-
- ```shell
- sudo touch /etc/gitlab/skip-auto-reconfigure
- ```
-
- Only the primary GitLab application server should handle migrations.
-
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-1. Go through the steps again for all the other Sentinel nodes, and
- make sure you set up the correct IPs.
-
-
-
## Configure Gitaly Cluster
-[Gitaly Cluster](../gitaly/praefect.md) is a GitLab provided and recommended fault tolerant solution for storing Git repositories.
+[Gitaly Cluster](../gitaly/praefect.md) is a GitLab-provided and recommended
+fault tolerant solution for storing Git repositories.
In this configuration, every Git repository is stored on every Gitaly node in the cluster, with one being designated the primary, and failover occurs automatically if the primary node goes down.
+NOTE:
+Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster).
+For implementations with Gitaly Sharded, the same Gitaly specs should be used. Follow the [separate Gitaly documentation](../gitaly/configure_gitaly.md) instead of this section.
+
The recommended cluster setup includes the following components:
- 3 Gitaly nodes: Replicated storage of Git repositories.
@@ -1527,7 +1283,7 @@ the details of each Gitaly node that makes up the cluster. Each storage is also
and this name is used in several areas of the configuration. In this guide, the name of the storage will be
`default`. Also, this guide is geared towards new installs, if upgrading an existing environment
to use Gitaly Cluster, you may need to use a different name.
-Refer to the [Praefect documentation](../gitaly/praefect.md#praefect) for more info.
+Refer to the [Praefect documentation](../gitaly/praefect.md#praefect) for more information.
The following IPs will be used as an example:
@@ -1854,7 +1610,7 @@ To configure the Sidekiq nodes, on each one:
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to follow _only_ installation steps 1 and 2
on the page.
-1. Open `/etc/gitlab/gitlab.rb` with your editor:
+1. Create or edit `/etc/gitlab/gitlab.rb` and use the following configuration:
```ruby
# Avoid running unnecessary services on the Sidekiq server
@@ -1869,36 +1625,40 @@ To configure the Sidekiq nodes, on each one:
gitlab_exporter['enable'] = false
nginx['enable'] = false
+ # External URL
+ ## This should match the URL of the external load balancer
+ external_url 'https://gitlab.example.com'
+
# Redis
## Redis connection details
## First cluster that will host the cache
gitlab_rails['redis_cache_instance'] = 'redis://:@gitlab-redis-cache'
gitlab_rails['redis_cache_sentinels'] = [
- {host: '10.6.0.71', port: 26379},
- {host: '10.6.0.72', port: 26379},
- {host: '10.6.0.73', port: 26379},
+ {host: '10.6.0.51', port: 26379},
+ {host: '10.6.0.52', port: 26379},
+ {host: '10.6.0.53', port: 26379},
]
- ## Second cluster that will host the queues, shared state, and actioncable
+ ## Second cluster that will host the persistent queues, shared state, and actioncable
gitlab_rails['redis_queues_instance'] = 'redis://:@gitlab-redis-persistent'
gitlab_rails['redis_shared_state_instance'] = 'redis://:@gitlab-redis-persistent'
gitlab_rails['redis_actioncable_instance'] = 'redis://:@gitlab-redis-persistent'
gitlab_rails['redis_queues_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
gitlab_rails['redis_shared_state_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
gitlab_rails['redis_actioncable_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
# Gitaly Cluster
@@ -2052,9 +1812,9 @@ On each node perform the following:
gitlab_rails['redis_cache_instance'] = 'redis://:@gitlab-redis-cache'
gitlab_rails['redis_cache_sentinels'] = [
- {host: '10.6.0.71', port: 26379},
- {host: '10.6.0.72', port: 26379},
- {host: '10.6.0.73', port: 26379},
+ {host: '10.6.0.51', port: 26379},
+ {host: '10.6.0.52', port: 26379},
+ {host: '10.6.0.53', port: 26379},
]
## Second cluster that will host the queues, shared state, and actionable
@@ -2063,19 +1823,19 @@ On each node perform the following:
gitlab_rails['redis_actioncable_instance'] = 'redis://:@gitlab-redis-persistent'
gitlab_rails['redis_queues_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
gitlab_rails['redis_shared_state_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
gitlab_rails['redis_actioncable_sentinels'] = [
- {host: '10.6.0.81', port: 26379},
- {host: '10.6.0.82', port: 26379},
- {host: '10.6.0.83', port: 26379},
+ {host: '10.6.0.61', port: 26379},
+ {host: '10.6.0.62', port: 26379},
+ {host: '10.6.0.63', port: 26379},
]
# Set the network addresses that the exporters used for monitoring will listen on
@@ -2147,7 +1907,7 @@ On each node perform the following:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-1. If you're [using NFS](#configure-nfs-optional):
+1. If you're [using NFS](#configure-nfs):
1. If necessary, install the NFS client utility packages using the following
commands:
@@ -2289,9 +2049,9 @@ To configure the Monitoring node:
## Configure the object storage
GitLab supports using an object storage service for holding numerous types of data.
-It's recommended over [NFS](#configure-nfs-optional) and in general it's better
-in larger setups as object storage is typically much more performant, reliable,
-and scalable.
+Object storage is also recommended over [NFS](#configure-nfs) and in general
+it's better in larger setups as object storage is typically much more performant,
+reliable, and scalable.
GitLab has been tested on a number of object storage providers:
@@ -2313,6 +2073,9 @@ There are two ways of specifying object storage configuration in GitLab:
Starting with GitLab 13.2, consolidated object storage configuration is available. It simplifies your GitLab configuration since the connection details are shared across object types. Refer to [Consolidated object storage configuration](../object_storage.md#consolidated-object-storage-configuration) guide for instructions on how to set it up.
+GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared via NFS on any GitLab Rails and Sidekiq nodes.
+In GitLab 13.6 and later, it's recommended to switch to [Incremental logging](../job_logs.md#incremental-logging-architecture), which uses Redis instead of disk space for temporary caching of job logs.
+
For configuring object storage in GitLab 13.1 and earlier, or for storage types not
supported by consolidated configuration form, refer to the following guides based
on what features you intend to use:
@@ -2359,7 +2122,7 @@ cluster alongside your instance, read how to
-## Configure NFS (optional)
+## Configure NFS
[Object storage](#configure-the-object-storage), along with [Gitaly](#configure-gitaly)
are recommended over NFS wherever possible for improved performance. If you intend
@@ -2373,7 +2136,7 @@ unavailable from GitLab 15.0. No further enhancements are planned for this featu
Read:
-- The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice).
+- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
@@ -2427,12 +2190,10 @@ services where applicable):
| PostgreSQL1 | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` |
| PgBouncer1 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Internal load balancing node3 | 1 | 4 vCPU, 3.6GB memory | `n1-highcpu-4` |
-| Redis - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
-| Redis - Queues / Shared State2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
-| Redis Sentinel - Cache2 | 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` |
-| Redis Sentinel - Queues / Shared State2 | 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` |
-| Gitaly | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` |
-| Praefect | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` |
+| Redis/Sentinel - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
+| Redis/Sentinel - Persistent2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
+| Gitaly5 | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` |
+| Praefect5 | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` |
| Praefect PostgreSQL1 | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Object storage4 | n/a | n/a | n/a |
@@ -2442,6 +2203,7 @@ services where applicable):
2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster). If Gitaly Sharded is desired, the same specs listed above for `Gitaly` should be used.
NOTE:
@@ -2486,11 +2248,6 @@ card "Database" as database {
card "redis" as redis {
collections "**Redis Persistent** x3" as redis_persistent #FF6347
collections "**Redis Cache** x3" as redis_cache #FF6347
- collections "**Redis Persistent Sentinel** x3" as redis_persistent_sentinel #FF6347
- collections "**Redis Cache Sentinel** x3"as redis_cache_sentinel #FF6347
-
- redis_persistent <.[#FF6347]- redis_persistent_sentinel
- redis_cache <.[#FF6347]- redis_cache_sentinel
}
cloud "**Object Storage**" as object_storage #white
diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md
index 99dd29c3a8..23b15b563f 100644
--- a/doc/administration/reference_architectures/2k_users.md
+++ b/doc/administration/reference_architectures/2k_users.md
@@ -13,6 +13,7 @@ For a full list of reference architectures, see
> - **Supported users (approximate):** 2,000
> - **High Availability:** No. For a highly-available environment, you can
> follow a modified [3K reference architecture](3k_users.md#supported-modifications-for-lower-user-counts-ha).
+> - **Cloud Native Hybrid:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
> - **Test requests per second (RPS) rates:** API: 40 RPS, Web: 4 RPS, Git (Pull): 4 RPS, Git (Push): 1 RPS
> - **[Latest 2k weekly performance testing results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/2k)**
@@ -302,8 +303,8 @@ further configuration steps.
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
1. Note the PostgreSQL node's IP address or hostname, port, and
- plain text password. These will be necessary when configuring the [GitLab
- application server](#configure-gitlab-rails) later.
+ plain text password. These will be necessary when configuring the
+ [GitLab application server](#configure-gitlab-rails) later.
Advanced [configuration options](https://docs.gitlab.com/omnibus/settings/database.html)
are supported and can be added if needed.
@@ -385,8 +386,8 @@ Omnibus:
1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
1. Note the Redis node's IP address or hostname, port, and
- Redis password. These will be necessary when [configuring the GitLab
- application servers](#configure-gitlab-rails) later.
+ Redis password. These will be necessary when
+ [configuring the GitLab application servers](#configure-gitlab-rails) later.
Advanced [configuration options](https://docs.gitlab.com/omnibus/settings/redis.html)
are supported and can be added if needed.
@@ -903,6 +904,9 @@ There are two ways of specifying object storage configuration in GitLab:
Starting with GitLab 13.2, consolidated object storage configuration is available. It simplifies your GitLab configuration since the connection details are shared across object types. Refer to [Consolidated object storage configuration](../object_storage.md#consolidated-object-storage-configuration) guide for instructions on how to set it up.
+GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared via NFS on any GitLab Rails and Sidekiq nodes.
+In GitLab 13.6 and later, it's recommended to switch to [Incremental logging](../job_logs.md#incremental-logging-architecture), which uses Redis instead of disk space for temporary caching of job logs.
+
For configuring object storage in GitLab 13.1 and earlier, or for storage types not
supported by consolidated configuration form, refer to the following guides based
on what features you intend to use:
@@ -964,7 +968,7 @@ unavailable from GitLab 15.0. No further enhancements are planned for this featu
Read:
-- The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice).
+- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index da36968f05..575dd22b72 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -22,6 +22,7 @@ For a full list of reference architectures, see
> - **Supported users (approximate):** 3,000
> - **High Availability:** Yes, although [Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution
+> - **Cloud Native Hybrid:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
> - **Test requests per second (RPS) rates:** API: 60 RPS, Web: 6 RPS, Git (Pull): 6 RPS, Git (Push): 1 RPS
> - **[Latest 3k weekly performance testing results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/3k)**
@@ -33,8 +34,8 @@ For a full list of reference architectures, see
| PostgreSQL1 | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
| PgBouncer1 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Internal load balancing node3 | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Gitaly | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Praefect | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Gitaly5 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Praefect5 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Praefect PostgreSQL1 | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Sidekiq | 4 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
| GitLab Rails | 3 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
@@ -48,6 +49,7 @@ For a full list of reference architectures, see
2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster). If Gitaly Sharded is desired, the same specs listed above for `Gitaly` should be used.
NOTE:
@@ -1065,6 +1067,10 @@ The following IPs will be used as an example:
[Gitaly Cluster](../gitaly/praefect.md) is a GitLab provided and recommended fault tolerant solution for storing Git repositories.
In this configuration, every Git repository is stored on every Gitaly node in the cluster, with one being designated the primary, and failover occurs automatically if the primary node goes down.
+NOTE:
+Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster).
+For implementations with Gitaly Sharded, the same Gitaly specs should be used. Follow the [separate Gitaly documentation](../gitaly/configure_gitaly.md) instead of this section.
+
The recommended cluster setup includes the following components:
- 3 Gitaly nodes: Replicated storage of Git repositories.
@@ -1230,7 +1236,7 @@ the details of each Gitaly node that makes up the cluster. Each storage is also
and this name is used in several areas of the configuration. In this guide, the name of the storage will be
`default`. Also, this guide is geared towards new installs, if upgrading an existing environment
to use Gitaly Cluster, you may need to use a different name.
-Refer to the [Praefect documentation](../gitaly/praefect.md#praefect) for more info.
+Refer to the [Praefect documentation](../gitaly/praefect.md#praefect) for more information.
The following IPs will be used as an example:
@@ -1559,7 +1565,7 @@ To configure the Sidekiq nodes, one each one:
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to follow _only_ installation steps 1 and 2
on the page.
-1. Open `/etc/gitlab/gitlab.rb` with your editor:
+1. Create or edit `/etc/gitlab/gitlab.rb` and use the following configuration:
```ruby
# Avoid running unnecessary services on the Sidekiq server
@@ -1574,6 +1580,10 @@ To configure the Sidekiq nodes, one each one:
gitlab_exporter['enable'] = false
nginx['enable'] = false
+ # External URL
+ ## This should match the URL of the external load balancer
+ external_url 'https://gitlab.example.com'
+
# Redis
redis['master_name'] = 'gitlab-redis'
@@ -2011,6 +2021,9 @@ There are two ways of specifying object storage configuration in GitLab:
Starting with GitLab 13.2, consolidated object storage configuration is available. It simplifies your GitLab configuration since the connection details are shared across object types. Refer to [Consolidated object storage configuration](../object_storage.md#consolidated-object-storage-configuration) guide for instructions on how to set it up.
+GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared via NFS on any GitLab Rails and Sidekiq nodes.
+In GitLab 13.6 and later, it's recommended to switch to [Incremental logging](../job_logs.md#incremental-logging-architecture), which uses Redis instead of disk space for temporary caching of job logs.
+
For configuring object storage in GitLab 13.1 and earlier, or for storage types not
supported by consolidated configuration form, refer to the following guides based
on what features you intend to use:
@@ -2071,7 +2084,7 @@ unavailable from GitLab 15.0. No further enhancements are planned for this featu
Read:
-- The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice).
+- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
## Supported modifications for lower user counts (HA)
@@ -2150,8 +2163,8 @@ services where applicable):
| PostgreSQL1 | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` |
| PgBouncer1 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Internal load balancing node3 | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
-| Gitaly | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
-| Praefect | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
+| Gitaly5 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
+| Praefect5 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Praefect PostgreSQL1 | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Object storage4 | n/a | n/a | n/a |
@@ -2161,6 +2174,7 @@ services where applicable):
2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster). If Gitaly Sharded is desired, the same specs listed above for `Gitaly` should be used.
NOTE:
diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md
index b071b48cbd..be44f464e7 100644
--- a/doc/administration/reference_architectures/50k_users.md
+++ b/doc/administration/reference_architectures/50k_users.md
@@ -12,6 +12,7 @@ full list of reference architectures, see
> - **Supported users (approximate):** 50,000
> - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA)
+> - **Cloud Native Hybrid:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
> - **Test requests per second (RPS) rates:** API: 1000 RPS, Web: 100 RPS, Git (Pull): 100 RPS, Git (Push): 20 RPS
> - **[Latest 50k weekly performance testing results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/50k)**
@@ -22,18 +23,16 @@ full list of reference architectures, see
| PostgreSQL1 | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` | `D32s v3` |
| PgBouncer1 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Internal load balancing node3 | 1 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
-| Redis - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Redis - Queues / Shared State2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Redis Sentinel - Cache2 | 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | `c5.large` | `A1 v2` |
-| Redis Sentinel - Queues / Shared State2| 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | `c5.large` | `A1 v2` |
-| Gitaly | 3 | 64 vCPU, 240 GB memory | `n1-standard-64` | `m5.16xlarge` | `D64s v3` |
-| Praefect | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Redis/Sentinel - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Redis/Sentinel - Persistent2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Gitaly5 | 3 | 64 vCPU, 240 GB memory | `n1-standard-64` | `m5.16xlarge` | `D64s v3` |
+| Praefect5 | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
| Praefect PostgreSQL1 | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
| GitLab Rails | 12 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` | `F32s v2` |
| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
| Object storage4 | n/a | n/a | n/a | n/a | n/a |
-| NFS server (optional, not recommended) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
@@ -41,6 +40,7 @@ full list of reference architectures, see
2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster). If Gitaly Sharded is desired, the same specs listed above for `Gitaly` should be used.
NOTE:
@@ -82,11 +82,6 @@ card "Database" as database {
card "redis" as redis {
collections "**Redis Persistent** x3" as redis_persistent #FF6347
collections "**Redis Cache** x3" as redis_cache #FF6347
- collections "**Redis Persistent Sentinel** x3" as redis_persistent_sentinel #FF6347
- collections "**Redis Cache Sentinel** x3"as redis_cache_sentinel #FF6347
-
- redis_persistent <.[#FF6347]- redis_persistent_sentinel
- redis_cache <.[#FF6347]- redis_cache_sentinel
}
cloud "**Object Storage**" as object_storage #white
@@ -137,8 +132,7 @@ our [Sysbench](https://github.com/akopytov/sysbench)-based
Due to better performance and availability, for data objects (such as LFS,
uploads, or artifacts), using an [object storage service](#configure-the-object-storage)
-is recommended instead of using NFS. Using an object storage service also
-doesn't require you to provision and maintain a node.
+is recommended.
It's also worth noting that at this time [Praefect requires its own database server](../gitaly/praefect.md#postgresql) and
that to achieve full High Availability a third-party PostgreSQL database solution will be required.
@@ -169,10 +163,8 @@ To set up GitLab and its components to accommodate up to 50,000 users:
used for shared data objects.
1. [Configure Advanced Search](#configure-advanced-search) (optional) for faster,
more advanced code search across your entire GitLab instance.
-1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
- to have shared disk storage service as an alternative to Gitaly or object
- storage. You can skip this step if you're not using GitLab Pages (which
- requires NFS).
+1. [Configure NFS](#configure-nfs)
+ to have shared disk storage service for certain GitLab operations (non Gitaly or Object Storage).
The servers start on the same 10.6.0.0/24 private network range, and can
connect to each other freely on these addresses.
@@ -193,15 +185,9 @@ The following list includes descriptions of each server and its assigned IP:
- `10.6.0.51`: Redis - Cache Primary
- `10.6.0.52`: Redis - Cache Replica 1
- `10.6.0.53`: Redis - Cache Replica 2
-- `10.6.0.71`: Sentinel - Cache 1
-- `10.6.0.72`: Sentinel - Cache 2
-- `10.6.0.73`: Sentinel - Cache 3
-- `10.6.0.61`: Redis - Queues Primary
-- `10.6.0.62`: Redis - Queues Replica 1
-- `10.6.0.63`: Redis - Queues Replica 2
-- `10.6.0.81`: Sentinel - Queues 1
-- `10.6.0.82`: Sentinel - Queues 2
-- `10.6.0.83`: Sentinel - Queues 3
+- `10.6.0.61`: Redis - Persistent Primary
+- `10.6.0.62`: Redis - Persistent Replica 1
+- `10.6.0.63`: Redis - Persistent Replica 2
- `10.6.0.91`: Gitaly 1
- `10.6.0.92`: Gitaly 2
- `10.6.0.93`: Gitaly 3
@@ -802,15 +788,9 @@ to be used with GitLab. The following IPs will be used as an example:
- `10.6.0.51`: Redis - Cache Primary
- `10.6.0.52`: Redis - Cache Replica 1
- `10.6.0.53`: Redis - Cache Replica 2
-- `10.6.0.71`: Sentinel - Cache 1
-- `10.6.0.72`: Sentinel - Cache 2
-- `10.6.0.73`: Sentinel - Cache 3
-- `10.6.0.61`: Redis - Queues Primary
-- `10.6.0.62`: Redis - Queues Replica 1
-- `10.6.0.63`: Redis - Queues Replica 2
-- `10.6.0.81`: Sentinel - Queues 1
-- `10.6.0.82`: Sentinel - Queues 2
-- `10.6.0.83`: Sentinel - Queues 3
+- `10.6.0.61`: Redis - Persistent Primary
+- `10.6.0.62`: Redis - Persistent Replica 1
+- `10.6.0.63`: Redis - Persistent Replica 2
### Providing your own Redis instance
@@ -822,7 +802,7 @@ optional count argument to SPOP, which is required for [Merge Trains](../../ci/p
Note the Redis node's IP address or hostname, port, and password (if required).
These will be necessary later when configuring the [GitLab application servers](#configure-gitlab-rails).
-### Configure the Redis and Sentinel Cache cluster
+### Configure the Redis Cache cluster
This is the section where we install and set up the new Redis Cache instances.
@@ -840,8 +820,12 @@ a node and change its status from primary to replica (and vice versa).
1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
```ruby
- # Specify server role as 'redis_master_role' and enable Consul agent
- roles(['redis_master_role', 'consul_role'])
+ # Specify server role as 'redis_master_role' with Sentinel and enable Consul agent
+ roles(['redis_sentinel_role', 'redis_master_role', 'consul_role'])
+
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = '0.0.0.0'
+ sentinel['quorum'] = 2
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
@@ -853,8 +837,19 @@ a node and change its status from primary to replica (and vice versa).
# machines to connect to it.
redis['port'] = 6379
- # Set up password authentication for Redis (use the same password in all nodes).
+ ## Port of primary Redis server for Sentinel, uncomment to change to non default. Defaults
+ ## to `6379`.
+ #redis['master_port'] = 6379
+
+ # Set up password authentication for Redis and replicas (use the same password in all nodes).
redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+
+ ## Must be the same in every Redis node
+ redis['master_name'] = 'gitlab-redis-cache'
+
+ ## The IP of this primary Redis node.
+ redis['master_ip'] = '10.6.0.51'
# Set the Redis Cache instance as an LRU
# 90% of available RAM in MB
@@ -888,10 +883,6 @@ a node and change its status from primary to replica (and vice versa).
1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles(['redis_sentinel_role', 'redis_master_role'])`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
-
#### Configure the replica Redis Cache nodes
1. SSH in to the **replica** Redis server.
@@ -899,11 +890,15 @@ You can specify multiple roles, like sentinel and Redis, as:
package of your choice. Be sure to both follow _only_ installation steps 1 and 2
on the page, and to select the correct Omnibus GitLab package, with the same version
and type (Community or Enterprise editions) as your current install.
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
+1. Edit `/etc/gitlab/gitlab.rb` and add the same contents as the priimary node in the previous section by replacing `redis_master_node` with `redis_replica_node`:
```ruby
- # Specify server role as 'redis_replica_role' and enable Consul agent
- roles(['redis_replica_role', 'consul_role'])
+ # Specify server role as 'redis_replica_role' with Sentinel and enable Consul agent
+ roles(['roles_sentinel_role', 'redis_replica_role', 'consul_role'])
+
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = '0.0.0.0'
+ sentinel['quorum'] = 2
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
@@ -915,16 +910,20 @@ You can specify multiple roles, like sentinel and Redis, as:
# machines to connect to it.
redis['port'] = 6379
- # The same password for Redis authentication you set up for the primary node.
- redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
-
- # The IP of the primary Redis node.
- redis['master_ip'] = '10.6.0.51'
-
- # Port of primary Redis server, uncomment to change to non default. Defaults
- # to `6379`.
+ ## Port of primary Redis server for Sentinel, uncomment to change to non default. Defaults
+ ## to `6379`.
#redis['master_port'] = 6379
+ # Set up password authentication for Redis and replicas (use the same password in all nodes).
+ redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+
+ ## Must be the same in every Redis node
+ redis['master_name'] = 'gitlab-redis-cache'
+
+ ## The IP of the primary Redis node.
+ redis['master_ip'] = '10.6.0.51'
+
# Set the Redis Cache instance as an LRU
# 90% of available RAM in MB
redis['maxmemory'] = '13500mb'
@@ -952,25 +951,18 @@ You can specify multiple roles, like sentinel and Redis, as:
gitlab_rails['auto_migrate'] = false
```
-1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
- the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
+ 1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus
+ node you configured and add or replace the file of the same name on this
+ server. If this is the first Omnibus node you are configuring then you
+ can skip this step.
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
+ 1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes
+ to take effect.
-1. Go through the steps again for all the other replica nodes, and
+ 1. Go through the steps again for all the other replica nodes, and
make sure to set up the IPs correctly.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles(['redis_sentinel_role', 'redis_master_role'])`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
-
-These values don't have to be changed again in `/etc/gitlab/gitlab.rb` after
-a failover, as the nodes will be managed by the [Sentinels](#configure-the-sentinel-cache-nodes), and even after a
-`gitlab-ctl reconfigure`, they will get their configuration restored by
-the same Sentinels.
-
-Advanced [configuration options](https://docs.gitlab.com/omnibus/settings/redis.html)
-are supported and can be added if needed.
+ Advanced [configuration options](https://docs.gitlab.com/omnibus/settings/redis.html) are supported and can be added if needed.
-#### Configure the Sentinel Cache nodes
-
-Now that the Redis servers are all set up, let's configure the Sentinel
-servers. The following IPs will be used as an example:
-
-- `10.6.0.71`: Sentinel - Cache 1
-- `10.6.0.72`: Sentinel - Cache 2
-- `10.6.0.73`: Sentinel - Cache 3
-
-NOTE:
-If you're using an external Redis Sentinel instance, be sure to exclude the
-`requirepass` parameter from the Sentinel configuration. This parameter causes
-clients to report `NOAUTH Authentication required.`.
-[Redis Sentinel 3.2.x doesn't support password authentication](https://github.com/antirez/redis/issues/3279).
-
-To configure the Sentinel Cache server:
-
-1. SSH in to the server that will host Consul/Sentinel.
-1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
- package of your choice. Be sure to both follow _only_ installation steps 1 and 2
- on the page, and to select the correct Omnibus GitLab package, with the same version
- and type (Community or Enterprise editions) as your current install.
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
-
- ```ruby
- roles(['redis_sentinel_role', 'consul_role'])
-
- ## Must be the same in every sentinel node
- redis['master_name'] = 'gitlab-redis-cache'
-
- ## The same password for Redis authentication you set up for the primary node.
- redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
-
- ## The IP of the primary Redis node.
- redis['master_ip'] = '10.6.0.51'
-
- ## Define a port so Redis can listen for TCP requests which will allow other
- ## machines to connect to it.
- redis['port'] = 6379
-
- ## Port of primary Redis server, uncomment to change to non default. Defaults
- ## to `6379`.
- #redis['master_port'] = 6379
-
- ## Configure Sentinel's IP
- sentinel['bind'] = '10.6.0.71'
-
- ## Port that Sentinel listens on, uncomment to change to non default. Defaults
- ## to `26379`.
- #sentinel['port'] = 26379
-
- ## Quorum must reflect the amount of voting sentinels it take to start a failover.
- ## Value must NOT be greater then the amount of sentinels.
- ##
- ## The quorum can be used to tune Sentinel in two ways:
- ## 1. If a the quorum is set to a value smaller than the majority of Sentinels
- ## we deploy, we are basically making Sentinel more sensible to primary failures,
- ## triggering a failover as soon as even just a minority of Sentinels is no longer
- ## able to talk with the primary.
- ## 1. If a quorum is set to a value greater than the majority of Sentinels, we are
- ## making Sentinel able to failover only when there are a very large number (larger
- ## than majority) of well connected Sentinels which agree about the primary being down.s
- sentinel['quorum'] = 2
-
- ## Consider unresponsive server down after x amount of ms.
- #sentinel['down_after_milliseconds'] = 10000
-
- ## Specifies the failover timeout in milliseconds. It is used in many ways:
- ##
- ## - The time needed to re-start a failover after a previous failover was
- ## already tried against the same primary by a given Sentinel, is two
- ## times the failover timeout.
- ##
- ## - The time needed for a replica replicating to a wrong primary according
- ## to a Sentinel current configuration, to be forced to replicate
- ## with the right primary, is exactly the failover timeout (counting since
- ## the moment a Sentinel detected the misconfiguration).
- ##
- ## - The time needed to cancel a failover that is already in progress but
- ## did not produced any configuration change (REPLICAOF NO ONE yet not
- ## acknowledged by the promoted replica).
- ##
- ## - The maximum time a failover in progress waits for all the replica to be
- ## reconfigured as replicas of the new primary. However even after this time
- ## the replicas will be reconfigured by the Sentinels anyway, but not with
- ## the exact parallel-syncs progression as specified.
- #sentinel['failover_timeout'] = 60000
-
- ## Enable service discovery for Prometheus
- consul['monitoring_service_discovery'] = true
-
- ## The IPs of the Consul server nodes
- ## You can also use FQDNs and intermix them with IPs
- consul['configuration'] = {
- retry_join: %w(10.6.0.11 10.6.0.12 10.6.0.13),
- }
-
- # Set the network addresses that the exporters will listen on
- node_exporter['listen_address'] = '0.0.0.0:9100'
- redis_exporter['listen_address'] = '0.0.0.0:9121'
-
- # Prevent database migrations from running on upgrade automatically
- gitlab_rails['auto_migrate'] = false
- ```
-
-1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
- the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
-
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-1. Go through the steps again for all the other Consul/Sentinel nodes, and
- make sure you set up the correct IPs.
-
-
-
-### Configure the Redis and Sentinel Queues cluster
+### Configure the Redis Persistent cluster
This is the section where we install and set up the new Redis Queues instances.
@@ -1105,7 +978,7 @@ Both the primary and replica Redis nodes need the same password defined in
`redis['password']`. At any time during a failover, the Sentinels can reconfigure
a node and change its status from primary to replica (and vice versa).
-#### Configure the primary Redis Queues node
+#### Configure the primary Redis Persistent node
1. SSH in to the **Primary** Redis server.
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
@@ -1115,8 +988,12 @@ a node and change its status from primary to replica (and vice versa).
1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
```ruby
- # Specify server role as 'redis_master_role' and enable Consul agent
- roles(['redis_master_role', 'consul_role'])
+ # Specify server roles as 'redis_master_role' with Sentinel and enable the Consul agent
+ roles ['redis_sentinel_role', 'redis_master_role', 'consul_role']
+
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = '0.0.0.0'
+ sentinel['quorum'] = 2
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
@@ -1128,8 +1005,19 @@ a node and change its status from primary to replica (and vice versa).
# machines to connect to it.
redis['port'] = 6379
- # Set up password authentication for Redis (use the same password in all nodes).
- redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+ ## Port of primary Redis server for Sentinel, uncomment to change to non default. Defaults
+ ## to `6379`.
+ #redis['master_port'] = 6379
+
+ # Set up password authentication for Redis and replicas (use the same password in all nodes).
+ redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_FIRST_CLUSTER'
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+
+ ## Must be the same in every Redis node
+ redis['master_name'] = 'gitlab-redis-persistent'
+
+ ## The IP of this primary Redis node.
+ redis['master_ip'] = '10.6.0.61'
## Enable service discovery for Prometheus
consul['monitoring_service_discovery'] = true
@@ -1153,13 +1041,9 @@ a node and change its status from primary to replica (and vice versa).
1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles ['redis_sentinel_role', 'redis_master_role']`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
+#### Configure the replica Redis Persistent nodes
-#### Configure the replica Redis Queues nodes
-
-1. SSH in to the **replica** Redis Queue server.
+1. SSH in to the **replica** Redis Persistent server.
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to both follow _only_ installation steps 1 and 2
on the page, and to select the correct Omnibus GitLab package, with the same version
@@ -1167,8 +1051,12 @@ You can specify multiple roles, like sentinel and Redis, as:
1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
```ruby
- # Specify server role as 'redis_replica_role' and enable Consul agent
- roles(['redis_replica_role', 'consul_role'])
+ # Specify server roles as 'redis_replica_role' with Sentinel and enable Consul agent
+ roles ['redis_sentinel_role', 'redis_replica_role', 'consul_role']
+
+ # Set IP bind address and Quorum number for Redis Sentinel service
+ sentinel['bind'] = '0.0.0.0'
+ sentinel['quorum'] = 2
# IP address pointing to a local IP that the other machines can reach to.
# You can also set bind to '0.0.0.0' which listen in all interfaces.
@@ -1180,16 +1068,20 @@ You can specify multiple roles, like sentinel and Redis, as:
# machines to connect to it.
redis['port'] = 6379
+ ## Port of primary Redis server for Sentinel, uncomment to change to non default. Defaults
+ ## to `6379`.
+ #redis['master_port'] = 6379
+
# The same password for Redis authentication you set up for the primary node.
redis['password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+ redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
+
+ ## Must be the same in every Redis node
+ redis['master_name'] = 'gitlab-redis-persistent'
# The IP of the primary Redis node.
redis['master_ip'] = '10.6.0.61'
- # Port of primary Redis server, uncomment to change to non default. Defaults
- # to `6379`.
- #redis['master_port'] = 6379
-
## Enable service discovery for Prometheus
consul['monitoring_service_discovery'] = true
@@ -1215,144 +1107,7 @@ You can specify multiple roles, like sentinel and Redis, as:
1. Go through the steps again for all the other replica nodes, and
make sure to set up the IPs correctly.
-You can specify multiple roles, like sentinel and Redis, as:
-`roles(['redis_sentinel_role', 'redis_master_role'])`. Read more about
-[roles](https://docs.gitlab.com/omnibus/roles/).
-
-These values don't have to be changed again in `/etc/gitlab/gitlab.rb` after
-a failover, as the nodes will be managed by the [Sentinels](#configure-the-sentinel-queues-nodes), and even after a
-`gitlab-ctl reconfigure`, they will get their configuration restored by
-the same Sentinels.
-
-Advanced [configuration options](https://docs.gitlab.com/omnibus/settings/redis.html)
-are supported and can be added if needed.
-
-
-
-#### Configure the Sentinel Queues nodes
-
-Now that the Redis servers are all set up, let's configure the Sentinel
-servers. The following IPs will be used as an example:
-
-- `10.6.0.81`: Sentinel - Queues 1
-- `10.6.0.82`: Sentinel - Queues 2
-- `10.6.0.83`: Sentinel - Queues 3
-
-NOTE:
-If you're using an external Redis Sentinel instance, be sure to exclude the
-`requirepass` parameter from the Sentinel configuration. This parameter causes
-clients to report `NOAUTH Authentication required.`.
-[Redis Sentinel 3.2.x doesn't support password authentication](https://github.com/antirez/redis/issues/3279).
-
-To configure the Sentinel Queues server:
-
-1. SSH in to the server that will host Sentinel.
-1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
- package of your choice. Be sure to both follow _only_ installation steps 1 and 2
- on the page, and to select the correct Omnibus GitLab package, with the same version
- and type (Community or Enterprise editions) as your current install.
-1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
-
- ```ruby
- roles(['redis_sentinel_role', 'consul_role'])
-
- ## Must be the same in every sentinel node
- redis['master_name'] = 'gitlab-redis-persistent'
-
- ## The same password for Redis authentication you set up for the primary node.
- redis['master_password'] = 'REDIS_PRIMARY_PASSWORD_OF_SECOND_CLUSTER'
-
- ## The IP of the primary Redis node.
- redis['master_ip'] = '10.6.0.61'
-
- ## Define a port so Redis can listen for TCP requests which will allow other
- ## machines to connect to it.
- redis['port'] = 6379
-
- ## Port of primary Redis server, uncomment to change to non default. Defaults
- ## to `6379`.
- #redis['master_port'] = 6379
-
- ## Configure Sentinel's IP
- sentinel['bind'] = '10.6.0.81'
-
- ## Port that Sentinel listens on, uncomment to change to non default. Defaults
- ## to `26379`.
- #sentinel['port'] = 26379
-
- ## Quorum must reflect the amount of voting sentinels it take to start a failover.
- ## Value must NOT be greater then the amount of sentinels.
- ##
- ## The quorum can be used to tune Sentinel in two ways:
- ## 1. If a the quorum is set to a value smaller than the majority of Sentinels
- ## we deploy, we are basically making Sentinel more sensible to primary failures,
- ## triggering a failover as soon as even just a minority of Sentinels is no longer
- ## able to talk with the primary.
- ## 1. If a quorum is set to a value greater than the majority of Sentinels, we are
- ## making Sentinel able to failover only when there are a very large number (larger
- ## than majority) of well connected Sentinels which agree about the primary being down.s
- sentinel['quorum'] = 2
-
- ## Consider unresponsive server down after x amount of ms.
- #sentinel['down_after_milliseconds'] = 10000
-
- ## Specifies the failover timeout in milliseconds. It is used in many ways:
- ##
- ## - The time needed to re-start a failover after a previous failover was
- ## already tried against the same primary by a given Sentinel, is two
- ## times the failover timeout.
- ##
- ## - The time needed for a replica replicating to a wrong primary according
- ## to a Sentinel current configuration, to be forced to replicate
- ## with the right primary, is exactly the failover timeout (counting since
- ## the moment a Sentinel detected the misconfiguration).
- ##
- ## - The time needed to cancel a failover that is already in progress but
- ## did not produced any configuration change (REPLICAOF NO ONE yet not
- ## acknowledged by the promoted replica).
- ##
- ## - The maximum time a failover in progress waits for all the replica to be
- ## reconfigured as replicas of the new primary. However even after this time
- ## the replicas will be reconfigured by the Sentinels anyway, but not with
- ## the exact parallel-syncs progression as specified.
- #sentinel['failover_timeout'] = 60000
-
- ## Enable service discovery for Prometheus
- consul['monitoring_service_discovery'] = true
-
- ## The IPs of the Consul server nodes
- ## You can also use FQDNs and intermix them with IPs
- consul['configuration'] = {
- retry_join: %w(10.6.0.11 10.6.0.12 10.6.0.13),
- }
-
- # Set the network addresses that the exporters will listen on
- node_exporter['listen_address'] = '0.0.0.0:9100'
- redis_exporter['listen_address'] = '0.0.0.0:9121'
-
- # Prevent database migrations from running on upgrade automatically
- gitlab_rails['auto_migrate'] = false
- ```
-
-1. To ensure database migrations are only run during reconfigure and not automatically on upgrade, run:
-
- ```shell
- sudo touch /etc/gitlab/skip-auto-reconfigure
- ```
-
- Only the primary GitLab application server should handle migrations.
-
-1. Copy the `/etc/gitlab/gitlab-secrets.json` file from the first Omnibus node you configured and add or replace
- the file of the same name on this server. If this is the first Omnibus node you are configuring then you can skip this step.
-
-1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-
-1. Go through the steps again for all the other Sentinel nodes, and
- make sure you set up the correct IPs.
+Advanced [configuration options](https://docs.gitlab.com/omnibus/settings/redis.html) are supported and can be added if needed.
-## Configure NFS (optional)
+## Configure NFS
[Object storage](#configure-the-object-storage), along with [Gitaly](#configure-gitaly)
are recommended over NFS wherever possible for improved performance. If you intend
@@ -2384,7 +2150,7 @@ unavailable from GitLab 15.0. No further enhancements are planned for this featu
Read:
-- The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice).
+- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
@@ -2438,12 +2204,10 @@ services where applicable):
| PostgreSQL1 | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` |
| PgBouncer1 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Internal load balancing node3 | 1 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` |
-| Redis - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
-| Redis - Queues / Shared State2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
-| Redis Sentinel - Cache2 | 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` |
-| Redis Sentinel - Queues / Shared State2 | 3 | 1 vCPU, 3.75 GB memory | `n1-standard-1` |
-| Gitaly | 3 | 64 vCPU, 240 GB memory | `n1-standard-64` |
-| Praefect | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` |
+| Redis/Sentinel - Cache2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
+| Redis/Sentinel - Persistent2 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
+| Gitaly5 | 3 | 64 vCPU, 240 GB memory | `n1-standard-64` |
+| Praefect5 | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` |
| Praefect PostgreSQL1 | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Object storage4 | n/a | n/a | n/a |
@@ -2453,6 +2217,7 @@ services where applicable):
2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster). If Gitaly Sharded is desired, the same specs listed above for `Gitaly` should be used.
NOTE:
@@ -2497,11 +2262,6 @@ card "Database" as database {
card "redis" as redis {
collections "**Redis Persistent** x3" as redis_persistent #FF6347
collections "**Redis Cache** x3" as redis_cache #FF6347
- collections "**Redis Persistent Sentinel** x3" as redis_persistent_sentinel #FF6347
- collections "**Redis Cache Sentinel** x3"as redis_cache_sentinel #FF6347
-
- redis_persistent <.[#FF6347]- redis_persistent_sentinel
- redis_cache <.[#FF6347]- redis_cache_sentinel
}
cloud "**Object Storage**" as object_storage #white
diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md
index 4dfe628039..a5526986be 100644
--- a/doc/administration/reference_architectures/5k_users.md
+++ b/doc/administration/reference_architectures/5k_users.md
@@ -19,6 +19,7 @@ costly-to-operate environment by using the
> - **Supported users (approximate):** 5,000
> - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA)
+> - **Cloud Native Hybrid:** [Yes](#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative)
> - **Test requests per second (RPS) rates:** API: 100 RPS, Web: 10 RPS, Git (Pull): 10 RPS, Git (Push): 2 RPS
> - **[Latest 5k weekly performance testing results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/5k)**
@@ -30,8 +31,8 @@ costly-to-operate environment by using the
| PostgreSQL1 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
| PgBouncer1 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Internal load balancing node3 | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Gitaly | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` | `D8s v3` |
-| Praefect | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Gitaly5 | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` | `D8s v3` |
+| Praefect5 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Praefect PostgreSQL1 | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
| Sidekiq | 4 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
| GitLab Rails | 3 | 16 vCPU, 14.4 GB memory | `n1-highcpu-16` | `c5.4xlarge` | `F16s v2`|
@@ -45,6 +46,7 @@ costly-to-operate environment by using the
2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster). If Gitaly Sharded is desired, the same specs listed above for `Gitaly` should be used.
NOTE:
@@ -1056,6 +1058,10 @@ The following IPs will be used as an example:
[Gitaly Cluster](../gitaly/praefect.md) is a GitLab provided and recommended fault tolerant solution for storing Git repositories.
In this configuration, every Git repository is stored on every Gitaly node in the cluster, with one being designated the primary, and failover occurs automatically if the primary node goes down.
+NOTE:
+Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster).
+For implementations with Gitaly Sharded, the same Gitaly specs should be used. Follow the [separate Gitaly documentation](../gitaly/configure_gitaly.md) instead of this section.
+
The recommended cluster setup includes the following components:
- 3 Gitaly nodes: Replicated storage of Git repositories.
@@ -1222,7 +1228,7 @@ the details of each Gitaly node that makes up the cluster. Each storage is also
and this name is used in several areas of the configuration. In this guide, the name of the storage will be
`default`. Also, this guide is geared towards new installs, if upgrading an existing environment
to use Gitaly Cluster, you may need to use a different name.
-Refer to the [Praefect documentation](../gitaly/praefect.md#praefect) for more info.
+Refer to the [Praefect documentation](../gitaly/praefect.md#praefect) for more information.
The following IPs will be used as an example:
@@ -1549,7 +1555,7 @@ To configure the Sidekiq nodes, one each one:
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
package of your choice. Be sure to follow _only_ installation steps 1 and 2
on the page.
-1. Open `/etc/gitlab/gitlab.rb` with your editor:
+1. Create or edit `/etc/gitlab/gitlab.rb` and use the following configuration:
```ruby
# Avoid running unnecessary services on the Sidekiq server
@@ -1564,6 +1570,10 @@ To configure the Sidekiq nodes, one each one:
gitlab_exporter['enable'] = false
nginx['enable'] = false
+ # External URL
+ ## This should match the URL of the external load balancer
+ external_url 'https://gitlab.example.com'
+
# Redis
## Must be the same in every sentinel node
redis['master_name'] = 'gitlab-redis'
@@ -2005,6 +2015,9 @@ There are two ways of specifying object storage configuration in GitLab:
Starting with GitLab 13.2, consolidated object storage configuration is available. It simplifies your GitLab configuration since the connection details are shared across object types. Refer to [Consolidated object storage configuration](../object_storage.md#consolidated-object-storage-configuration) guide for instructions on how to set it up.
+GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared via NFS on any GitLab Rails and Sidekiq nodes.
+In GitLab 13.6 and later, it's recommended to switch to [Incremental logging](../job_logs.md#incremental-logging-architecture), which uses Redis instead of disk space for temporary caching of job logs.
+
For configuring object storage in GitLab 13.1 and earlier, or for storage types not
supported by consolidated configuration form, refer to the following guides based
on what features you intend to use:
@@ -2065,7 +2078,7 @@ unavailable from GitLab 15.0. No further enhancements are planned for this featu
Read:
-- The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice).
+- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
@@ -2120,8 +2133,8 @@ services where applicable):
| PostgreSQL1 | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` |
| PgBouncer1 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Internal load balancing node3 | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
-| Gitaly | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` |
-| Praefect | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
+| Gitaly5 | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` |
+| Praefect5 | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Praefect PostgreSQL1 | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` |
| Object storage4 | n/a | n/a | n/a |
@@ -2131,6 +2144,7 @@ services where applicable):
2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Please [review the existing technical limitations and considerations prior to deploying Gitaly Cluster](../gitaly/index.md#guidance-regarding-gitaly-cluster). If Gitaly Sharded is desired, the same specs listed above for `Gitaly` should be used.
NOTE:
diff --git a/doc/administration/reply_by_email.md b/doc/administration/reply_by_email.md
index c249f48b76..055f40ead6 100644
--- a/doc/administration/reply_by_email.md
+++ b/doc/administration/reply_by_email.md
@@ -45,6 +45,8 @@ following headers, in this order:
1. `To` header
1. `References` header
+1. `Delivered-To` header
+1. `Envelope-To` header
If it finds a reply key, it leaves your reply as a comment on
the entity the notification was about (issue, merge request, commit...).
diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md
index e7edfb9f33..ed50d0e726 100644
--- a/doc/administration/repository_storage_paths.md
+++ b/doc/administration/repository_storage_paths.md
@@ -11,9 +11,10 @@ GitLab stores [repositories](../user/project/repository/index.md) on repository
storage is either:
- A `gitaly_address`, which points to a [Gitaly node](gitaly/index.md).
-- A `path`, which points directly to the directory where the repositories are stored. This method is
- deprecated and [scheduled to be removed](https://gitlab.com/gitlab-org/gitaly/-/issues/1690) in
- GitLab 14.0.
+- A `path`, which points directly to the directory where the repositories are stored. GitLab
+ directly accessing a directory containing repositories
+ [is deprecated](https://gitlab.com/gitlab-org/gitaly/-/issues/1690).
+ GitLab should be configured to access GitLab repositories though a `gitaly_address`.
GitLab allows you to define multiple repository storages to distribute the storage load between
several mount points. For example:
@@ -173,4 +174,4 @@ information.
## Move repositories
To move a repository to a different repository storage (for example, from `default` to `storage2`), use the
-same process as [migrating to Gitaly Cluster](gitaly/index.md#migrate-to-gitaly-cluster).
+same process as [migrating to Gitaly Cluster](gitaly/index.md#migrating-to-gitaly-cluster).
diff --git a/doc/administration/sidekiq.md b/doc/administration/sidekiq.md
index e753832f2c..4aee88ed9c 100644
--- a/doc/administration/sidekiq.md
+++ b/doc/administration/sidekiq.md
@@ -104,6 +104,16 @@ you want using steps 1 and 2 from the GitLab downloads page.
You must also copy the `registry.key` file to each Sidekiq node.
+1. Define the `external_url`. To maintain uniformity of links across nodes, the
+ `external_url` on the Sidekiq server should point to the external URL that users
+ will use to access GitLab. This will either be the `external_url` set on your
+ application server or the URL of a external load balancer which will route traffic
+ to the GitLab application server:
+
+ ```ruby
+ external_url 'https://gitlab.example.com'
+ ```
+
1. Run `gitlab-ctl reconfigure`.
You will need to restart the Sidekiq nodes after an update has occurred and database
@@ -194,6 +204,9 @@ gitlab_rails['monitoring_whitelist'] = ['10.10.1.42', '127.0.0.1']
# Container Registry URL for cleanup jobs
registry_external_url 'https://registry.example.com'
gitlab_rails['registry_api_url'] = "https://registry.example.com"
+
+# External URL (this should match the URL used to access your GitLab instance)
+external_url 'https://gitlab.example.com'
```
## Further reading
diff --git a/doc/administration/smime_signing_email.md b/doc/administration/smime_signing_email.md
index ebc1723076..8fdd87a5b2 100644
--- a/doc/administration/smime_signing_email.md
+++ b/doc/administration/smime_signing_email.md
@@ -69,16 +69,16 @@ The key needs to be readable by the GitLab system user (`git` by default).
The key needs to be readable by the GitLab system user (`git` by default).
-### How to convert S/MIME PKCS#12 / PFX format to PEM encoding
+### How to convert S/MIME PKCS #12 format to PEM encoding
-Typically S/MIME certificates are handled in binary PKCS#12 format (`.pfx` or `.p12`
-extensions), which contain the following in a single encrypted file:
+Typically S/MIME certificates are handled in binary Public Key Cryptography Standards (PKCS) #12 format
+(`.pfx` or `.p12` extensions), which contain the following in a single encrypted file:
- Public certificate
- Intermediate certificates (if any)
- Private key
-To export the required files in PEM encoding from the PKCS#12 file, the
+To export the required files in PEM encoding from the PKCS #12 file, the
`openssl` command can be used:
```shell
diff --git a/doc/administration/timezone.md b/doc/administration/timezone.md
index b6076c8ed2..f4339263d3 100644
--- a/doc/administration/timezone.md
+++ b/doc/administration/timezone.md
@@ -14,22 +14,22 @@ The global time zone configuration parameter can be changed in `config/gitlab.ym
Uncomment and customize if you want to change the default time zone of the GitLab application.
-## Viewing available timezones
+## Viewing available time zones
To see all available time zones, run `bundle exec rake time:zones:all`.
For Omnibus installations, run `gitlab-rake time:zones:all`.
NOTE:
-This Rake task does not list timezones in TZInfo format required by Omnibus GitLab during a reconfigure: [#27209](https://gitlab.com/gitlab-org/gitlab/-/issues/27209).
+This Rake task does not list time zones in TZInfo format required by Omnibus GitLab during a reconfigure: [#27209](https://gitlab.com/gitlab-org/gitlab/-/issues/27209).
## Changing time zone in Omnibus installations
-GitLab defaults its time zone to UTC. It has a global timezone configuration parameter in `/etc/gitlab/gitlab.rb`.
+GitLab defaults its time zone to UTC. It has a global time zone configuration parameter in `/etc/gitlab/gitlab.rb`.
-To obtain a list of timezones, log in to your GitLab application server and run a command that generates a list of timezones in TZInfo format for the server. For example, install `timedatectl` and run `timedatectl list-timezones`.
+To obtain a list of time zones, log in to your GitLab application server and run a command that generates a list of time zones in TZInfo format for the server. For example, install `timedatectl` and run `timedatectl list-timezones`.
-To update, add the timezone that best applies to your location. For example:
+To update, add the time zone that best applies to your location. For example:
```ruby
gitlab_rails['time_zone'] = 'America/New_York'
@@ -48,8 +48,12 @@ gitlab-ctl restart
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/29669) in GitLab 13.9.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/29669) in GitLab 14.1.
-A user can set their time zone in their profile. If a user has not set their time zone, it defaults
-to the time zone [configured at the instance level](#changing-your-time-zone). On GitLab.com, the
-default time zone is UTC.
+Users can set their time zone in their profile. On GitLab.com, the default time zone is UTC.
+
+New users do not have a default time zone in [GitLab 14.4 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/340795). New users must
+explicitly set their time zone before it displays on their profile.
+
+In GitLab 14.3 and earlier, users with no configured time zone default to the time zone
+[configured at the instance level](#changing-your-time-zone).
For more information, see [Set your time zone](../user/profile/index.md#set-your-time-zone).
diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md
index aea891b8a7..e00243aca0 100644
--- a/doc/administration/troubleshooting/debug.md
+++ b/doc/administration/troubleshooting/debug.md
@@ -225,7 +225,7 @@ gitlab_rails['env'] = {
```
For source installations, set the environment variable.
-Refer to [Puma Worker timeout](https://docs.gitlab.com/omnibus/settings/puma.html#worker-timeout).
+Refer to [Puma Worker timeout](../operations/puma.md#worker-timeout).
[Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect.
diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
index 491db37d9d..109f451be5 100644
--- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
+++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
@@ -26,7 +26,7 @@ As GitLab changes, changes to the code are inevitable,
and so some scripts may not work as they once used to. These are not kept
up-to-date as these scripts/commands were added as they were found/needed. As
mentioned above, we recommend running these scripts under the supervision of a
-Support Engineer, who can also verify that they will continue to work as they
+Support Engineer, who can also verify that they continue to work as they
should and, if needed, update the script for the latest version of GitLab.
## Find specific methods for an object
@@ -38,8 +38,6 @@ Array.methods.grep(/sing/)
## Find method source
-Works for [non-instrumented methods](../../development/instrumentation.md#checking-instrumented-methods):
-
```ruby
instance_of_object.method(:foo).source_location
@@ -187,7 +185,7 @@ Project.update_all(visibility_level: 0)
```ruby
#
-# This section will list all the projects which are pending deletion
+# This section lists all the projects which are pending deletion
#
projects = Project.where(pending_delete: true)
projects.each do |p|
@@ -197,7 +195,7 @@ projects.each do |p|
end
#
-# Assign a user (the root user will do)
+# Assign a user (the root user does)
#
user = User.find_by_username('root')
@@ -257,7 +255,7 @@ namespace = Namespace.find_by_full_path("")
### For Removing webhooks that is getting timeout due to large webhook logs
```ruby
-# ID will be the webhook_id
+# ID is the webhook_id
hook=WebHook.find(ID)
WebHooks::DestroyService.new(current_user).execute(hook)
@@ -399,7 +397,7 @@ projects = Project.find_by_sql("SELECT * FROM projects WHERE name LIKE '%ject'")
### Recreate
WARNING:
-This is a destructive operation, the Wiki will be empty.
+This is a destructive operation, the Wiki becomes empty.
A Projects Wiki can be recreated by this command:
@@ -476,13 +474,13 @@ Projects::ImportExport::ExportService.new(project, user).execute
If the project you wish to export is available at `https://gitlab.example.com/baltig/pipeline-templates`, the value to use for `PROJECT_PATH` would be `baltig/pipeline-templates`.
-If this all runs successfully, you will see output like the following before being returned to the Rails console prompt:
+If this all runs successfully, you see an output like the following before being returned to the Rails console prompt:
```ruby
=> nil
```
-The exported project will be located within a `.tar.gz` file in `/var/opt/gitlab/gitlab-rails/uploads/-/system/import_export_upload/export_file/`.
+The exported project is located within a `.tar.gz` file in `/var/opt/gitlab/gitlab-rails/uploads/-/system/import_export_upload/export_file/`.
## Repository
@@ -490,29 +488,31 @@ The exported project will be located within a `.tar.gz` file in `/var/opt/gitlab
If it seems that a commit has gone "missing", search the sequence of pushes to a repository.
[This StackOverflow article](https://stackoverflow.com/questions/13468027/the-mystery-of-the-missing-commit-across-merges)
-describes how you can end up in this state without a force push.
+describes how you can end up in this state without a force push. Another cause can be a misconfigured [server hook](../server_hooks.md) that changes a HEAD ref via a `git reset` operation.
-If you look at the output from the sample code below for the target branch, you will
-see a discontinuity in the from/to commits as you step through the output. Each new
-push should be "from" the "to" SHA of the previous push. When this discontinuity happens,
-you will see two pushes with the same "from" SHA:
+If you look at the output from the sample code below for the target branch, you
+see a discontinuity in the from/to commits as you step through the output. The `commit_from` of each new push should equal the `commit_to` of the previous push. A break in that sequence indicates one or more commits have been "lost" from the repository history.
-```ruby
-p = Project.find_with_namespace('u/p')
-p.events.pushed_action.last(100).each do |e|
- printf "%-20.20s %8s...%8s (%s)\n", e.data[:ref], e.data[:before], e.data[:after], e.author.try(:username)
-end
-```
-
-GitLab 9.5 and above:
+The following example checks the last 100 pushes and prints the `commit_from` and `commit_to` entries:
```ruby
p = Project.find_by_full_path('u/p')
p.events.pushed_action.last(100).each do |e|
- printf "%-20.20s %8s...%8s (%s)\n", e.push_event_payload[:ref], e.push_event_payload[:commit_from], e.push_event_payload[:commit_to], e.author.try(:username)
+ printf "%-20.20s %8s...%8s (%s)
+", e.push_event_payload[:ref], e.push_event_payload[:commit_from], e.push_event_payload[:commit_to], e.author.try(:username)
end
```
+Example output showing break in sequence at line 4:
+
+```plaintext
+master f21b07713251e04575908149bdc8ac1f105aabc3...6bc56c1f46244792222f6c85b11606933af171de (root)
+master 6bc56c1f46244792222f6c85b11606933af171de...132da6064f5d3453d445fd7cb452b148705bdc1b (root)
+master 132da6064f5d3453d445fd7cb452b148705bdc1b...a62e1e693150a2e46ace0ce696cd4a52856dfa65 (root)
+master 58b07b719a4b0039fec810efa52f479ba1b84756...f05321a5b5728bd8a89b7bf530aa44043c951dce (root)
+master f05321a5b5728bd8a89b7bf530aa44043c951dce...7d02e575fd790e76a3284ee435368279a5eb3773 (root)
+```
+
## Mirrors
### Find mirrors with "bad decrypt" errors
@@ -558,7 +558,7 @@ end
```ruby
u = User.new(username: 'test_user', email: 'test@example.com', name: 'Test User', password: 'password', password_confirmation: 'password')
-u.skip_confirmation! # Use it only if you wish user to be automatically confirmed. If skipped, user will recieve confirmation e-mail
+u.skip_confirmation! # Use it only if you wish user to be automatically confirmed. If skipped, user receives confirmation e-mail
u.save!
```
@@ -612,7 +612,7 @@ identifier = Analytics::UsageTrends::Measurement.identifiers[:billable_users]
```ruby
users = User.where('id NOT IN (select distinct(user_id) from project_authorizations)')
-# How many users will be removed?
+# How many users are removed?
users.count
# If that count looks sane:
@@ -726,6 +726,18 @@ group.require_two_factor_authentication=false
group.save
```
+## Authentication
+
+### Re-enable standard web sign-in form
+
+Re-enable the standard username and password-based sign-in form if it was disabled as a [Sign-in restriction](../../user/admin_area/settings/sign_in_restrictions.md#password-authentication-enabled).
+
+You can use this method when a configured external authentication provider (through SSO or an LDAP configuration) is facing an outage and direct sign-in access to GitLab is required.
+
+```ruby
+Gitlab::CurrentSettings.update!(password_authentication_enabled_for_web: true)
+```
+
## SCIM
### Fixing bad SCIM identities
@@ -1061,7 +1073,7 @@ encrypted credentials to allow manual reentry:
If `User OTP Secret Bad count:` is detected. For each user listed disable/enable
two-factor authentication.
-The following script will search in some of the tables for encrypted tokens that are
+The following script searches in some of the tables for encrypted tokens that are
causing decryption errors, and update or reset as needed:
```shell
@@ -1123,7 +1135,7 @@ Geo::ProjectRegistry.sync_failed('repository')
### Resync repositories
-#### Queue up all repositories for resync. Sidekiq will handle each sync
+#### Queue up all repositories for resync. Sidekiq handles each sync
```ruby
Geo::ProjectRegistry.update_all(resync_repository: true, resync_wiki: true)
@@ -1170,10 +1182,10 @@ registry.replicator.send(:download)
#### Verify package files on the secondary manually
-This will iterate over all package files on the secondary, looking at the
+This iterates over all package files on the secondary, looking at the
`verification_checksum` stored in the database (which came from the primary)
and then calculate this value on the secondary to check if they match. This
-won't change anything in the UI:
+does not change anything in the UI:
```ruby
# Run on secondary
@@ -1235,7 +1247,7 @@ Gitlab::UsageData.to_json
### Generate a fresh new Service Ping
-This will also refresh the cached Service Ping displayed in the admin area
+This also refreshes the cached Service Ping displayed in the Admin Area
```ruby
Gitlab::UsageData.to_json(force_refresh: true)
@@ -1269,7 +1281,7 @@ cluster = Clusters::Cluster.find_by(name: 'cluster_name')
Delete cluster without associated resources:
```ruby
-# Find an admin user
+# Find users with the Administrator role
user = User.find_by(username: 'admin_user')
# Find the cluster with the ID
@@ -1289,7 +1301,7 @@ Open the rails console (`gitlab rails c`) and run the following command to see a
ApplicationSetting.last.attributes
```
-Among other attributes, in the output you will notice that all the settings available in the [Elasticsearch Integration page](../../integration/elasticsearch.md), like: `elasticsearch_indexing`, `elasticsearch_url`, `elasticsearch_replicas`, `elasticsearch_pause_indexing`, and so on.
+Among other attributes, the output contains all the settings available in the [Elasticsearch Integration page](../../integration/elasticsearch.md), such as `elasticsearch_indexing`, `elasticsearch_url`, `elasticsearch_replicas`, and `elasticsearch_pause_indexing`.
#### Setting attributes
diff --git a/doc/administration/troubleshooting/img/database-query-dialog_v14_3.png b/doc/administration/troubleshooting/img/database-query-dialog_v14_3.png
new file mode 100644
index 0000000000..197cfa17da
Binary files /dev/null and b/doc/administration/troubleshooting/img/database-query-dialog_v14_3.png differ
diff --git a/doc/administration/troubleshooting/img/obtaining-a-session-cookie-for-request_v14_3.png b/doc/administration/troubleshooting/img/obtaining-a-session-cookie-for-request_v14_3.png
new file mode 100644
index 0000000000..a63ebf9ecf
Binary files /dev/null and b/doc/administration/troubleshooting/img/obtaining-a-session-cookie-for-request_v14_3.png differ
diff --git a/doc/administration/troubleshooting/img/paste-request-id-into-progress-bar_v14_3.png b/doc/administration/troubleshooting/img/paste-request-id-into-progress-bar_v14_3.png
new file mode 100644
index 0000000000..a19585d7a8
Binary files /dev/null and b/doc/administration/troubleshooting/img/paste-request-id-into-progress-bar_v14_3.png differ
diff --git a/doc/administration/troubleshooting/img/select-request-id-from-request-selector-drop-down-menu_v14_3.png b/doc/administration/troubleshooting/img/select-request-id-from-request-selector-drop-down-menu_v14_3.png
new file mode 100644
index 0000000000..b8314056c9
Binary files /dev/null and b/doc/administration/troubleshooting/img/select-request-id-from-request-selector-drop-down-menu_v14_3.png differ
diff --git a/doc/administration/troubleshooting/img/view-pg-details_v14_3.png b/doc/administration/troubleshooting/img/view-pg-details_v14_3.png
new file mode 100644
index 0000000000..1fe12462e4
Binary files /dev/null and b/doc/administration/troubleshooting/img/view-pg-details_v14_3.png differ
diff --git a/doc/administration/troubleshooting/linux_cheat_sheet.md b/doc/administration/troubleshooting/linux_cheat_sheet.md
index 9eadbad171..b66e88a8d6 100644
--- a/doc/administration/troubleshooting/linux_cheat_sheet.md
+++ b/doc/administration/troubleshooting/linux_cheat_sheet.md
@@ -14,7 +14,7 @@ having an issue with GitLab, you may want to check your [support options](https:
first, before attempting to use this information.
WARNING:
-If you are administering GitLab you are expected to know these commands for your distribution
+If you administer GitLab you are expected to know these commands for your distribution
of choice. If you are a GitLab Support Engineer, consider this a cross-reference to
translate `yum` -> `apt-get` and the like.
diff --git a/doc/administration/troubleshooting/ssl.md b/doc/administration/troubleshooting/ssl.md
index 80be30a650..01a4c4af65 100644
--- a/doc/administration/troubleshooting/ssl.md
+++ b/doc/administration/troubleshooting/ssl.md
@@ -45,7 +45,7 @@ following issues:
```
- The error `SSL certificate problem: unable to get local issuer certificate`
- is displayed when setting up a [mirror](../../user/project/repository/repository_mirroring.md#repository-mirroring)
+ is displayed when setting up a [mirror](../../user/project/repository/mirror/index.md)
from this GitLab instance.
- `openssl` works when specifying the path to the certificate:
@@ -115,7 +115,7 @@ and the restart the runner by running `gitlab-runner restart`.
## Mirroring a remote GitLab repository that uses a self-signed SSL certificate
-When configuring a local GitLab instance to [mirror a repository](../../user/project/repository/repository_mirroring.md)
+When configuring a local GitLab instance to [mirror a repository](../../user/project/repository/mirror/index.md)
from a remote GitLab instance that uses a self-signed certificate, you may see
the `SSL certificate problem: self signed certificate` error message in the
user interface.
diff --git a/doc/administration/troubleshooting/tracing_correlation_id.md b/doc/administration/troubleshooting/tracing_correlation_id.md
index 3518f52e6f..3bafbed4b3 100644
--- a/doc/administration/troubleshooting/tracing_correlation_id.md
+++ b/doc/administration/troubleshooting/tracing_correlation_id.md
@@ -135,3 +135,69 @@ You can use the [performance bar](../monitoring/performance/performance_bar.md)
To view the data, the correlation ID of the request must match the same session as the user
viewing the performance bar. For API requests, this means that you must perform the request
using the session cookie of the signed-in user.
+
+For example, if you want to view the database queries executed for the following API endpoint:
+
+```shell
+https://gitlab.com/api/v4/groups/2564205/projects?with_security_reports=true&page=1&per_page=1
+```
+
+First, enable the **Developer Tools** panel. See [Getting the correlation ID in your browser](#getting-the-correlation-id-in-your-browser) for details on how to do this.
+
+After developer tools have been enabled, obtain a session cookie as follows:
+
+1. Visit while logged in.
+1. (Optional) Select **Fetch/XHR** request filter in the **Developer Tools** panel. This step is described for Google Chrome developer tools and is not strictly necessary, it just makes it easier to find the correct request.
+1. Select the `results?request_id=` request on the left hand side.
+1. The session cookie is displayed under the `Request Headers` section of the `Headers` panel. Right-click on the cookie value and select `Copy value`.
+
+![Obtaining a session cookie for request](img/obtaining-a-session-cookie-for-request_v14_3.png)
+
+You have the value of the session cookie copied to your clipboard, for example:
+
+```shell
+experimentation_subject_id=; _gitlab_session=; event_filter=all; visitor_id=; perf_bar_enabled=true; sidebar_collapsed=true; diff_view=inline; sast_entry_point_dismissed=true; auto_devops_settings_dismissed=true; cf_clearance=; collapsed_gutter=false; frequently_used_emojis=clap,thumbsup,rofl,tada,eyes,bow
+```
+
+Use the value of the session cookie to craft an API request by pasting it into a custom header of a `curl` request:
+
+```shell
+$ curl --include "https://gitlab.com/api/v4/groups/2564205/projects?with_security_reports=true&page=1&per_page=1" \
+--header 'cookie: experimentation_subject_id=; _gitlab_session=; event_filter=all; visitor_id=; perf_bar_enabled=true; sidebar_collapsed=true; diff_view=inline; sast_entry_point_dismissed=true; auto_devops_settings_dismissed=true; cf_clearance=; collapsed_gutter=false; frequently_used_emojis=clap,thumbsup,rofl,tada,eyes,bow'
+
+ date: Tue, 28 Sep 2021 03:55:33 GMT
+ content-type: application/json
+ ...
+ x-request-id: 01FGN8P881GF2E5J91JYA338Y3
+ ...
+ [
+ {
+ "id":27497069,
+ "description":"Analyzer for images used on live K8S containers based on Starboard"
+ },
+ "container_registry_image_prefix":"registry.gitlab.com/gitlab-org/security-products/analyzers/cluster-image-scanning",
+ "..."
+ ]
+```
+
+The response contains the data from the API endpoint, and a `correlation_id` value, returned in the `x-request-id` header, as described in the [Identify the correlation ID for a request](#identify-the-correlation-id-for-a-request) section.
+
+You can then view the database details for this request:
+
+1. Paste the `x-request-id` value into the `request details` field of the [performance bar](../monitoring/performance/performance_bar.md) and press Enter/Return. This example uses the `x-request-id` value `01FGN8P881GF2E5J91JYA338Y3`, returned by the above response:
+
+ ![Paste request ID into progress bar](img/paste-request-id-into-progress-bar_v14_3.png)
+
+1. A new request is inserted into the `Request Selector` dropdown on the right-hand side of the Performance Bar. Select the new request to view the metrics of the API request:
+
+ ![Select request ID from request selector drop down menu](img/select-request-id-from-request-selector-drop-down-menu_v14_3.png)
+
+
+1. Select the `pg` link in the Progress Bar to view the database queries executed by the API request:
+
+ ![View pg database details](img/view-pg-details_v14_3.png)
+
+
+ The database query dialog is displayed:
+
+ ![Database query dialog](img/database-query-dialog_v14_3.png)
diff --git a/doc/administration/user_settings.md b/doc/administration/user_settings.md
index 681ce87edb..891c11afaf 100644
--- a/doc/administration/user_settings.md
+++ b/doc/administration/user_settings.md
@@ -4,40 +4,56 @@ group: Access
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/#assignments
---
-# Modifying global user settings
+# Modify global user settings **(FREE SELF)**
GitLab administrators can modify user settings for the entire GitLab instance.
-## Disallow users creating top-level groups
+## Prevent users from creating top-level groups
-By default, new users can create top-level groups. To disable this, modify the appropriate configuration file,
-and then [reconfigure and restart GitLab](restart_gitlab.md).
+By default, new users can create top-level groups. To disable your users'
+ability to create top-level groups:
-For Omnibus installations, add the following to `/etc/gitlab/gitlab.rb`:
+**Omnibus GitLab installations**
-```ruby
-gitlab_rails['gitlab_default_can_create_group'] = false
-```
+1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
-For source installations, uncomment the following line in `config/gitlab.yml`:
+ ```ruby
+ gitlab_rails['gitlab_default_can_create_group'] = false
+ ```
-```yaml
-# default_can_create_group: false # default: true
-```
+1. [Reconfigure and restart GitLab](restart_gitlab.md#omnibus-installations).
-## Disallow users changing usernames
+**Source installations**
-By default, new users can change their usernames. To disable this, modify the appropriate configuration file,
-and then [reconfigure and restart GitLab](restart_gitlab.md).
+1. Edit `config/gitlab.yml` and uncomment the following line:
-For Omnibus installations, add the following to `/etc/gitlab/gitlab.rb`:
+ ```yaml
+ # default_can_create_group: false # default: true
+ ```
-```ruby
-gitlab_rails['gitlab_username_changing_enabled'] = false
-```
+1. [Restart GitLab](restart_gitlab.md#installations-from-source).
-For source installations, uncomment the following line in `config/gitlab.yml`:
+## Prevent users from changing their usernames
-```yaml
-# username_changing_enabled: false # default: true - User can change their username/namespace
-```
+By default, new users can change their usernames. To disable your users'
+ability to change their usernames:
+
+**Omnibus GitLab installations**
+
+1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
+
+ ```ruby
+ gitlab_rails['gitlab_username_changing_enabled'] = false
+ ```
+
+1. [Reconfigure and restart GitLab](restart_gitlab.md#omnibus-installations).
+
+**Source installations**
+
+1. Edit `config/gitlab.yml` and uncomment the following line:
+
+ ```yaml
+ # username_changing_enabled: false # default: true - User can change their username/namespace
+ ```
+
+1. [Restart GitLab](restart_gitlab.md#installations-from-source).
diff --git a/doc/api/README.md b/doc/api/README.md
deleted file mode 100644
index 0e6c2f63f9..0000000000
--- a/doc/api/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: 'index.md'
-remove_date: '2021-09-28'
----
-
-This document was moved to [another location](index.md).
-
-
-
diff --git a/doc/api/access_requests.md b/doc/api/access_requests.md
index 1634184a37..df830a1607 100644
--- a/doc/api/access_requests.md
+++ b/doc/api/access_requests.md
@@ -2,13 +2,10 @@
stage: Manage
group: Access
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/#assignments"
-type: reference, api
---
# Group and project access requests API **(FREE)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/18583) in GitLab 8.11.
-
## Valid access levels
The access levels are defined in the `Gitlab::Access` module, and the
diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md
index 8706b1e7e7..7dc3fd1db2 100644
--- a/doc/api/api_resources.md
+++ b/doc/api/api_resources.md
@@ -42,6 +42,7 @@ The following API resources are available in the project context:
| [Events](events.md) | `/projects/:id/events` (also available for users and standalone) |
| [Feature Flags](feature_flags.md) | `/projects/:id/feature_flags` |
| [Feature Flag User Lists](feature_flag_user_lists.md) | `/projects/:id/feature_flags_user_lists` |
+| [Integrations](integrations.md) | `/projects/:id/integrations` |
| [Invitations](invitations.md) | `/projects/:id/invitations` (also available for groups) |
| [Issues](issues.md) | `/projects/:id/issues` (also available for groups and standalone) |
| [Issues Statistics](issues_statistics.md) | `/projects/:id/issues_statistics` (also available for groups and standalone) |
@@ -82,7 +83,7 @@ The following API resources are available in the project context:
| [Resource label events](resource_label_events.md) | `/projects/:id/issues/.../resource_label_events`, `/projects/:id/merge_requests/.../resource_label_events` (also available for groups) |
| [Runners](runners.md) | `/projects/:id/runners` (also available standalone) |
| [Search](search.md) | `/projects/:id/search` (also available for groups and standalone) |
-| [Services](services.md) | `/projects/:id/services` |
+| [Services](services.md) (renamed to [Integrations](integrations.md)) | `/projects/:id/services` |
| [Tags](tags.md) | `/projects/:id/repository/tags` |
| [User-starred metrics dashboards](metrics_user_starred_dashboards.md ) | `/projects/:id/metrics/user_starred_dashboards` |
| [Visual Review discussions](visual_review_discussions.md) **(PREMIUM)** | `/projects/:id/merge_requests/:merge_request_id/visual_review_discussions` |
diff --git a/doc/api/applications.md b/doc/api/applications.md
index 2b5eff6801..22d858bd68 100644
--- a/doc/api/applications.md
+++ b/doc/api/applications.md
@@ -34,14 +34,14 @@ Parameters:
|:---------------|:--------|:---------|:---------------------------------|
| `name` | string | yes | Name of the application. |
| `redirect_uri` | string | yes | Redirect URI of the application. |
-| `scopes` | string | yes | Scopes of the application. |
+| `scopes` | string | yes | Scopes of the application. You can specify multiple scopes by separating each scope using a space. |
| `confidential` | boolean | no | The application is used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential. Defaults to `true` if not supplied |
Example request:
```shell
curl --request POST --header "PRIVATE-TOKEN: " \
- --data "name=MyApplication&redirect_uri=http://redirect.uri&scopes=" \
+ --data "name=MyApplication&redirect_uri=http://redirect.uri&scopes=api read_user email" \
"https://gitlab.example.com/api/v4/applications"
```
diff --git a/doc/api/commits.md b/doc/api/commits.md
index e91da23596..94d1ced181 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -2,7 +2,6 @@
stage: Create
group: Source Code
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/#assignments"
-type: reference, api
---
# Commits API **(FREE)**
@@ -75,8 +74,6 @@ Example response:
## Create a commit with multiple files and actions
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/6096) in GitLab 8.13.
-
Create a commit by posting a JSON payload
```plaintext
@@ -256,8 +253,6 @@ Example response:
## Get references a commit is pushed to
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/15026) in GitLab 10.6
-
Get all references (from branches or tags) a commit is pushed to.
The pagination parameters `page` and `per_page` can be used to restrict the list of references.
@@ -291,8 +286,6 @@ Example response:
## Cherry-pick a commit
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/8047) in GitLab 8.15.
-
Cherry-picks a commit to a given branch.
```plaintext
@@ -366,8 +359,6 @@ dry run.
## Revert a commit
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/22919) in GitLab 11.5.
-
Reverts a commit in a given branch.
```plaintext
@@ -622,7 +613,7 @@ Example response:
## Commit status
-In GitLab 8.1 and later, this is the new commit status API.
+This is the commit status API for use with GitLab.
### List the statuses of a commit
@@ -752,8 +743,6 @@ Example response:
## List Merge Requests associated with a commit
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/18004) in GitLab 10.7.
-
Get a list of Merge Requests related to the specified commit.
```plaintext
diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md
index 2885cc7d80..1b9778a01b 100644
--- a/doc/api/container_registry.md
+++ b/doc/api/container_registry.md
@@ -350,7 +350,7 @@ curl --request DELETE --header "PRIVATE-TOKEN: " \
```
This action doesn't delete blobs. To delete them and recycle disk space,
-[run the garbage collection](https://docs.gitlab.com/omnibus/maintenance/README.html#removing-unused-layers-not-referenced-by-manifests).
+[run the garbage collection](https://docs.gitlab.com/omnibus/maintenance/index.html#removing-unused-layers-not-referenced-by-manifests).
## Delete registry repository tags in bulk
@@ -388,7 +388,7 @@ This API call performs the following operations:
These operations are executed asynchronously and can take time to get executed.
You can run this at most once an hour for a given container repository. This
action doesn't delete blobs. To delete them and recycle disk space,
-[run the garbage collection](https://docs.gitlab.com/omnibus/maintenance/README.html#removing-unused-layers-not-referenced-by-manifests).
+[run the garbage collection](https://docs.gitlab.com/omnibus/maintenance/index.html#removing-unused-layers-not-referenced-by-manifests).
WARNING:
The number of tags deleted by this API is limited on GitLab.com
diff --git a/doc/api/dependencies.md b/doc/api/dependencies.md
index 6e9c37980a..b421a32b88 100644
--- a/doc/api/dependencies.md
+++ b/doc/api/dependencies.md
@@ -34,7 +34,7 @@ GET /projects/:id/dependencies?package_manager=yarn,bundler
| Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
-| `package_manager` | string array | no | Returns dependencies belonging to specified package manager. Valid values: `bundler`, `composer`, `conan`, `maven`, `npm`, `pip` or `yarn`. |
+| `package_manager` | string array | no | Returns dependencies belonging to specified package manager. Valid values: `bundler`, `composer`, `conan`, `go`, `maven`, `npm`, `nuget`, `pip`, `yarn`, or `sbt`. |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/4/dependencies"
diff --git a/doc/api/deploy_tokens.md b/doc/api/deploy_tokens.md
index 3de7ff4ac4..c718958623 100644
--- a/doc/api/deploy_tokens.md
+++ b/doc/api/deploy_tokens.md
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21811) in GitLab 12.9.
-Get a list of all deploy tokens across the GitLab instance. This endpoint requires administrator access.
+Get a list of all deploy tokens across the GitLab instance. This endpoint requires the Administrator role.
```plaintext
GET /deploy_tokens
diff --git a/doc/api/dora/metrics.md b/doc/api/dora/metrics.md
index 8c82446db2..4fbd2b0fa8 100644
--- a/doc/api/dora/metrics.md
+++ b/doc/api/dora/metrics.md
@@ -1,6 +1,6 @@
---
-stage: Release
-group: Release
+stage: Manage
+group: Optimize
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/#assignments
type: reference, api
---
diff --git a/doc/api/dora4_project_analytics.md b/doc/api/dora4_project_analytics.md
index 5a6e1576a3..f69c918c6e 100644
--- a/doc/api/dora4_project_analytics.md
+++ b/doc/api/dora4_project_analytics.md
@@ -1,6 +1,6 @@
---
-stage: Release
-group: Release
+stage: Manage
+group: Optimize
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/#assignments
type: reference, api
---
diff --git a/doc/api/environments.md b/doc/api/environments.md
index 5187ac7c1b..8188e0e7b8 100644
--- a/doc/api/environments.md
+++ b/doc/api/environments.md
@@ -64,6 +64,8 @@ Example of response
"slug": "review-fix-foo-dfjre3",
"external_url": "https://review-fix-foo-dfjre3.gitlab.example.com",
"state": "available",
+ "created_at": "2019-05-25T18:55:13.252Z",
+ "updated_at": "2019-05-27T18:55:13.252Z",
"last_deployment": {
"id": 100,
"iid": 34,
@@ -176,7 +178,9 @@ Example response:
"name": "deploy",
"slug": "deploy",
"external_url": "https://deploy.gitlab.example.com",
- "state": "available"
+ "state": "available",
+ "created_at": "2019-05-25T18:55:13.252Z",
+ "updated_at": "2019-05-27T18:55:13.252Z"
}
```
@@ -210,7 +214,9 @@ Example response:
"name": "staging",
"slug": "staging",
"external_url": "https://staging.gitlab.example.com",
- "state": "available"
+ "state": "available",
+ "created_at": "2019-05-25T18:55:13.252Z",
+ "updated_at": "2019-05-27T18:55:13.252Z"
}
```
@@ -302,6 +308,8 @@ Example response:
"name": "deploy",
"slug": "deploy",
"external_url": "https://deploy.gitlab.example.com",
- "state": "stopped"
+ "state": "stopped",
+ "created_at": "2019-05-25T18:55:13.252Z",
+ "updated_at": "2019-05-27T18:55:13.252Z"
}
```
diff --git a/doc/api/error_tracking.md b/doc/api/error_tracking.md
index 1abe552284..203c1a2399 100644
--- a/doc/api/error_tracking.md
+++ b/doc/api/error_tracking.md
@@ -73,7 +73,7 @@ Example response:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68384) in GitLab 14.3.
-For [integrated error tracking](https://gitlab.com/gitlab-org/gitlab/-/issues/329596) feature that is behind a disabled feature flag. Only for project maintainers.
+For [integrated error tracking](https://gitlab.com/gitlab-org/gitlab/-/issues/329596) feature. Only for project maintainers.
### List project client keys
diff --git a/doc/api/events.md b/doc/api/events.md
index 8800e7f7f9..2d173f0053 100644
--- a/doc/api/events.md
+++ b/doc/api/events.md
@@ -87,10 +87,6 @@ GitLab removes events older than 3 years from the events table for performance r
## List currently authenticated user's events
->**Notes:**
-> This endpoint was introduced in GitLab 9.3.
-> `read_user` access was introduced in GitLab 11.3.
-
Get a list of events for the authenticated user. Scope `read_user` or `api` is required.
```plaintext
@@ -163,10 +159,6 @@ Example response:
### Get user contribution events
->**Notes:**
-> Documentation was formerly located in the [Users API pages](users.md).
-> `read_user` access was introduced in GitLab 11.3.
-
Get the contribution events for the specified user, sorted from newest to oldest. Scope `read_user` or `api` is required.
```plaintext
diff --git a/doc/api/experiments.md b/doc/api/experiments.md
index c5e217a3d6..669d00454b 100644
--- a/doc/api/experiments.md
+++ b/doc/api/experiments.md
@@ -4,7 +4,7 @@ group: Expansion
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/#assignments
---
-# Experiments API
+# Experiments API (GitLab team only) **(FREE SAAS)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/262725) in GitLab 13.5.
diff --git a/doc/api/freeze_periods.md b/doc/api/freeze_periods.md
index 6ca69d047d..6dc4e8745d 100644
--- a/doc/api/freeze_periods.md
+++ b/doc/api/freeze_periods.md
@@ -94,7 +94,7 @@ POST /projects/:id/freeze_periods
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `freeze_start` | string | yes | Start of the Freeze Period in [cron](https://crontab.guru/) format. |
| `freeze_end` | string | yes | End of the Freeze Period in [cron](https://crontab.guru/) format. |
-| `cron_timezone` | string | no | The timezone for the cron fields, defaults to UTC if not provided. |
+| `cron_timezone` | string | no | The time zone for the cron fields, defaults to UTC if not provided. |
Example request:
@@ -131,7 +131,7 @@ PUT /projects/:id/freeze_periods/:freeze_period_id
| `freeze_period_id` | integer or string | yes | The database ID of the Freeze Period. |
| `freeze_start` | string | no | Start of the Freeze Period in [cron](https://crontab.guru/) format. |
| `freeze_end` | string | no | End of the Freeze Period in [cron](https://crontab.guru/) format. |
-| `cron_timezone` | string | no | The timezone for the cron fields. |
+| `cron_timezone` | string | no | The time zone for the cron fields. |
Example request:
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index c4e73f9c05..e0f18f931f 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -25,13 +25,30 @@ Items (fields, enums, etc) that have been removed according to our [deprecation
in [Removed Items](../removed_items.md).
-
+
+
+
+
+
## `Query` type
The `Query` type contains the API's top-level entry points for all executable queries.
+### `Query.boardList`
+
+Find an issue board list.
+
+Returns [`BoardList`](#boardlist).
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `id` | [`ListID!`](#listid) | Global ID of the list. |
+| `issueFilters` | [`BoardIssueInput`](#boardissueinput) | Filters applied when getting issue metadata in the board list. |
+
### `Query.ciApplicationSettings`
CI related settings that apply to the entire instance.
@@ -469,6 +486,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| `hasIssues` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked issues. |
| `hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
+| `image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| `projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| `reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| `scanner` | [`[String!]`](#string) | Filter vulnerabilities by VulnerabilityScanner.externalId. |
@@ -1266,6 +1284,9 @@ Input type: `CreateIssueInput`
| `epicId` | [`EpicID`](#epicid) | ID of an epic to associate the issue with. |
| `healthStatus` | [`HealthStatus`](#healthstatus) | Desired health status. |
| `iid` | [`Int`](#int) | IID (internal ID) of a project issue. Only admins and project owners can modify. |
+| `iterationCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | Global iteration cadence ID. Required when `iterationWildcardId` is provided. |
+| `iterationId` | [`IterationID`](#iterationid) | Global iteration ID. Mutually exlusive argument with `iterationWildcardId`. |
+| `iterationWildcardId` | [`IssueCreationIterationWildcardId`](#issuecreationiterationwildcardid) | Iteration wildcard ID. Supported values are: `CURRENT`. Mutually exclusive argument with `iterationId`. iterationCadenceId also required when this argument is provided. |
| `labelIds` | [`[LabelID!]`](#labelid) | IDs of labels to be added to the issue. |
| `labels` | [`[String!]`](#string) | Labels of the issue. |
| `locked` | [`Boolean`](#boolean) | Indicates discussion is locked on the issue. |
@@ -1408,6 +1429,56 @@ Input type: `CreateTestCaseInput`
| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| `testCase` | [`Issue`](#issue) | Test case created. |
+### `Mutation.customerRelationsContactCreate`
+
+Input type: `CustomerRelationsContactCreateInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `description` | [`String`](#string) | Description of or notes for the contact. |
+| `email` | [`String`](#string) | Email address of the contact. |
+| `firstName` | [`String!`](#string) | First name of the contact. |
+| `groupId` | [`GroupID!`](#groupid) | Group for the contact. |
+| `lastName` | [`String!`](#string) | Last name of the contact. |
+| `organizationId` | [`CustomerRelationsOrganizationID`](#customerrelationsorganizationid) | Organization for the contact. |
+| `phone` | [`String`](#string) | Phone number of the contact. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `contact` | [`CustomerRelationsContact`](#customerrelationscontact) | Contact after the mutation. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+
+### `Mutation.customerRelationsContactUpdate`
+
+Input type: `CustomerRelationsContactUpdateInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `description` | [`String`](#string) | Description of or notes for the contact. |
+| `email` | [`String`](#string) | Email address of the contact. |
+| `firstName` | [`String`](#string) | First name of the contact. |
+| `id` | [`CustomerRelationsContactID!`](#customerrelationscontactid) | Global ID of the contact. |
+| `lastName` | [`String`](#string) | Last name of the contact. |
+| `organizationId` | [`CustomerRelationsOrganizationID`](#customerrelationsorganizationid) | Organization of the contact. |
+| `phone` | [`String`](#string) | Phone number of the contact. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `contact` | [`CustomerRelationsContact`](#customerrelationscontact) | Contact after the mutation. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+
### `Mutation.customerRelationsOrganizationCreate`
Input type: `CustomerRelationsOrganizationCreateInput`
@@ -1418,7 +1489,7 @@ Input type: `CustomerRelationsOrganizationCreateInput`
| ---- | ---- | ----------- |
| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| `defaultRate` | [`Float`](#float) | Standard billing rate for the organization. |
-| `description` | [`String`](#string) | Description or notes for the organization. |
+| `description` | [`String`](#string) | Description of or notes for the organization. |
| `groupId` | [`GroupID!`](#groupid) | Group for the organization. |
| `name` | [`String!`](#string) | Name of the organization. |
@@ -1440,7 +1511,7 @@ Input type: `CustomerRelationsOrganizationUpdateInput`
| ---- | ---- | ----------- |
| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| `defaultRate` | [`Float`](#float) | Standard billing rate for the organization. |
-| `description` | [`String`](#string) | Description or notes for the organization. |
+| `description` | [`String`](#string) | Description of or notes for the organization. |
| `id` | [`CustomerRelationsOrganizationID!`](#customerrelationsorganizationid) | Global ID of the organization. |
| `name` | [`String`](#string) | Name of the organization. |
@@ -2432,6 +2503,64 @@ Input type: `ExportRequirementsInput`
| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+### `Mutation.externalAuditEventDestinationCreate`
+
+Input type: `ExternalAuditEventDestinationCreateInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `destinationUrl` | [`String!`](#string) | Destination URL. |
+| `groupPath` | [`ID!`](#id) | Group path. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| `externalAuditEventDestination` | [`ExternalAuditEventDestination`](#externalauditeventdestination) | Destination created. |
+
+### `Mutation.externalAuditEventDestinationDestroy`
+
+Input type: `ExternalAuditEventDestinationDestroyInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `id` | [`AuditEventsExternalAuditEventDestinationID!`](#auditeventsexternalauditeventdestinationid) | ID of external audit event destination to destroy. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+
+### `Mutation.externalAuditEventDestinationUpdate`
+
+Input type: `ExternalAuditEventDestinationUpdateInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `destinationUrl` | [`String`](#string) | Destination URL to change. |
+| `id` | [`AuditEventsExternalAuditEventDestinationID!`](#auditeventsexternalauditeventdestinationid) | ID of external audit event destination to destroy. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| `externalAuditEventDestination` | [`ExternalAuditEventDestination`](#externalauditeventdestination) | Updated destination. |
+
### `Mutation.gitlabSubscriptionActivate`
Input type: `GitlabSubscriptionActivateInput`
@@ -3904,6 +4033,8 @@ Input type: `RunnersRegistrationTokenResetInput`
### `Mutation.scanExecutionPolicyCommit`
+Commits the `policy_yaml` content to the assigned security policy project for the given project(`project_path`).
+
Input type: `ScanExecutionPolicyCommitInput`
#### Arguments
@@ -3925,6 +4056,8 @@ Input type: `ScanExecutionPolicyCommitInput`
### `Mutation.securityPolicyProjectAssign`
+Assigns the specified project(`security_policy_project_id`) as security policy project for the given project(`project_path`). If the project already has a security policy project, this reassigns the project's security policy project with the given `security_policy_project_id`.
+
Input type: `SecurityPolicyProjectAssignInput`
#### Arguments
@@ -3944,6 +4077,8 @@ Input type: `SecurityPolicyProjectAssignInput`
### `Mutation.securityPolicyProjectCreate`
+Creates and assigns a security policy project for the given project(`project_path`).
+
Input type: `SecurityPolicyProjectCreateInput`
#### Arguments
@@ -4269,6 +4404,26 @@ Input type: `UpdateDependencyProxyImageTtlGroupPolicyInput`
| `dependencyProxyImageTtlPolicy` | [`DependencyProxyImageTtlGroupPolicy`](#dependencyproxyimagettlgrouppolicy) | Group image TTL policy after mutation. |
| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+### `Mutation.updateDependencyProxySettings`
+
+Input type: `UpdateDependencyProxySettingsInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `enabled` | [`Boolean`](#boolean) | Indicates whether the policy is enabled or disabled. |
+| `groupPath` | [`ID!`](#id) | Group path for the group dependency proxy. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `dependencyProxySetting` | [`DependencyProxySetting`](#dependencyproxysetting) | Group dependency proxy settings after mutation. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+
### `Mutation.updateEpic`
Input type: `UpdateEpicInput`
@@ -4560,13 +4715,13 @@ Input type: `VulnerabilityCreateInput`
| `dismissedAt` | [`Time`](#time) | Timestamp of when the vulnerability state changed to dismissed (defaults to creation time if status is `dismissed`). |
| `identifiers` | [`[VulnerabilityIdentifierInput!]!`](#vulnerabilityidentifierinput) | Array of CVE or CWE identifiers for the vulnerability. |
| `message` | [`String`](#string) | Additional information about the vulnerability. |
+| `name` | [`String!`](#string) | Name of the vulnerability. |
| `project` | [`ProjectID!`](#projectid) | ID of the project to attach the vulnerability to. |
| `resolvedAt` | [`Time`](#time) | Timestamp of when the vulnerability state changed to resolved (defaults to creation time if status is `resolved`). |
-| `scannerName` | [`String!`](#string) | Name of the security scanner used to discover the vulnerability. |
+| `scanner` | [`VulnerabilityScannerInput!`](#vulnerabilityscannerinput) | Information about the scanner used to discover the vulnerability. |
| `severity` | [`VulnerabilitySeverity`](#vulnerabilityseverity) | Severity of the vulnerability (defaults to `unknown`). |
| `solution` | [`String`](#string) | How to fix this vulnerability. |
| `state` | [`VulnerabilityState`](#vulnerabilitystate) | State of the vulnerability (defaults to `detected`). |
-| `title` | [`String!`](#string) | Title of the vulnerability. |
#### Fields
@@ -5173,6 +5328,7 @@ The edge type for [`CiRunner`](#cirunner).
| ---- | ---- | ----------- |
| `cursor` | [`String!`](#string) | A cursor for use in pagination. |
| `node` | [`CiRunner`](#cirunner) | The item at the end of the edge. |
+| `webUrl` | [`String`](#string) | Web URL of the runner. The value depends on where you put this field in the query. You can use it for projects or groups. |
#### `CiStageConnection`
@@ -5915,6 +6071,29 @@ The edge type for [`Event`](#event).
| `cursor` | [`String!`](#string) | A cursor for use in pagination. |
| `node` | [`Event`](#event) | The item at the end of the edge. |
+#### `ExternalAuditEventDestinationConnection`
+
+The connection type for [`ExternalAuditEventDestination`](#externalauditeventdestination).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `edges` | [`[ExternalAuditEventDestinationEdge]`](#externalauditeventdestinationedge) | A list of edges. |
+| `nodes` | [`[ExternalAuditEventDestination]`](#externalauditeventdestination) | A list of nodes. |
+| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `ExternalAuditEventDestinationEdge`
+
+The edge type for [`ExternalAuditEventDestination`](#externalauditeventdestination).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| `node` | [`ExternalAuditEventDestination`](#externalauditeventdestination) | The item at the end of the edge. |
+
#### `GroupConnection`
The connection type for [`Group`](#group).
@@ -6503,6 +6682,7 @@ The connection type for [`Package`](#package).
| Name | Type | Description |
| ---- | ---- | ----------- |
+| `count` | [`Int!`](#int) | Total count of collection. |
| `edges` | [`[PackageEdge]`](#packageedge) | A list of edges. |
| `nodes` | [`[Package]`](#package) | A list of nodes. |
| `pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
@@ -8072,7 +8252,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `milestoneTitle` | [`String`](#string) | Filter epics by milestone title, computed from epic's issues. |
| `myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. |
| `not` | [`NegatedEpicFilterInput`](#negatedepicfilterinput) | Negated epic arguments. |
-| `search` | [`String`](#string) | Search query for epic title or description. |
+| `search` | [`String`](#string) | Search query for title or description. |
| `sort` | [`EpicSort`](#epicsort) | List epics by sort order. |
| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| `state` | [`EpicState`](#epicstate) | Filter epics by state. |
@@ -8105,7 +8285,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `milestoneTitle` | [`String`](#string) | Filter epics by milestone title, computed from epic's issues. |
| `myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. |
| `not` | [`NegatedEpicFilterInput`](#negatedepicfilterinput) | Negated epic arguments. |
-| `search` | [`String`](#string) | Search query for epic title or description. |
+| `search` | [`String`](#string) | Search query for title or description. |
| `sort` | [`EpicSort`](#epicsort) | List epics by sort order. |
| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| `state` | [`EpicState`](#epicstate) | Filter epics by state. |
@@ -8391,6 +8571,7 @@ Represents the total number of issues and their weights for a particular day.
| ---- | ---- | ----------- |
| `accessLevel` | [`CiRunnerAccessLevel!`](#cirunneraccesslevel) | Access level of the runner. |
| `active` | [`Boolean!`](#boolean) | Indicates the runner is allowed to receive jobs. |
+| `adminUrl` | [`String`](#string) | Admin URL of the runner. Only available for adminstrators. |
| `contactedAt` | [`Time`](#time) | Last contact from the runner. |
| `description` | [`String`](#string) | Description of the runner. |
| `id` | [`CiRunnerID!`](#cirunnerid) | ID of the runner. |
@@ -8407,6 +8588,7 @@ Represents the total number of issues and their weights for a particular day.
| `shortSha` | [`String`](#string) | First eight characters of the runner's token used to authenticate new job requests. Used as the runner's unique ID. |
| `status` | [`CiRunnerStatus!`](#cirunnerstatus) | Status of the runner. |
| `tagList` | [`[String!]`](#string) | Tags associated with the runner. |
+| `userPermissions` | [`RunnerPermissions!`](#runnerpermissions) | Permissions for the current user on the resource. |
| `version` | [`String`](#string) | Version of the runner. |
### `CiStage`
@@ -8737,7 +8919,7 @@ A custom emoji uploaded by user.
| Name | Type | Description |
| ---- | ---- | ----------- |
| `createdAt` | [`Time!`](#time) | Timestamp the contact was created. |
-| `description` | [`String`](#string) | Description or notes for the contact. |
+| `description` | [`String`](#string) | Description of or notes for the contact. |
| `email` | [`String`](#string) | Email address of the contact. |
| `firstName` | [`String!`](#string) | First name of the contact. |
| `id` | [`ID!`](#id) | Internal ID of the contact. |
@@ -8754,7 +8936,7 @@ A custom emoji uploaded by user.
| ---- | ---- | ----------- |
| `createdAt` | [`Time!`](#time) | Timestamp the organization was created. |
| `defaultRate` | [`Float`](#float) | Standard billing rate for the organization. |
-| `description` | [`String`](#string) | Description or notes for the organization. |
+| `description` | [`String`](#string) | Description of or notes for the organization. |
| `id` | [`ID!`](#id) | Internal ID of the organization. |
| `name` | [`String!`](#string) | Name of the organization. |
| `updatedAt` | [`Time!`](#time) | Timestamp the organization was last updated. |
@@ -9477,7 +9659,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `milestoneTitle` | [`String`](#string) | Filter epics by milestone title, computed from epic's issues. |
| `myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. |
| `not` | [`NegatedEpicFilterInput`](#negatedepicfilterinput) | Negated epic arguments. |
-| `search` | [`String`](#string) | Search query for epic title or description. |
+| `search` | [`String`](#string) | Search query for title or description. |
| `sort` | [`EpicSort`](#epicsort) | List epics by sort order. |
| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| `state` | [`EpicState`](#epicstate) | Filter epics by state. |
@@ -9510,7 +9692,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `milestoneTitle` | [`String`](#string) | Filter epics by milestone title, computed from epic's issues. |
| `myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. |
| `not` | [`NegatedEpicFilterInput`](#negatedepicfilterinput) | Negated epic arguments. |
-| `search` | [`String`](#string) | Search query for epic title or description. |
+| `search` | [`String`](#string) | Search query for title or description. |
| `sort` | [`EpicSort`](#epicsort) | List epics by sort order. |
| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| `state` | [`EpicState`](#epicstate) | Filter epics by state. |
@@ -9634,6 +9816,7 @@ Relationship between an epic and an issue.
| `confidential` | [`Boolean!`](#boolean) | Indicates the issue is confidential. |
| `createNoteEmail` | [`String`](#string) | User specific email address for the issue. |
| `createdAt` | [`Time!`](#time) | Timestamp of when the issue was created. |
+| `customerRelationsContacts` | [`CustomerRelationsContactConnection`](#customerrelationscontactconnection) | Customer relations contacts of the issue. (see [Connections](#connections)) |
| `description` | [`String`](#string) | Description of the issue. |
| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. |
| `designCollection` | [`DesignCollection`](#designcollection) | Collection of design images associated with this issue. |
@@ -9806,6 +9989,18 @@ Representing an event.
| `id` | [`ID!`](#id) | ID of the event. |
| `updatedAt` | [`Time!`](#time) | When this event was updated. |
+### `ExternalAuditEventDestination`
+
+Represents an external resource to send audit events to.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `destinationUrl` | [`String!`](#string) | External destination to send audit events to. |
+| `group` | [`Group!`](#group) | Group the destination belongs to. |
+| `id` | [`ID!`](#id) | ID of the destination. |
+
### `ExternalIssue`
Represents an external issue.
@@ -9976,7 +10171,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
##### `GeoNode.uploadRegistries`
-Find Upload registries on this Geo node Available only when feature flag `geo_upload_replication` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
+Find Upload registries on this Geo node.
Returns [`UploadRegistryConnection`](#uploadregistryconnection).
@@ -10031,6 +10226,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `emailsDisabled` | [`Boolean`](#boolean) | Indicates if a group has email notifications disabled. |
| `epicBoards` | [`EpicBoardConnection`](#epicboardconnection) | Find epic boards. (see [Connections](#connections)) |
| `epicsEnabled` | [`Boolean`](#boolean) | Indicates if Epics are enabled for namespace. |
+| `externalAuditEventDestinations` | [`ExternalAuditEventDestinationConnection`](#externalauditeventdestinationconnection) | External locations that receive audit events belonging to the group. Available only when feature flag `ff_external_audit_events_namespace` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. (see [Connections](#connections)) |
| `fullName` | [`String!`](#string) | Full name of the namespace. |
| `fullPath` | [`ID!`](#id) | Full path of the namespace. |
| `id` | [`ID!`](#id) | ID of the namespace. |
@@ -10181,7 +10377,7 @@ Returns [`Epic`](#epic).
| `milestoneTitle` | [`String`](#string) | Filter epics by milestone title, computed from epic's issues. |
| `myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. |
| `not` | [`NegatedEpicFilterInput`](#negatedepicfilterinput) | Negated epic arguments. |
-| `search` | [`String`](#string) | Search query for epic title or description. |
+| `search` | [`String`](#string) | Search query for title or description. |
| `sort` | [`EpicSort`](#epicsort) | List epics by sort order. |
| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| `state` | [`EpicState`](#epicstate) | Filter epics by state. |
@@ -10226,7 +10422,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `milestoneTitle` | [`String`](#string) | Filter epics by milestone title, computed from epic's issues. |
| `myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. |
| `not` | [`NegatedEpicFilterInput`](#negatedepicfilterinput) | Negated epic arguments. |
-| `search` | [`String`](#string) | Search query for epic title or description. |
+| `search` | [`String`](#string) | Search query for title or description. |
| `sort` | [`EpicSort`](#epicsort) | List epics by sort order. |
| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| `state` | [`EpicState`](#epicstate) | Filter epics by state. |
@@ -10269,11 +10465,13 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `authorUsername` | [`String`](#string) | Username of the author of the issue. |
| `closedAfter` | [`Time`](#time) | Issues closed after this date. |
| `closedBefore` | [`Time`](#time) | Issues closed before this date. |
+| `confidential` | [`Boolean`](#boolean) | Filter for confidential issues. If "false", excludes confidential issues. If "true", returns only confidential issues. |
| `createdAfter` | [`Time`](#time) | Issues created after this date. |
| `createdBefore` | [`Time`](#time) | Issues created before this date. |
| `epicId` | [`String`](#string) | ID of an epic associated with the issues, "none" and "any" values are supported. |
| `iid` | [`String`](#string) | IID of the issue. For example, "1". |
| `iids` | [`[String!]`](#string) | List of IIDs of issues. For example, `["1", "2"]`. |
+| `includeSubepics` | [`Boolean`](#boolean) | Whether to include subepics when filtering issues by epicId. |
| `includeSubgroups` | [`Boolean`](#boolean) | Include issues belonging to subgroups. |
| `iterationId` | [`[ID]`](#id) | List of iteration Global IDs applied to the issue. |
| `iterationWildcardId` | [`IterationWildcardId`](#iterationwildcardid) | Filter by iteration ID wildcard. |
@@ -10282,7 +10480,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `milestoneWildcardId` | [`MilestoneWildcardId`](#milestonewildcardid) | Filter issues by milestone ID wildcard. |
| `myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. Wildcard values "NONE" and "ANY" are supported. |
| `not` | [`NegatedIssueFilterInput`](#negatedissuefilterinput) | Negated arguments. |
-| `search` | [`String`](#string) | Search query for issue title or description. |
+| `search` | [`String`](#string) | Search query for title or description. |
| `sort` | [`IssueSort`](#issuesort) | Sort issues by this criteria. |
| `state` | [`IssuableState`](#issuablestate) | Current state of this issue. |
| `types` | [`[IssueType!]`](#issuetype) | Filter issues by the given issue types. |
@@ -10520,6 +10718,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| `hasIssues` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked issues. |
| `hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
+| `image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| `projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| `reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| `scanner` | [`[String!]`](#string) | Filter vulnerabilities by VulnerabilityScanner.externalId. |
@@ -10781,6 +10980,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
| `confidential` | [`Boolean!`](#boolean) | Indicates the issue is confidential. |
| `createNoteEmail` | [`String`](#string) | User specific email address for the issue. |
| `createdAt` | [`Time!`](#time) | Timestamp of when the issue was created. |
+| `customerRelationsContacts` | [`CustomerRelationsContactConnection`](#customerrelationscontactconnection) | Customer relations contacts of the issue. (see [Connections](#connections)) |
| `description` | [`String`](#string) | Description of the issue. |
| `descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. |
| `designCollection` | [`DesignCollection`](#designcollection) | Collection of design images associated with this issue. |
@@ -11948,10 +12148,10 @@ Nuget metadata.
| Name | Type | Description |
| ---- | ---- | ----------- |
-| `iconUrl` | [`String!`](#string) | Icon URL of the Nuget package. |
+| `iconUrl` | [`String`](#string) | Icon URL of the Nuget package. |
| `id` | [`PackagesNugetMetadatumID!`](#packagesnugetmetadatumid) | ID of the metadatum. |
-| `licenseUrl` | [`String!`](#string) | License URL of the Nuget package. |
-| `projectUrl` | [`String!`](#string) | Project URL of the Nuget package. |
+| `licenseUrl` | [`String`](#string) | License URL of the Nuget package. |
+| `projectUrl` | [`String`](#string) | Project URL of the Nuget package. |
### `OncallParticipantType`
@@ -11985,6 +12185,7 @@ Represents a package in the Package Registry. Note that this type is in beta and
| Name | Type | Description |
| ---- | ---- | ----------- |
+| `canDestroy` | [`Boolean!`](#boolean) | Whether the user can destroy the package. |
| `createdAt` | [`Time!`](#time) | Date of creation. |
| `id` | [`PackagesPackageID!`](#packagespackageid) | ID of the package. |
| `metadata` | [`PackageMetadata`](#packagemetadata) | Package metadata. |
@@ -12044,6 +12245,7 @@ Represents a package details in the Package Registry. Note that this type is in
| Name | Type | Description |
| ---- | ---- | ----------- |
+| `canDestroy` | [`Boolean!`](#boolean) | Whether the user can destroy the package. |
| `createdAt` | [`Time!`](#time) | Date of creation. |
| `dependencyLinks` | [`PackageDependencyLinkConnection`](#packagedependencylinkconnection) | Dependency link. (see [Connections](#connections)) |
| `id` | [`PackagesPackageID!`](#packagespackageid) | ID of the package. |
@@ -12179,6 +12381,7 @@ Represents a file or directory in the project repository that has been locked.
| `configSource` | [`PipelineConfigSourceEnum`](#pipelineconfigsourceenum) | Configuration source of the pipeline (UNKNOWN_SOURCE, REPOSITORY_SOURCE, AUTO_DEVOPS_SOURCE, WEBIDE_SOURCE, REMOTE_SOURCE, EXTERNAL_PROJECT_SOURCE, BRIDGE_SOURCE, PARAMETER_SOURCE, COMPLIANCE_SOURCE). |
| `coverage` | [`Float`](#float) | Coverage percentage. |
| `createdAt` | [`Time!`](#time) | Timestamp of the pipeline's creation. |
+| `dastProfile` | [`DastProfile`](#dastprofile) | DAST profile associated with the pipeline. Returns `null`if `dast_view_scans` feature flag is disabled. |
| `detailedStatus` | [`DetailedStatus!`](#detailedstatus) | Detailed status of the pipeline. |
| `downstream` | [`PipelineConnection`](#pipelineconnection) | Pipelines this pipeline will trigger. (see [Connections](#connections)) |
| `duration` | [`Int`](#int) | Duration of the pipeline in seconds. |
@@ -12624,7 +12827,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| `normalizedTargetUrls` | [`[String!]`](#string) | Normalized URL of the target to be scanned. |
-| `status` | [`DastSiteValidationStatusEnum`](#dastsitevalidationstatusenum) | Status of the site validation. Ignored if `dast_failed_site_validations` feature flag is disabled. |
+| `status` | [`DastSiteValidationStatusEnum`](#dastsitevalidationstatusenum) | Status of the site validation. |
##### `Project.environment`
@@ -12702,11 +12905,13 @@ Returns [`Issue`](#issue).
| `authorUsername` | [`String`](#string) | Username of the author of the issue. |
| `closedAfter` | [`Time`](#time) | Issues closed after this date. |
| `closedBefore` | [`Time`](#time) | Issues closed before this date. |
+| `confidential` | [`Boolean`](#boolean) | Filter for confidential issues. If "false", excludes confidential issues. If "true", returns only confidential issues. |
| `createdAfter` | [`Time`](#time) | Issues created after this date. |
| `createdBefore` | [`Time`](#time) | Issues created before this date. |
| `epicId` | [`String`](#string) | ID of an epic associated with the issues, "none" and "any" values are supported. |
| `iid` | [`String`](#string) | IID of the issue. For example, "1". |
| `iids` | [`[String!]`](#string) | List of IIDs of issues. For example, `["1", "2"]`. |
+| `includeSubepics` | [`Boolean`](#boolean) | Whether to include subepics when filtering issues by epicId. |
| `iterationId` | [`[ID]`](#id) | List of iteration Global IDs applied to the issue. |
| `iterationWildcardId` | [`IterationWildcardId`](#iterationwildcardid) | Filter by iteration ID wildcard. |
| `labelName` | [`[String]`](#string) | Labels applied to this issue. |
@@ -12714,7 +12919,7 @@ Returns [`Issue`](#issue).
| `milestoneWildcardId` | [`MilestoneWildcardId`](#milestonewildcardid) | Filter issues by milestone ID wildcard. |
| `myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. Wildcard values "NONE" and "ANY" are supported. |
| `not` | [`NegatedIssueFilterInput`](#negatedissuefilterinput) | Negated arguments. |
-| `search` | [`String`](#string) | Search query for issue title or description. |
+| `search` | [`String`](#string) | Search query for title or description. |
| `sort` | [`IssueSort`](#issuesort) | Sort issues by this criteria. |
| `state` | [`IssuableState`](#issuablestate) | Current state of this issue. |
| `types` | [`[IssueType!]`](#issuetype) | Filter issues by the given issue types. |
@@ -12738,6 +12943,7 @@ Returns [`IssueStatusCountsType`](#issuestatuscountstype).
| `authorUsername` | [`String`](#string) | Username of the author of the issue. |
| `closedAfter` | [`Time`](#time) | Issues closed after this date. |
| `closedBefore` | [`Time`](#time) | Issues closed before this date. |
+| `confidential` | [`Boolean`](#boolean) | Filter for confidential issues. If "false", excludes confidential issues. If "true", returns only confidential issues. |
| `createdAfter` | [`Time`](#time) | Issues created after this date. |
| `createdBefore` | [`Time`](#time) | Issues created before this date. |
| `iid` | [`String`](#string) | IID of the issue. For example, "1". |
@@ -12747,7 +12953,7 @@ Returns [`IssueStatusCountsType`](#issuestatuscountstype).
| `milestoneWildcardId` | [`MilestoneWildcardId`](#milestonewildcardid) | Filter issues by milestone ID wildcard. |
| `myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. Wildcard values "NONE" and "ANY" are supported. |
| `not` | [`NegatedIssueFilterInput`](#negatedissuefilterinput) | Negated arguments. |
-| `search` | [`String`](#string) | Search query for issue title or description. |
+| `search` | [`String`](#string) | Search query for title or description. |
| `types` | [`[IssueType!]`](#issuetype) | Filter issues by the given issue types. |
| `updatedAfter` | [`Time`](#time) | Issues updated after this date. |
| `updatedBefore` | [`Time`](#time) | Issues updated before this date. |
@@ -12772,11 +12978,13 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `authorUsername` | [`String`](#string) | Username of the author of the issue. |
| `closedAfter` | [`Time`](#time) | Issues closed after this date. |
| `closedBefore` | [`Time`](#time) | Issues closed before this date. |
+| `confidential` | [`Boolean`](#boolean) | Filter for confidential issues. If "false", excludes confidential issues. If "true", returns only confidential issues. |
| `createdAfter` | [`Time`](#time) | Issues created after this date. |
| `createdBefore` | [`Time`](#time) | Issues created before this date. |
| `epicId` | [`String`](#string) | ID of an epic associated with the issues, "none" and "any" values are supported. |
| `iid` | [`String`](#string) | IID of the issue. For example, "1". |
| `iids` | [`[String!]`](#string) | List of IIDs of issues. For example, `["1", "2"]`. |
+| `includeSubepics` | [`Boolean`](#boolean) | Whether to include subepics when filtering issues by epicId. |
| `iterationId` | [`[ID]`](#id) | List of iteration Global IDs applied to the issue. |
| `iterationWildcardId` | [`IterationWildcardId`](#iterationwildcardid) | Filter by iteration ID wildcard. |
| `labelName` | [`[String]`](#string) | Labels applied to this issue. |
@@ -12784,7 +12992,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| `milestoneWildcardId` | [`MilestoneWildcardId`](#milestonewildcardid) | Filter issues by milestone ID wildcard. |
| `myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. Wildcard values "NONE" and "ANY" are supported. |
| `not` | [`NegatedIssueFilterInput`](#negatedissuefilterinput) | Negated arguments. |
-| `search` | [`String`](#string) | Search query for issue title or description. |
+| `search` | [`String`](#string) | Search query for title or description. |
| `sort` | [`IssueSort`](#issuesort) | Sort issues by this criteria. |
| `state` | [`IssuableState`](#issuablestate) | Current state of this issue. |
| `types` | [`[IssueType!]`](#issuetype) | Filter issues by the given issue types. |
@@ -13196,6 +13404,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| `hasIssues` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have linked issues. |
| `hasResolution` | [`Boolean`](#boolean) | Returns only the vulnerabilities which have been resolved on default branch. |
+| `image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| `projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| `reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| `scanner` | [`[String!]`](#string) | Filter vulnerabilities by VulnerabilityScanner.externalId. |
@@ -13524,7 +13733,7 @@ Returns [`[String!]`](#string).
##### `Repository.paginatedTree`
-Paginated tree of the repository. Available only when feature flag `paginated_tree_graphql_query` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
+Paginated tree of the repository. Available only when feature flag `paginated_tree_graphql_query` is enabled. This flag is enabled by default.
Returns [`TreeConnection`](#treeconnection).
@@ -13676,6 +13885,16 @@ Counts of requirements by their state.
| `downloadLocation` | [`String!`](#string) | Download location for the runner for the platform architecture. |
| `name` | [`String!`](#string) | Name of the runner platform architecture. |
+### `RunnerPermissions`
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `deleteRunner` | [`Boolean!`](#boolean) | Indicates the user can perform `delete_runner` on this resource. |
+| `readRunner` | [`Boolean!`](#boolean) | Indicates the user can perform `read_runner` on this resource. |
+| `updateRunner` | [`Boolean!`](#boolean) | Indicates the user can perform `update_runner` on this resource. |
+
### `RunnerPlatform`
#### Fields
@@ -13850,6 +14069,7 @@ A Sentry error.
| `gitlabCommitPath` | [`String`](#string) | Path to the GitLab page for the GitLab commit attributed to the error. |
| `gitlabIssuePath` | [`String`](#string) | URL of GitLab Issue. |
| `id` | [`ID!`](#id) | ID (global ID) of the error. |
+| `integrated` | [`Boolean`](#boolean) | Error tracking backend. |
| `lastReleaseLastCommit` | [`String`](#string) | Commit the error was last seen. |
| `lastReleaseShortVersion` | [`String`](#string) | Release short version the error was last seen. |
| `lastReleaseVersion` | [`String`](#string) | Release version the error was last seen. |
@@ -14709,8 +14929,10 @@ Represents a vulnerability.
| `hasSolutions` | [`Boolean`](#boolean) | Indicates whether there is a solution available for this vulnerability. |
| `id` | [`ID!`](#id) | GraphQL ID of the vulnerability. |
| `identifiers` | [`[VulnerabilityIdentifier!]!`](#vulnerabilityidentifier) | Identifiers of the vulnerability. |
+| `links` | [`[VulnerabilityLink!]!`](#vulnerabilitylink) | List of links associated with the vulnerability. |
| `location` | [`VulnerabilityLocation`](#vulnerabilitylocation) | Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability. |
| `mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request that fixes the vulnerability. |
+| `message` | [`String`](#string) | Short text description of the vulnerability. This may include the finding's specific information. |
| `notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) |
| `primaryIdentifier` | [`VulnerabilityIdentifier`](#vulnerabilityidentifier) | Primary identifier of the vulnerability. |
| `project` | [`Project`](#project) | Project on which the vulnerability was found. |
@@ -14956,6 +15178,17 @@ Represents an issue link of a vulnerability.
| `issue` | [`Issue!`](#issue) | Issue attached to issue link. |
| `linkType` | [`VulnerabilityIssueLinkType!`](#vulnerabilityissuelinktype) | Type of the issue link. |
+### `VulnerabilityLink`
+
+Represents a link related to a vulnerability.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `name` | [`String`](#string) | Name of the link. |
+| `url` | [`String!`](#string) | URL of the link. |
+
### `VulnerabilityLocationContainerScanning`
Represents the location of a vulnerability found by a container security scan.
@@ -15345,10 +15578,10 @@ Values for sorting runners.
| Value | Description |
| ----- | ----------- |
-| `ACTIVE` | A runner that is active. |
-| `NOT_CONNECTED` | A runner that is not connected. |
-| `OFFLINE` | A runner that is offline. |
-| `ONLINE` | A runner that is online. |
+| `ACTIVE` | A runner that is not paused. |
+| `NOT_CONNECTED` | A runner that has never contacted this instance. |
+| `OFFLINE` | A runner that has not contacted this instance within the last 2 hours. |
+| `ONLINE` | A runner that contacted this instance within the last 2 hours. |
| `PAUSED` | A runner that is paused. |
### `CiRunnerType`
@@ -15425,6 +15658,7 @@ Conan file types.
| `FOURTEEN_DAYS` | 14 days until tags are automatically removed. |
| `NINETY_DAYS` | 90 days until tags are automatically removed. |
| `SEVEN_DAYS` | 7 days until tags are automatically removed. |
+| `SIXTY_DAYS` | 60 days until tags are automatically removed. |
| `THIRTY_DAYS` | 30 days until tags are automatically removed. |
### `ContainerRepositoryCleanupStatus`
@@ -15750,6 +15984,14 @@ State of a GitLab issue or merge request.
| `locked` | Discussion has been locked. |
| `opened` | In open state. |
+### `IssueCreationIterationWildcardId`
+
+Iteration ID wildcard values for issue creation.
+
+| Value | Description |
+| ----- | ----------- |
+| `CURRENT` | Current iteration. |
+
### `IssueSort`
Values for sorting issues.
@@ -16013,7 +16255,7 @@ Milestone ID wildcard values.
| `ANY` | Milestone is assigned. |
| `NONE` | No milestone is assigned. |
| `STARTED` | Milestone assigned is open and started (start date <= today). |
-| `UPCOMING` | Milestone assigned is due closest in the future (due date > today). |
+| `UPCOMING` | Milestone assigned is due in the future (due date > today). |
### `MoveType`
@@ -16455,6 +16697,7 @@ Name of the feature that the callout is for.
| `REGISTRATION_ENABLED_CALLOUT` | Callout feature name for registration_enabled_callout. |
| `SECURITY_CONFIGURATION_DEVOPS_ALERT` | Callout feature name for security_configuration_devops_alert. |
| `SECURITY_CONFIGURATION_UPGRADE_BANNER` | Callout feature name for security_configuration_upgrade_banner. |
+| `SECURITY_NEWSLETTER_CALLOUT` | Callout feature name for security_newsletter_callout. |
| `SUGGEST_PIPELINE` | Callout feature name for suggest_pipeline. |
| `SUGGEST_POPOVER_DISMISSED` | Callout feature name for suggest_popover_dismissed. |
| `TABS_POSITION_HIGHLIGHT` | Callout feature name for tabs_position_highlight. |
@@ -16646,6 +16889,12 @@ A `AnalyticsDevopsAdoptionEnabledNamespaceID` is a global ID. It is encoded as a
An example `AnalyticsDevopsAdoptionEnabledNamespaceID` is: `"gid://gitlab/Analytics::DevopsAdoption::EnabledNamespace/1"`.
+### `AuditEventsExternalAuditEventDestinationID`
+
+A `AuditEventsExternalAuditEventDestinationID` is a global ID. It is encoded as a string.
+
+An example `AuditEventsExternalAuditEventDestinationID` is: `"gid://gitlab/AuditEvents::ExternalAuditEventDestination/1"`.
+
### `AwardableID`
A `AwardableID` is a global ID. It is encoded as a string.
@@ -16732,6 +16981,12 @@ A `CustomEmojiID` is a global ID. It is encoded as a string.
An example `CustomEmojiID` is: `"gid://gitlab/CustomEmoji/1"`.
+### `CustomerRelationsContactID`
+
+A `CustomerRelationsContactID` is a global ID. It is encoded as a string.
+
+An example `CustomerRelationsContactID` is: `"gid://gitlab/CustomerRelations::Contact/1"`.
+
### `CustomerRelationsOrganizationID`
A `CustomerRelationsOrganizationID` is a global ID. It is encoded as a string.
@@ -17906,6 +18161,7 @@ Represents an escalation rule.
| `milestoneTitle` | [`[String!]`](#string) | Milestone not applied to this issue. |
| `milestoneWildcardId` | [`NegatedMilestoneWildcardId`](#negatedmilestonewildcardid) | Filter by negated milestone wildcard values. |
| `myReactionEmoji` | [`String`](#string) | Filter by reaction emoji applied by the current user. |
+| `types` | [`[IssueType!]`](#issuetype) | Filters out issues by the given issue types. |
| `weight` | [`String`](#string) | Weight not applied to the issue. |
### `OncallRotationActivePeriodInputType`
@@ -18057,3 +18313,23 @@ A time-frame defined as a closed inclusive range of two dates.
| `externalType` | [`String`](#string) | External type of the vulnerability identifier. |
| `name` | [`String!`](#string) | Name of the vulnerability identifier. |
| `url` | [`String!`](#string) | URL of the vulnerability identifier. |
+
+### `VulnerabilityScannerInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `id` | [`String!`](#string) | Unique ID that identifies the scanner. |
+| `name` | [`String!`](#string) | Human readable value that identifies the analyzer, not required to be unique. |
+| `url` | [`String!`](#string) | Link to more information about the analyzer. |
+| `vendor` | [`VulnerabilityScannerVendorInput`](#vulnerabilityscannervendorinput) | Information about vendor/maintainer of the scanner. |
+| `version` | [`String!`](#string) | Version of the scanner. |
+
+### `VulnerabilityScannerVendorInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `name` | [`String!`](#string) | Name of the vendor/maintainer. |
diff --git a/doc/api/group_clusters.md b/doc/api/group_clusters.md
index 83373368fc..81ca45c453 100644
--- a/doc/api/group_clusters.md
+++ b/doc/api/group_clusters.md
@@ -250,7 +250,7 @@ Parameters:
NOTE:
`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added
-through the ["Add existing Kubernetes cluster"](../user/project/clusters/add_remove_clusters.md#add-existing-cluster) option or
+through the ["Add existing Kubernetes cluster"](../user/project/clusters/add_existing_cluster.md) option or
through the ["Add existing cluster to group"](#add-existing-cluster-to-group) endpoint.
Example request:
diff --git a/doc/api/group_import_export.md b/doc/api/group_import_export.md
index 0a431cfdfb..212a62516f 100644
--- a/doc/api/group_import_export.md
+++ b/doc/api/group_import_export.md
@@ -57,8 +57,12 @@ GET /groups/:id/export/download
| `id` | integer/string | yes | ID of the group owned by the authenticated user |
```shell
-curl --header "PRIVATE-TOKEN: " --remote-header-name \
- --remote-name "https://gitlab.example.com/api/v4/groups/1/export/download"
+group=1
+token=secret
+curl --request GET\
+ --header "PRIVATE-TOKEN: ${token}" \
+ --output download_group_${group}.tar.gz \
+ "https://gitlab.example.com/api/v4/groups/${group}/export/download"
```
```shell
diff --git a/doc/api/group_repository_storage_moves.md b/doc/api/group_repository_storage_moves.md
index a893bffb1f..9d4120ec35 100644
--- a/doc/api/group_repository_storage_moves.md
+++ b/doc/api/group_repository_storage_moves.md
@@ -10,7 +10,7 @@ type: reference
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53016) in GitLab 13.9.
Group repositories can be moved between storages. This API can help you when
-[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrate-to-gitaly-cluster), for
+[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrating-to-gitaly-cluster), for
example, or to migrate a [group wiki](../user/project/wiki/index.md#group-wikis).
As group repository storage moves are processed, they transition through different states. Values
diff --git a/doc/api/groups.md b/doc/api/groups.md
index bd4c7de567..7efecfc2c9 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -830,7 +830,7 @@ curl --request POST --header "PRIVATE-TOKEN: " \
## Transfer project to group
-Transfer a project to the Group namespace. Available only to instance administrators, although an [alternative API endpoint](projects.md#transfer-a-project-to-a-new-namespace) is available which does not require instance administrator access. Transferring projects may fail when tagged packages exist in the project's repository.
+Transfer a project to the Group namespace. Available only to instance administrators, although an [alternative API endpoint](projects.md#transfer-a-project-to-a-new-namespace) is available which does not require instance administrator role. Transferring projects may fail when tagged packages exist in the project's repository.
```plaintext
POST /groups/:id/projects/:project_id
diff --git a/doc/api/instance_clusters.md b/doc/api/instance_clusters.md
index 4e0ec3bd43..58f88b26bc 100644
--- a/doc/api/instance_clusters.md
+++ b/doc/api/instance_clusters.md
@@ -8,10 +8,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36001) in GitLab 13.2.
-Instance-level Kubernetes clusters allow you to connect a Kubernetes cluster to the GitLab instance, which enables you to use the same cluster across multiple projects. [More information](../user/instance/clusters/index.md)
+With [instance-level Kubernetes clusters](../user/instance/clusters/index.md),
+you can connect a Kubernetes cluster to the GitLab instance and use the same cluster across all of
+the projects within your instance.
NOTE:
-Users need administrator access to use these endpoints.
+Users need the Administrator role to use these endpoints.
## List instance clusters
@@ -240,7 +242,7 @@ Parameters:
NOTE:
`name`, `api_url`, `ca_cert` and `token` can only be updated if the cluster was added
-through the [Add existing Kubernetes cluster](../user/project/clusters/add_remove_clusters.md#add-existing-cluster) option or
+through the [Add existing Kubernetes cluster](../user/project/clusters/add_existing_cluster.md) option or
through the [Add existing instance cluster](#add-existing-instance-cluster) endpoint.
Example request:
diff --git a/doc/api/integrations.md b/doc/api/integrations.md
new file mode 100644
index 0000000000..3c649e8d04
--- /dev/null
+++ b/doc/api/integrations.md
@@ -0,0 +1,1566 @@
+---
+stage: Ecosystem
+group: Integrations
+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/#assignments
+---
+
+# Integrations API **(FREE)**
+
+This API enables you to work with external services that integrate with GitLab.
+
+NOTE:
+In GitLab 14.4, the `services` endpoint was [renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/334500) to `integrations`.
+Calls to the Integrations API can be made to both `/projects/:id/services` and `/projects/:id/integrations`.
+The examples in this document refer to the endpoint at `/projects/:id/integrations`.
+
+This API requires an access token with the [Maintainer or Owner role](../user/permissions.md).
+
+## List all active integrations
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21330) in GitLab 12.7.
+
+Get a list of all active project integrations.
+
+```plaintext
+GET /projects/:id/integrations
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 75,
+ "title": "Jenkins CI",
+ "slug": "jenkins",
+ "created_at": "2019-11-20T11:20:25.297Z",
+ "updated_at": "2019-11-20T12:24:37.498Z",
+ "active": true,
+ "commit_events": true,
+ "push_events": true,
+ "issues_events": true,
+ "confidential_issues_events": true,
+ "merge_requests_events": true,
+ "tag_push_events": false,
+ "note_events": true,
+ "confidential_note_events": true,
+ "pipeline_events": true,
+ "wiki_page_events": true,
+ "job_events": true,
+ "comment_on_event_enabled": true
+ },
+ {
+ "id": 76,
+ "title": "Alerts endpoint",
+ "slug": "alerts",
+ "created_at": "2019-11-20T11:20:25.297Z",
+ "updated_at": "2019-11-20T12:24:37.498Z",
+ "active": true,
+ "commit_events": true,
+ "push_events": true,
+ "issues_events": true,
+ "confidential_issues_events": true,
+ "merge_requests_events": true,
+ "tag_push_events": true,
+ "note_events": true,
+ "confidential_note_events": true,
+ "pipeline_events": true,
+ "wiki_page_events": true,
+ "job_events": true,
+ "comment_on_event_enabled": true
+ }
+]
+```
+
+## Asana
+
+Add commit messages as comments to Asana tasks.
+
+See also the [Asana integration documentation](../user/project/integrations/asana.md).
+
+### Create/Edit Asana integration
+
+Set Asana integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/asana
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `api_key` | string | true | User API token. User must have access to task. All comments are attributed to this user. |
+| `restrict_to_branch` | string | false | Comma-separated list of branches to be are automatically inspected. Leave blank to include all branches. |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Asana integration
+
+Delete Asana integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/asana
+```
+
+### Get Asana integration settings
+
+Get Asana integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/asana
+```
+
+## Assembla
+
+Project Management Software (Source Commits Endpoint)
+
+### Create/Edit Assembla integration
+
+Set Assembla integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/assembla
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | The authentication token
+| `subdomain` | string | false | The subdomain setting |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Assembla integration
+
+Delete Assembla integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/assembla
+```
+
+### Get Assembla integration settings
+
+Get Assembla integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/assembla
+```
+
+## Atlassian Bamboo CI
+
+A continuous integration and build server
+
+### Create/Edit Atlassian Bamboo CI integration
+
+Set Atlassian Bamboo CI integration for a project.
+
+> You must set up automatic revision labeling and a repository trigger in Bamboo.
+
+```plaintext
+PUT /projects/:id/integrations/bamboo
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `bamboo_url` | string | true | Bamboo root URL. For example, `https://bamboo.example.com`. |
+| `build_key` | string | true | Bamboo build plan key like KEY |
+| `username` | string | true | A user with API access, if applicable |
+| `password` | string | true | Password of the user |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Atlassian Bamboo CI integration
+
+Delete Atlassian Bamboo CI integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/bamboo
+```
+
+### Get Atlassian Bamboo CI integration settings
+
+Get Atlassian Bamboo CI integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/bamboo
+```
+
+## Bugzilla
+
+Bugzilla Issue Tracker
+
+### Create/Edit Bugzilla integration
+
+Set Bugzilla integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/bugzilla
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `new_issue_url` | string | true | New Issue URL |
+| `issues_url` | string | true | Issue URL |
+| `project_url` | string | true | Project URL |
+| `description` | string | false | Description |
+| `title` | string | false | Title |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Bugzilla integration
+
+Delete Bugzilla integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/bugzilla
+```
+
+### Get Bugzilla integration settings
+
+Get Bugzilla integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/bugzilla
+```
+
+## Buildkite
+
+Continuous integration and deployments
+
+### Create/Edit Buildkite integration
+
+Set Buildkite integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/buildkite
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | Buildkite project GitLab token |
+| `project_url` | string | true | Pipeline URL. For example, `https://buildkite.com/example/pipeline` |
+| `enable_ssl_verification` | boolean | false | DEPRECATED: This parameter has no effect since SSL verification is always enabled |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Buildkite integration
+
+Delete Buildkite integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/buildkite
+```
+
+### Get Buildkite integration settings
+
+Get Buildkite integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/buildkite
+```
+
+## Campfire
+
+Send notifications about push events to Campfire chat rooms.
+[New users can no longer sign up for Campfire](https://basecamp.com/retired/campfire).
+
+### Create/Edit Campfire integration
+
+Set Campfire integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/campfire
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+|---------------|---------|----------|---------------------------------------------------------------------------------------------|
+| `token` | string | true | Campfire API token. To find it, log into Campfire and select **My info**. |
+| `subdomain` | string | false | Campfire subdomain. Text between `https://` and `.campfirenow.com` when you're logged in. |
+| `room` | string | false | Campfire room. The last part of the URL when you're in a room. |
+| `push_events` | boolean | false | Enable notifications for push events. |
+
+### Delete Campfire integration
+
+Delete Campfire integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/campfire
+```
+
+### Get Campfire integration settings
+
+Get Campfire integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/campfire
+```
+
+## Datadog
+
+Datadog system monitoring.
+
+### Create/Edit Datadog integration
+
+Set Datadog integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/datadog
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `api_key` | string | true | API key used for authentication with Datadog |
+| `api_url` | string | false | (Advanced) Define the full URL for your Datadog site directly |
+| `datadog_site` | string | false | Choose the Datadog site to send data to. Set to `datadoghq.eu` to send data to the EU site |
+| `datadog_service` | string | false | Name of this GitLab instance that all data will be tagged with |
+| `datadog_env` | string | false | The environment tag that traces will be tagged with |
+
+### Delete Datadog integration
+
+Delete Datadog integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/datadog
+```
+
+### Get Datadog integration settings
+
+Get Datadog integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/datadog
+```
+
+## Unify Circuit
+
+Unify Circuit RTC and collaboration tool.
+
+### Create/Edit Unify Circuit integration
+
+Set Unify Circuit integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/unify-circuit
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Unify Circuit webhook. For example, `https://circuit.com/rest/v2/webhooks/incoming/...`. |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Unify Circuit integration
+
+Delete Unify Circuit integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/unify-circuit
+```
+
+### Get Unify Circuit integration settings
+
+Get Unify Circuit integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/unify-circuit
+```
+
+## Webex Teams
+
+Webex Teams collaboration tool.
+
+### Create/Edit Webex Teams integration
+
+Set Webex Teams integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/webex-teams
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Webex Teams webhook. For example, `https://api.ciscospark.com/v1/webhooks/incoming/...`. |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Webex Teams integration
+
+Delete Webex Teams integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/webex-teams
+```
+
+### Get Webex Teams integration settings
+
+Get Webex Teams integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/webex-teams
+```
+
+## Custom Issue Tracker
+
+Custom issue tracker
+
+### Create/Edit Custom Issue Tracker integration
+
+Set Custom Issue Tracker integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/custom-issue-tracker
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `new_issue_url` | string | true | New Issue URL |
+| `issues_url` | string | true | Issue URL |
+| `project_url` | string | true | Project URL |
+| `description` | string | false | Description |
+| `title` | string | false | Title |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Custom Issue Tracker integration
+
+Delete Custom Issue Tracker integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/custom-issue-tracker
+```
+
+### Get Custom Issue Tracker integration settings
+
+Get Custom Issue Tracker integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/custom-issue-tracker
+```
+
+## Discord
+
+Send notifications about project events to a Discord channel.
+
+### Create/Edit Discord integration
+
+Set Discord integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/discord
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | Discord webhook. For example, `https://discord.com/api/webhooks/…` |
+
+### Delete Discord integration
+
+Delete Discord integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/discord
+```
+
+### Get Discord integration settings
+
+Get Discord integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/discord
+```
+
+## Drone CI
+
+Drone is a Continuous Integration platform built on Docker, written in Go
+
+### Create/Edit Drone CI integration
+
+Set Drone CI integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/drone-ci
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | Drone CI project specific token |
+| `drone_url` | string | true | `http://drone.example.com` |
+| `enable_ssl_verification` | boolean | false | Enable SSL verification |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+
+### Delete Drone CI integration
+
+Delete Drone CI integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/drone-ci
+```
+
+### Get Drone CI integration settings
+
+Get Drone CI integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/drone-ci
+```
+
+## Emails on Push
+
+Email the commits and diff of each push to a list of recipients.
+
+### Create/Edit Emails on Push integration
+
+Set Emails on Push integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/emails-on-push
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `recipients` | string | true | Emails separated by whitespace |
+| `disable_diffs` | boolean | false | Disable code diffs |
+| `send_from_committer_email` | boolean | false | Send from committer |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". Notifications are always fired for tag pushes. The default value is "all" |
+
+### Delete Emails on Push integration
+
+Delete Emails on Push integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/emails-on-push
+```
+
+### Get Emails on Push integration settings
+
+Get Emails on Push integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/emails-on-push
+```
+
+## Engineering Workflow Management (EWM)
+
+Use IBM Engineering Workflow Management (EWM) as a project's issue tracker.
+
+### Create/Edit EWM integration
+
+Set EWM integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/ewm
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `new_issue_url` | string | true | The URL to create an issue in EWM |
+| `project_url` | string | true | The URL to the project in EWM |
+| `issues_url` | string | true | The URL to view an issue in EWM. Must contain `:id` |
+
+### Delete EWM integration
+
+Delete EWM integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/ewm
+```
+
+### Get EWM integration settings
+
+Get EWM integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/ewm
+```
+
+## Confluence integration
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220934) in GitLab 13.2.
+
+Replaces the link to the internal wiki with a link to a Confluence Cloud Workspace.
+
+### Create/Edit Confluence integration
+
+Set Confluence integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/confluence
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `confluence_url` | string | true | The URL of the Confluence Cloud Workspace hosted on atlassian.net. |
+
+### Delete Confluence integration
+
+Delete Confluence integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/confluence
+```
+
+### Get Confluence integration settings
+
+Get Confluence integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/confluence
+```
+
+## External wiki
+
+Replaces the link to the internal wiki with a link to an external wiki.
+
+### Create/Edit External wiki integration
+
+Set External wiki integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/external-wiki
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `external_wiki_url` | string | true | The URL of the external wiki |
+
+### Delete External wiki integration
+
+Delete External wiki integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/external-wiki
+```
+
+### Get External wiki integration settings
+
+Get External wiki integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/external-wiki
+```
+
+## Flowdock
+
+Flowdock is a ChatOps application for collaboration in software engineering
+companies. You can send notifications from GitLab events to Flowdock flows.
+For integration instructions, see the [Flowdock documentation](https://www.flowdock.com/help/gitlab).
+
+### Create/Edit Flowdock integration
+
+Set Flowdock integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/flowdock
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | Flowdock Git source token |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Flowdock integration
+
+Delete Flowdock integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/flowdock
+```
+
+### Get Flowdock integration settings
+
+Get Flowdock integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/flowdock
+```
+
+## GitHub **(PREMIUM)**
+
+Code collaboration software.
+
+### Create/Edit GitHub integration
+
+Set GitHub integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/github
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | GitHub API token with `repo:status` OAuth scope |
+| `repository_url` | string | true | GitHub repository URL |
+| `static_context` | boolean | false | Append instance name instead of branch to [status check name](../user/project/integrations/github.md#static--dynamic-status-check-names) |
+
+### Delete GitHub integration
+
+Delete GitHub integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/github
+```
+
+### Get GitHub integration settings
+
+Get GitHub integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/github
+```
+
+## Hangouts Chat
+
+Google Workspace team collaboration tool.
+
+### Create/Edit Hangouts Chat integration
+
+Set Hangouts Chat integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/hangouts-chat
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Hangouts Chat webhook. For example, `https://chat.googleapis.com/v1/spaces...`. |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Hangouts Chat integration
+
+Delete Hangouts Chat integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/hangouts-chat
+```
+
+### Get Hangouts Chat integration settings
+
+Get Hangouts Chat integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/hangouts-chat
+```
+
+## Irker (IRC gateway)
+
+Send IRC messages, on update, to a list of recipients through an irker gateway.
+
+For more information, see the [irker integration documentation](../user/project/integrations/irker.md).
+
+### Create/Edit Irker (IRC gateway) integration
+
+Set Irker (IRC gateway) integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/irker
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `recipients` | string | true | Recipients/channels separated by whitespaces |
+| `default_irc_uri` | string | false | `irc://irc.network.net:6697/` |
+| `server_host` | string | false | localhost |
+| `server_port` | integer | false | 6659 |
+| `colorize_messages` | boolean | false | Colorize messages |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Irker (IRC gateway) integration
+
+Delete Irker (IRC gateway) integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/irker
+```
+
+### Get Irker (IRC gateway) integration settings
+
+Get Irker (IRC gateway) integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/irker
+```
+
+## Jira
+
+Jira issue tracker.
+
+### Get Jira integration settings
+
+Get Jira integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/jira
+```
+
+### Create/Edit Jira integration
+
+Set Jira integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/jira
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `url` | string | yes | The URL to the Jira project which is being linked to this GitLab project. For example, `https://jira.example.com`. |
+| `api_url` | string | no | The base URL to the Jira instance API. Web URL value is used if not set. For example, `https://jira-api.example.com`. |
+| `username` | string | yes | The username of the user created to be used with GitLab/Jira. |
+| `password` | string | yes | The password of the user created to be used with GitLab/Jira. |
+| `active` | boolean | no | Activates or deactivates the integration. Defaults to false (deactivated). |
+| `jira_issue_transition_automatic` | boolean | no | Enable [automatic issue transitions](../integration/jira/issues.md#automatic-issue-transitions). Takes precedence over `jira_issue_transition_id` if enabled. Defaults to `false` |
+| `jira_issue_transition_id` | string | no | The ID of one or more transitions for [custom issue transitions](../integration/jira/issues.md#custom-issue-transitions). Ignored if `jira_issue_transition_automatic` is enabled. Defaults to a blank string, which disables custom transitions. |
+| `commit_events` | boolean | false | Enable notifications for commit events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `comment_on_event_enabled` | boolean | false | Enable comments inside Jira issues on each GitLab event (commit / merge request) |
+
+### Delete Jira integration
+
+Remove all previously Jira integrations from a project.
+
+```plaintext
+DELETE /projects/:id/integrations/jira
+```
+
+## Slack Slash Commands
+
+Ability to receive slash commands from a Slack chat instance.
+
+### Get Slack Slash Command integration settings
+
+Get Slack Slash Command integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/slack-slash-commands
+```
+
+Example response:
+
+```json
+{
+ "id": 4,
+ "title": "Slack slash commands",
+ "slug": "slack-slash-commands",
+ "created_at": "2017-06-27T05:51:39-07:00",
+ "updated_at": "2017-06-27T05:51:39-07:00",
+ "active": true,
+ "push_events": true,
+ "issues_events": true,
+ "confidential_issues_events": true,
+ "merge_requests_events": true,
+ "tag_push_events": true,
+ "note_events": true,
+ "job_events": true,
+ "pipeline_events": true,
+ "comment_on_event_enabled": false,
+ "properties": {
+ "token": ""
+ }
+}
+```
+
+### Create/Edit Slack Slash Commands integration
+
+Set Slack Slash Command for a project.
+
+```plaintext
+PUT /projects/:id/integrations/slack-slash-commands
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | yes | The Slack token |
+
+### Delete Slack Slash Command integration
+
+Delete Slack Slash Command integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/slack-slash-commands
+```
+
+## Mattermost Slash Commands
+
+Ability to receive slash commands from a Mattermost chat instance.
+
+### Get Mattermost Slash Command integration settings
+
+Get Mattermost Slash Command integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/mattermost-slash-commands
+```
+
+### Create/Edit Mattermost Slash Command integration
+
+Set Mattermost Slash Command for a project.
+
+```plaintext
+PUT /projects/:id/integrations/mattermost-slash-commands
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | yes | The Mattermost token |
+| `username` | string | no | The username to use to post the message |
+
+### Delete Mattermost Slash Command integration
+
+Delete Mattermost Slash Command integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/mattermost-slash-commands
+```
+
+## Packagist
+
+Update your project on Packagist (the main Composer repository) when commits or tags are pushed to GitLab.
+
+### Create/Edit Packagist integration
+
+Set Packagist integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/packagist
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `username` | string | yes | The username of a Packagist account |
+| `token` | string | yes | API token to the Packagist server |
+| `server` | boolean | no | URL of the Packagist server. Leave blank for default: |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+
+### Delete Packagist integration
+
+Delete Packagist integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/packagist
+```
+
+### Get Packagist integration settings
+
+Get Packagist integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/packagist
+```
+
+## Pipeline-Emails
+
+Get emails for GitLab CI/CD pipelines.
+
+### Create/Edit Pipeline-Emails integration
+
+Set Pipeline-Emails integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/pipelines-email
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `recipients` | string | yes | Comma-separated list of recipient email addresses |
+| `add_pusher` | boolean | no | Add pusher to recipients list |
+| `notify_only_broken_pipelines` | boolean | no | Notify only broken pipelines |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected. The default value is "default" |
+| `notify_only_default_branch` | boolean | no | Send notifications only for the default branch ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/28271)) |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+
+### Delete Pipeline-Emails integration
+
+Delete Pipeline-Emails integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/pipelines-email
+```
+
+### Get Pipeline-Emails integration settings
+
+Get Pipeline-Emails integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/pipelines-email
+```
+
+## Pivotal Tracker
+
+Add commit messages as comments to Pivotal Tracker stories.
+
+See also the [Pivotal Tracker integration documentation](../user/project/integrations/pivotal_tracker.md).
+
+### Create/Edit Pivotal Tracker integration
+
+Set Pivotal Tracker integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/pivotaltracker
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | The Pivotal Tracker token |
+| `restrict_to_branch` | boolean | false | Comma-separated list of branches to automatically inspect. Leave blank to include all branches. |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Pivotal Tracker integration
+
+Delete Pivotal Tracker integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/pivotaltracker
+```
+
+### Get Pivotal Tracker integration settings
+
+Get Pivotal Tracker integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/pivotaltracker
+```
+
+## Prometheus
+
+Prometheus is a powerful time-series monitoring service.
+
+### Create/Edit Prometheus integration
+
+Set Prometheus integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/prometheus
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `api_url` | string | true | Prometheus API Base URL. For example, `http://prometheus.example.com/`. |
+| `google_iap_audience_client_id` | string | false | Client ID of the IAP secured resource (looks like IAP_CLIENT_ID.apps.googleusercontent.com) |
+| `google_iap_service_account_json` | string | false | `credentials.json` file for your service account, like { "type": "service_account", "project_id": ... } |
+
+### Delete Prometheus integration
+
+Delete Prometheus integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/prometheus
+```
+
+### Get Prometheus integration settings
+
+Get Prometheus integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/prometheus
+```
+
+## Pushover
+
+Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop.
+
+### Create/Edit Pushover integration
+
+Set Pushover integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/pushover
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `api_key` | string | true | Your application key |
+| `user_key` | string | true | Your user key |
+| `priority` | string | true | The priority |
+| `device` | string | false | Leave blank for all active devices |
+| `sound` | string | false | The sound of the notification |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Pushover integration
+
+Delete Pushover integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/pushover
+```
+
+### Get Pushover integration settings
+
+Get Pushover integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/pushover
+```
+
+## Redmine
+
+Redmine issue tracker
+
+### Create/Edit Redmine integration
+
+Set Redmine integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/redmine
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `new_issue_url` | string | true | New Issue URL |
+| `project_url` | string | true | Project URL |
+| `issues_url` | string | true | Issue URL |
+| `description` | string | false | Description |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Redmine integration
+
+Delete Redmine integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/redmine
+```
+
+### Get Redmine integration settings
+
+Get Redmine integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/redmine
+```
+
+## Slack notifications
+
+Receive event notifications in Slack
+
+### Create/Edit Slack integration
+
+Set Slack integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/slack
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | `https://hooks.slack.com/services/...` |
+| `username` | string | false | username |
+| `channel` | string | false | Default channel to use if others are not configured |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `commit_events` | boolean | false | Enable notifications for commit events |
+| `confidential_issue_channel` | string | false | The name of the channel to receive confidential issues events notifications |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `confidential_note_channel` | string | false | The name of the channel to receive confidential note events notifications |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `deployment_channel` | string | false | The name of the channel to receive deployment events notifications |
+| `deployment_events` | boolean | false | Enable notifications for deployment events |
+| `issue_channel` | string | false | The name of the channel to receive issues events notifications |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `job_events` | boolean | false | Enable notifications for job events |
+| `merge_request_channel` | string | false | The name of the channel to receive merge request events notifications |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `note_channel` | string | false | The name of the channel to receive note events notifications |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `pipeline_channel` | string | false | The name of the channel to receive pipeline events notifications |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `push_channel` | string | false | The name of the channel to receive push events notifications |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `tag_push_channel` | string | false | The name of the channel to receive tag push events notifications |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Slack integration
+
+Delete Slack integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/slack
+```
+
+### Get Slack integration settings
+
+Get Slack integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/slack
+```
+
+## Microsoft Teams
+
+Group Chat Software
+
+### Create/Edit Microsoft Teams integration
+
+Set Microsoft Teams integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/microsoft-teams
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Microsoft Teams webhook. For example, `https://outlook.office.com/webhook/...` |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Microsoft Teams integration
+
+Delete Microsoft Teams integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/microsoft-teams
+```
+
+### Get Microsoft Teams integration settings
+
+Get Microsoft Teams integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/microsoft-teams
+```
+
+## Mattermost notifications
+
+Receive event notifications in Mattermost
+
+### Create/Edit Mattermost notifications integration
+
+Set Mattermost notifications integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/mattermost
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Mattermost webhook. For example, `http://mattermost_host/hooks/...` |
+| `username` | string | false | username |
+| `channel` | string | false | Default channel to use if others are not configured |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+| `push_channel` | string | false | The name of the channel to receive push events notifications |
+| `issue_channel` | string | false | The name of the channel to receive issues events notifications |
+| `confidential_issue_channel` | string | false | The name of the channel to receive confidential issues events notifications |
+| `merge_request_channel` | string | false | The name of the channel to receive merge request events notifications |
+| `note_channel` | string | false | The name of the channel to receive note events notifications |
+| `confidential_note_channel` | string | false | The name of the channel to receive confidential note events notifications |
+| `tag_push_channel` | string | false | The name of the channel to receive tag push events notifications |
+| `pipeline_channel` | string | false | The name of the channel to receive pipeline events notifications |
+| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications |
+
+### Delete Mattermost notifications integration
+
+Delete Mattermost notifications integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/mattermost
+```
+
+### Get Mattermost notifications integration settings
+
+Get Mattermost notifications integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/mattermost
+```
+
+## JetBrains TeamCity CI
+
+A continuous integration and build server
+
+### Create/Edit JetBrains TeamCity CI integration
+
+Set JetBrains TeamCity CI integration for a project.
+
+> The build configuration in TeamCity must use the build format number `%build.vcs.number%`. Configure monitoring of all branches so merge requests build. That setting is in the VSC root advanced settings.
+
+```plaintext
+PUT /projects/:id/integrations/teamcity
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `teamcity_url` | string | true | TeamCity root URL. For example, `https://teamcity.example.com` |
+| `build_type` | string | true | Build configuration ID |
+| `username` | string | true | A user with permissions to trigger a manual build |
+| `password` | string | true | The password of the user |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete JetBrains TeamCity CI integration
+
+Delete JetBrains TeamCity CI integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/teamcity
+```
+
+### Get JetBrains TeamCity CI integration settings
+
+Get JetBrains TeamCity CI integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/teamcity
+```
+
+## Jenkins CI
+
+A continuous integration and build server
+
+### Create/Edit Jenkins CI integration
+
+Set Jenkins CI integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/jenkins
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `jenkins_url` | string | true | Jenkins URL like `http://jenkins.example.com`. |
+| `project_name` | string | true | The URL-friendly project name. Example: `my_project_name`. |
+| `username` | string | false | Username for authentication with the Jenkins server, if authentication is required by the server. |
+| `password` | string | false | Password for authentication with the Jenkins server, if authentication is required by the server. |
+| `push_events` | boolean | false | Enable notifications for push events. |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events. |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events. |
+
+### Delete Jenkins CI integration
+
+Delete Jenkins CI integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/jenkins
+```
+
+### Get Jenkins CI integration settings
+
+Get Jenkins CI integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/jenkins
+```
+
+## Jenkins CI (Deprecated) integration
+
+A continuous integration and build server
+
+NOTE:
+This integration was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/1600) in GitLab 13.0.
+
+### Create/Edit Jenkins CI (Deprecated) integration
+
+Set Jenkins CI (Deprecated) integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/jenkins-deprecated
+```
+
+Parameters:
+
+- `project_url` (**required**) - Jenkins project URL like `http://jenkins.example.com/job/my-project/`
+- `multiproject_enabled` (optional) - Multi-project mode is configured in Jenkins GitLab Hook plugin
+- `pass_unstable` (optional) - Unstable builds are treated as passing
+
+### Delete Jenkins CI (Deprecated) integration
+
+Delete Jenkins CI (Deprecated) integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/jenkins-deprecated
+```
+
+### Get Jenkins CI (Deprecated) integration settings
+
+Get Jenkins CI (Deprecated) integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/jenkins-deprecated
+```
+
+## MockCI
+
+Mock an external CI. See [`gitlab-org/gitlab-mock-ci-service`](https://gitlab.com/gitlab-org/gitlab-mock-ci-service) for an example of a companion mock integration.
+
+This integration is only available when your environment is set to development.
+
+### Create/Edit MockCI integration
+
+Set MockCI integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/mock-ci
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `mock_service_url` | string | true | `http://localhost:4004` |
+
+### Delete MockCI integration
+
+Delete MockCI integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/mock-ci
+```
+
+### Get MockCI integration settings
+
+Get MockCI integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/mock-ci
+```
+
+## YouTrack
+
+YouTrack issue tracker
+
+### Create/Edit YouTrack integration
+
+Set YouTrack integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/youtrack
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `issues_url` | string | true | Issue URL |
+| `project_url` | string | true | Project URL |
+| `description` | string | false | Description |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete YouTrack integration
+
+Delete YouTrack integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/youtrack
+```
+
+### Get YouTrack integration settings
+
+Get YouTrack integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/youtrack
+```
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 97d0fd3ce8..204d75e9ee 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -1487,6 +1487,113 @@ NOTE:
The `closed_by` attribute was [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042) in GitLab 10.6. This value is only present for issues closed after GitLab 10.6 and if the user account that closed
the issue still exists.
+## Clone an issue
+
+Clone the issue to given project. If the user has insufficient permissions,
+an error message with status code `400` is returned.
+
+Copies as much data as possible as long as the target project contains equivalent labels, milestones,
+and so on.
+
+```plaintext
+POST /projects/:id/issues/:issue_iid/clone
+```
+
+| Attribute | Type | Required | Description |
+| --------------- | -------------- | ---------------------- | --------------------------------- |
+| `id` | integer/string | **{check-circle}** Yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `issue_iid` | integer | **{check-circle}** Yes | Internal ID of a project's issue. |
+| `to_project_id` | integer | **{check-circle}** Yes | ID of the new project. |
+| `with_notes` | boolean | **{dotted-circle}** No | Clone the issue with [notes](notes.md). Default is `false`. |
+
+```shell
+curl --request POST \
+--header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/5/issues/1/clone?with_notes=true&to_project_id=6"
+```
+
+Example response:
+
+```json
+{
+ "id":290,
+ "iid":1,
+ "project_id":143,
+ "title":"foo",
+ "description":"closed",
+ "state":"opened",
+ "created_at":"2021-09-14T22:24:11.696Z",
+ "updated_at":"2021-09-14T22:24:11.696Z",
+ "closed_at":null,
+ "closed_by":null,
+ "labels":[
+
+ ],
+ "milestone":null,
+ "assignees":[
+ {
+ "id":179,
+ "name":"John Doe2",
+ "username":"john",
+ "state":"active",
+ "avatar_url":"https://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80\u0026d=identicon",
+ "web_url":"https://gitlab.example.com/john"
+ }
+ ],
+ "author":{
+ "id":179,
+ "name":"John Doe2",
+ "username":"john",
+ "state":"active",
+ "avatar_url":"https://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80\u0026d=identicon",
+ "web_url":"https://gitlab.example.com/john"
+ },
+ "type":"ISSUE",
+ "assignee":{
+ "id":179,
+ "name":"John Doe2",
+ "username":"john",
+ "state":"active",
+ "avatar_url":"https://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80\u0026d=identicon",
+ "web_url":"https://gitlab.example.com/john"
+ },
+ "user_notes_count":1,
+ "merge_requests_count":0,
+ "upvotes":0,
+ "downvotes":0,
+ "due_date":null,
+ "confidential":false,
+ "discussion_locked":null,
+ "issue_type":"issue",
+ "web_url":"https://gitlab.example.com/namespace1/project2/-/issues/1",
+ "time_stats":{
+ "time_estimate":0,
+ "total_time_spent":0,
+ "human_time_estimate":null,
+ "human_total_time_spent":null
+ },
+ "task_completion_status":{
+ "count":0,
+ "completed_count":0
+ },
+ "blocking_issues_count":0,
+ "has_tasks":false,
+ "_links":{
+ "self":"https://gitlab.example.com/api/v4/projects/143/issues/1",
+ "notes":"https://gitlab.example.com/api/v4/projects/143/issues/1/notes",
+ "award_emoji":"https://gitlab.example.com/api/v4/projects/143/issues/1/award_emoji",
+ "project":"https://gitlab.example.com/api/v4/projects/143"
+ },
+ "references":{
+ "short":"#1",
+ "relative":"#1",
+ "full":"namespace1/project2#1"
+ },
+ "subscribed":true,
+ "moved_to_id":null,
+ "service_desk_reply_to":null
+}
+```
+
## Subscribe to an issue
Subscribes the authenticated user to an issue to receive notifications.
diff --git a/doc/api/members.md b/doc/api/members.md
index 0b8cf686b8..44e58f49d3 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -336,7 +336,7 @@ The response represents only direct memberships. Inherited memberships are not i
This API endpoint works on top-level groups only. It does not work on subgroups.
-This API endpoint requires permission to admin memberships for the group.
+This API endpoint requires permission to administer memberships for the group.
This API endpoint takes [pagination](index.md#pagination) parameters `page` and `per_page` to restrict the list of memberships.
@@ -561,7 +561,12 @@ Example response:
## Remove a member from a group or project
-Removes a user from a group or project.
+Removes a user from a group or project where the user has been explicitly assigned a role.
+
+The user needs to be a group member to qualify for removal.
+For example, if the user was added directly to a project within the group but not this
+group explicitly, you cannot use this API to remove them. See
+[Remove a billable member from a group](#remove-a-billable-member-from-a-group) for an alternative approach.
```plaintext
DELETE /groups/:id/members/:user_id
diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md
index b4403e1d9b..4ede95ea18 100644
--- a/doc/api/merge_request_approvals.md
+++ b/doc/api/merge_request_approvals.md
@@ -2,7 +2,6 @@
stage: Create
group: Source Code
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/#assignments"
-type: reference, api
---
# Merge request approvals API **(PREMIUM)**
@@ -15,8 +14,7 @@ in the project. Must be authenticated for all endpoints.
### Get Configuration
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/183) in GitLab 10.6.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
You can request information about a project's approval configuration using the
following endpoint:
@@ -44,8 +42,7 @@ GET /projects/:id/approvals
### Change configuration
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/183) in GitLab 10.6.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
If you are allowed to, you can change approval configuration using the following
endpoint:
@@ -180,7 +177,7 @@ GET /projects/:id/approval_rules
### Get a single project-level rule
-> - Introduced 13.7.
+> Introduced in GitLab 13.7.
You can request information about a single project approval rules using the following endpoint:
@@ -294,9 +291,10 @@ POST /projects/:id/approval_rules
| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
| `name` | string | yes | The name of the approval rule |
| `approvals_required` | integer | yes | The number of required approvals for this rule |
+| `rule_type` | string | no | The type of rule. `any_approver` is a pre-configured default rule with `approvals_required` at `0`. Other rules are `regular`.
| `user_ids` | Array | no | The ids of users as approvers |
| `group_ids` | Array | no | The ids of groups as approvers |
-| `protected_branch_ids` | Array | no | **(PREMIUM)** The ids of protected branches to scope the rule by |
+| `protected_branch_ids` | Array | no | **(PREMIUM)** The ids of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). |
```json
{
@@ -379,6 +377,23 @@ POST /projects/:id/approval_rules
}
```
+You can increase the default number of 0 required approvers like this:
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: " \
+ --header 'Content-Type: application/json' \
+ --data '{"name": "Any name", "rule_type": "any_approver", "approvals_required": 2}'
+```
+
+Another example is creating an additional, user-specific rule:
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: " \
+ --header 'Content-Type: application/json' \
+ --data '{"name": "Name of your rule", "approvals_required": 3, "user_ids": [123, 456, 789]}' \
+ https://gitlab.example.com/api/v4/projects//approval_rules
+```
+
### Update project-level rule
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in GitLab 12.3.
@@ -402,7 +417,7 @@ PUT /projects/:id/approval_rules/:approval_rule_id
| `approvals_required` | integer | yes | The number of required approvals for this rule |
| `user_ids` | Array | no | The ids of users as approvers |
| `group_ids` | Array | no | The ids of groups as approvers |
-| `protected_branch_ids` | Array | no | **(PREMIUM)** The ids of protected branches to scope the rule by |
+| `protected_branch_ids` | Array | no | **(PREMIUM)** The ids of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). |
```json
{
@@ -509,8 +524,7 @@ Configuration for approvals on a specific Merge Request. Must be authenticated f
### Get Configuration
-> - Introduced in GitLab 8.9.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
You can request information about a merge request's approval status using the
following endpoint:
@@ -556,8 +570,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/approvals
### Change approval configuration
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/183) in GitLab 10.6.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
If you are allowed to, you can change `approvals_required` using the following
endpoint:
@@ -937,8 +950,7 @@ These are system generated rules.
## Approve Merge Request
-> - Introduced in GitLab 8.9.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
If you are allowed to, you can approve a merge request using the following
endpoint:
@@ -1001,8 +1013,7 @@ does not match, the response code is `409`.
## Unapprove Merge Request
-> - Introduced in GitLab 9.0.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
If you did approve a merge request, you can unapprove it using the following
endpoint:
diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md
index abf9d7af22..8a8a54a753 100644
--- a/doc/api/oauth2.md
+++ b/doc/api/oauth2.md
@@ -5,15 +5,15 @@ group: Access
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 as an OAuth 2.0 provider **(FREE)**
+# OAuth 2.0 identity provider API **(FREE)**
-This document covers using the [OAuth2](https://oauth.net/2/) protocol to allow
-other services to access GitLab resources on user's behalf.
+GitLab provides an API to allow third-party services to access GitLab resources on a user's behalf
+with the [OAuth2](https://oauth.net/2/) protocol.
-If you want GitLab to be an OAuth authentication service provider to sign into
-other services, see the [OAuth2 authentication service provider](../integration/oauth_provider.md)
-documentation. This functionality is based on the
-[doorkeeper Ruby gem](https://github.com/doorkeeper-gem/doorkeeper).
+To configure GitLab for this, see
+[Configure GitLab as an OAuth 2.0 authentication identity provider](../integration/oauth_provider.md).
+
+This functionality is based on the [doorkeeper Ruby gem](https://github.com/doorkeeper-gem/doorkeeper).
## Supported OAuth 2.0 flows
@@ -25,7 +25,7 @@ GitLab supports the following authorization flows:
- **Authorization code:** Secure and common flow. Recommended option for secure
server-side apps.
- **Implicit grant:** Originally designed for user-agent only apps, such as
- single page web apps running on GitLab Pages).
+ single page web apps running on GitLab Pages.
The [Internet Engineering Task Force (IETF)](https://tools.ietf.org/html/draft-ietf-oauth-security-topics-09#section-2.1.2)
recommends against Implicit grant flow.
- **Resource owner password credentials:** To be used **only** for securely
@@ -412,6 +412,16 @@ prevent breaking changes introduced in [doorkeeper 5.0.2](https://github.com/doo
Don't rely on these fields as they are slated for removal in a later release.
+## Revoke a token
+
+To revoke a token, use the `revoke` endpoint. The API returns a 200 response code and an empty
+JSON hash to indicate success.
+
+```ruby
+parameters = 'client_id=APP_ID&client_secret=APP_SECRET&token=TOKEN'
+RestClient.post 'https://gitlab.example.com/oauth/revoke', parameters
+```
+
## OAuth 2.0 tokens and GitLab registries
Standard OAuth 2.0 tokens support different degrees of access to GitLab
diff --git a/doc/api/packages/debian.md b/doc/api/packages/debian.md
index 154c99d7e0..66377850c4 100644
--- a/doc/api/packages/debian.md
+++ b/doc/api/packages/debian.md
@@ -78,7 +78,7 @@ GET projects/:id/packages/debian/pool/:distribution/:letter/:package_name/:packa
| `letter` | string | yes | The Debian Classification (first-letter or lib-first-letter). |
| `package_name` | string | yes | The source package name. |
| `package_version` | string | yes | The source package version. |
-| `file_name` | string | yes | The file name. |
+| `file_name` | string | yes | The filename. |
```shell
curl --header "Private-Token: " "https://gitlab.example.com/api/v4/projects/1/packages/pool/my-distro/a/my-pkg/1.0.0/example_1.0.0~alpha2_amd64.deb"
@@ -92,7 +92,7 @@ curl --header "Private-Token: " \
--remote-name
```
-This writes the downloaded file using the remote file name in the current directory.
+This writes the downloaded file using the remote filename in the current directory.
## Route prefix
@@ -150,7 +150,7 @@ curl --header "Private-Token: " \
--remote-name
```
-This writes the downloaded file using the remote file name in the current directory.
+This writes the downloaded file using the remote filename in the current directory.
## Download a signed distribution Release file
@@ -178,7 +178,7 @@ curl --header "Private-Token: " \
--remote-name
```
-This writes the downloaded file using the remote file name in the current directory.
+This writes the downloaded file using the remote filename in the current directory.
## Download a release file signature
@@ -206,7 +206,7 @@ curl --header "Private-Token: " \
--remote-name
```
-This writes the downloaded file using the remote file name in the current directory.
+This writes the downloaded file using the remote filename in the current directory.
## Download a binary file's index
@@ -236,4 +236,4 @@ curl --header "Private-Token: " \
--remote-name
```
-This writes the downloaded file using the remote file name in the current directory.
+This writes the downloaded file using the remote filename in the current directory.
diff --git a/doc/api/packages/helm.md b/doc/api/packages/helm.md
index 8c3b986936..82b3f5225b 100644
--- a/doc/api/packages/helm.md
+++ b/doc/api/packages/helm.md
@@ -63,7 +63,7 @@ GET projects/:id/packages/helm/:channel/charts/:file_name.tgz
| ----------- | ------ | -------- | ----------- |
| `id` | string | yes | The ID or full path of the project. |
| `channel` | string | yes | Helm repository channel. |
-| `file_name` | string | yes | Chart file name. |
+| `file_name` | string | yes | Chart filename. |
```shell
curl --user : \
diff --git a/doc/api/pages.md b/doc/api/pages.md
index f81a3c3c72..a115f0b0a0 100644
--- a/doc/api/pages.md
+++ b/doc/api/pages.md
@@ -12,7 +12,7 @@ The GitLab Pages feature must be enabled to use these endpoints. Find out more a
## Unpublish pages
-Remove pages. The user must have admin privileges.
+Remove pages. The user must have the Administrator role.
```plaintext
DELETE /projects/:id/pages
diff --git a/doc/api/pages_domains.md b/doc/api/pages_domains.md
index 47a8df3875..624bdf29e5 100644
--- a/doc/api/pages_domains.md
+++ b/doc/api/pages_domains.md
@@ -12,7 +12,7 @@ The GitLab Pages feature must be enabled to use these endpoints. Find out more a
## List all Pages domains
-Get a list of all Pages domains. The user must have admin permissions.
+Get a list of all Pages domains. The user must have the administrator role.
```plaintext
GET /pages/domains
diff --git a/doc/api/personal_access_tokens.md b/doc/api/personal_access_tokens.md
index b96ee81f67..9c9551a510 100644
--- a/doc/api/personal_access_tokens.md
+++ b/doc/api/personal_access_tokens.md
@@ -95,6 +95,6 @@ curl --request DELETE --header "PRIVATE-TOKEN: " "https://git
- `204: No Content` if successfully revoked.
- `400 Bad Request` if not revoked successfully.
-## Create a personal access token (admin only)
+## Create a personal access token (administrator only)
See the [Users API documentation](users.md#create-a-personal-access-token) for information on creating a personal access token.
diff --git a/doc/api/pipeline_schedules.md b/doc/api/pipeline_schedules.md
index 74f96e5374..625a92f9b8 100644
--- a/doc/api/pipeline_schedules.md
+++ b/doc/api/pipeline_schedules.md
@@ -115,7 +115,7 @@ POST /projects/:id/pipeline_schedules
| `description` | string | yes | The description of the pipeline schedule. |
| `ref` | string | yes | The branch or tag name that is triggered. |
| `cron` | string | yes | The [cron](https://en.wikipedia.org/wiki/Cron) schedule, for example: `0 1 * * *`. |
-| `cron_timezone` | string | no | The timezone supported by `ActiveSupport::TimeZone`, for example: `Pacific Time (US & Canada)` (default: `UTC`). |
+| `cron_timezone` | string | no | The time zone supported by `ActiveSupport::TimeZone`, for example: `Pacific Time (US & Canada)` (default: `UTC`). |
| `active` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule is initially deactivated (default: `true`). |
```shell
@@ -162,7 +162,7 @@ PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id
| `description` | string | no | The description of the pipeline schedule. |
| `ref` | string | no | The branch or tag name that is triggered. |
| `cron` | string | no | The [cron](https://en.wikipedia.org/wiki/Cron) schedule, for example: `0 1 * * *`. |
-| `cron_timezone` | string | no | The timezone supported by `ActiveSupport::TimeZone` (for example `Pacific Time (US & Canada)`), or `TZInfo::Timezone` (for example `America/Los_Angeles`). |
+| `cron_timezone` | string | no | The time zone supported by `ActiveSupport::TimeZone` (for example `Pacific Time (US & Canada)`), or `TZInfo::Timezone` (for example `America/Los_Angeles`). |
| `active` | boolean | no | The activation of pipeline schedule. If false is set, the pipeline schedule is initially deactivated. |
```shell
diff --git a/doc/api/plan_limits.md b/doc/api/plan_limits.md
index 52152dd6e1..8bd87f5a89 100644
--- a/doc/api/plan_limits.md
+++ b/doc/api/plan_limits.md
@@ -14,7 +14,7 @@ The existing plans depend on the GitLab edition. In the Community Edition, only
is available. In the Enterprise Edition, additional plans are available as well.
NOTE:
-Administrator access is required to use this API.
+The Administrator role is required to use this API.
## Get current plan limits
diff --git a/doc/api/project_level_variables.md b/doc/api/project_level_variables.md
index 2dcef40aac..a2c2da9065 100644
--- a/doc/api/project_level_variables.md
+++ b/doc/api/project_level_variables.md
@@ -15,8 +15,8 @@ Get list of a project's variables.
GET /projects/:id/variables
```
-| Attribute | Type | required | Description |
-|-----------|---------|----------|---------------------|
+| Attribute | Type | required | Description |
+| --------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | integer/string | yes | The ID of a project or [URL-encoded NAMESPACE/PROJECT_NAME of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
```shell
@@ -26,14 +26,20 @@ curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/a
```json
[
{
- "key": "TEST_VARIABLE_1",
"variable_type": "env_var",
- "value": "TEST_1"
+ "key": "TEST_VARIABLE_1",
+ "value": "TEST_1",
+ "protected": false,
+ "masked": true,
+ "environment_scope": "*"
},
{
- "key": "TEST_VARIABLE_2",
"variable_type": "env_var",
- "value": "TEST_2"
+ "key": "TEST_VARIABLE_2",
+ "value": "TEST_2",
+ "protected": false,
+ "masked": false,
+ "environment_scope": "*"
}
]
```
@@ -46,11 +52,11 @@ Get the details of a project's specific variable.
GET /projects/:id/variables/:key
```
-| Attribute | Type | required | Description |
-|-----------|---------|----------|-----------------------|
-| `id` | integer/string | yes | The ID of a project or [URL-encoded NAMESPACE/PROJECT_NAME of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
-| `key` | string | yes | The `key` of a variable |
-| `filter` | hash | no | Available filters: `[environment_scope]`. See the [`filter` parameter details](#the-filter-parameter). |
+| Attribute | Type | required | Description |
+| --------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
+| `id` | integer/string | yes | The ID of a project or [URL-encoded NAMESPACE/PROJECT_NAME of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable |
+| `filter` | hash | no | Available filters: `[environment_scope]`. See the [`filter` parameter details](#the-filter-parameter). |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/1/variables/TEST_VARIABLE_1"
@@ -62,7 +68,8 @@ curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/a
"variable_type": "env_var",
"value": "TEST_1",
"protected": false,
- "masked": true
+ "masked": true,
+ "environment_scope": "*"
}
```
@@ -74,15 +81,15 @@ Create a new variable.
POST /projects/:id/variables
```
-| Attribute | Type | required | Description |
-|---------------------|---------|----------|-----------------------|
-| `id` | integer/string | yes | The ID of a project or [URL-encoded NAMESPACE/PROJECT_NAME of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
-| `key` | string | yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed |
-| `value` | string | yes | The `value` of a variable |
-| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
-| `protected` | boolean | no | Whether the variable is protected. Default: `false` |
-| `masked` | boolean | no | Whether the variable is masked. Default: `false` |
-| `environment_scope` | string | no | The `environment_scope` of the variable. Default: `*` |
+| Attribute | Type | required | Description |
+| ------------------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
+| `id` | integer/string | yes | The ID of a project or [URL-encoded NAMESPACE/PROJECT_NAME of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed |
+| `value` | string | yes | The `value` of a variable |
+| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
+| `protected` | boolean | no | Whether the variable is protected. Default: `false` |
+| `masked` | boolean | no | Whether the variable is masked. Default: `false` |
+| `environment_scope` | string | no | The `environment_scope` of the variable. Default: `*` |
```shell
curl --request POST --header "PRIVATE-TOKEN: " \
@@ -91,10 +98,10 @@ curl --request POST --header "PRIVATE-TOKEN: " \
```json
{
+ "variable_type": "env_var",
"key": "NEW_VARIABLE",
"value": "new value",
"protected": false,
- "variable_type": "env_var",
"masked": false,
"environment_scope": "*"
}
@@ -108,16 +115,16 @@ Update a project's variable.
PUT /projects/:id/variables/:key
```
-| Attribute | Type | required | Description |
-|---------------------|---------|----------|-------------------------|
-| `id` | integer/string | yes | The ID of a project or [URL-encoded NAMESPACE/PROJECT_NAME of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
-| `key` | string | yes | The `key` of a variable |
-| `value` | string | yes | The `value` of a variable |
-| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
-| `protected` | boolean | no | Whether the variable is protected |
-| `masked` | boolean | no | Whether the variable is masked |
-| `environment_scope` | string | no | The `environment_scope` of the variable |
-| `filter` | hash | no | Available filters: `[environment_scope]`. See the [`filter` parameter details](#the-filter-parameter). |
+| Attribute | Type | required | Description |
+| ------------------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
+| `id` | integer/string | yes | The ID of a project or [URL-encoded NAMESPACE/PROJECT_NAME of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable |
+| `value` | string | yes | The `value` of a variable |
+| `variable_type` | string | no | The type of a variable. Available types are: `env_var` (default) and `file` |
+| `protected` | boolean | no | Whether the variable is protected |
+| `masked` | boolean | no | Whether the variable is masked |
+| `environment_scope` | string | no | The `environment_scope` of the variable |
+| `filter` | hash | no | Available filters: `[environment_scope]`. See the [`filter` parameter details](#the-filter-parameter). |
```shell
curl --request PUT --header "PRIVATE-TOKEN: " \
@@ -126,9 +133,9 @@ curl --request PUT --header "PRIVATE-TOKEN: " \
```json
{
+ "variable_type": "env_var",
"key": "NEW_VARIABLE",
"value": "updated value",
- "variable_type": "env_var",
"protected": true,
"masked": false,
"environment_scope": "*"
@@ -143,11 +150,11 @@ Remove a project's variable.
DELETE /projects/:id/variables/:key
```
-| Attribute | Type | required | Description |
-|-----------|---------|----------|-------------------------|
-| `id` | integer/string | yes | The ID of a project or [URL-encoded NAMESPACE/PROJECT_NAME of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
-| `key` | string | yes | The `key` of a variable |
-| `filter` | hash | no | Available filters: `[environment_scope]`. See the [`filter` parameter details](#the-filter-parameter). |
+| Attribute | Type | required | Description |
+| --------- | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
+| `id` | integer/string | yes | The ID of a project or [URL-encoded NAMESPACE/PROJECT_NAME of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The `key` of a variable |
+| `filter` | hash | no | Available filters: `[environment_scope]`. See the [`filter` parameter details](#the-filter-parameter). |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/1/variables/VARIABLE_1"
diff --git a/doc/api/project_relations_export.md b/doc/api/project_relations_export.md
new file mode 100644
index 0000000000..2016dcbd14
--- /dev/null
+++ b/doc/api/project_relations_export.md
@@ -0,0 +1,111 @@
+---
+stage: Manage
+group: Import
+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/#assignments
+---
+
+# Project Relations Export API **(FREE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70330) in GitLab 14.4 behind the `bulk_import` [feature flag](../administration/feature_flags.md), disabled by default.
+
+FLAG:
+On GitLab.com, this feature is available.
+On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to
+[disable the `bulk_import` flag](../administration/feature_flags.md).
+The feature is not ready for production use. It is still in experimental stage and might change in the future.
+
+With the Project Relations Export API, you can partially export project structure. This API is
+similar to [project export](project_import_export.md),
+but it exports each top-level relation (for example, milestones/boards/labels) as a separate file
+instead of one archive. The project relations export API is primarily used in
+[group migration](../user/group/import/index.md#enable-or-disable-gitlab-group-migration)
+to support group project import.
+
+## Schedule new export
+
+Start a new project relations export:
+
+```plaintext
+POST /projects/:id/export_relations
+```
+
+| Attribute | Type | Required | Description |
+| --------- | -------------- | -------- | ---------------------------------------- |
+| `id` | integer/string | yes | ID of the project owned by the authenticated user. |
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/1/export_relations"
+```
+
+```json
+{
+ "message": "202 Accepted"
+}
+```
+
+## Export status
+
+View the status of the relations export:
+
+```plaintext
+GET /projects/:id/export_relations/status
+```
+
+| Attribute | Type | Required | Description |
+| --------- | -------------- | -------- | ---------------------------------------- |
+| `id` | integer/string | yes | ID of the project owned by the authenticated user. |
+
+```shell
+curl --request GET --header "PRIVATE-TOKEN: " \
+ "https://gitlab.example.com/api/v4/projects/1/export_relations/status"
+```
+
+The status can be one of the following:
+
+- `0`: `started`
+- `1`: `finished`
+- `-1`: `failed`
+
+- `0` - `started`
+- `1` - `finished`
+- `-1` - `failed`
+
+```json
+[
+ {
+ "relation": "project_badges",
+ "status": 1,
+ "error": null,
+ "updated_at": "2021-05-04T11:25:20.423Z"
+ },
+ {
+ "relation": "boards",
+ "status": 1,
+ "error": null,
+ "updated_at": "2021-05-04T11:25:20.085Z"
+ }
+]
+```
+
+## Export download
+
+Download the finished relations export:
+
+```plaintext
+GET /projects/:id/export_relations/download
+```
+
+| Attribute | Type | Required | Description |
+| --------------- | -------------- | -------- | ---------------------------------------- |
+| `id` | integer/string | yes | ID of the project owned by the authenticated user. |
+| `relation` | string | yes | Name of the project top-level relation to download. |
+
+```shell
+curl --header "PRIVATE-TOKEN: " --remote-header-name \
+ --remote-name "https://gitlab.example.com/api/v4/projects/1/export_relations/download?relation=labels"
+```
+
+```shell
+ls labels.ndjson.gz
+labels.ndjson.gz
+```
diff --git a/doc/api/project_repository_storage_moves.md b/doc/api/project_repository_storage_moves.md
index ebb15e1c1d..b779a0046c 100644
--- a/doc/api/project_repository_storage_moves.md
+++ b/doc/api/project_repository_storage_moves.md
@@ -10,7 +10,7 @@ type: reference
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31285) in GitLab 13.0.
Project repositories including wiki and design repositories can be moved between storages. This can be useful when
-[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrate-to-gitaly-cluster),
+[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrating-to-gitaly-cluster),
for example.
As project repository storage moves are processed, they transition through different states. Values
diff --git a/doc/api/project_statistics.md b/doc/api/project_statistics.md
index a16bcc513f..c69e41a423 100644
--- a/doc/api/project_statistics.md
+++ b/doc/api/project_statistics.md
@@ -8,13 +8,13 @@ type: reference, api
# Project statistics API **(FREE)**
Every API call to [project](../user/project/index.md) statistics must be authenticated.
+Retrieving these statistics requires write access to the repository.
+
+This API retrieves the number of times the project is either cloned or pulled
+with the HTTP method. SSH fetches are not included.
## Get the statistics of the last 30 days
-Retrieving the statistics requires write access to the repository.
-Currently only HTTP fetches statistics are returned.
-Fetches statistics includes both clones and pulls count and are HTTP only, SSH fetches are not included.
-
```plaintext
GET /projects/:id/statistics
```
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 29e3cdf6cb..024362f324 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -53,10 +53,10 @@ GET /projects
| `last_activity_before` | datetime | **{dotted-circle}** No | Limit results to projects with last_activity before specified time. Format: ISO 8601 (`YYYY-MM-DDTHH:MM:SSZ`) |
| `membership` | boolean | **{dotted-circle}** No | Limit by projects that the current user is a member of. |
| `min_access_level` | integer | **{dotted-circle}** No | Limit by current user minimal [access level](members.md#valid-access-levels). |
-| `order_by` | string | **{dotted-circle}** No | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, `last_activity_at`, or `similarity` fields. `repository_size`, `storage_size`, `packages_size` or `wiki_size` fields are only allowed for admins. `similarity` ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332890) in GitLab 14.1) is only available when searching and is limited to projects that the current user is a member of. Default is `created_at`. |
+| `order_by` | string | **{dotted-circle}** No | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, `last_activity_at`, or `similarity` fields. `repository_size`, `storage_size`, `packages_size` or `wiki_size` fields are only allowed for administrators. `similarity` ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/332890) in GitLab 14.1) is only available when searching and is limited to projects that the current user is a member of. Default is `created_at`. |
| `owned` | boolean | **{dotted-circle}** No | Limit by projects explicitly owned by the current user. |
| `repository_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the repository checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in GitLab 11.2). |
-| `repository_storage` | string | **{dotted-circle}** No | Limit results to projects stored on `repository_storage`. _(admins only)_ |
+| `repository_storage` | string | **{dotted-circle}** No | Limit results to projects stored on `repository_storage`. _(administrators only)_ |
| `search_namespaces` | boolean | **{dotted-circle}** No | Include ancestor namespaces when matching search criteria. Default is `false`. |
| `search` | string | **{dotted-circle}** No | Return list of projects matching the search criteria. |
| `simple` | boolean | **{dotted-circle}** No | Return only limited fields for each project. This is a no-op without authentication as then _only_ simple fields are returned. |
@@ -66,7 +66,7 @@ GET /projects
| `topic` | string | **{dotted-circle}** No | Comma-separated topic names. Limit results to projects that match all of given topics. See `topics` attribute. |
| `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. |
| `wiki_checksum_failed` **(PREMIUM)** | boolean | **{dotted-circle}** No | Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6137) in GitLab 11.2). |
-| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ |
+| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(administrator only)_ |
| `with_issues_enabled` | boolean | **{dotted-circle}** No | Limit by enabled issues feature. |
| `with_merge_requests_enabled` | boolean | **{dotted-circle}** No | Limit by enabled merge requests feature. |
| `with_programming_language` | string | **{dotted-circle}** No | Limit by projects which use the given programming language. |
@@ -386,7 +386,7 @@ GET /users/:user_id/projects
| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. Only available to Reporter or higher level role members. |
| `user_id` | string | **{check-circle}** Yes | The ID or username of the user. |
| `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. |
-| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ |
+| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(administrator only)_ |
| `with_issues_enabled` | boolean | **{dotted-circle}** No | Limit by enabled issues feature. |
| `with_merge_requests_enabled` | boolean | **{dotted-circle}** No | Limit by enabled merge requests feature. |
| `with_programming_language` | string | **{dotted-circle}** No | Limit by projects which use the given programming language. |
@@ -621,7 +621,7 @@ GET /users/:user_id/starred_projects
| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. Only available to Reporter or higher level role members. |
| `user_id` | string | **{check-circle}** Yes | The ID or username of the user. |
| `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. |
-| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ |
+| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(administrator only)_ |
| `with_issues_enabled` | boolean | **{dotted-circle}** No | Limit by enabled issues feature. |
| `with_merge_requests_enabled` | boolean | **{dotted-circle}** No | Limit by enabled merge requests feature. |
@@ -840,7 +840,7 @@ GET /projects/:id
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `license` | boolean | **{dotted-circle}** No | Include project license data. |
| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. Only available to Reporter or higher level role members. |
-| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ |
+| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(administrators only)_ |
```json
{
@@ -1218,7 +1218,7 @@ POST /projects
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
-| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(admins only)_ |
+| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrator only)_ |
| `request_access_enabled` | boolean | **{dotted-circle}** No | Allow users to request member access. |
| `resolve_outdated_diff_discussions` | boolean | **{dotted-circle}** No | Automatically resolve merge request diffs discussions on lines changed with a push. |
| `shared_runners_enabled` | boolean | **{dotted-circle}** No | Enable shared runners for this project. |
@@ -1237,7 +1237,7 @@ POST /projects
## Create project for user
-Creates a new project owned by the specified user. Available only for admins.
+Creates a new project owned by the specified user. Available only for administrators.
If your HTTP repository isn't publicly accessible, add authentication information
to the URL `https://username:password@gitlab.company.com/group/project.git`,
@@ -1295,7 +1295,7 @@ POST /projects/user/:user_id
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project-members. |
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
-| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(admins only)_ |
+| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrators only)_ |
| `request_access_enabled` | boolean | **{dotted-circle}** No | Allow users to request member access. |
| `resolve_outdated_diff_discussions` | boolean | **{dotted-circle}** No | Automatically resolve merge request diffs discussions on lines changed with a push. |
| `shared_runners_enabled` | boolean | **{dotted-circle}** No | Enable shared runners for this project. |
@@ -1360,7 +1360,7 @@ PUT /projects/:id
| `merge_requests_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable merge requests for this project. Use `merge_requests_access_level` instead. |
| `mirror_overwrites_diverged_branches` **(PREMIUM)** | boolean | **{dotted-circle}** No | Pull mirror overwrites diverged branches. |
| `mirror_trigger_builds` **(PREMIUM)** | boolean | **{dotted-circle}** No | Pull mirroring triggers builds. |
-| `mirror_user_id` **(PREMIUM)** | integer | **{dotted-circle}** No | User responsible for all the activity surrounding a pull mirror event. _(admins only)_ |
+| `mirror_user_id` **(PREMIUM)** | integer | **{dotted-circle}** No | User responsible for all the activity surrounding a pull mirror event. _(administrators only)_ |
| `mirror` **(PREMIUM)** | boolean | **{dotted-circle}** No | Enables pull mirroring in a project. |
| `name` | string | **{dotted-circle}** No | The name of the project. |
| `operations_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
@@ -1375,7 +1375,7 @@ PUT /projects/:id
| `public_builds` | boolean | **{dotted-circle}** No | If `true`, jobs can be viewed by non-project members. |
| `remove_source_branch_after_merge` | boolean | **{dotted-circle}** No | Enable `Delete source branch` option by default for all new merge requests. |
| `repository_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
-| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(admins only)_ |
+| `repository_storage` | string | **{dotted-circle}** No | Which storage shard the repository is on. _(administrators only)_ |
| `request_access_enabled` | boolean | **{dotted-circle}** No | Allow users to request member access. |
| `resolve_outdated_diff_discussions` | boolean | **{dotted-circle}** No | Automatically resolve merge request diffs discussions on lines changed with a push. |
| `service_desk_enabled` | boolean | **{dotted-circle}** No | Enable or disable Service Desk feature. |
@@ -1442,7 +1442,7 @@ GET /projects/:id/forks
| `starred` | boolean | **{dotted-circle}** No | Limit by projects starred by the current user. |
| `statistics` | boolean | **{dotted-circle}** No | Include project statistics. Only available to Reporter or higher level role members. |
| `visibility` | string | **{dotted-circle}** No | Limit by visibility `public`, `internal`, or `private`. |
-| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(admins only)_ |
+| `with_custom_attributes` | boolean | **{dotted-circle}** No | Include [custom attributes](custom_attributes.md) in response. _(administrators only)_ |
| `with_issues_enabled` | boolean | **{dotted-circle}** No | Limit by enabled issues feature. |
| `with_merge_requests_enabled` | boolean | **{dotted-circle}** No | Limit by enabled merge requests feature. |
@@ -2047,7 +2047,7 @@ This endpoint:
merge requests).
- From [GitLab 13.2](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) on
[Premium or higher](https://about.gitlab.com/pricing/) tiers, group
- admins can [configure](../user/group/index.md#enable-delayed-project-removal)
+ administrators can [configure](../user/group/index.md#enable-delayed-project-removal)
projects within a group to be deleted after a delayed period. When enabled,
actual deletion happens after the number of days specified in the
[default deletion delay](../user/admin_area/settings/visibility_and_access_controls.md#default-deletion-delay).
@@ -2373,7 +2373,7 @@ is returned.
## Fork relationship
Allows modification of the forked relationship between existing projects.
-Available only for project owners and admins.
+Available only for project owners and administrators.
### Create a forked from/to relation between existing projects
@@ -2678,13 +2678,21 @@ Read more in the [Project members](members.md) documentation.
> - Introduced in GitLab 11.
> - Moved to GitLab Premium in 13.9.
-Configure pull mirroring while [creating a new project](#create-project) or [updating an existing project](#edit-project) using the API if the remote repository is publicly accessible or via `username/password` authentication. In case your HTTP repository is not publicly accessible, you can add the authentication information to the URL: `https://username:password@gitlab.company.com/group/project.git`, where password is a [personal access token](../user/profile/personal_access_tokens.md) with the API scope enabled.
+Configure pull mirroring while [creating a new project](#create-project)
+or [updating an existing project](#edit-project) using the API
+if the remote repository is publicly accessible
+or via `username:token` authentication.
+In case your HTTP repository is not publicly accessible,
+you can add the authentication information to the URL:
+`https://username:token@gitlab.company.com/group/project.git`,
+where `token` is a [personal access token](../user/profile/personal_access_tokens.md)
+with the API scope enabled.
-The relevant API parameters to update are:
-
-- `import_url`: URL of remote repository being mirrored (with `username:password` if needed).
-- `mirror`: Enables pull mirroring on project when set to `true`.
-- `only_mirror_protected_branches`: Set to `true` for protected branches.
+| Attribute | Type | Required | Description |
+|--------------|---------|------------------------|-------------|
+| `import_url` | string | **{check-circle}** Yes | URL of remote repository being mirrored (with `user:token` if needed). |
+| `mirror` | boolean | **{check-circle}** Yes | Enables pull mirroring on project when set to `true`. |
+| `only_mirror_protected_branches`| boolean | **{dotted-circle}** No | Limits mirroring to only protected branches when set to `true`. |
## Start the pull mirroring process for a Project **(PREMIUM)**
diff --git a/doc/api/releases/index.md b/doc/api/releases/index.md
index 7262778394..ded47b24c1 100644
--- a/doc/api/releases/index.md
+++ b/doc/api/releases/index.md
@@ -729,3 +729,5 @@ Example response:
A release with a `released_at` attribute set to a future date is labeled
as an **Upcoming Release** [in the UI](../../user/project/releases/index.md#upcoming-releases).
+
+Additionally, if a [release is requested from the API](#list-releases), for each release with a `release_at` attribute set to a future date, an additional attribute `upcoming_release` (set to true) will be returned as part of the response.
diff --git a/doc/api/remote_mirrors.md b/doc/api/remote_mirrors.md
index 799763a7da..8b58428503 100644
--- a/doc/api/remote_mirrors.md
+++ b/doc/api/remote_mirrors.md
@@ -7,7 +7,7 @@ type: reference, api
# Project remote mirrors API **(FREE)**
-[Push mirrors](../user/project/repository/repository_mirroring.md#push-to-a-remote-repository)
+[Push mirrors](../user/project/repository/mirror/push.md)
defined on a project's repository settings are called "remote mirrors", and the
state of these mirrors can be queried and modified via the remote mirror API
outlined below.
@@ -51,11 +51,15 @@ NOTE:
For security reasons, the `url` attribute is always scrubbed of username
and password information.
-## Create a remote mirror
+## Create a pull mirror
+
+Learn how to [configure a pull mirror](projects.md#configure-pull-mirroring-for-a-project) using the Projects API.
+
+## Create a push mirror
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24189) in GitLab 12.9.
-Create a remote mirror for a project. The mirror is disabled by default. You can enable it by including the optional parameter `enabled` when creating it:
+Push mirroring is disabled by default. You can enable it by including the optional parameter `enabled` when creating it:
```plaintext
POST /projects/:id/remote_mirrors
@@ -63,7 +67,7 @@ POST /projects/:id/remote_mirrors
| Attribute | Type | Required | Description |
| :---------- | :----- | :--------- | :------------ |
-| `url` | String | yes | The URL of the remote repository to be mirrored. |
+| `url` | String | yes | The target URL to which the repository is mirrored. |
| `enabled` | Boolean | no | Determines if the mirror is enabled. |
| `only_protected_branches` | Boolean | no | Determines if only protected branches are mirrored. |
| `keep_divergent_refs` | Boolean | no | Determines if divergent refs are skipped. |
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 1c9136d22a..e93ffbc5e7 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -14,6 +14,12 @@ be accessed without authentication if the repository is publicly accessible.
This command provides essentially the same functionality as the `git ls-tree` command. For more information, see the section _Tree Objects_ in the [Git internals documentation](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects/#_tree_objects).
+WARNING:
+This endpoint is changing to keyset-based pagination. Iterating pages of results
+with a number (`?page=2`) [is deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67509).
+Support for iterating with a number will become unsupported in GitLab 15.0. Use
+the new [keyset pagination system](index.md#keyset-based-pagination) instead.
+
```plaintext
GET /projects/:id/repository/tree
```
@@ -27,6 +33,8 @@ Supported attributes:
| `ref` | string | no | The name of a repository branch or tag or if not given the default branch. |
| `recursive` | boolean | no | Boolean value used to get a recursive tree (false by default). |
| `per_page` | integer | no | Number of results to show per page. If not specified, defaults to `20`. [Learn more on pagination](index.md#pagination). |
+| `pagination` | string | no | If set to `keyset`, use the new keyset pagination method. |
+| `page_token` | string | no | The tree record ID at which to fetch the next page. Used only with keyset pagination. |
```json
[
@@ -118,6 +126,7 @@ Supported attributes:
## Get file archive
> Support for [including Git LFS blobs](../topics/git/lfs/index.md#lfs-objects-in-project-archives) was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15079) in GitLab 13.5.
+> Support for downloading a subfolder was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/28827) in GitLab 14.4.
Get an archive of the repository. This endpoint can be accessed without
authentication if the repository is publicly accessible.
@@ -139,11 +148,12 @@ Supported attributes:
|:------------|:---------------|:---------|:----------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
| `sha` | string | no | The commit SHA to download. A tag, branch reference, or SHA can be used. This defaults to the tip of the default branch if not specified. |
+| `path` | string | no | The subpath of the repository to download. This defaults to the whole repository (empty string). |
Example request:
```shell
-curl --header "PRIVATE-TOKEN: " "https://gitlab.com/api/v4/projects//repository/archive?sha="
+curl --header "PRIVATE-TOKEN: " "https://gitlab.com/api/v4/projects//repository/archive?sha=&path="
```
## Compare branches, tags or commits
@@ -640,7 +650,7 @@ In an entry, the following variables are available (here `foo.bar` means that
- `author.reference`: a reference to the commit author (for example, `@alice`).
- `author.contributor`: a boolean set to `true` when the author is not a project
member, otherwise `false`.
-- `author.credit`: a boolean set to `true` when `author.contributor` is `true` or
+- `author.credit`: a boolean set to `true` when `author.contributor` is `true` or
when `include_groups` is configured, and the author is a member of one of the
groups.
- `merge_request.reference`: a reference to the merge request that first
diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md
index 1e33aadbc1..4fb1a94e29 100644
--- a/doc/api/repository_files.md
+++ b/doc/api/repository_files.md
@@ -7,9 +7,11 @@ type: reference, api
# Repository files API **(FREE)**
-**CRUD for repository files**
+You can fetch, create, update, and delete files in your repository with this API.
+You can also [configure rate limits](../user/admin_area/settings/files_api_rate_limits.md)
+for this API.
-**Create, read, update, and delete repository files using this API**
+## Available scopes for personal access tokens
The different scopes available using [personal access tokens](../user/profile/personal_access_tokens.md) are depicted
in the following table.
@@ -19,8 +21,6 @@ in the following table.
| `read_repository` | Allows read-access to the repository files. |
| `api` | Allows read-write access to the repository files. |
-> `read_repository` scope was [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/23534) in GitLab 11.6.
-
## Get file from repository
Allows you to receive information about file in repository like name, size,
diff --git a/doc/api/resource_groups.md b/doc/api/resource_groups.md
new file mode 100644
index 0000000000..ce4fa33d7f
--- /dev/null
+++ b/doc/api/resource_groups.md
@@ -0,0 +1,70 @@
+---
+stage: Release
+group: Release
+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/#assignments
+type: concepts, howto
+---
+
+# Resource Groups API
+
+You can read more about [controling the job concurrency with resource groups](../ci/resource_groups/index.md).
+
+## Get a specific resource group
+
+```plaintext
+GET /projects/:id/resource_groups/:key
+```
+
+| Attribute | Type | Required | Description |
+|-----------|---------|----------|---------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The key of the resource group |
+
+```shell
+curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/1/resource_groups/production"
+```
+
+Example of response
+
+```json
+{
+ "id": 3,
+ "key": "production",
+ "process_mode": "unordered",
+ "created_at": "2021-09-01T08:04:59.650Z",
+ "updated_at": "2021-09-01T08:04:59.650Z"
+}
+```
+
+## Edit an existing resource group
+
+Updates an existing resource group's properties.
+
+It returns `200` if the resource group was successfully updated. In case of an error, a status code `400` is returned.
+
+```plaintext
+PUT /projects/:id/resource_groups/:key
+```
+
+| Attribute | Type | Required | Description |
+| --------------- | ------- | --------------------------------- | ------------------------------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `key` | string | yes | The key of the resource group |
+| `process_mode` | string | no | The process mode of the resource group. One of `unordered`, `oldest_first` or `newest_first`. Read [process modes](../ci/resource_groups/index.md#process-modes) for more information. |
+
+```shell
+curl --request PUT --data "process_mode=oldest_first" \
+ --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/1/resource_groups/production"
+```
+
+Example response:
+
+```json
+{
+ "id": 3,
+ "key": "production",
+ "process_mode": "oldest_first",
+ "created_at": "2021-09-01T08:04:59.650Z",
+ "updated_at": "2021-09-01T08:13:38.679Z"
+}
+```
diff --git a/doc/api/runners.md b/doc/api/runners.md
index bfdf2d4910..5e84080ecb 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -15,7 +15,7 @@ There are two tokens to take into account when connecting a runner with GitLab.
| Token | Description |
| ----- | ----------- |
| Registration token | Token used to [register the runner](https://docs.gitlab.com/runner/register/). It can be [obtained through GitLab](../ci/runners/index.md). |
-| Authentication token | Token used to authenticate the runner with the GitLab instance. It is obtained either automatically when [registering a runner](https://docs.gitlab.com/runner/register/), or manually when [registering the runner via the Runner API](#register-a-new-runner). |
+| Authentication token | Token used to authenticate the runner with the GitLab instance. It is obtained automatically when you [register a runner](https://docs.gitlab.com/runner/register/) or by the Runner API when you manually [register a runner](#register-a-new-runner) or [reset the authentication token](#reset-runners-authentication-token). |
Here's an example of how the two tokens are used in runner registration:
@@ -86,7 +86,7 @@ Example response:
## List all runners **(FREE SELF)**
Get a list of all runners in the GitLab instance (specific and shared). Access
-is restricted to users with `admin` privileges.
+is restricted to users with the administrator role.
```plaintext
GET /runners/all
@@ -720,3 +720,28 @@ POST /groups/:id/runners/reset_registration_token
curl --request POST --header "PRIVATE-TOKEN: " \
"https://gitlab.example.com/api/v4/groups/9/runners/reset_registration_token"
```
+
+## Reset runner's authentication token
+
+Resets the runner's authentication token.
+
+```plaintext
+POST /runners/:id/reset_authentication_token
+```
+
+| Attribute | Type | Required | Description |
+|-----------|---------|----------|---------------------|
+| `id` | integer | yes | The ID of a runner |
+
+```shell
+curl --request POST --header "PRIVATE-TOKEN: " \
+ "https://gitlab.example.com/api/v4/runners/1/reset_authentication_token"
+```
+
+Example response:
+
+```json
+{
+ "token": "6337ff461c94fd3fa32ba3b1ff4125"
+}
+```
diff --git a/doc/api/search.md b/doc/api/search.md
index d3a2f9c41b..d3f0cba923 100644
--- a/doc/api/search.md
+++ b/doc/api/search.md
@@ -1161,7 +1161,7 @@ Blobs searches are performed on both filenames and contents. Search results:
times in the content.
```shell
-curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/6/search?scope=blobs&search=installation&ref=feature"
+curl --request GET --header "PRIVATE-TOKEN: " https://gitlab.example.com/api/v4/projects/6/search?scope=blobs&search=keyword%20filename:*.py
```
Example response:
@@ -1175,7 +1175,7 @@ Example response:
"path": "README.md",
"filename": "README.md",
"id": null,
- "ref": "feature",
+ "ref": "master",
"startline": 46,
"project_id": 6
}
diff --git a/doc/api/services.md b/doc/api/services.md
index cf6c5ec511..7587e53c9d 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -1,1471 +1,9 @@
---
-stage: Ecosystem
-group: Integrations
-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/#assignments
+redirect_to: 'integrations.md'
+remove_date: '2021-11-09'
---
-# Services API **(FREE)**
+This file was moved to [another location](integrations.md).
-NOTE:
-This API requires an access token with the [Maintainer or Owner role](../user/permissions.md).
-
-## List all active services
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21330) in GitLab 12.7.
-
-Get a list of all active project services.
-
-```plaintext
-GET /projects/:id/services
-```
-
-Example response:
-
-```json
-[
- {
- "id": 75,
- "title": "Jenkins CI",
- "slug": "jenkins",
- "created_at": "2019-11-20T11:20:25.297Z",
- "updated_at": "2019-11-20T12:24:37.498Z",
- "active": true,
- "commit_events": true,
- "push_events": true,
- "issues_events": true,
- "confidential_issues_events": true,
- "merge_requests_events": true,
- "tag_push_events": false,
- "note_events": true,
- "confidential_note_events": true,
- "pipeline_events": true,
- "wiki_page_events": true,
- "job_events": true,
- "comment_on_event_enabled": true
- },
- {
- "id": 76,
- "title": "Alerts endpoint",
- "slug": "alerts",
- "created_at": "2019-11-20T11:20:25.297Z",
- "updated_at": "2019-11-20T12:24:37.498Z",
- "active": true,
- "commit_events": true,
- "push_events": true,
- "issues_events": true,
- "confidential_issues_events": true,
- "merge_requests_events": true,
- "tag_push_events": true,
- "note_events": true,
- "confidential_note_events": true,
- "pipeline_events": true,
- "wiki_page_events": true,
- "job_events": true,
- "comment_on_event_enabled": true
- }
-]
-```
-
-## Asana
-
-Add commit messages as comments to Asana tasks.
-
-See also the [Asana service documentation](../user/project/integrations/asana.md).
-
-### Create/Edit Asana service
-
-Set Asana service for a project.
-
-```plaintext
-PUT /projects/:id/services/asana
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `api_key` | string | true | User API token. User must have access to task. All comments are attributed to this user. |
-| `restrict_to_branch` | string | false | Comma-separated list of branches to be are automatically inspected. Leave blank to include all branches. |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Asana service
-
-Delete Asana service for a project.
-
-```plaintext
-DELETE /projects/:id/services/asana
-```
-
-### Get Asana service settings
-
-Get Asana service settings for a project.
-
-```plaintext
-GET /projects/:id/services/asana
-```
-
-## Assembla
-
-Project Management Software (Source Commits Endpoint)
-
-### Create/Edit Assembla service
-
-Set Assembla service for a project.
-
-```plaintext
-PUT /projects/:id/services/assembla
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | The authentication token
-| `subdomain` | string | false | The subdomain setting |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Assembla service
-
-Delete Assembla service for a project.
-
-```plaintext
-DELETE /projects/:id/services/assembla
-```
-
-### Get Assembla service settings
-
-Get Assembla service settings for a project.
-
-```plaintext
-GET /projects/:id/services/assembla
-```
-
-## Atlassian Bamboo CI
-
-A continuous integration and build server
-
-### Create/Edit Atlassian Bamboo CI service
-
-Set Atlassian Bamboo CI service for a project.
-
-> You must set up automatic revision labeling and a repository trigger in Bamboo.
-
-```plaintext
-PUT /projects/:id/services/bamboo
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `bamboo_url` | string | true | Bamboo root URL. For example, `https://bamboo.example.com`. |
-| `build_key` | string | true | Bamboo build plan key like KEY |
-| `username` | string | true | A user with API access, if applicable |
-| `password` | string | true | Password of the user |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Atlassian Bamboo CI service
-
-Delete Atlassian Bamboo CI service for a project.
-
-```plaintext
-DELETE /projects/:id/services/bamboo
-```
-
-### Get Atlassian Bamboo CI service settings
-
-Get Atlassian Bamboo CI service settings for a project.
-
-```plaintext
-GET /projects/:id/services/bamboo
-```
-
-## Bugzilla
-
-Bugzilla Issue Tracker
-
-### Create/Edit Bugzilla service
-
-Set Bugzilla service for a project.
-
-```plaintext
-PUT /projects/:id/services/bugzilla
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `new_issue_url` | string | true | New Issue URL |
-| `issues_url` | string | true | Issue URL |
-| `project_url` | string | true | Project URL |
-| `description` | string | false | Description |
-| `title` | string | false | Title |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Bugzilla Service
-
-Delete Bugzilla service for a project.
-
-```plaintext
-DELETE /projects/:id/services/bugzilla
-```
-
-### Get Bugzilla Service Settings
-
-Get Bugzilla service settings for a project.
-
-```plaintext
-GET /projects/:id/services/bugzilla
-```
-
-## Buildkite
-
-Continuous integration and deployments
-
-### Create/Edit Buildkite service
-
-Set Buildkite service for a project.
-
-```plaintext
-PUT /projects/:id/services/buildkite
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | Buildkite project GitLab token |
-| `project_url` | string | true | Pipeline URL. For example, `https://buildkite.com/example/pipeline` |
-| `enable_ssl_verification` | boolean | false | DEPRECATED: This parameter has no effect since SSL verification is always enabled |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Buildkite service
-
-Delete Buildkite service for a project.
-
-```plaintext
-DELETE /projects/:id/services/buildkite
-```
-
-### Get Buildkite service settings
-
-Get Buildkite service settings for a project.
-
-```plaintext
-GET /projects/:id/services/buildkite
-```
-
-## Campfire
-
-Send notifications about push events to Campfire chat rooms.
-[New users can no longer sign up for Campfire](https://basecamp.com/retired/campfire).
-
-### Create/Edit Campfire service
-
-Set Campfire service for a project.
-
-```plaintext
-PUT /projects/:id/services/campfire
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-|---------------|---------|----------|---------------------------------------------------------------------------------------------|
-| `token` | string | true | Campfire API token. To find it, log into Campfire and select **My info**. |
-| `subdomain` | string | false | Campfire subdomain. Text between `https://` and `.campfirenow.com` when you're logged in. |
-| `room` | string | false | Campfire room. The last part of the URL when you're in a room. |
-| `push_events` | boolean | false | Enable notifications for push events. |
-
-### Delete Campfire service
-
-Delete Campfire service for a project.
-
-```plaintext
-DELETE /projects/:id/services/campfire
-```
-
-### Get Campfire service settings
-
-Get Campfire service settings for a project.
-
-```plaintext
-GET /projects/:id/services/campfire
-```
-
-## Unify Circuit
-
-Unify Circuit RTC and collaboration tool.
-
-### Create/Edit Unify Circuit service
-
-Set Unify Circuit service for a project.
-
-```plaintext
-PUT /projects/:id/services/unify-circuit
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Unify Circuit webhook. For example, `https://circuit.com/rest/v2/webhooks/incoming/...`. |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-
-### Delete Unify Circuit service
-
-Delete Unify Circuit service for a project.
-
-```plaintext
-DELETE /projects/:id/services/unify-circuit
-```
-
-### Get Unify Circuit service settings
-
-Get Unify Circuit service settings for a project.
-
-```plaintext
-GET /projects/:id/services/unify-circuit
-```
-
-## Webex Teams
-
-Webex Teams collaboration tool.
-
-### Create/Edit Webex Teams service
-
-Set Webex Teams service for a project.
-
-```plaintext
-PUT /projects/:id/services/webex-teams
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Webex Teams webhook. For example, `https://api.ciscospark.com/v1/webhooks/incoming/...`. |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-
-### Delete Webex Teams service
-
-Delete Webex Teams service for a project.
-
-```plaintext
-DELETE /projects/:id/services/webex-teams
-```
-
-### Get Webex Teams service settings
-
-Get Webex Teams service settings for a project.
-
-```plaintext
-GET /projects/:id/services/webex-teams
-```
-
-## Custom Issue Tracker
-
-Custom issue tracker
-
-### Create/Edit Custom Issue Tracker service
-
-Set Custom Issue Tracker service for a project.
-
-```plaintext
-PUT /projects/:id/services/custom-issue-tracker
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `new_issue_url` | string | true | New Issue URL |
-| `issues_url` | string | true | Issue URL |
-| `project_url` | string | true | Project URL |
-| `description` | string | false | Description |
-| `title` | string | false | Title |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Custom Issue Tracker service
-
-Delete Custom Issue Tracker service for a project.
-
-```plaintext
-DELETE /projects/:id/services/custom-issue-tracker
-```
-
-### Get Custom Issue Tracker service settings
-
-Get Custom Issue Tracker service settings for a project.
-
-```plaintext
-GET /projects/:id/services/custom-issue-tracker
-```
-
-## Drone CI
-
-Drone is a Continuous Integration platform built on Docker, written in Go
-
-### Create/Edit Drone CI service
-
-Set Drone CI service for a project.
-
-```plaintext
-PUT /projects/:id/services/drone-ci
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | Drone CI project specific token |
-| `drone_url` | string | true | `http://drone.example.com` |
-| `enable_ssl_verification` | boolean | false | Enable SSL verification |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-
-### Delete Drone CI service
-
-Delete Drone CI service for a project.
-
-```plaintext
-DELETE /projects/:id/services/drone-ci
-```
-
-### Get Drone CI service settings
-
-Get Drone CI service settings for a project.
-
-```plaintext
-GET /projects/:id/services/drone-ci
-```
-
-## Emails on push
-
-Email the commits and diff of each push to a list of recipients.
-
-### Create/Edit Emails on push service
-
-Set Emails on push service for a project.
-
-```plaintext
-PUT /projects/:id/services/emails-on-push
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `recipients` | string | true | Emails separated by whitespace |
-| `disable_diffs` | boolean | false | Disable code diffs |
-| `send_from_committer_email` | boolean | false | Send from committer |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". Notifications are always fired for tag pushes. The default value is "all" |
-
-### Delete Emails on push service
-
-Delete Emails on push service for a project.
-
-```plaintext
-DELETE /projects/:id/services/emails-on-push
-```
-
-### Get Emails on push service settings
-
-Get Emails on push service settings for a project.
-
-```plaintext
-GET /projects/:id/services/emails-on-push
-```
-
-## Confluence service
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220934) in GitLab 13.2.
-
-Replaces the link to the internal wiki with a link to a Confluence Cloud Workspace.
-
-### Create/Edit Confluence service
-
-Set Confluence service for a project.
-
-```plaintext
-PUT /projects/:id/services/confluence
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `confluence_url` | string | true | The URL of the Confluence Cloud Workspace hosted on atlassian.net. |
-
-### Delete Confluence service
-
-Delete Confluence service for a project.
-
-```plaintext
-DELETE /projects/:id/services/confluence
-```
-
-### Get Confluence service settings
-
-Get Confluence service settings for a project.
-
-```plaintext
-GET /projects/:id/services/confluence
-```
-
-## External wiki
-
-Replaces the link to the internal wiki with a link to an external wiki.
-
-### Create/Edit External wiki service
-
-Set External wiki service for a project.
-
-```plaintext
-PUT /projects/:id/services/external-wiki
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `external_wiki_url` | string | true | The URL of the external wiki |
-
-### Delete External wiki service
-
-Delete External wiki service for a project.
-
-```plaintext
-DELETE /projects/:id/services/external-wiki
-```
-
-### Get External wiki service settings
-
-Get External wiki service settings for a project.
-
-```plaintext
-GET /projects/:id/services/external-wiki
-```
-
-## Flowdock
-
-Flowdock is a ChatOps application for collaboration in software engineering
-companies. You can send notifications from GitLab events to Flowdock flows.
-For integration instructions, see the [Flowdock documentation](https://www.flowdock.com/help/gitlab).
-
-### Create/Edit Flowdock service
-
-Set Flowdock service for a project.
-
-```plaintext
-PUT /projects/:id/services/flowdock
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | Flowdock Git source token |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Flowdock service
-
-Delete Flowdock service for a project.
-
-```plaintext
-DELETE /projects/:id/services/flowdock
-```
-
-### Get Flowdock service settings
-
-Get Flowdock service settings for a project.
-
-```plaintext
-GET /projects/:id/services/flowdock
-```
-
-## GitHub **(PREMIUM)**
-
-Code collaboration software.
-
-### Create/Edit GitHub service
-
-Set GitHub service for a project.
-
-```plaintext
-PUT /projects/:id/services/github
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | GitHub API token with `repo:status` OAuth scope |
-| `repository_url` | string | true | GitHub repository URL |
-| `static_context` | boolean | false | Append instance name instead of branch to [status check name](../user/project/integrations/github.md#static--dynamic-status-check-names) |
-
-### Delete GitHub service
-
-Delete GitHub service for a project.
-
-```plaintext
-DELETE /projects/:id/services/github
-```
-
-### Get GitHub service settings
-
-Get GitHub service settings for a project.
-
-```plaintext
-GET /projects/:id/services/github
-```
-
-## Hangouts Chat
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/20290) in GitLab 11.2.
-
-Google Workspace team collaboration tool.
-
-### Create/Edit Hangouts Chat service
-
-Set Hangouts Chat service for a project.
-
-```plaintext
-PUT /projects/:id/services/hangouts-chat
-```
-
-NOTE:
-Specific event parameters (for example, `push_events` flag) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Hangouts Chat webhook. For example, `https://chat.googleapis.com/v1/spaces...`. |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-
-### Delete Hangouts Chat service
-
-Delete Hangouts Chat service for a project.
-
-```plaintext
-DELETE /projects/:id/services/hangouts-chat
-```
-
-### Get Hangouts Chat service settings
-
-Get Hangouts Chat service settings for a project.
-
-```plaintext
-GET /projects/:id/services/hangouts-chat
-```
-
-## irker (IRC gateway)
-
-Send IRC messages, on update, to a list of recipients through an irker gateway.
-
-For more information, see the [irker integration documentation](../user/project/integrations/irker.md).
-
-### Create/Edit irker (IRC gateway) service
-
-Set irker (IRC gateway) service for a project.
-
-```plaintext
-PUT /projects/:id/services/irker
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `recipients` | string | true | Recipients/channels separated by whitespaces |
-| `default_irc_uri` | string | false | `irc://irc.network.net:6697/` |
-| `server_host` | string | false | localhost |
-| `server_port` | integer | false | 6659 |
-| `colorize_messages` | boolean | false | Colorize messages |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete irker (IRC gateway) service
-
-Delete irker (IRC gateway) service for a project.
-
-```plaintext
-DELETE /projects/:id/services/irker
-```
-
-### Get irker (IRC gateway) service settings
-
-Get irker (IRC gateway) service settings for a project.
-
-```plaintext
-GET /projects/:id/services/irker
-```
-
-## Jira
-
-Jira issue tracker.
-
-### Get Jira service settings
-
-Get Jira service settings for a project.
-
-```plaintext
-GET /projects/:id/services/jira
-```
-
-### Create/Edit Jira service
-
-Set Jira service for a project.
-
-> Starting with GitLab 8.14, `api_url`, `issues_url`, `new_issue_url` and
-> `project_url` are replaced by `url`. If you are using an
-> older version, [follow this documentation](https://gitlab.com/gitlab-org/gitlab/-/blob/8-13-stable-ee/doc/api/services.md#jira).
-
-```plaintext
-PUT /projects/:id/services/jira
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `url` | string | yes | The URL to the Jira project which is being linked to this GitLab project. For example, `https://jira.example.com`. |
-| `api_url` | string | no | The base URL to the Jira instance API. Web URL value is used if not set. For example, `https://jira-api.example.com`. |
-| `username` | string | yes | The username of the user created to be used with GitLab/Jira. |
-| `password` | string | yes | The password of the user created to be used with GitLab/Jira. |
-| `active` | boolean | no | Activates or deactivates the service. Defaults to false (deactivated). |
-| `jira_issue_transition_automatic` | boolean | no | Enable [automatic issue transitions](../integration/jira/issues.md#automatic-issue-transitions). Takes precedence over `jira_issue_transition_id` if enabled. Defaults to `false` |
-| `jira_issue_transition_id` | string | no | The ID of one or more transitions for [custom issue transitions](../integration/jira/issues.md#custom-issue-transitions). Ignored if `jira_issue_transition_automatic` is enabled. Defaults to a blank string, which disables custom transitions. |
-| `commit_events` | boolean | false | Enable notifications for commit events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `comment_on_event_enabled` | boolean | false | Enable comments inside Jira issues on each GitLab event (commit / merge request) |
-
-### Delete Jira service
-
-Remove all previously Jira settings from a project.
-
-```plaintext
-DELETE /projects/:id/services/jira
-```
-
-## Slack slash commands
-
-Ability to receive slash commands from a Slack chat instance.
-
-### Get Slack slash command service settings
-
-Get Slack slash command service settings for a project.
-
-```plaintext
-GET /projects/:id/services/slack-slash-commands
-```
-
-Example response:
-
-```json
-{
- "id": 4,
- "title": "Slack slash commands",
- "slug": "slack-slash-commands",
- "created_at": "2017-06-27T05:51:39-07:00",
- "updated_at": "2017-06-27T05:51:39-07:00",
- "active": true,
- "push_events": true,
- "issues_events": true,
- "confidential_issues_events": true,
- "merge_requests_events": true,
- "tag_push_events": true,
- "note_events": true,
- "job_events": true,
- "pipeline_events": true,
- "comment_on_event_enabled": false,
- "properties": {
- "token": ""
- }
-}
-```
-
-### Create/Edit Slack slash command service
-
-Set Slack slash command for a project.
-
-```plaintext
-PUT /projects/:id/services/slack-slash-commands
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | yes | The Slack token |
-
-### Delete Slack slash command service
-
-Delete Slack slash command service for a project.
-
-```plaintext
-DELETE /projects/:id/services/slack-slash-commands
-```
-
-## Mattermost slash commands
-
-Ability to receive slash commands from a Mattermost chat instance.
-
-### Get Mattermost slash command service settings
-
-Get Mattermost slash command service settings for a project.
-
-```plaintext
-GET /projects/:id/services/mattermost-slash-commands
-```
-
-### Create/Edit Mattermost slash command service
-
-Set Mattermost slash command for a project.
-
-```plaintext
-PUT /projects/:id/services/mattermost-slash-commands
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | yes | The Mattermost token |
-| `username` | string | no | The username to use to post the message |
-
-### Delete Mattermost slash command service
-
-Delete Mattermost slash command service for a project.
-
-```plaintext
-DELETE /projects/:id/services/mattermost-slash-commands
-```
-
-## Packagist
-
-Update your project on Packagist (the main Composer repository) when commits or tags are pushed to GitLab.
-
-### Create/Edit Packagist service
-
-Set Packagist service for a project.
-
-```plaintext
-PUT /projects/:id/services/packagist
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `username` | string | yes | The username of a Packagist account |
-| `token` | string | yes | API token to the Packagist server |
-| `server` | boolean | no | URL of the Packagist server. Leave blank for default: |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-
-### Delete Packagist service
-
-Delete Packagist service for a project.
-
-```plaintext
-DELETE /projects/:id/services/packagist
-```
-
-### Get Packagist service settings
-
-Get Packagist service settings for a project.
-
-```plaintext
-GET /projects/:id/services/packagist
-```
-
-## Pipeline-Emails
-
-Get emails for GitLab CI/CD pipelines.
-
-### Create/Edit Pipeline-Emails service
-
-Set Pipeline-Emails service for a project.
-
-```plaintext
-PUT /projects/:id/services/pipelines-email
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `recipients` | string | yes | Comma-separated list of recipient email addresses |
-| `add_pusher` | boolean | no | Add pusher to recipients list |
-| `notify_only_broken_pipelines` | boolean | no | Notify only broken pipelines |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected. The default value is "default" |
-| `notify_only_default_branch` | boolean | no | Send notifications only for the default branch ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/28271)) |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-
-### Delete Pipeline-Emails service
-
-Delete Pipeline-Emails service for a project.
-
-```plaintext
-DELETE /projects/:id/services/pipelines-email
-```
-
-### Get Pipeline-Emails service settings
-
-Get Pipeline-Emails service settings for a project.
-
-```plaintext
-GET /projects/:id/services/pipelines-email
-```
-
-## Pivotal Tracker
-
-Add commit messages as comments to Pivotal Tracker stories.
-
-See also the [Pivotal Tracker service documentation](../user/project/integrations/pivotal_tracker.md).
-
-### Create/Edit Pivotal Tracker service
-
-Set Pivotal Tracker service for a project.
-
-```plaintext
-PUT /projects/:id/services/pivotaltracker
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | The Pivotal Tracker token |
-| `restrict_to_branch` | boolean | false | Comma-separated list of branches to automatically inspect. Leave blank to include all branches. |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Pivotal Tracker service
-
-Delete Pivotal Tracker service for a project.
-
-```plaintext
-DELETE /projects/:id/services/pivotaltracker
-```
-
-### Get Pivotal Tracker service settings
-
-Get Pivotal Tracker service settings for a project.
-
-```plaintext
-GET /projects/:id/services/pivotaltracker
-```
-
-## Prometheus
-
-Prometheus is a powerful time-series monitoring service.
-
-### Create/Edit Prometheus service
-
-Set Prometheus service for a project.
-
-```plaintext
-PUT /projects/:id/services/prometheus
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `api_url` | string | true | Prometheus API Base URL. For example, `http://prometheus.example.com/`. |
-| `google_iap_audience_client_id` | string | false | Client ID of the IAP secured resource (looks like IAP_CLIENT_ID.apps.googleusercontent.com) |
-| `google_iap_service_account_json` | string | false | `credentials.json` file for your service account, like { "type": "service_account", "project_id": ... } |
-
-### Delete Prometheus service
-
-Delete Prometheus service for a project.
-
-```plaintext
-DELETE /projects/:id/services/prometheus
-```
-
-### Get Prometheus service settings
-
-Get Prometheus service settings for a project.
-
-```plaintext
-GET /projects/:id/services/prometheus
-```
-
-## Pushover
-
-Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop.
-
-### Create/Edit Pushover service
-
-Set Pushover service for a project.
-
-```plaintext
-PUT /projects/:id/services/pushover
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `api_key` | string | true | Your application key |
-| `user_key` | string | true | Your user key |
-| `priority` | string | true | The priority |
-| `device` | string | false | Leave blank for all active devices |
-| `sound` | string | false | The sound of the notification |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Pushover service
-
-Delete Pushover service for a project.
-
-```plaintext
-DELETE /projects/:id/services/pushover
-```
-
-### Get Pushover service settings
-
-Get Pushover service settings for a project.
-
-```plaintext
-GET /projects/:id/services/pushover
-```
-
-## Redmine
-
-Redmine issue tracker
-
-### Create/Edit Redmine service
-
-Set Redmine service for a project.
-
-```plaintext
-PUT /projects/:id/services/redmine
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `new_issue_url` | string | true | New Issue URL |
-| `project_url` | string | true | Project URL |
-| `issues_url` | string | true | Issue URL |
-| `description` | string | false | Description |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Redmine service
-
-Delete Redmine service for a project.
-
-```plaintext
-DELETE /projects/:id/services/redmine
-```
-
-### Get Redmine service settings
-
-Get Redmine service settings for a project.
-
-```plaintext
-GET /projects/:id/services/redmine
-```
-
-## Slack notifications
-
-Receive event notifications in Slack
-
-### Create/Edit Slack service
-
-Set Slack service for a project.
-
-```plaintext
-PUT /projects/:id/services/slack
-```
-
-NOTE:
-Specific event parameters (for example, `push_events` flag and `push_channel`) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | `https://hooks.slack.com/services/...` |
-| `username` | string | false | username |
-| `channel` | string | false | Default channel to use if others are not configured |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `commit_events` | boolean | false | Enable notifications for commit events |
-| `confidential_issue_channel` | string | false | The name of the channel to receive confidential issues events notifications |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `confidential_note_channel` | string | false | The name of the channel to receive confidential note events notifications |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `deployment_channel` | string | false | The name of the channel to receive deployment events notifications |
-| `deployment_events` | boolean | false | Enable notifications for deployment events |
-| `issue_channel` | string | false | The name of the channel to receive issues events notifications |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `job_events` | boolean | false | Enable notifications for job events |
-| `merge_request_channel` | string | false | The name of the channel to receive merge request events notifications |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `note_channel` | string | false | The name of the channel to receive note events notifications |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `pipeline_channel` | string | false | The name of the channel to receive pipeline events notifications |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `push_channel` | string | false | The name of the channel to receive push events notifications |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `tag_push_channel` | string | false | The name of the channel to receive tag push events notifications |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-| `vulnerability_channel` | string | false | **(ULTIMATE)** The name of the channel to receive vulnerability event notifications. |
-| `vulnerability_events` | boolean | false | **(ULTIMATE)** Enable notifications for vulnerability events |
-
-### Delete Slack service
-
-Delete Slack service for a project.
-
-```plaintext
-DELETE /projects/:id/services/slack
-```
-
-### Get Slack service settings
-
-Get Slack service settings for a project.
-
-```plaintext
-GET /projects/:id/services/slack
-```
-
-## Microsoft Teams
-
-Group Chat Software
-
-### Create/Edit Microsoft Teams service
-
-Set Microsoft Teams service for a project.
-
-```plaintext
-PUT /projects/:id/services/microsoft-teams
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Microsoft Teams webhook. For example, `https://outlook.office.com/webhook/...` |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-
-### Delete Microsoft Teams service
-
-Delete Microsoft Teams service for a project.
-
-```plaintext
-DELETE /projects/:id/services/microsoft-teams
-```
-
-### Get Microsoft Teams service settings
-
-Get Microsoft Teams service settings for a project.
-
-```plaintext
-GET /projects/:id/services/microsoft-teams
-```
-
-## Mattermost notifications
-
-Receive event notifications in Mattermost
-
-### Create/Edit Mattermost notifications service
-
-Set Mattermost service for a project.
-
-```plaintext
-PUT /projects/:id/services/mattermost
-```
-
-NOTE:
-Specific event parameters (for example, `push_events` flag and `push_channel`) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Mattermost webhook. For example, `http://mattermost_host/hooks/...` |
-| `username` | string | false | username |
-| `channel` | string | false | Default channel to use if others are not configured |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-| `vulnerability_events` | boolean | false | **(ULTIMATE)** Enable notifications for vulnerability events |
-| `push_channel` | string | false | The name of the channel to receive push events notifications |
-| `issue_channel` | string | false | The name of the channel to receive issues events notifications |
-| `confidential_issue_channel` | string | false | The name of the channel to receive confidential issues events notifications |
-| `merge_request_channel` | string | false | The name of the channel to receive merge request events notifications |
-| `note_channel` | string | false | The name of the channel to receive note events notifications |
-| `confidential_note_channel` | string | false | The name of the channel to receive confidential note events notifications |
-| `tag_push_channel` | string | false | The name of the channel to receive tag push events notifications |
-| `pipeline_channel` | string | false | The name of the channel to receive pipeline events notifications |
-| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications |
-| `vulnerability_channel` | string | false | **(ULTIMATE)** The name of the channel to receive vulnerability events notifications |
-
-### Delete Mattermost notifications service
-
-Delete Mattermost Notifications service for a project.
-
-```plaintext
-DELETE /projects/:id/services/mattermost
-```
-
-### Get Mattermost notifications service settings
-
-Get Mattermost notifications service settings for a project.
-
-```plaintext
-GET /projects/:id/services/mattermost
-```
-
-## JetBrains TeamCity CI
-
-A continuous integration and build server
-
-### Create/Edit JetBrains TeamCity CI service
-
-Set JetBrains TeamCity CI service for a project.
-
-> The build configuration in TeamCity must use the build format number `%build.vcs.number%`. Configure monitoring of all branches so merge requests build. That setting is in the VSC root advanced settings.
-
-```plaintext
-PUT /projects/:id/services/teamcity
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `teamcity_url` | string | true | TeamCity root URL. For example, `https://teamcity.example.com` |
-| `build_type` | string | true | Build configuration ID |
-| `username` | string | true | A user with permissions to trigger a manual build |
-| `password` | string | true | The password of the user |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete JetBrains TeamCity CI service
-
-Delete JetBrains TeamCity CI service for a project.
-
-```plaintext
-DELETE /projects/:id/services/teamcity
-```
-
-### Get JetBrains TeamCity CI service settings
-
-Get JetBrains TeamCity CI service settings for a project.
-
-```plaintext
-GET /projects/:id/services/teamcity
-```
-
-## Jenkins CI
-
-A continuous integration and build server
-
-### Create/Edit Jenkins CI service
-
-Set Jenkins CI service for a project.
-
-```plaintext
-PUT /projects/:id/services/jenkins
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `jenkins_url` | string | true | Jenkins URL like `http://jenkins.example.com`. |
-| `project_name` | string | true | The URL-friendly project name. Example: `my_project_name`. |
-| `username` | string | false | Username for authentication with the Jenkins server, if authentication is required by the server. |
-| `password` | string | false | Password for authentication with the Jenkins server, if authentication is required by the server. |
-| `push_events` | boolean | false | Enable notifications for push events. |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events. |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events. |
-
-### Delete Jenkins CI service
-
-Delete Jenkins CI service for a project.
-
-```plaintext
-DELETE /projects/:id/services/jenkins
-```
-
-### Get Jenkins CI service settings
-
-Get Jenkins CI service settings for a project.
-
-```plaintext
-GET /projects/:id/services/jenkins
-```
-
-## Jenkins CI (Deprecated) Service
-
-A continuous integration and build server
-
-NOTE:
-This service was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/1600) in GitLab 13.0.
-
-### Create/Edit Jenkins CI (Deprecated) service
-
-Set Jenkins CI (Deprecated) service for a project.
-
-```plaintext
-PUT /projects/:id/services/jenkins-deprecated
-```
-
-Parameters:
-
-- `project_url` (**required**) - Jenkins project URL like `http://jenkins.example.com/job/my-project/`
-- `multiproject_enabled` (optional) - Multi-project mode is configured in Jenkins GitLab Hook plugin
-- `pass_unstable` (optional) - Unstable builds are treated as passing
-
-### Delete Jenkins CI (Deprecated) service
-
-Delete Jenkins CI (Deprecated) service for a project.
-
-```plaintext
-DELETE /projects/:id/services/jenkins-deprecated
-```
-
-### Get Jenkins CI (Deprecated) service settings
-
-Get Jenkins CI (Deprecated) service settings for a project.
-
-```plaintext
-GET /projects/:id/services/jenkins-deprecated
-```
-
-## MockCI
-
-Mock an external CI. See [`gitlab-org/gitlab-mock-ci-service`](https://gitlab.com/gitlab-org/gitlab-mock-ci-service) for an example of a companion mock service.
-
-This service is only available when your environment is set to development.
-
-### Create/Edit MockCI service
-
-Set MockCI service for a project.
-
-```plaintext
-PUT /projects/:id/services/mock-ci
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `mock_service_url` | string | true | `http://localhost:4004` |
-
-### Delete MockCI service
-
-Delete MockCI service for a project.
-
-```plaintext
-DELETE /projects/:id/services/mock-ci
-```
-
-### Get MockCI service settings
-
-Get MockCI service settings for a project.
-
-```plaintext
-GET /projects/:id/services/mock-ci
-```
-
-## YouTrack
-
-YouTrack issue tracker
-
-### Create/Edit YouTrack service
-
-Set YouTrack service for a project.
-
-```plaintext
-PUT /projects/:id/services/youtrack
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `issues_url` | string | true | Issue URL |
-| `project_url` | string | true | Project URL |
-| `description` | string | false | Description |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete YouTrack Service
-
-Delete YouTrack service for a project.
-
-```plaintext
-DELETE /projects/:id/services/youtrack
-```
-
-### Get YouTrack Service Settings
-
-Get YouTrack service settings for a project.
-
-```plaintext
-GET /projects/:id/services/youtrack
-```
+
+
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 6b1faa0402..7b8778973f 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -248,7 +248,7 @@ listed in the descriptions of the relevant settings.
| `deactivate_dormant_users` | boolean | no | Enable [automatic deactivation of dormant users](../user/admin_area/moderate_users.md#automatically-deactivate-dormant-users). |
| `default_artifacts_expire_in` | string | no | Set the default expiration time for each job's artifacts. |
| `default_branch_name` | string | no | [Instance-level custom initial branch name](../user/project/repository/branches/default.md#instance-level-custom-initial-branch-name) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225258) in GitLab 13.2). |
-| `default_branch_protection` | integer | no | Determine if developers can push to the default branch. Can take: `0` _(not protected, both developers and maintainers can push new commits, force push, or delete the branch)_, `1` _(partially protected, developers and maintainers can push new commits, but cannot force push, or delete, the branch)_ or `2` _(fully protected, developers cannot push new commits, but maintainers can; no-one can force push or delete the branch)_ as a parameter. Default is `2`. |
+| `default_branch_protection` | integer | no | Determine if developers can push to the default branch. Can take: `0` _(not protected, both developers and maintainers can push new commits and force push)_, `1` _(partially protected, developers and maintainers can push new commits, but cannot force push)_ or `2` _(fully protected, developers cannot push new commits, but maintainers can; no one can force push)_ as a parameter. Default is `2`. |
| `default_ci_config_path` | string | no | Default CI/CD configuration file and path for new projects (`.gitlab-ci.yml` if not set). |
| `default_group_visibility` | string | no | What visibility level new groups receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
| `default_project_creation` | integer | no | Default project creation protection. Can take: `0` _(No one)_, `1` _(Maintainers)_ or `2` _(Developers + Maintainers)_|
@@ -407,6 +407,7 @@ listed in the descriptions of the relevant settings.
| `spam_check_endpoint_enabled` | boolean | no | Enables Spam Check via external API endpoint. Default is `false`. |
| `spam_check_endpoint_url` | string | no | URL of the external Spam Check service endpoint. |
| `spam_check_api_key` | string | no | The API key used by GitLab for accessing the Spam Check service endpoint. |
+| `suggest_pipeline_enabled` | boolean | no | Enable pipeline suggestion banner. |
| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to `0` for unlimited time. |
| `terms` | text | required by: `enforce_terms` | (**Required by:** `enforce_terms`) Markdown content for the ToS. |
| `throttle_authenticated_api_enabled` | boolean | no | (**If enabled, requires:** `throttle_authenticated_api_period_in_seconds` and `throttle_authenticated_api_requests_per_period`) Enable authenticated API request rate limit. Helps reduce request volume (for example, from crawlers or abusive bots). |
diff --git a/doc/api/snippet_repository_storage_moves.md b/doc/api/snippet_repository_storage_moves.md
index a73542c850..81473fdff9 100644
--- a/doc/api/snippet_repository_storage_moves.md
+++ b/doc/api/snippet_repository_storage_moves.md
@@ -10,7 +10,7 @@ type: reference
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49228) in GitLab 13.8.
Snippet repositories can be moved between storages. This can be useful when
-[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrate-to-gitaly-cluster), for
+[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrating-to-gitaly-cluster), for
example.
As snippet repository storage moves are processed, they transition through different states. Values
diff --git a/doc/api/usage_data.md b/doc/api/usage_data.md
index f244c68108..4809166f35 100644
--- a/doc/api/usage_data.md
+++ b/doc/api/usage_data.md
@@ -13,7 +13,7 @@ The Service Data API is associated with [Service Ping](../development/service_pi
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57270) in GitLab 13.11.
-Export all metric definitions as a single YAML file, similar to the [Metrics Dictionary](https://gitlab-org.gitlab.io/growth/product-intelligence/metric-dictionary), for easier importing.
+Export all metric definitions as a single YAML file, similar to the [Metrics Dictionary](https://metrics.gitlab.com/index.html), for easier importing.
```plaintext
GET /usage_data/metric_definitions
diff --git a/doc/api/users.md b/doc/api/users.md
index 4ec609e62e..d8effc4d38 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -987,7 +987,7 @@ error occurs a `400 Bad Request` is returned with a message explaining the error
## Add SSH key for user
-Create new key owned by specified user. Available only for admin
+Create new key owned by specified user. Available only for administrator.
```plaintext
POST /users/:id/keys
@@ -1018,7 +1018,7 @@ Parameters:
## Delete SSH key for given user
-Deletes key owned by a specified user. Available only for admin.
+Deletes key owned by a specified user. Available only for administrator.
```plaintext
DELETE /users/:id/keys/:key_id
@@ -1165,7 +1165,7 @@ Example response:
## Get a specific GPG key for a given user
Get a specific GPG key for a given user. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43693)
-in GitLab 13.5, this endpoint can be accessed without admin authentication.
+in GitLab 13.5, this endpoint can be accessed without administrator authentication.
```plaintext
GET /users/:id/gpg_keys/:key_id
@@ -1194,7 +1194,7 @@ Example response:
## Add a GPG key for a given user
-Create new GPG key owned by the specified user. Available only for admins.
+Create new GPG key owned by the specified user. Available only for administrator.
```plaintext
POST /users/:id/gpg_keys
@@ -1226,7 +1226,7 @@ Example response:
## Delete a GPG key for a given user
-Delete a GPG key owned by a specified user. Available only for admins.
+Delete a GPG key owned by a specified user. Available only for administrator.
```plaintext
DELETE /users/:id/gpg_keys/:key_id
@@ -1276,7 +1276,7 @@ Parameters:
## List emails for user
-Get a list of a specified user's emails. Available only for admin
+Get a list of a specified user's emails. Available only for administrator
NOTE:
Due to [a bug](https://gitlab.com/gitlab-org/gitlab/-/issues/25077) this endpoint currently
@@ -1345,7 +1345,7 @@ error occurs a `400 Bad Request` is returned with a message explaining the error
## Add email for user
-Create new email owned by specified user. Available only for admin
+Create new email owned by specified user. Available only for administrator
```plaintext
POST /users/:id/emails
@@ -1372,7 +1372,7 @@ Parameters:
## Delete email for given user
-Deletes email owned by a specified user. Available only for admin.
+Deletes email owned by a specified user. Available only for administrator.
```plaintext
DELETE /users/:id/emails/:email_id
@@ -1385,7 +1385,7 @@ Parameters:
## Block user
-Blocks the specified user. Available only for admin.
+Blocks the specified user. Available only for administrator.
```plaintext
POST /users/:id/block
@@ -1405,7 +1405,7 @@ Returns:
## Unblock user
-Unblocks the specified user. Available only for admin.
+Unblocks the specified user. Available only for administrator.
```plaintext
POST /users/:id/unblock
@@ -1422,7 +1422,7 @@ Returns `201 OK` on success, `404 User Not Found` is user cannot be found or
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
-Deactivates the specified user. Available only for admin.
+Deactivates the specified user. Available only for administrator.
```plaintext
POST /users/:id/deactivate
@@ -1437,7 +1437,7 @@ Returns:
- `201 OK` on success.
- `404 User Not Found` if user cannot be found.
- `403 Forbidden` when trying to deactivate a user:
- - Blocked by admin or by LDAP synchronization.
+ - Blocked by administrator or by LDAP synchronization.
- That has any activity in past 90 days. These users cannot be deactivated.
- That is internal.
@@ -1445,7 +1445,7 @@ Returns:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22257) in GitLab 12.4.
-Activates the specified user. Available only for admin.
+Activates the specified user. Available only for administrator.
```plaintext
POST /users/:id/activate
@@ -1465,7 +1465,7 @@ Returns:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327354) in GitLab 14.3.
-Bans the specified user. Available only for admin.
+Bans the specified user. Available only for administrator.
```plaintext
POST /users/:id/ban
@@ -1485,7 +1485,7 @@ Returns:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/327354) in GitLab 14.3.
-Unbans the specified user. Available only for admin.
+Unbans the specified user. Available only for administrator.
```plaintext
POST /users/:id/unban
@@ -1507,7 +1507,7 @@ Please refer to the [Events API documentation](events.md#get-user-contribution-e
## Get all impersonation tokens of a user
-> Requires admin permissions.
+> Requires administrator permissions.
It retrieves every impersonation token of the user. Use the pagination
parameters `page` and `per_page` to restrict the list of impersonation tokens.
@@ -1639,7 +1639,7 @@ Example Responses:
## Get an impersonation token of a user
-> Requires admin permissions.
+> Requires administrators permissions.
It shows a user's impersonation token.
@@ -1678,7 +1678,7 @@ Example response:
## Create an impersonation token
-> Requires admin permissions.
+> Requires administrator permissions.
> Token values are returned once. Make sure you save it - you can't access it again.
It creates a new impersonation token. Only administrators can do this.
@@ -1723,7 +1723,7 @@ Example response:
## Revoke an impersonation token
-> Requires admin permissions.
+> Requires administrator permissions.
It revokes an impersonation token.
@@ -1845,7 +1845,7 @@ Example response:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/20532) in GitLab 12.8.
-Lists all projects and groups a user is a member of. This endpoint is available for admins only.
+Lists all projects and groups a user is a member of. This endpoint is available for administrators only.
It returns the `source_id`, `source_name`, `source_type` and `access_level` of a membership.
Source can be of type `Namespace` (representing a group) or `Project`. The response represents only direct memberships. Inherited memberships, for example in subgroups, are not included.
Access levels are represented by an integer value. For more details, read about the meaning of [access level values](access_requests.md#valid-access-levels).
@@ -1865,7 +1865,7 @@ Returns:
- `200 OK` on success.
- `404 User Not Found` if user can't be found.
-- `403 Forbidden` when not requested by an admin.
+- `403 Forbidden` when not requested by an administrator.
- `400 Bad Request` when requested type is not supported.
```shell
diff --git a/doc/api/version.md b/doc/api/version.md
index b076993f00..80269bc569 100644
--- a/doc/api/version.md
+++ b/doc/api/version.md
@@ -6,8 +6,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Version API **(FREE)**
-> Introduced in GitLab 8.13.
-
Retrieve version information for this GitLab instance. Responds `200 OK` for
authenticated users.
diff --git a/doc/api/vulnerability_findings.md b/doc/api/vulnerability_findings.md
index dfc6074a1a..36604ebf87 100644
--- a/doc/api/vulnerability_findings.md
+++ b/doc/api/vulnerability_findings.md
@@ -74,77 +74,66 @@ Example response:
[
{
"id": null,
- "report_type": "dependency_scanning",
- "name": "Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js",
- "severity": "unknown",
- "confidence": "undefined",
+ "report_type": "sast",
+ "name": "Possible command injection",
+ "severity": "high",
+ "confidence": "high",
"scanner": {
- "external_id": "gemnasium",
- "name": "Gemnasium"
+ "external_id": "brakeman",
+ "name": "Brakeman",
+ "vendor": "GitLab"
},
"identifiers": [
{
- "external_type": "gemnasium",
- "external_id": "9952e574-7b5b-46fa-a270-aeb694198a98",
- "name": "Gemnasium-9952e574-7b5b-46fa-a270-aeb694198a98",
- "url": "https://deps.sec.gitlab.com/packages/npm/saml2-js/versions/1.5.0/advisories"
- },
- {
- "external_type": "cve",
- "external_id": "CVE-2017-11429",
- "name": "CVE-2017-11429",
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-11429"
+ "external_type": "brakeman_warning_code",
+ "external_id": "14",
+ "name": "Brakeman Warning Code 14",
+ "url": "https://brakemanscanner.org/docs/warning_types/command_injection/"
}
],
- "project_fingerprint": "fa6f5b6c5d240b834ac5e901dc69f9484cef89ec",
- "uuid": "31f483bc-bfc0-586d-9b92-f1015c4535b8",
- "create_vulnerability_feedback_issue_path": "/tests/yarn-remediation-test/vulnerability_feedback",
- "create_vulnerability_feedback_merge_request_path": "/tests/yarn-remediation-test/vulnerability_feedback",
- "create_vulnerability_feedback_dismissal_path": "/tests/yarn-remediation-test/vulnerability_feedback",
+ "project_fingerprint": "ac218b1770af030cfeef967752ab803c55afb36d",
+ "uuid": "ad5e3be3-a193-55f5-a200-bc12865fb09c",
+ "create_jira_issue_url": null,
+ "false_positive": true,
+ "create_vulnerability_feedback_issue_path": "/root/test-false-positive/-/vulnerability_feedback",
+ "create_vulnerability_feedback_merge_request_path": "/root/test-false-positive/-/vulnerability_feedback",
+ "create_vulnerability_feedback_dismissal_path": "/root/test-false-positive/-/vulnerability_feedback",
"project": {
- "id": 31,
- "name": "yarn-remediation-test",
- "full_path": "/tests/yarn-remediation-test",
- "full_name": "tests / yarn-remediation-test"
+ "id": 2,
+ "name": "Test False Positive",
+ "full_path": "/root/test-false-positive",
+ "full_name": "Administrator / Test False Positive"
},
"dismissal_feedback": null,
"issue_feedback": null,
"merge_request_feedback": null,
- "description": "Some XML DOM traversal and canonicalization APIs may be inconsistent in handling of comments within XML nodes. Incorrect use of these APIs by some SAML libraries results in incorrect parsing of the inner text of XML nodes such that any inner text after the comment is lost prior to cryptographically signing the SAML message. Text after the comment therefore has no impact on the signature on the SAML message.\r\n\r\nA remote attacker can modify SAML content for a SAML service provider without invalidating the cryptographic signature, which may allow attackers to bypass primary authentication for the affected SAML service provider.",
- "links": [
- {
- "url": "https://github.com/Clever/saml2/commit/3546cb61fd541f219abda364c5b919633609ef3d#diff-af730f9f738de1c9ad87596df3f6de84R279"
- },
- {
- "url": "https://www.kb.cert.org/vuls/id/475445"
- },
- {
- "url": "https://github.com/Clever/saml2/issues/127"
- }
- ],
+ "description": null,
+ "links": [],
"location": {
- "file": "yarn.lock",
- "dependency": {
- "package": {
- "name": "saml2-js"
- },
- "version": "1.5.0"
- }
+ "file": "app/controllers/users_controller.rb",
+ "start_line": 42,
+ "class": "UsersController",
+ "method": "list_users"
},
- "details": {
- "custom_field": {
- "name": "URLs",
- "type": "list",
- "items": [
- {
- "type": "url",
- "href": "http://site.com/page/1"
- }
- ]
- }
+ "remediations": [
+ null
+ ],
+ "solution": null,
+ "evidence": null,
+ "request": null,
+ "response": null,
+ "evidence_source": null,
+ "supporting_messages": [],
+ "assets": [],
+ "details": {},
+ "state": "detected",
+ "scan": {
+ "type": "sast",
+ "status": "success",
+ "start_time": "2021-09-02T20:55:48",
+ "end_time": "2021-09-02T20:55:48"
},
- "solution": "Upgrade to fixed version.\r\n",
- "blob_path": "/tests/yarn-remediation-test/blob/cc6c4a0778460455ae5d16ca7025ca9ca1ca75ac/yarn.lock"
+ "blob_path": "/root/test-false-positive/-/blob/dfd75607752a839bbc9c7362d111effaa470fecd/app/controllers/users_controller.rb#L42"
}
]
```
diff --git a/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md b/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md
index 095eaaec23..82529a4c19 100644
--- a/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md
+++ b/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md
@@ -495,8 +495,8 @@ Pros:
Cons:
- It is harder to implement GraphQL subscriptions as in case of Sidekiq as we need another way to pass subscriptions
-- `api_v4` paths can be used in some services that are used by Sidekiq (e.g. `api_v4_projects_path`)
-- url_helpers paths are used in models and services, that could be used by Sidekiq (e.g. `Gitlab::Routing.url_helpers.project_pipelines_path` is used by [ExpirePipelineCacheService](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/ci/expire_pipeline_cache_service.rb#L20) in [ExpirePipelineCacheWorker](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/workers/expire_pipeline_cache_worker.rb#L18))
+- `api_v4` paths can be used in some services that are used by Sidekiq (for example `api_v4_projects_path`)
+- url_helpers paths are used in models and services, that could be used by Sidekiq (for example `Gitlab::Routing.url_helpers.project_pipelines_path` is used by [ExpirePipelineCacheService](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/ci/expire_pipeline_cache_service.rb#L20) in [ExpirePipelineCacheWorker](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/workers/expire_pipeline_cache_worker.rb#L18))
#### Example: GraphQL
@@ -514,7 +514,7 @@ Today, loading GraphQL requires a bunch of [dependencies](https://gitlab.com/git
GraphQL only needs to run in a specific context. If we could limit when it is being loaded we could effectively improve application efficiency, by reducing application load time and required memory. This, for example, is applicable for every size installation.
-A potential challenge with GraphQL and Websockets is that at some point we might want to use Action Cable subscriptions and push GraphQL/API payload from Sidekiq to clients. This would likely utilize Redis to pass data through. Where Sidekiq would publish information on Redis and ActionCable Node would pass through that information to connected clients. This way of working is possible in the above model, but we would have to use GraphQL or API (over HTTP endpoint) to calculate what should be sent.
+A potential challenge with GraphQL and Websockets is that at some point we might want to use Action Cable subscriptions and push GraphQL/API payload from Sidekiq to clients. This would likely use Redis to pass data through. Where Sidekiq would publish information on Redis and ActionCable Node would pass through that information to connected clients. This way of working is possible in the above model, but we would have to use GraphQL or API (over HTTP endpoint) to calculate what should be sent.
An alternative way is to use a notification system that would always make an `ActionCable` node (the one handling WebSockets) generate a payload based on a send query instead of performing passthrough. This could be applicable since `ActionCable` is the one handling a given connection for a client. This could have a downside of having to recalculate the same payload if many clients would be watching the same resource. However, this behavior of system might still be desired for security purposes, as generated payload might be dependent on permission of watching client (we would show different for anonymous, and different for the member of the project).
@@ -529,8 +529,8 @@ Grape::API is another example that only needs to run only in a web server contex
Potential challenges with Grape API:
-- Currently there are some API::API dependencies in the models (e.g. `API::Helpers::Version` dependency in [project model](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/project.rb#L2019) or API::API dependency in GeoNode model for [`geo_retrieve_url`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/geo_node.rb#L183))
-- `api_v4` paths are used in helpers, presenters, and views (e.g. `api_v4_projects_path` in [PackagesHelper](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/helpers/packages_helper.rb#L17))
+- Currently there are some API::API dependencies in the models (for example `API::Helpers::Version` dependency in [project model](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/project.rb#L2019) or API::API dependency in GeoNode model for [`geo_retrieve_url`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/geo_node.rb#L183))
+- `api_v4` paths are used in helpers, presenters, and views (for example `api_v4_projects_path` in [PackagesHelper](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/helpers/packages_helper.rb#L17))
#### Example: Controllers
@@ -544,7 +544,7 @@ Controllers, Serializers, some presenters and some of the Grape:Entities are als
Potential challenges with moving Controllers:
- We needed to extend `Gitlab::Patch::DrawRoute` in order to support `engines/web_engine/config/routes` and `engines/web_engine/ee/config/routes` in case when `web_engine` is loaded. Here is potential [solution](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53720#note_506957398).
-- `Gitlab::Routing.url_helpers` paths are used in models and services, that could be used by Sidekiq (e.g. `Gitlab::Routing.url_helpers.project_pipelines_path` is used by [ExpirePipelineCacheService](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/ci/expire_pipeline_cache_service.rb#L20) in [ExpirePipelineCacheWorker](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/workers/expire_pipeline_cache_worker.rb#L18)))
+- `Gitlab::Routing.url_helpers` paths are used in models and services, that could be used by Sidekiq (for example `Gitlab::Routing.url_helpers.project_pipelines_path` is used by [ExpirePipelineCacheService](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/services/ci/expire_pipeline_cache_service.rb#L20) in [ExpirePipelineCacheWorker](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/workers/expire_pipeline_cache_worker.rb#L18)))
### Packwerk
diff --git a/doc/architecture/blueprints/consolidating_groups_and_projects/index.md b/doc/architecture/blueprints/consolidating_groups_and_projects/index.md
index fab886808e..a8d87e5f96 100644
--- a/doc/architecture/blueprints/consolidating_groups_and_projects/index.md
+++ b/doc/architecture/blueprints/consolidating_groups_and_projects/index.md
@@ -32,7 +32,7 @@ they all live on their own. A few more problems with this approach:
- Features are coupled to their container. In practice it is not straight
forward to decouple a feature from its container. The degree of coupling
varies across features.
-- Naive duplication of features will result in a more complex and fragile code base.
+- Naive duplication of features will result in a more complex and fragile codebase.
- Generalizing solutions across groups and projects may degrade system performance.
- The range of features span across many teams, and these changes will need to
manage development interference.
diff --git a/doc/ci/README.md b/doc/ci/README.md
deleted file mode 100644
index 0e6c2f63f9..0000000000
--- a/doc/ci/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: 'index.md'
-remove_date: '2021-09-28'
----
-
-This document was moved to [another location](index.md).
-
-
-
diff --git a/doc/ci/chatops/index.md b/doc/ci/chatops/index.md
index a2d9cf9054..698b467a81 100644
--- a/doc/ci/chatops/index.md
+++ b/doc/ci/chatops/index.md
@@ -9,6 +9,7 @@ type: index, concepts, howto
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4466) in GitLab Ultimate 10.6.
> - [Moved](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24780) to GitLab Free in 11.9.
+> - `CHAT_USER_ID` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/341798) in GitLab 14.4.
GitLab ChatOps provides a method to interact with CI/CD jobs through chat services
like Slack. Many organizations' discussion, collaboration, and troubleshooting takes
@@ -17,7 +18,7 @@ posted back to the channel can significantly augment your team's workflow.
## How GitLab ChatOps works
-GitLab ChatOps is built upon [GitLab CI/CD](../README.md) and
+GitLab ChatOps is built upon [GitLab CI/CD](../index.md) and
[Slack Slash Commands](../../user/project/integrations/slack_slash_commands.md).
ChatOps provides a `run` action for [slash commands](../../integration/slash_commands.md)
with the following arguments:
@@ -30,6 +31,7 @@ to the job:
- `CHAT_INPUT` contains any additional arguments.
- `CHAT_CHANNEL` is set to the name of channel the action was triggered in.
+- `CHAT_USER_ID` is set to the chat service's user ID of the user who triggered the slash command.
When executed, ChatOps looks up the specified job name and attempts to match it
to a corresponding job in [`.gitlab-ci.yml`](../yaml/index.md). If a matching job
diff --git a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
index e69daf6651..cf4e846ebb 100644
--- a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
@@ -21,7 +21,7 @@ To use GitLab CI/CD with a Bitbucket Cloud repository:
![Create project](img/external_repository.png)
- GitLab imports the repository and enables [Pull Mirroring](../../user/project/repository/repository_mirroring.md#pull-from-a-remote-repository).
+ GitLab imports the repository and enables [Pull Mirroring](../../user/project/repository/mirror/pull.md).
1. In GitLab, create a
[Personal Access Token](../../user/profile/personal_access_tokens.md)
diff --git a/doc/ci/ci_cd_for_external_repos/github_integration.md b/doc/ci/ci_cd_for_external_repos/github_integration.md
index 70a9c8fc77..4e4b742069 100644
--- a/doc/ci/ci_cd_for_external_repos/github_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/github_integration.md
@@ -45,7 +45,7 @@ repositories:
GitLab:
1. Imports the project.
-1. Enables [Pull Mirroring](../../user/project/repository/repository_mirroring.md#pull-from-a-remote-repository).
+1. Enables [Pull Mirroring](../../user/project/repository/mirror/pull.md).
1. Enables [GitHub project integration](../../user/project/integrations/github.md).
1. Creates a web hook on GitHub to notify GitLab of new commits.
diff --git a/doc/ci/ci_cd_for_external_repos/index.md b/doc/ci/ci_cd_for_external_repos/index.md
index 365e2ee31d..fbfcdcbf64 100644
--- a/doc/ci/ci_cd_for_external_repos/index.md
+++ b/doc/ci/ci_cd_for_external_repos/index.md
@@ -18,7 +18,7 @@ GitLab CI/CD can be used with:
Instead of moving your entire project to GitLab, you can connect your
external repository to get the benefits of GitLab CI/CD.
-Connecting an external repository sets up [repository mirroring](../../user/project/repository/repository_mirroring.md)
+Connecting an external repository sets up [repository mirroring](../../user/project/repository/mirror/index.md)
and create a lightweight project with issues, merge requests, wiki, and
snippets disabled. These features
[can be re-enabled later](../../user/project/settings/index.md#sharing-and-permissions).
diff --git a/doc/ci/cloud_deployment/index.md b/doc/ci/cloud_deployment/index.md
index 25b87366a3..408d68fb72 100644
--- a/doc/ci/cloud_deployment/index.md
+++ b/doc/ci/cloud_deployment/index.md
@@ -58,6 +58,9 @@ Some credentials are required to be able to run `aws` commands:
| `AWS_SECRET_ACCESS_KEY` | Your Secret access key |
| `AWS_DEFAULT_REGION` | Your region code |
+ NOTE:
+ When you create a variable it's set to be [protected by default](../variables/index.md#protect-a-cicd-variable). If you want to use the `aws` commands on branches or tags that are not protected, make sure to uncheck the **Protect variable** checkbox.
+
1. You can now use `aws` commands in the `.gitlab-ci.yml` file of this project:
```yaml
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index d5adedc611..9a4290ead4 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -572,7 +572,7 @@ The configuration is picked up by the `dind` service.
## Authenticate with registry in Docker-in-Docker
When you use Docker-in-Docker, the
-[standard authentication methods](using_docker_images.md#define-an-image-from-a-private-container-registry)
+[standard authentication methods](using_docker_images.md#access-an-image-from-a-private-container-registry)
don't work because a fresh Docker daemon is started with the service.
### Option 1: Run `docker login`
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index c2991ce66f..79c23d73a6 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -214,7 +214,7 @@ Look for the `[runners.docker]` section:
The image and services defined this way are added to all jobs run by
that runner.
-## Define an image from a private Container Registry
+## Access an image from a private Container Registry
To access private container registries, the GitLab Runner process can use:
@@ -224,19 +224,12 @@ To access private container registries, the GitLab Runner process can use:
To define which option should be used, the runner process reads the configuration in this order:
-- A `DOCKER_AUTH_CONFIG` variable provided as either:
- - A [CI/CD variable](../variables/index.md) in the `.gitlab-ci.yml` file.
- - A project's variables stored on the project's **Settings > CI/CD** page.
-- A `DOCKER_AUTH_CONFIG` variable provided as environment variable in the runner's `config.toml` file.
+- A `DOCKER_AUTH_CONFIG` [CI/CD variable](../variables/index.md).
+- A `DOCKER_AUTH_CONFIG` environment variable set in the runner's `config.toml` file.
- A `config.json` file in `$HOME/.docker` directory of the user running the process.
If the `--user` flag is provided to run the child processes as unprivileged user,
the home directory of the main runner process user is used.
-The runner reads this configuration **only** from the `config.toml` file and ignores it if
-it's provided as a CI/CD variable. This is because the runner uses **only**
-`config.toml` configuration and does not interpolate **any** CI/CD variables at
-runtime.
-
### Requirements and limitations
- Available for [Kubernetes executor](https://docs.gitlab.com/runner/executors/kubernetes.html)
@@ -253,9 +246,9 @@ private registry. Both require setting the CI/CD variable
`DOCKER_AUTH_CONFIG` with appropriate authentication information.
1. Per-job: To configure one job to access a private registry, add
- `DOCKER_AUTH_CONFIG` as a job variable.
+ `DOCKER_AUTH_CONFIG` as a [CI/CD variable](../variables/index.md).
1. Per-runner: To configure a runner so all its jobs can access a
- private registry, add `DOCKER_AUTH_CONFIG` to the environment in the
+ private registry, add `DOCKER_AUTH_CONFIG` as an environment variable in the
runner's configuration.
See below for examples of each.
@@ -274,7 +267,7 @@ Let's also assume that these are the sign-in credentials:
| username | `my_username` |
| password | `my_password` |
-Use one of the following methods to determine the value of `DOCKER_AUTH_CONFIG`:
+Use one of the following methods to determine the value for `DOCKER_AUTH_CONFIG`:
- Do a `docker login` on your local machine:
diff --git a/doc/ci/docker/using_kaniko.md b/doc/ci/docker/using_kaniko.md
index 6886899a54..69c4557dcb 100644
--- a/doc/ci/docker/using_kaniko.md
+++ b/doc/ci/docker/using_kaniko.md
@@ -13,8 +13,8 @@ type: howto
container images from a Dockerfile, inside a container or Kubernetes cluster.
kaniko solves two problems with using the
-[Docker-in-Docker
-build](using_docker_build.md#use-the-docker-executor-with-the-docker-image-docker-in-docker) method:
+[Docker-in-Docker build](using_docker_build.md#use-the-docker-executor-with-the-docker-image-docker-in-docker)
+method:
- Docker-in-Docker requires [privileged mode](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities)
to function, which is a significant security concern.
@@ -64,7 +64,7 @@ build:
entrypoint: [""]
script:
- mkdir -p /kaniko/.docker
- - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64)\"}}}" > /kaniko/.docker/config.json
+ - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
rules:
- if: $CI_COMMIT_TAG
@@ -91,7 +91,7 @@ build:
- mkdir -p /kaniko/.docker
- |-
KANIKOPROXYBUILDARGS=""
- KANIKOCFG="{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64)\"}}}"
+ KANIKOCFG="{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64 | tr -d '\n')\"}}}"
if [ "x${http_proxy}" != "x" -o "x${https_proxy}" != "x" ]; then
KANIKOCFG="${KANIKOCFG}, \"proxies\": { \"default\": { \"httpProxy\": \"${http_proxy}\", \"httpsProxy\": \"${https_proxy}\", \"noProxy\": \"${no_proxy}\"}}"
KANIKOPROXYBUILDARGS="--build-arg http_proxy=${http_proxy} --build-arg https_proxy=${https_proxy} --build-arg no_proxy=${no_proxy}"
@@ -120,7 +120,7 @@ store:
```yaml
before_script:
- mkdir -p /kaniko/.docker
- - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64)\"}}}" > /kaniko/.docker/config.json
+ - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
- |
echo "-----BEGIN CERTIFICATE-----
...
diff --git a/doc/ci/environments/deployment_safety.md b/doc/ci/environments/deployment_safety.md
index 1b34b52000..78f30b29e0 100644
--- a/doc/ci/environments/deployment_safety.md
+++ b/doc/ci/environments/deployment_safety.md
@@ -60,7 +60,7 @@ The improved pipeline flow **after** using the resource group:
1. `deploy` job in Pipeline-A finishes.
1. `deploy` job in Pipeline-B starts running.
-For more information, see [`resource_group` keyword in `.gitlab-ci.yml`](../yaml/index.md#resource_group).
+For more information, see [Resource Group documentation](../resource_groups/index.md).
## Skip outdated deployment jobs
diff --git a/doc/ci/environments/img/environments_list.png b/doc/ci/environments/img/environments_list.png
deleted file mode 100644
index 6ddfd858ab..0000000000
Binary files a/doc/ci/environments/img/environments_list.png and /dev/null differ
diff --git a/doc/ci/environments/img/environments_list_v14_3.png b/doc/ci/environments/img/environments_list_v14_3.png
new file mode 100644
index 0000000000..8fdb85338e
Binary files /dev/null and b/doc/ci/environments/img/environments_list_v14_3.png differ
diff --git a/doc/ci/environments/img/environments_terminal_button_on_index_v13_10.png b/doc/ci/environments/img/environments_terminal_button_on_index_v13_10.png
deleted file mode 100644
index 4a9a4e65d0..0000000000
Binary files a/doc/ci/environments/img/environments_terminal_button_on_index_v13_10.png and /dev/null differ
diff --git a/doc/ci/environments/img/environments_terminal_button_on_index_v14_3.png b/doc/ci/environments/img/environments_terminal_button_on_index_v14_3.png
new file mode 100644
index 0000000000..d1faf48927
Binary files /dev/null and b/doc/ci/environments/img/environments_terminal_button_on_index_v14_3.png differ
diff --git a/doc/ci/environments/index.md b/doc/ci/environments/index.md
index 6bac004fcd..ca81ad3031 100644
--- a/doc/ci/environments/index.md
+++ b/doc/ci/environments/index.md
@@ -19,7 +19,7 @@ GitLab:
- Tracks your deployments, so you always know what is deployed on your
servers.
-If you have a deployment service like [Kubernetes](../../user/project/clusters/index.md)
+If you have a deployment service like [Kubernetes](../../user/infrastructure/clusters/index.md)
associated with your project, you can use it to assist with your deployments.
You can even access a [web terminal](#web-terminals) for your environment from within GitLab.
@@ -35,7 +35,7 @@ To view a list of environments and deployments:
1. On the left sidebar, select **Deployments > Environments**.
The environments are displayed.
- ![Environments list](img/environments_list.png)
+ ![Environments list](img/environments_list_v14_3.png)
1. To view a list of deployments for an environment, select the environment name,
for example, `staging`.
@@ -175,13 +175,13 @@ You can find the play button in the pipelines, environments, deployments, and jo
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27630) in GitLab 12.6.
-If you are deploying to a [Kubernetes cluster](../../user/project/clusters/index.md)
+If you are deploying to a [Kubernetes cluster](../../user/infrastructure/clusters/index.md)
associated with your project, you can configure these deployments from your
`.gitlab-ci.yml` file.
NOTE:
Kubernetes configuration isn't supported for Kubernetes clusters that are
-[managed by GitLab](../../user/project/clusters/index.md#gitlab-managed-clusters).
+[managed by GitLab](../../user/project/clusters/gitlab_managed_clusters.md).
To follow progress on support for GitLab-managed clusters, see the
[relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/38054).
@@ -634,7 +634,7 @@ Metric charts can be embedded in GitLab Flavored Markdown. See [Embedding Metric
### Web terminals
If you deploy to your environments with the help of a deployment service (for example,
-the [Kubernetes integration](../../user/project/clusters/index.md)), GitLab can open
+the [Kubernetes integration](../../user/infrastructure/clusters/index.md)), GitLab can open
a terminal session to your environment. You can then debug issues without leaving your web browser.
The Web terminal is a container-based deployment, which often lack basic tools (like an editor),
@@ -646,9 +646,9 @@ Web terminals:
- Are available to project Maintainers and Owners only.
- Must [be enabled](../../administration/integration/terminal.md).
-In the UI, you can view the Web terminal by selecting a **Terminal** button:
+In the UI, you can view the Web terminal by selecting **Terminal** from the actions menu:
-![Terminal button on environment index](img/environments_terminal_button_on_index_v13_10.png)
+![Terminal button on environment index](img/environments_terminal_button_on_index_v14_3.png)
You can also access the terminal button from the page for a specific environment:
@@ -816,3 +816,62 @@ To ensure the `action: stop` can always run when needed, you can:
action: stop
when: manual
```
+
+### A deployment job failed with "This job could not be executed because it would create an environment with an invalid parameter" error
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21182) in GitLab 14.4.
+
+FLAG:
+On self-managed GitLab, by default this bug fix is not available. To make it available per project or for your entire instance, ask an administrator to [enable the `surface_environment_creation_failure` flag](../../administration/feature_flags.md). On GitLab.com, this bug fix is not available, but will be rolled out shortly.
+
+If your project is configured to [create a dynamic environment](#create-a-dynamic-environment),
+you might encounter this error because the dynamically generated parameter can't be used for creating an environment.
+
+For example, your project has the following `.gitlab-ci.yml`:
+
+```yaml
+deploy:
+ script: echo
+ environment: production/$ENVIRONMENT
+```
+
+Since `$ENVIRONMENT` variable does not exist in the pipeline, GitLab tries to
+create an environment with a name `production/`, which is invalid in
+[the environment name constraint](../yaml/index.md).
+
+To fix this, use one of the following solutions:
+
+- Remove `environment` keyword from the deployment job. GitLab has already been
+ ignoring the invalid keyword, therefore your deployment pipelines stay intact
+ even after the keyword removal.
+- Ensure the variable exists in the pipeline. Please note that there is
+ [a limitation on supported variables](../variables/where_variables_can_be_used.md#gitlab-ciyml-file).
+
+#### If you get this error on Review Apps
+
+For example, if you have the following in your `.gitlab-ci.yml`:
+
+```yaml
+review:
+ script: deploy review app
+ environment: review/$CI_COMMIT_REF_NAME
+```
+
+When you create a new merge request with a branch name `bug-fix!`,
+the `review` job tries to create an environment with `review/bug-fix!`.
+However, the `!` is an invalid character for environments, so the
+deployment job fails since it was about to run without an environment.
+
+To fix this, use one of the following solutions:
+
+- Re-create your feature branch without the invalid characters,
+ such as `bug-fix`.
+- Replace the `CI_COMMIT_REF_NAME`
+ [predefined variable](../variables/predefined_variables.md) with
+ `CI_COMMIT_REF_SLUG` which strips any invalid characters:
+
+ ```yaml
+ review:
+ script: deploy review app
+ environment: review/$CI_COMMIT_REF_SLUG
+ ```
diff --git a/doc/ci/environments/protected_environments.md b/doc/ci/environments/protected_environments.md
index b31e51b35f..47f93b0313 100644
--- a/doc/ci/environments/protected_environments.md
+++ b/doc/ci/environments/protected_environments.md
@@ -2,26 +2,22 @@
stage: Release
group: Release
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/#assignments
-type: concepts, howto
---
# Protected environments **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6303) in GitLab 11.3.
+[Environments](../environments/index.md) can be used for both testing and
+production reasons.
-[Environments](../environments/index.md) can be used for different reasons:
+Because deploy jobs can be raised by different users with different roles, it's
+important to be able to protect specific environments from the effects of
+unauthorized users.
-- Some of them are just for testing.
-- Others are for production.
-
-Since deploy jobs can be raised by different users with different roles, it is important that
-specific environments are "protected" to prevent unauthorized people from affecting them.
-
-By default, a protected environment does one thing: it ensures that only people
-with the right privileges can deploy to it, thus keeping it safe.
+By default, a protected environment ensures that only people with the
+appropriate privileges can deploy to it, keeping the environment safe.
NOTE:
-A GitLab admin is always allowed to use environments, even if they are protected.
+GitLab administrators can use all environments, including protected environments.
To protect, update, or unprotect an environment, you need to have at least the
[Maintainer role](../../user/permissions.md).
@@ -157,9 +153,9 @@ For more information, see [Deployment safety](deployment_safety.md).
## Group-level protected environments
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/215888) in GitLab 14.0. [Deployed behind the `group_level_protected_environments` flag](../../administration/feature_flags.md), disabled by default.
-> - [Feature flag `group_level_protected_environments`](https://gitlab.com/gitlab-org/gitlab/-/issues/331085) removed in GitLab 14.3.
-> - [Generally Available](https://gitlab.com/gitlab-org/gitlab/-/issues/331085) on GitLab and on GitLab.com in 14.3.
+> - Introduced in GitLab 14.0 [with a flag](https://gitlab.com/gitlab-org/gitlab/-/issues/215888) named `group_level_protected_environments`. Disabled by default.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/331085) in GitLab 14.3.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/331085) in GitLab 14.3.
Typically, large enterprise organizations have an explicit permission boundary
between [developers and operators](https://about.gitlab.com/topics/devops/).
@@ -210,8 +206,8 @@ configured:
(or above) to the top-level group. They can maintain CI/CD configurations for
the higher environments (such as production) in the group-level settings page,
which includes group-level protected environments,
- [group-level runners](../runners/runners_scope.md#group-runners),
- [group-level clusters](../../user/group/clusters/index.md), etc. Those
+ [group-level runners](../runners/runners_scope.md#group-runners), and
+ [group-level clusters](../../user/group/clusters/index.md). Those
configurations are inherited to the child projects as read-only entries.
This ensures that only operators can configure the organization-wide
deployment ruleset.
@@ -246,11 +242,11 @@ To protect a group-level environment:
1. Make sure your environments have the correct
[`deployment_tier`](index.md#deployment-tier-of-environments) defined in
`.gitlab-ci.yml`.
-1. Configure the group-level protected environments via the
+1. Configure the group-level protected environments by using the
[REST API](../../api/group_protected_environments.md).
NOTE:
-Configuration [via the UI](https://gitlab.com/gitlab-org/gitlab/-/issues/325249)
+Configuration [with the UI](https://gitlab.com/gitlab-org/gitlab/-/issues/325249)
is scheduled for a later release.
-
diff --git a/doc/ci/examples/deployment/README.md b/doc/ci/examples/deployment/README.md
deleted file mode 100644
index 0e6c2f63f9..0000000000
--- a/doc/ci/examples/deployment/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: 'index.md'
-remove_date: '2021-09-28'
----
-
-This document was moved to [another location](index.md).
-
-
-
diff --git a/doc/ci/examples/deployment/composer-npm-deploy.md b/doc/ci/examples/deployment/composer-npm-deploy.md
index 6817c7cac8..aa4055c00e 100644
--- a/doc/ci/examples/deployment/composer-npm-deploy.md
+++ b/doc/ci/examples/deployment/composer-npm-deploy.md
@@ -7,7 +7,7 @@ type: tutorial
# Running Composer and npm scripts with deployment via SCP in GitLab CI/CD **(FREE)**
-This guide covers the building of dependencies of a PHP project while compiling assets via an npm script using [GitLab CI/CD](../../README.md).
+This guide covers the building of dependencies of a PHP project while compiling assets via an npm script using [GitLab CI/CD](../../index.md).
While it is possible to create your own image with custom PHP and Node.js versions, for brevity we use an existing [Docker image](https://hub.docker.com/r/tetraweb/php/) that contains both PHP and Node.js installed.
diff --git a/doc/ci/examples/deployment/index.md b/doc/ci/examples/deployment/index.md
index 14fb77dc49..b083dbb817 100644
--- a/doc/ci/examples/deployment/index.md
+++ b/doc/ci/examples/deployment/index.md
@@ -56,7 +56,7 @@ To use different provider take a look at long list of [Supported Providers](http
## Using Dpl with Docker
In most cases, you configured [GitLab Runner](https://docs.gitlab.com/runner/) to use your server's shell commands.
-This means that all commands are run in the context of local user (e.g. `gitlab_runner` or `gitlab_ci_multi_runner`).
+This means that all commands are run in the context of local user (for example `gitlab_runner` or `gitlab_ci_multi_runner`).
It also means that most probably in your Docker container you don't have the Ruby runtime installed.
You must install it:
@@ -69,7 +69,7 @@ staging:
- gem install dpl
- dpl --provider=heroku --app=my-app-staging --api_key=$HEROKU_STAGING_API_KEY
only:
- - master
+ - main
```
The first line `apt-get update -yq` updates the list of available packages,
@@ -81,7 +81,7 @@ The above example is valid for all Debian-compatible systems.
It's pretty common in the development workflow to have staging (development) and
production environments
-Let's consider the following example: we would like to deploy the `master`
+Let's consider the following example: we would like to deploy the `main`
branch to `staging` and all tags to the `production` environment.
The final `.gitlab-ci.yml` for that setup would look like this:
@@ -92,7 +92,7 @@ staging:
- gem install dpl
- dpl --provider=heroku --app=my-app-staging --api_key=$HEROKU_STAGING_API_KEY
only:
- - master
+ - main
production:
stage: deploy
@@ -105,7 +105,7 @@ production:
We created two deploy jobs that are executed on different events:
-- `staging`: Executed for all commits pushed to the `master` branch
+- `staging`: Executed for all commits pushed to the `main` branch
- `production`: Executed for all pushed tags
We also use two secure variables:
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/index.md b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
index 06074d6edc..a9794afaf1 100644
--- a/doc/ci/examples/end_to_end_testing_webdriverio/index.md
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -33,7 +33,7 @@ to write such end-to-end tests, and how to set up GitLab CI/CD to automatically
against your new code, on a branch-by-branch basis. For the scope of this article, we will walk you
through the process of setting up GitLab CI/CD for end-to-end testing JavaScript-based applications
with WebdriverIO, but the general strategy should carry over to other languages.
-We assume you are familiar with GitLab, [GitLab CI/CD](../../README.md), [Review Apps](../../review_apps/index.md), and running your app locally, e.g., on `localhost:8000`.
+We assume you are familiar with GitLab, [GitLab CI/CD](../../index.md), [Review Apps](../../review_apps/index.md), and running your app locally, e.g., on `localhost:8000`.
## What to test
@@ -150,8 +150,8 @@ need to do for this:
1. Update our WebdriverIO configuration to use those browsers to visit the review apps.
For the scope of this article, we've defined an additional [CI/CD stage](../../yaml/index.md#stages)
-`confidence-check` that is executed _after_ the stage that deploys the review app. It uses the `node:latest` [Docker
-image](../../docker/using_docker_images.md). However, WebdriverIO fires up actual browsers
+`confidence-check` that is executed _after_ the stage that deploys the review app. It uses the `node:latest`
+[Docker image](../../docker/using_docker_images.md). However, WebdriverIO fires up actual browsers
to interact with your application, so we need to install and run them.
Furthermore, WebdriverIO uses Selenium as a common interface to control different browsers,
so we need to install and run Selenium as well. Luckily, the Selenium project provides the Docker images
diff --git a/doc/ci/examples/index.md b/doc/ci/examples/index.md
index 2c2c6ecd30..0fc7b06a58 100644
--- a/doc/ci/examples/index.md
+++ b/doc/ci/examples/index.md
@@ -9,7 +9,7 @@ type: index
# GitLab CI/CD Examples **(FREE)**
This page contains links to a variety of examples that can help you understand how to
-implement [GitLab CI/CD](../README.md) for your specific use case.
+implement [GitLab CI/CD](../index.md) for your specific use case.
Examples are available in several forms. As a collection of:
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
index c511839b3e..e2e12235eb 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
@@ -17,7 +17,7 @@ date: 2017-08-31
GitLab features our applications with Continuous Integration, and it is possible to easily deploy the new code changes to the production server whenever we want.
-In this tutorial, we'll show you how to initialize a [Laravel](https://laravel.com) application and set up our [Envoy](https://laravel.com/docs/master/envoy) tasks, then we'll jump into see how to test and deploy it with [GitLab CI/CD](../README.md) via [Continuous Delivery](https://about.gitlab.com/blog/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/).
+In this tutorial, we'll show you how to initialize a [Laravel](https://laravel.com) application and set up our [Envoy](https://laravel.com/docs/master/envoy) tasks, then we'll jump into see how to test and deploy it with [GitLab CI/CD](../index.md) via [Continuous Delivery](https://about.gitlab.com/blog/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/).
We assume you have a basic experience with Laravel, Linux servers,
and you know how to use GitLab.
@@ -394,7 +394,7 @@ We have our app ready on GitLab, and we also can deploy it manually.
But let's take a step forward to do it automatically with [Continuous Delivery](https://about.gitlab.com/blog/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#continuous-delivery) method.
We need to check every commit with a set of automated tests to become aware of issues at the earliest, and then, we can deploy to the target environment if we are happy with the result of the tests.
-[GitLab CI/CD](../../README.md) allows us to use [Docker](https://www.docker.com) engine to handle the process of testing and deploying our app.
+[GitLab CI/CD](../../index.md) allows us to use [Docker](https://www.docker.com) engine to handle the process of testing and deploying our app.
In case you're not familiar with Docker, refer to [Set up automated builds](https://docs.docker.com/get-started/).
To be able to build, test, and deploy our app with GitLab CI/CD, we need to prepare our work environment.
diff --git a/doc/ci/index.md b/doc/ci/index.md
index 87b259af62..2f18bd2864 100644
--- a/doc/ci/index.md
+++ b/doc/ci/index.md
@@ -32,10 +32,10 @@ For a complete overview of these methodologies and GitLab CI/CD,
read the [Introduction to CI/CD with GitLab](introduction/index.md).
## GitLab CI/CD concepts
@@ -46,7 +46,7 @@ GitLab CI/CD uses a number of concepts to describe and run your build and deploy
|:--------------------------------------------------------|:-------------------------------------------------------------------------------|
| [Pipelines](pipelines/index.md) | Structure your CI/CD process through pipelines. |
| [CI/CD variables](variables/index.md) | Reuse values based on a variable/value key pair. |
-| [Environments](environments/index.md) | Deploy your application to different environments (e.g., staging, production). |
+| [Environments](environments/index.md) | Deploy your application to different environments (for example, staging, production). |
| [Job artifacts](pipelines/job_artifacts.md) | Output, use, and reuse job artifacts. |
| [Cache dependencies](caching/index.md) | Cache your dependencies for a faster execution. |
| [GitLab Runner](https://docs.gitlab.com/runner/) | Configure your own runners to execute your scripts. |
@@ -65,7 +65,7 @@ GitLab CI/CD supports numerous configuration options:
| [SSH keys for CI/CD](ssh_keys/index.md) | Using SSH keys in your CI pipelines. |
| [Pipeline triggers](triggers/index.md) | Trigger pipelines through the API. |
| [Pipelines for Merge Requests](pipelines/merge_request_pipelines.md) | Design a pipeline structure for running a pipeline in merge requests. |
-| [Integrate with Kubernetes clusters](../user/project/clusters/index.md) | Connect your project to Google Kubernetes Engine (GKE) or an existing Kubernetes cluster. |
+| [Integrate with Kubernetes clusters](../user/infrastructure/clusters/index.md) | Connect your project to Google Kubernetes Engine (GKE) or an existing Kubernetes cluster. |
| [Optimize GitLab and GitLab Runner for large repositories](large_repositories/index.md) | Recommended strategies for handling large repositories. |
| [`.gitlab-ci.yml` full reference](yaml/index.md) | All the attributes you can use with GitLab CI/CD. |
@@ -97,7 +97,7 @@ GitLab CI/CD features, grouped by DevOps stage, include:
| [Building Docker images](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. |
| [Canary Deployments](../user/project/canary_deployments.md) | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. |
| [Deploy boards](../user/project/deploy_boards.md) | Check the current health and status of each CI/CD environment running on Kubernetes. |
-| [Feature Flags](../operations/feature_flags.md) **(PREMIUM)** | Deploy your features behind Feature Flags. |
+| [Feature Flags](../operations/feature_flags.md) | Deploy your features behind Feature Flags. |
| [GitLab Pages](../user/project/pages/index.md) | Deploy static websites. |
| [GitLab Releases](../user/project/releases/index.md) | Add release notes to Git tags. |
| [Review Apps](review_apps/index.md) | Configure GitLab CI/CD to preview code changes. |
@@ -111,7 +111,7 @@ GitLab CI/CD features, grouped by DevOps stage, include:
## GitLab CI/CD examples
-See the [CI/CD examples](examples/README.md) page for example project code and tutorials for
+See the [CI/CD examples](examples/index.md) page for example project code and tutorials for
using GitLab CI/CD with various:
- App frameworks
diff --git a/doc/ci/jobs/ci_job_token.md b/doc/ci/jobs/ci_job_token.md
index 70c22d566e..308f38b22b 100644
--- a/doc/ci/jobs/ci_job_token.md
+++ b/doc/ci/jobs/ci_job_token.md
@@ -12,9 +12,8 @@ When a pipeline job is about to run, GitLab generates a unique token and injects
You can use a GitLab CI/CD job token to authenticate with specific API endpoints:
- Packages:
- - [Package Registry](../../user/packages/package_registry/index.md). To push to the
- Package Registry, you can use [deploy tokens](../../user/project/deploy_tokens/index.md).
- - [Container Registry](../../user/packages/container_registry/index.md)
+ - [Package Registry](../../user/packages/package_registry/index.md#use-gitlab-cicd-to-build-packages).
+ - [Container Registry](../../user/packages/container_registry/index.md#build-and-push-by-using-gitlab-cicd)
(the `$CI_REGISTRY_PASSWORD` is `$CI_JOB_TOKEN`).
- [Container Registry API](../../api/container_registry.md)
(scoped to the job's project, when the `ci_job_token_scope` feature flag is enabled).
@@ -24,8 +23,8 @@ You can use a GitLab CI/CD job token to authenticate with specific API endpoints
- [Release creation](../../api/releases/index.md#create-a-release).
- [Terraform plan](../../user/infrastructure/index.md).
-The token has the same permissions to access the API as the user that triggers the
-pipeline. Therefore, this user must be assigned to [a role that has the required privileges](../../user/permissions.md#gitlab-cicd-permissions).
+The token has the same permissions to access the API as the user that executes the
+job. Therefore, this user must be assigned to [a role that has the required privileges](../../user/permissions.md#gitlab-cicd-permissions).
The token is valid only while the pipeline job runs. After the job finishes, you can't
use the token anymore.
@@ -60,20 +59,20 @@ tries to steal tokens from other jobs.
## Limit GitLab CI/CD job token access
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/328553) in GitLab 14.1.
-> - [Deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
-> - Disabled on GitLab.com.
-> - Not recommended for production use.
-> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-ci-job-token-scope-limit). **(FREE SELF)**
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/328553) in GitLab 14.1. [Deployed behind the `:ci_scoped_job_token` feature flag](../../user/feature_flags.md), disabled by default.
+> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/332272) in GitLab 14.4.
-This in-development feature might not be available for your use. There can be
-[risks when enabling features still in development](../../administration/feature_flags.md#risks-when-enabling-features-still-in-development).
-Refer to this feature's version history for more details.
+FLAG:
+On self-managed GitLab, by default this feature is available. To hide the feature,
+ask an administrator to [disable the `ci_scoped_job_token` flag](../../administration/feature_flags.md).
+On GitLab.com, this feature is available.
You can limit the access scope of a project's CI/CD job token to increase the
job token's security. A job token might give extra permissions that aren't necessary
-to access specific private resources. Limiting the job token access scope reduces the risk of a leaked
-token being used to access private data that the user associated to the job can access.
+to access specific private resources.
+If a job token is leaked it could potentially be used to access data that is private
+to the job token's user. By limiting the job token access scope, private data cannot
+be accessed unless projects are explicitly authorized.
Control the job token access scope with an allowlist of other projects authorized
to be accessed by authenticating with the current project's job token. By default
@@ -87,10 +86,10 @@ setting at all times, and configure the allowlist for cross-project access if ne
For example, when the setting is enabled, jobs in a pipeline in project `A` have
a `CI_JOB_TOKEN` scope limited to project `A`. If the job needs to use the token
to make an API request to a private project `B`, then `B` must be added to the allowlist for `A`.
-If project `B` is public or internal, it doesn't need to be added to the allowlist.
+If project `B` is public or internal, it's not required to be added to the allowlist.
The job token scope is only for controlling access to private projects.
-To enable and configure the job token scope limit:
+### Configure the job token scope limit
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
@@ -99,31 +98,9 @@ To enable and configure the job token scope limit:
1. (Optional) Add existing projects to the token's access scope. The user adding a
project must have the [maintainer role](../../user/permissions.md) in both projects.
-If the job token scope limit is disabled, the token can potentially be used to authenticate
-API requests to all projects accessible to the user that triggered the job.
-
There is [a proposal](https://gitlab.com/groups/gitlab-org/-/epics/3559) to improve
the feature with more strategic control of the access permissions.
-### Enable or disable CI job token scope limit **(FREE SELF)**
-
-The GitLab CI/CD job token access scope limit is under development and not ready for production
-use. It is deployed behind a feature flag that is **disabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
-can enable it.
-
-To enable it:
-
-```ruby
-Feature.enable(:ci_scoped_job_token)
-```
-
-To disable it:
-
-```ruby
-Feature.disable(:ci_scoped_job_token)
-```
-
## Trigger a multi-project pipeline by using a CI job token
> `CI_JOB_TOKEN` for multi-project pipelines was [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/31573) from GitLab Premium to GitLab Free in 12.4.
@@ -163,3 +140,50 @@ build_submodule:
```
Read more about the [jobs artifacts API](../../api/job_artifacts.md#download-the-artifacts-archive).
+
+## Troubleshooting
+
+CI job token failures are usually shown as responses like `404 Not Found` or similar:
+
+- Unauthorized Git clone:
+
+ ```plaintext
+ $ git clone https://gitlab-ci-token:$CI_JOB_TOKEN@gitlab.com/fabiopitino/test2.git
+
+ Cloning into 'test2'...
+ remote: The project you were looking for could not be found or you don't have permission to view it.
+ fatal: repository 'https://gitlab-ci-token:[MASKED]@gitlab.com//.git/' not found
+ ```
+
+- Unauthorized package download:
+
+ ```plaintext
+ $ wget --header="JOB-TOKEN: $CI_JOB_TOKEN" ${CI_API_V4_URL}/projects/1234/packages/generic/my_package/0.0.1/file.txt
+
+ --2021-09-23 11:00:13-- https://gitlab.com/api/v4/projects/1234/packages/generic/my_package/0.0.1/file.txt
+ Resolving gitlab.com (gitlab.com)... 172.65.251.78, 2606:4700:90:0:f22e:fbec:5bed:a9b9
+ Connecting to gitlab.com (gitlab.com)|172.65.251.78|:443... connected.
+ HTTP request sent, awaiting response... 404 Not Found
+ 2021-09-23 11:00:13 ERROR 404: Not Found.
+ ```
+
+- Unauthorized API request:
+
+ ```plaintext
+ $ curl --verbose --request POST --form "token=$CI_JOB_TOKEN" --form ref=master "https://gitlab.com/api/v4/projects/1234/trigger/pipeline"
+
+ < HTTP/2 404
+ < date: Thu, 23 Sep 2021 11:00:12 GMT
+ {"message":"404 Not Found"}
+ < content-type: application/json
+ ```
+
+While troubleshooting CI/CD job token authentication issues, be aware that:
+
+- When the [CI/CD job token limit](#limit-gitlab-cicd-job-token-access) is enabled,
+ and the job token is being used to access a different project:
+ - The user that executes the job must be a member of the project that is being accessed.
+ - The user must have the [permissions](../../user/permissions.md) to perform the action.
+ - The target project must be [allowlisted for the job token scope limit](#configure-the-job-token-scope-limit).
+- The CI job token becomes invalid if the job is no longer running, has been erased,
+ or if the project is in the process of being deleted.
diff --git a/doc/ci/jobs/index.md b/doc/ci/jobs/index.md
index 54704d5574..cb3b11ebf9 100644
--- a/doc/ci/jobs/index.md
+++ b/doc/ci/jobs/index.md
@@ -45,8 +45,6 @@ Clicking an individual job shows you its job log, and allows you to:
## See why a job failed
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17782) in GitLab 10.7.
-
When a pipeline fails or is allowed to fail, there are several places where you
can find the reason:
@@ -58,8 +56,7 @@ In each place, if you hover over the failed job you can see the reason it failed
![Pipeline detail](img/job_failure_reason.png)
-In [GitLab 10.8 and later](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17814),
-you can also see the reason it failed on the Job detail page.
+You can also see the reason it failed on the Job detail page.
## The order of jobs in a pipeline
@@ -87,8 +84,6 @@ For example:
## Group jobs in a pipeline
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/6242) in GitLab 8.12.
-
If you have many similar jobs, your [pipeline graph](../pipelines/index.md#visualize-pipelines) becomes long and hard
to read.
@@ -164,8 +159,6 @@ for a single run of the manual job.
## Delay a job
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/21767) in GitLab 11.4.
-
When you do not want to run a job immediately, you can use the [`when:delayed`](../jobs/job_control.md#run-a-job-after-a-delay) keyword to
delay a job's execution for a certain period.
diff --git a/doc/ci/lint.md b/doc/ci/lint.md
index e01bd6b79f..45152e5a0d 100644
--- a/doc/ci/lint.md
+++ b/doc/ci/lint.md
@@ -4,7 +4,7 @@ group: Pipeline Authoring
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/#assignments
---
-# Validate `.gitlab-ci.yml` syntax with the CI Lint tool
+# Validate `.gitlab-ci.yml` syntax with the CI Lint tool **(FREE)**
If you want to test the validity of your GitLab CI/CD configuration before committing
the changes, you can use the CI Lint tool. This tool checks for syntax and logical
diff --git a/doc/ci/merge_request_pipelines/index.md b/doc/ci/merge_request_pipelines/index.md
deleted file mode 100644
index 13f4ca428c..0000000000
--- a/doc/ci/merge_request_pipelines/index.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: '../pipelines/merge_request_pipelines.md'
-remove_date: '2021-09-29'
----
-
-This document was moved to [another location](../pipelines/merge_request_pipelines.md).
-
-
-
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
deleted file mode 100644
index 5b68c4ca93..0000000000
--- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: '../../pipelines/pipelines_for_merged_results.md'
-remove_date: '2021-09-29'
----
-
-This document was moved to [another location](../../pipelines/pipelines_for_merged_results.md).
-
-
-
diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
deleted file mode 100644
index c8e8dffbdc..0000000000
--- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: '../../../pipelines/merge_trains.md'
-remove_date: '2021-09-29'
----
-
-This document was moved to [another location](../../../pipelines/merge_trains.md).
-
-
-
diff --git a/doc/ci/metrics_reports.md b/doc/ci/metrics_reports.md
index 9a220121f5..5343af1648 100644
--- a/doc/ci/metrics_reports.md
+++ b/doc/ci/metrics_reports.md
@@ -7,7 +7,7 @@ type: reference
# Metrics Reports **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9788) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10. Requires GitLab Runner 11.10 and above.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9788) in GitLab 11.10. Requires GitLab Runner 11.10 and above.
GitLab provides a lot of great reporting tools for things like [merge requests](../user/project/merge_requests/index.md) - [Unit test reports](unit_test_reports.md), [code quality](../user/project/merge_requests/code_quality.md), and performance tests. While JUnit is a great open framework for tests that "pass" or "fail", it is also important to see other types of metrics from a given change.
diff --git a/doc/ci/migration/jenkins.md b/doc/ci/migration/jenkins.md
index 925dff8751..c2c06375d7 100644
--- a/doc/ci/migration/jenkins.md
+++ b/doc/ci/migration/jenkins.md
@@ -42,8 +42,8 @@ can be a great resource.
## Manage organizational transition
An important part of transitioning from Jenkins to GitLab is the cultural and organizational
-changes that comes with the move, and successfully managing them. There are a few
-things we have found that helps this:
+changes that come with the move, and successfully managing them. There are a few
+things we have found that help this:
- Setting and communicating a clear vision of what your migration goals are helps
your users understand why the effort is worth it. The value is clear when
diff --git a/doc/ci/multi_project_pipelines.md b/doc/ci/multi_project_pipelines.md
deleted file mode 100644
index 93e04bf8a0..0000000000
--- a/doc/ci/multi_project_pipelines.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: 'pipelines/multi_project_pipelines.md'
-remove_date: '2021-09-29'
----
-
-This document was moved to [another location](pipelines/multi_project_pipelines.md).
-
-
-
diff --git a/doc/ci/parent_child_pipelines.md b/doc/ci/parent_child_pipelines.md
deleted file mode 100644
index f2edc26339..0000000000
--- a/doc/ci/parent_child_pipelines.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: 'pipelines/parent_child_pipelines.md'
-remove_date: '2021-09-29'
----
-
-This document was moved to [another location](pipelines/parent_child_pipelines.md).
-
-
-
diff --git a/doc/ci/pipelines/img/multi_project_pipeline_graph.png b/doc/ci/pipelines/img/multi_project_pipeline_graph.png
deleted file mode 100644
index 723a455cb4..0000000000
Binary files a/doc/ci/pipelines/img/multi_project_pipeline_graph.png and /dev/null differ
diff --git a/doc/ci/pipelines/img/multi_project_pipeline_graph_v14_3.png b/doc/ci/pipelines/img/multi_project_pipeline_graph_v14_3.png
new file mode 100644
index 0000000000..aadf8bb097
Binary files /dev/null and b/doc/ci/pipelines/img/multi_project_pipeline_graph_v14_3.png differ
diff --git a/doc/ci/pipelines/img/parent_pipeline_graph_expanded_v12_6.png b/doc/ci/pipelines/img/parent_pipeline_graph_expanded_v12_6.png
deleted file mode 100644
index db18cc201f..0000000000
Binary files a/doc/ci/pipelines/img/parent_pipeline_graph_expanded_v12_6.png and /dev/null differ
diff --git a/doc/ci/pipelines/img/parent_pipeline_graph_expanded_v14_3.png b/doc/ci/pipelines/img/parent_pipeline_graph_expanded_v14_3.png
new file mode 100644
index 0000000000..206e4eeec0
Binary files /dev/null and b/doc/ci/pipelines/img/parent_pipeline_graph_expanded_v14_3.png differ
diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md
index 1eacc79963..69e974406e 100644
--- a/doc/ci/pipelines/index.md
+++ b/doc/ci/pipelines/index.md
@@ -38,7 +38,7 @@ A typical pipeline might consist of four stages, executed in the following order
- A `production` stage, with a job called `deploy-to-prod`.
NOTE:
-If you have a [mirrored repository that GitLab pulls from](../../user/project/repository/repository_mirroring.md#pull-from-a-remote-repository),
+If you have a [mirrored repository that GitLab pulls from](../../user/project/repository/mirror/pull.md),
you may need to enable pipeline triggering in your project's
**Settings > Repository > Pull from a remote repository > Trigger pipelines for mirror updates**.
@@ -148,7 +148,7 @@ The pipeline now executes the jobs as configured.
#### Prefill variables in manual pipelines
-> [Introduced in](https://gitlab.com/gitlab-org/gitlab/-/issues/30101) GitLab 13.7.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30101) in GitLab 13.7.
You can use the [`value` and `description`](../yaml/index.md#prefill-variables-in-manual-pipelines)
keywords to define
@@ -339,7 +339,7 @@ GitLab capitalizes the stages' names in the pipeline graphs.
### View full pipeline graph
-> - [Visualization improvements introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276949) in GitLab 13.11.
+> - Visualization improvements [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276949) in GitLab 13.11.
The [pipeline details page](#view-pipelines) displays the full pipeline graph of
all the jobs in the pipeline.
diff --git a/doc/ci/pipelines/job_artifacts.md b/doc/ci/pipelines/job_artifacts.md
index c6b6f61ef1..7ecee5508e 100644
--- a/doc/ci/pipelines/job_artifacts.md
+++ b/doc/ci/pipelines/job_artifacts.md
@@ -8,7 +8,7 @@ type: reference, howto
# Job artifacts **(FREE)**
-> Introduced in [GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16675), artifacts in internal and private projects can be previewed when [GitLab Pages access control](../../administration/pages/index.md#access-control) is enabled.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16675) in GitLab 12.4, artifacts in internal and private projects can be previewed when [GitLab Pages access control](../../administration/pages/index.md#access-control) is enabled.
Jobs can output an archive of files and directories. This output is known as a job artifact.
@@ -111,7 +111,7 @@ the artifact.
## How searching for job artifacts works
-In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/201784) and later, artifacts
+In [GitLab 13.5 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/201784), artifacts
for [parent and child pipelines](parent_child_pipelines.md) are searched in hierarchical
order from parent to child. For example, if both parent and child pipelines have a
job with the same name, the job artifact from the parent pipeline is returned.
diff --git a/doc/ci/pipelines/merge_request_pipelines.md b/doc/ci/pipelines/merge_request_pipelines.md
index b3dfe8753c..5b40744aa7 100644
--- a/doc/ci/pipelines/merge_request_pipelines.md
+++ b/doc/ci/pipelines/merge_request_pipelines.md
@@ -198,7 +198,7 @@ which helps you get your starting configuration correct.
If you are seeing two pipelines when using `only/except`, please see the caveats
related to using `only/except` above (or, consider moving to `rules`).
-In [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/201845) and later,
+In [GitLab 13.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/201845),
you can add `workflow:rules` to [switch from branch pipelines to merge request pipelines](../yaml/index.md#switch-between-branch-pipelines-and-merge-request-pipelines).
After a merge request is open on the branch, the pipeline switches to a merge request pipeline.
diff --git a/doc/ci/pipelines/merge_trains.md b/doc/ci/pipelines/merge_trains.md
index 06c1a6fef4..6074909a88 100644
--- a/doc/ci/pipelines/merge_trains.md
+++ b/doc/ci/pipelines/merge_trains.md
@@ -8,8 +8,8 @@ last_update: 2019-07-03
# Merge trains **(PREMIUM)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9186) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.0.
-> - [Squash and merge](../../user/project/merge_requests/squash_and_merge.md) support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13001) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.6.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9186) in GitLab 12.0.
+> - [Squash and merge](../../user/project/merge_requests/squash_and_merge.md) support [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13001) in GitLab 12.6.
For more information about why you might want to use merge trains, read [How merge trains keep your master green](https://about.gitlab.com/blog/2020/01/30/all-aboard-merge-trains/).
@@ -59,8 +59,6 @@ to run. If more merge requests are added to the train, they now include the `A`
changes that are included in the target branch, and the `C` changes that are from
the merge request already in the train.
-Read more about [how merge trains keep your master green](https://about.gitlab.com/blog/2020/01/30/all-aboard-merge-trains/).
-
Watch this video for a demonstration on [how parallel execution
of merge trains can prevent commits from breaking the default
diff --git a/doc/ci/pipelines/multi_project_pipelines.md b/doc/ci/pipelines/multi_project_pipelines.md
index d31ddcf736..184961f4c9 100644
--- a/doc/ci/pipelines/multi_project_pipelines.md
+++ b/doc/ci/pipelines/multi_project_pipelines.md
@@ -88,7 +88,7 @@ The keywords available for use in trigger jobs are:
- [`only` and `except`](../yaml/index.md#only--except)
- [`when`](../yaml/index.md#when) (only with a value of `on_success`, `on_failure`, or `always`)
- [`extends`](../yaml/index.md#extends)
-- [`needs`](../yaml/index.md#needs)
+- [`needs`](../yaml/index.md#needs), but not [cross project artifact downloads with `needs`](../yaml/index.md#cross-project-artifact-downloads-with-needs)
#### Specify a downstream pipeline branch
@@ -112,7 +112,7 @@ Use:
- The `project` keyword to specify the full path to a downstream project.
- The `branch` keyword to specify the name of a branch in the project specified by `project`.
- [In GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/issues/10126) and later, variable expansion is
+ In [GitLab 12.4 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/10126), variable expansion is
supported.
Pipelines triggered on a protected branch in a downstream project use the [role](../../user/permissions.md)
@@ -290,7 +290,7 @@ When using:
## Trigger a pipeline when an upstream project is rebuilt **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9045) in GitLab Premium 12.8.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9045) in GitLab 12.8.
You can trigger a pipeline in your project whenever a pipeline finishes for a new
tag in a different project.
@@ -321,7 +321,7 @@ downstream projects. On self-managed instances, an administrator can change this
When you configure GitLab CI/CD for your project, you can visualize the stages of your
[jobs](index.md#configure-a-pipeline) on a [pipeline graph](index.md#visualize-pipelines).
-![Multi-project pipeline graph](img/multi_project_pipeline_graph.png)
+![Multi-project pipeline graph](img/multi_project_pipeline_graph_v14_3.png)
In the merge request, on the **Pipelines** tab, multi-project pipeline mini-graphs are displayed.
They expand and are shown adjacent to each other when hovering (or tapping on touchscreen devices).
diff --git a/doc/ci/pipelines/parent_child_pipelines.md b/doc/ci/pipelines/parent_child_pipelines.md
index 71f778d81b..e48728a904 100644
--- a/doc/ci/pipelines/parent_child_pipelines.md
+++ b/doc/ci/pipelines/parent_child_pipelines.md
@@ -23,7 +23,7 @@ Additionally, sometimes the behavior of a pipeline needs to be more dynamic. The
to choose to start sub-pipelines (or not) is a powerful ability, especially if the
YAML is dynamically generated.
-![Parent pipeline graph expanded](img/parent_pipeline_graph_expanded_v12_6.png)
+![Parent pipeline graph expanded](img/parent_pipeline_graph_expanded_v14_3.png)
Similarly to [multi-project pipelines](multi_project_pipelines.md), a pipeline can trigger a
set of concurrently running child pipelines, but within the same project:
@@ -72,7 +72,7 @@ microservice_a:
- template: Security/SAST.gitlab-ci.yml
```
-In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/205157) and later,
+In [GitLab 13.5 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/205157),
you can use [`include:file`](../yaml/index.md#includefile) to trigger child pipelines
with a configuration file in a different project:
@@ -169,7 +169,7 @@ runner for testing, the path separator for the trigger job would be `/`. Other C
configuration for jobs, like scripts, that use the Windows runner would use `\`.
In GitLab 12.9, the child pipeline could fail to be created in certain cases, causing the parent pipeline to fail.
-This is [resolved in GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/issues/209070).
+This is [resolved](https://gitlab.com/gitlab-org/gitlab/-/issues/209070) in GitLab 12.10.
## Nested child pipelines
diff --git a/doc/ci/pipelines/pipeline_efficiency.md b/doc/ci/pipelines/pipeline_efficiency.md
index 5c3cdd0633..94cfeff3ac 100644
--- a/doc/ci/pipelines/pipeline_efficiency.md
+++ b/doc/ci/pipelines/pipeline_efficiency.md
@@ -7,7 +7,7 @@ type: reference
# Pipeline efficiency **(FREE)**
-[CI/CD Pipelines](index.md) are the fundamental building blocks for [GitLab CI/CD](../README.md).
+[CI/CD Pipelines](index.md) are the fundamental building blocks for [GitLab CI/CD](../index.md).
Making pipelines more efficient helps you save developer time, which:
- Speeds up your DevOps processes
diff --git a/doc/ci/pipelines/pipelines_for_merged_results.md b/doc/ci/pipelines/pipelines_for_merged_results.md
index 08d7d11978..2acef9be55 100644
--- a/doc/ci/pipelines/pipelines_for_merged_results.md
+++ b/doc/ci/pipelines/pipelines_for_merged_results.md
@@ -8,7 +8,7 @@ last_update: 2019-07-03
# Pipelines for merged results **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7380) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.10.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/7380) in GitLab 11.10.
When you submit a merge request, you are requesting to merge changes from a
source branch into a target branch. By default, the CI pipeline runs jobs
@@ -49,7 +49,7 @@ To enable pipelines for merge results:
- You must be using [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner) 11.9 or later.
- You must not be using
[fast forward merges](../../user/project/merge_requests/fast_forward_merge.md) yet.
- To follow progress, see [#58226](https://gitlab.com/gitlab-org/gitlab/-/issues/26996).
+ To follow progress, see [#26996](https://gitlab.com/gitlab-org/gitlab/-/issues/26996).
- Your repository must be a GitLab repository, not an
[external repository](../ci_cd_for_external_repos/index.md).
@@ -82,7 +82,7 @@ For more information, read the [documentation on Merge Trains](merge_trains.md).
## Automatic pipeline cancellation
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12996) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.3.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12996) in GitLab 12.3.
GitLab CI/CD can detect the presence of redundant pipelines, and cancels them
to conserve CI resources.
diff --git a/doc/ci/pipelines/schedules.md b/doc/ci/pipelines/schedules.md
index 9cb600ae55..90494a715e 100644
--- a/doc/ci/pipelines/schedules.md
+++ b/doc/ci/pipelines/schedules.md
@@ -73,7 +73,7 @@ job:on-schedule:
job:
rules:
- - if: $CI_PIPELINE_SOURCE = "push"
+ - if: $CI_PIPELINE_SOURCE == "push"
script:
- make build
```
diff --git a/doc/ci/pipelines/settings.md b/doc/ci/pipelines/settings.md
index 94d7e31710..e14c1aa621 100644
--- a/doc/ci/pipelines/settings.md
+++ b/doc/ci/pipelines/settings.md
@@ -94,7 +94,7 @@ For more information, see [Deployment safety](../environments/deployment_safety.
## Specify a custom CI/CD configuration file
-> [Support for external `.gitlab-ci.yml` locations](https://gitlab.com/gitlab-org/gitlab/-/issues/14376) introduced in GitLab 12.6.
+> Support for external `.gitlab-ci.yml` locations [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/14376) in GitLab 12.6.
GitLab expects to find the CI/CD configuration file (`.gitlab-ci.yml`) in the project's root
directory. However, you can specify an alternate filename path, including locations outside the project.
@@ -241,7 +241,7 @@ Use this regex for commonly used test tools.
### View code coverage history
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/209121) the ability to download a `.csv` in GitLab 12.10.
-> - [Graph introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33743) in GitLab 13.1.
+> - Graph [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33743) in GitLab 13.1.
To see the evolution of your project code coverage over time,
you can view a graph or download a CSV file with this data.
@@ -358,6 +358,29 @@ in your `README.md`:
![coverage](https://gitlab.com/gitlab-org/gitlab/badges/main/coverage.svg?job=coverage)
```
+#### Test coverage report badge colors and limits
+
+The default colors and limits for the badge are as follows:
+
+- 95 up to and including 100% - good (`#4c1`)
+- 90 up to 95% - acceptable (`#a3c51c`)
+- 75 up to 90% - medium (`#dfb317`)
+- 0 up to 75% - low (`#e05d44`)
+- no coverage - unknown (`#9f9f9f`)
+
+NOTE:
+*Up to* means up to, but not including, the upper bound.
+
+You can overwrite the limits by using the following additional parameters ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/28317) in GitLab 14.4):
+
+- `min_good` (default 95, can use any value between 3 and 100)
+- `min_acceptable` (default 90, can use any value between 2 and min_good-1)
+- `min_medium` (default 75, can use any value between 1 and min_acceptable-1)
+
+If an invalid boundary is set, GitLab automatically adjusts it to be valid. For example,
+if `min_good` is set `80`, and `min_acceptable` is set to `85` (too high), GitLab automatically
+sets `min_acceptable` to `79` (`min_good` - `1`).
+
### Badge styles
Pipeline badges can be rendered in different styles by adding the `style=style_name` parameter to the URL. Two styles are available:
diff --git a/doc/ci/quick_start/index.md b/doc/ci/quick_start/index.md
index e238123831..4006a1c9c3 100644
--- a/doc/ci/quick_start/index.md
+++ b/doc/ci/quick_start/index.md
@@ -152,7 +152,7 @@ The pipeline starts when the commit is committed.
- Use the [`rules`](../yaml/index.md#rules) keyword to specify when to run or skip jobs.
The `only` and `except` legacy keywords are still supported, but can't be used
with `rules` in the same job.
- - Keep information across jobs and stages persistent in a pipeline with [`cache`](../yaml/index.md#cache))
+ - Keep information across jobs and stages persistent in a pipeline with [`cache`](../yaml/index.md#cache)
and [`artifacts`](../yaml/index.md#artifacts). These keywords are ways to store
dependencies and job output, even when using ephemeral runners for each job.
- For the complete `.gitlab-ci.yml` syntax, see [the full `.gitlab-ci.yml` reference topic](../yaml/index.md).
diff --git a/doc/ci/resource_groups/index.md b/doc/ci/resource_groups/index.md
new file mode 100644
index 0000000000..7de3643c0d
--- /dev/null
+++ b/doc/ci/resource_groups/index.md
@@ -0,0 +1,203 @@
+---
+stage: Release
+group: Release
+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/#assignments
+description: Control the job concurrency in GitLab CI/CD
+---
+
+# Resource Group **(FREE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15536) in GitLab 12.7.
+
+By default, pipelines in GitLab CI/CD run in parallel. The parallelization is an important factor to improve
+the feedback loop in merge requests, however, there are some situations that
+you may want to limit the concurrency on deployment
+jobs to run them one by one.
+Resource Group allows you to strategically control
+the concurrency of the jobs for optimizing your continuous deployments workflow with safety.
+
+## Add a resource group
+
+Provided that you have the following pipeline configuration (`.gitlab-ci.yml` file in your repository):
+
+```yaml
+build:
+ stage: build
+ script: echo "Your build script"
+
+deploy:
+ stage: deploy
+ script: echo "Your deployment script"
+ environment: production
+```
+
+Every time you push a new commit to a branch, it runs a new pipeline that has
+two jobs `build` and `deploy`. But if you push multiple commits in a short interval, multiple
+pipelines start running simultaneously, for example:
+
+- The first pipeline runs the jobs `build` -> `deploy`
+- The second pipeline runs the jobs `build` -> `deploy`
+
+In this case, the `deploy` jobs across different pipelines could run concurrently
+to the `production` environment. Running multiple deployment scripts to the same
+infrastructure could harm/confuse the instance and leave it in a corrupted state in the worst case.
+
+In order to ensure that a `deploy` job runs once at a time, you can specify
+[`resource_group` keyword](../yaml/index.md#resource_group) to the concurrency sensitive job:
+
+```yaml
+deploy:
+ ...
+ resource_group: production
+```
+
+With this configuration, the safety on the deployments is assured while you
+can still run `build` jobs concurrently for maximizing the pipeline efficency.
+
+## Requirements
+
+- The basic knowledge of the [GitLab CI/CD pipelines](../pipelines/index.md)
+- The basic knowledge of the [GitLab Environments and Deployments](../environments/index.md)
+- [Developer role](../../user/permissions.md) (or above) in the project to configure CI/CD pipelines.
+
+### Limitations
+
+Only one resource can be attached to a resource group.
+
+## Process modes
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202186) in GitLab 14.3.
+> - [Feature flag `ci_resource_group_process_modes`](https://gitlab.com/gitlab-org/gitlab/-/issues/340380) removed in GitLab 14.4.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/202186) in GitLab 14.4.
+
+You can choose a process mode to strategically control the job concurrency for your deployment preferences.
+The following modes are supported:
+
+- **Unordered:** This is the default process mode that limits the concurrency on running jobs.
+ It's the easiest option to use and useful when you don't care about the execution order
+ of the jobs. It starts processing the jobs whenever a job ready to run.
+- **Oldest first:** This process mode limits the concurrency of the jobs. When a resource is free,
+ it picks the first job from the list of upcoming jobs (`created`, `scheduled`, or `waiting_for_resource` state)
+ that are sorted by pipeline ID in ascending order.
+
+ This mode is useful when you want to ensure that the jobs are executed from the oldest pipeline.
+ This is less efficient compared to the `unordered` mode in terms of the pipeline efficiency,
+ but safer for continuous deployments.
+
+- **Newest first:** This process mode limits the concurrency of the jobs. When a resource is free,
+ it picks the first job from the list of upcoming jobs (`created`, `scheduled` or `waiting_for_resource` state)
+ that are sorted by pipeline ID in descending order.
+
+ This mode is useful when you want to ensure that the jobs are executed from the newest pipeline and
+ cancel all of the old deploy jobs with the [skip outdated deployment jobs](../environments/deployment_safety.md#skip-outdated-deployment-jobs) feature.
+ This is the most efficient option in terms of the pipeline efficiency, but you must ensure that each deployment job is idempotent.
+
+### Change the process mode
+
+To change the process mode of a resource group, you need to use the API and
+send a request to [edit an existing resource group](../../api/resource_groups.md#edit-an-existing-resource-group)
+by specifying the `process_mode`:
+
+- `unordered`
+- `oldest_first`
+- `newest_first`
+
+### An example of difference between the process modes
+
+Consider the following `.gitlab-ci.yml`, where we have two jobs `build` and `deploy`
+each running in their own stage, and the `deploy` job has a resource group set to
+`production`:
+
+```yaml
+build:
+ stage: build
+ script: echo "Your build script"
+
+deploy:
+ stage: deploy
+ script: echo "Your deployment script"
+ environment: production
+ resource_group: production
+```
+
+If three commits are pushed to the project in a short interval, that means that three
+pipelines run almost at the same time:
+
+- The first pipeline runs the jobs `build` -> `deploy`. Let's call this deployment job `deploy-1`.
+- The second pipeline runs the jobs `build` -> `deploy`. Let's call this deployment job `deploy-2`.
+- The third pipeline runs the jobs `build` -> `deploy`. Let's call this deployment job `deploy-3`.
+
+Depending on the process mode of the resource group:
+
+- If the process mode is set to `unordered`:
+ - `deploy-1`, `deploy-2`, and `deploy-3` do not run in parallel.
+ - There is no guarantee on the job execution order, for example, `deploy-1` could run before or after `deploy-3` runs.
+- If the process mode is `oldest_first`:
+ - `deploy-1`, `deploy-2`, and `deploy-3` do not run in parallel.
+ - `deploy-1` runs first, `deploy-2` runs second, and `deploy-3` runs last.
+- If the process mode is `newest_first`:
+ - `deploy-1`, `deploy-2`, and `deploy-3` do not run in parallel.
+ - `deploy-3` runs first, `deploy-2` runs second and `deploy-1` runs last.
+
+## Pipeline-level concurrency control with Cross-Project/Parent-Child pipelines
+
+See the how to [control the pipeline concurrency in cross-project pipelines](../yaml/index.md#pipeline-level-concurrency-control-with-cross-projectparent-child-pipelines).
+
+## API
+
+See the [API documentation](../../api/resource_groups.md).
+
+## Related features
+
+Read more how you can use GitLab for [safe deployments](../environments/deployment_safety.md).
+
+## Troubleshooting
+
+### Avoid dead locks in pipeline configurations
+
+Since [`oldest_first` process mode](#process-modes) enforces the jobs to be executed in a pipeline order,
+there is a case that it doesn't work well with the other CI features.
+
+For example, when you run [a child pipeline](../pipelines/parent_child_pipelines.md)
+that requires the same resource group with the parent pipeline,
+a dead lock could happen. Here is an example of a _bad_ setup:
+
+```yaml
+# BAD
+test:
+ stage: test
+ trigger:
+ include: child-pipeline-requires-production-resource-group.yml
+ strategy: depend
+
+deploy:
+ stage: deploy
+ script: echo
+ resource_group: production
+```
+
+In a parent pipeline, it runs the `test` job that subsequently runs a child pipeline,
+and the [`strategy: depend` option](../yaml/index.md#linking-pipelines-with-triggerstrategy) makes the `test` job wait until the child pipeline has finished.
+The parent pipeline runs the `deploy` job in the next stage, that requires a resource from the `production` resource group.
+If the process mode is `oldest_first`, it executes the jobs from the oldest pipelines, meaning the `deploy` job is going to be executed next.
+
+However, a child pipeline also requires a resource from the `production` resource group.
+Since the child pipeline is newer than the parent pipeline, the child pipeline
+waits until the `deploy` job is finished, something that will never happen.
+
+In this case, you should specify the `resource_group` keyword in the parent pipeline configuration instead:
+
+```yaml
+# GOOD
+test:
+ stage: test
+ trigger:
+ include: child-pipeline.yml
+ strategy: depend
+ resource_group: production # Specify the resource group in the parent pipeline
+
+deploy:
+ stage: deploy
+ script: echo
+ resource_group: production
+```
diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md
deleted file mode 100644
index 0e6c2f63f9..0000000000
--- a/doc/ci/runners/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: 'index.md'
-remove_date: '2021-09-28'
----
-
-This document was moved to [another location](index.md).
-
-
-
diff --git a/doc/ci/runners/build_cloud/linux_build_cloud.md b/doc/ci/runners/build_cloud/linux_build_cloud.md
index 1125d8dbcb..6cdc93591a 100644
--- a/doc/ci/runners/build_cloud/linux_build_cloud.md
+++ b/doc/ci/runners/build_cloud/linux_build_cloud.md
@@ -4,7 +4,7 @@ group: Runner
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/#assignments
---
-# Build Cloud runners for Linux
+# Build Cloud runners for Linux **(FREE)**
GitLab Build Cloud runners for Linux run in autoscale mode and are powered by Google Cloud Platform.
@@ -15,13 +15,13 @@ GitLab offers Ultimate tier capabilities and included CI/CD minutes per group pe
All your CI/CD jobs run on [n1-standard-1 instances](https://cloud.google.com/compute/docs/machine-types) with 3.75GB of RAM, CoreOS and the latest Docker Engine
installed. Instances provide 1 vCPU and 25GB of HDD disk space. The default
region of the VMs is US East1.
-Each instance is used only for one job, this ensures any sensitive data left on the system can't be accessed by other people their CI jobs.
+Each instance is used only for one job. This ensures that any sensitive data left on the system can't be accessed by other people's CI/CD jobs.
The `gitlab-shared-runners-manager-X.gitlab.com` fleet of runners are dedicated for GitLab projects as well as community forks of them. They use a slightly larger machine type (n1-standard-2) and have a bigger SSD disk size. They don't run untagged jobs and unlike the general fleet of shared runners, the instances are re-used up to 40 times.
Jobs handled by the shared runners on GitLab.com (`shared-runners-manager-X.gitlab.com`),
**time out after 3 hours**, regardless of the timeout configured in a
-project. Check the issues [4010](https://gitlab.com/gitlab-com/infrastructure/-/issues/4010) and [4070](https://gitlab.com/gitlab-com/infrastructure/-/issues/4070) for the reference.
+project. Check the issues [#4010](https://gitlab.com/gitlab-com/infrastructure/-/issues/4010) and [#4070](https://gitlab.com/gitlab-com/infrastructure/-/issues/4070) for the reference.
Below are the runners' settings.
diff --git a/doc/ci/runners/build_cloud/macos/environment.md b/doc/ci/runners/build_cloud/macos/environment.md
index 5336890b93..8a2417186a 100644
--- a/doc/ci/runners/build_cloud/macos/environment.md
+++ b/doc/ci/runners/build_cloud/macos/environment.md
@@ -4,7 +4,7 @@ group: Runner
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/#assignments
---
-# VM instances and images for Build Cloud for macOS
+# VM instances and images for Build Cloud for macOS **(FREE)**
When you use the Build Cloud for macOS:
diff --git a/doc/ci/runners/build_cloud/macos_build_cloud.md b/doc/ci/runners/build_cloud/macos_build_cloud.md
index 794bc2e597..5d55462fb6 100644
--- a/doc/ci/runners/build_cloud/macos_build_cloud.md
+++ b/doc/ci/runners/build_cloud/macos_build_cloud.md
@@ -4,7 +4,7 @@ group: Runner
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/#assignments
---
-# Build Cloud runners for macOS (Beta)
+# Build Cloud runners for macOS (Beta) **(FREE SAAS)**
The GitLab Build Cloud for macOS Beta provides on-demand runners integrated with GitLab SaaS [CI/CD](../../../ci/index.md).
Use these runners to build, test, and deploy apps for the Apple ecosystem (macOS, iOS, tvOS). You can take advantage
diff --git a/doc/ci/runners/build_cloud/windows_build_cloud.md b/doc/ci/runners/build_cloud/windows_build_cloud.md
index 0004041a3e..e01de72305 100644
--- a/doc/ci/runners/build_cloud/windows_build_cloud.md
+++ b/doc/ci/runners/build_cloud/windows_build_cloud.md
@@ -4,7 +4,7 @@ group: Runner
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/#assignments
---
-# Build Cloud runners for Windows (beta)
+# Build Cloud runners for Windows (beta) **(FREE)**
GitLab Build Cloud runners for Windows are in [beta](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta)
and shouldn't be used for production workloads.
@@ -19,7 +19,7 @@ the Google Cloud Platform. This solution uses an
developed by GitLab for the [custom executor](https://docs.gitlab.com/runner/executors/custom.html).
Windows runners execute your CI/CD jobs on `n1-standard-2` instances with
2 vCPUs and 7.5 GB RAM. You can find a full list of available Windows packages in
-the [package documentation](https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/gcp/windows-containers/blob/master/cookbooks/preinstalled-software/README.md).
+the [package documentation](https://gitlab.com/gitlab-org/ci-cd/shared-runners/images/gcp/windows-containers/blob/main/cookbooks/preinstalled-software/README.md).
We want to keep iterating to get Windows runners in a stable state and
[generally available](https://about.gitlab.com/handbook/product/gitlab-the-product/#generally-available-ga).
diff --git a/doc/ci/secrets/index.md b/doc/ci/secrets/index.md
index 898d310598..4d42bc69df 100644
--- a/doc/ci/secrets/index.md
+++ b/doc/ci/secrets/index.md
@@ -1,6 +1,6 @@
---
-stage: Release
-group: Release
+stage: Configure
+group: Configure
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/#assignments
type: concepts, howto
---
diff --git a/doc/ci/ssh_keys/index.md b/doc/ci/ssh_keys/index.md
index 1e761643a1..817263374f 100644
--- a/doc/ci/ssh_keys/index.md
+++ b/doc/ci/ssh_keys/index.md
@@ -10,7 +10,7 @@ type: tutorial
GitLab currently doesn't have built-in support for managing SSH keys in a build
environment (where the GitLab Runner runs).
-The SSH keys can be useful when:
+Use SSH keys when:
1. You want to checkout internal submodules
1. You want to download private packages using your package manager (for example, Bundler)
@@ -45,9 +45,9 @@ check the [visibility of your pipelines](../pipelines/settings.md#change-which-u
When your CI/CD jobs run inside Docker containers (meaning the environment is
contained) and you want to deploy your code in a private server, you need a way
-to access it. This is where an SSH key pair comes in handy.
+to access it. In this case, you can use an SSH key pair.
-1. You first need to create an SSH key pair. For more information, follow
+1. You first must create an SSH key pair. For more information, follow
the instructions to [generate an SSH key](../../ssh/index.md#generate-an-ssh-key-pair).
**Do not** add a passphrase to the SSH key, or the `before_script` will
prompt for it.
@@ -101,7 +101,7 @@ to access it. This is where an SSH key pair comes in handy.
1. As a final step, add the _public_ key from the one you created in the first
step to the services that you want to have an access to from within the build
- environment. If you are accessing a private GitLab repository you need to add
+ environment. If you are accessing a private GitLab repository you must add
it as a [deploy key](../../user/project/deploy_keys/index.md).
That's it! You can now have access to private servers or repositories in your
@@ -130,7 +130,7 @@ on, and use that key for all projects that are run on this machine.
1. As a final step, add the _public_ key from the one you created earlier to the
services that you want to have an access to from within the build environment.
- If you are accessing a private GitLab repository you need to add it as a
+ If you are accessing a private GitLab repository you must add it as a
[deploy key](../../user/project/deploy_keys/index.md).
After generating the key, try to sign in to the remote server to accept the
@@ -163,8 +163,8 @@ ssh-keyscan 1.2.3.4
Create a new [CI/CD variable](../variables/index.md) with
`SSH_KNOWN_HOSTS` as "Key", and as a "Value" add the output of `ssh-keyscan`.
-If you need to connect to multiple servers, all the server host keys
-need to be collected in the **Value** of the variable, one key per line.
+If you must connect to multiple servers, all the server host keys
+must be collected in the **Value** of the variable, one key per line.
NOTE:
By using a variable instead of `ssh-keyscan` directly inside
@@ -175,7 +175,7 @@ so there's something wrong with the server or the network.
Now that the `SSH_KNOWN_HOSTS` variable is created, in addition to the
[content of `.gitlab-ci.yml`](#ssh-keys-when-using-the-docker-executor)
-above, here's what more you need to add:
+above, you must add:
```yaml
before_script:
@@ -209,5 +209,5 @@ We have set up an [Example SSH Project](https://gitlab.com/gitlab-examples/ssh-p
that runs on [GitLab.com](https://gitlab.com) using our publicly available
[shared runners](../runners/index.md).
-Want to hack on it? Simply fork it, commit and push your changes. Within a few
+Want to hack on it? Fork it, commit, and push your changes. In a few
moments the changes is picked by a public runner and the job starts.
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
deleted file mode 100644
index 0e6c2f63f9..0000000000
--- a/doc/ci/triggers/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: 'index.md'
-remove_date: '2021-09-28'
----
-
-This document was moved to [another location](index.md).
-
-
-
diff --git a/doc/ci/troubleshooting.md b/doc/ci/troubleshooting.md
index 827a89fa99..994e9294ff 100644
--- a/doc/ci/troubleshooting.md
+++ b/doc/ci/troubleshooting.md
@@ -50,7 +50,7 @@ and check if their values are what you expect.
## GitLab CI/CD documentation
The [complete `.gitlab-ci.yml` reference](yaml/index.md) contains a full list of
-every keyword you may need to use to configure your pipelines.
+every keyword you can use to configure your pipelines.
You can also look at a large number of pipeline configuration [examples](examples/index.md)
and [templates](examples/index.md#cicd-templates).
@@ -76,7 +76,7 @@ if you are using that type:
### Troubleshooting Guides for CI/CD features
-There are troubleshooting guides available for some CI/CD features and related topics:
+Troubleshooting guides are available for some CI/CD features and related topics:
- [Container Registry](../user/packages/container_registry/index.md#troubleshooting-the-gitlab-container-registry)
- [GitLab Runner](https://docs.gitlab.com/runner/faq/)
@@ -118,7 +118,7 @@ Two pipelines can run when pushing a commit to a branch that has an open merge r
associated with it. Usually one pipeline is a merge request pipeline, and the other
is a branch pipeline.
-This is usually caused by the `rules` configuration, and there are several ways to
+This situation is usually caused by the `rules` configuration, and there are several ways to
[prevent duplicate pipelines](jobs/job_control.md#avoid-duplicate-pipelines).
#### A job is not in the pipeline
@@ -168,7 +168,7 @@ a branch to its remote repository. To illustrate the problem, suppose you've had
1. A new pipeline starts running on the `example` branch again, however,
the previous pipeline (2) fails because of `fatal: reference is not a tree:` error.
-This is because the previous pipeline cannot find a checkout-SHA (which is associated with the pipeline record)
+This occurs because the previous pipeline cannot find a checkout-SHA (which is associated with the pipeline record)
from the `example` branch that the commit history has already been overwritten by the force-push.
Similarly, [Pipelines for merged results](pipelines/pipelines_for_merged_results.md)
might have failed intermittently due to [the same reason](pipelines/pipelines_for_merged_results.md#intermittently-pipelines-fail-by-fatal-reference-is-not-a-tree-error).
@@ -199,6 +199,7 @@ latest commit yet. This might be because:
- You are not using CI/CD pipelines in your project.
- You are using CI/CD pipelines in your project, but your configuration prevented a pipeline from running on the source branch for your merge request.
- The latest pipeline was deleted (this is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/214323)).
+- The source branch of the merge request is on a private fork.
After the pipeline is created, the message updates with the pipeline status.
@@ -262,7 +263,7 @@ To [prevent duplicate pipelines](jobs/job_control.md#avoid-duplicate-pipelines),
[`workflow: rules`](yaml/index.md#workflow) or rewrite your rules to control
which pipelines can run.
-### Console workaround if job using resource_group gets stuck
+### Console workaround if job using resource_group gets stuck **(FREE SELF)**
```ruby
# find resource group by name
diff --git a/doc/ci/unit_test_reports.md b/doc/ci/unit_test_reports.md
index 7677908e93..c37d7f2797 100644
--- a/doc/ci/unit_test_reports.md
+++ b/doc/ci/unit_test_reports.md
@@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: reference
---
-# Unit test reports
+# Unit test reports **(FREE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/45318) in GitLab 11.2. Requires GitLab Runner 11.2 and above.
> - [Renamed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39737) from JUnit test reports to Unit test reports in GitLab 13.4.
@@ -67,9 +67,9 @@ execution time and the error output.
### Number of recent failures
-> - [Introduced in Merge Requests](https://gitlab.com/gitlab-org/gitlab/-/issues/241759) in GitLab 13.7.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241759) in Merge Requests in GitLab 13.7.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/268249) in GitLab 13.8.
-> - [Introduced in Test Reports](https://gitlab.com/gitlab-org/gitlab/-/issues/235525) in GitLab 13.9.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235525) in Test Reports in GitLab 13.9.
If a test failed in the project's default branch in the last 14 days, a message like
`Failed {n} time(s) in {default_branch} in the last 14 days` is displayed for that test.
@@ -328,7 +328,7 @@ phpunit:
## Viewing Unit test reports on GitLab
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24792) in GitLab 12.5 behind a feature flag (`junit_pipeline_view`), disabled by default.
-> - The feature flag was removed and the feature was [made generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/216478) in GitLab 13.3.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/216478) in GitLab 13.3.
If JUnit report format XML files are generated and uploaded as part of a pipeline, these reports
can be viewed inside the pipelines details page. The **Tests** tab on this page
@@ -357,7 +357,7 @@ GitLab does not parse very [large nodes](https://nokogiri.org/tutorials/parsing_
## Viewing JUnit screenshots on GitLab
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202114) in GitLab 13.0 behind the `:junit_pipeline_screenshots_view` feature flag, disabled by default.
-> - The feature flag was removed and was [made generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/216979) in GitLab 13.12.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/216979) in GitLab 13.12.
Upload your screenshots as [artifacts](yaml/index.md#artifactsreportsjunit) to GitLab. If JUnit
report format XML files contain an `attachment` tag, GitLab parses the attachment. Note that:
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
deleted file mode 100644
index 0e6c2f63f9..0000000000
--- a/doc/ci/variables/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: 'index.md'
-remove_date: '2021-09-28'
----
-
-This document was moved to [another location](index.md).
-
-
-
diff --git a/doc/ci/variables/index.md b/doc/ci/variables/index.md
index fa4bc6e39f..8650adf035 100644
--- a/doc/ci/variables/index.md
+++ b/doc/ci/variables/index.md
@@ -191,7 +191,7 @@ The output is:
### Add a CI/CD variable to a group
-> Support for [environment scopes](https://gitlab.com/gitlab-org/gitlab/-/issues/2874) added to GitLab Premium in 13.11
+> Support for environment scopes [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2874) in GitLab Premium 13.11
To make a CI/CD variable available to all projects in a group, define a group CI/CD variable.
@@ -244,7 +244,7 @@ To add an instance variable:
1. Select the **Add variable** button, and fill in the details:
- **Key**: Must be one line, with no spaces, using only letters, numbers, or `_`.
- - **Value**: [In GitLab 13.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/220028),
+ - **Value**: In [GitLab 13.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/220028),
10,000 characters is allowed. This is also bounded by the limits of the selected
runner operating system. In GitLab 13.0 to 13.2, 700 characters is allowed.
- **Type**: [`File` or `Variable`](#cicd-variable-types).
@@ -301,7 +301,7 @@ An alternative to `File` type variables is to:
```shell
# Read certificate stored in $KUBE_CA_PEM variable and save it in a new file
-echo "$KUBE_CA_PEM" > "$(pwd)/kube.ca.pem"
+cat "$KUBE_CA_PEM" > "$(pwd)/kube.ca.pem"
# Pass the newly created file to kubectl
kubectl config set-cluster e2e --server="$KUBE_URL" --certificate-authority="$(pwd)/kube.ca.pem"
```
@@ -346,9 +346,9 @@ The value of the variable must:
- Be a single line.
- Be 8 characters or longer, consisting only of:
- Characters from the Base64 alphabet (RFC4648).
- - The `@` and `:` characters ([In GitLab 12.2](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/63043) and later).
- - The `.` character ([In GitLab 12.10](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29022) and later).
- - The `~` character ([In GitLab 13.12](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61517) and later).
+ - The `@` and `:` characters (In [GitLab 12.2 and later](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/63043)).
+ - The `.` character (In [GitLab 12.10 and later](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29022)).
+ - The `~` character (In [GitLab 13.12 and later](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61517)).
- Not match the name of an existing predefined or custom CI/CD variable.
NOTE:
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index c8688d2433..45fa199434 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -20,6 +20,7 @@ There are also [Kubernetes-specific deployment variables](../../user/project/clu
|------------------------------------------|--------|--------|-------------|
| `CHAT_CHANNEL` | 10.6 | all | The Source chat channel that triggered the [ChatOps](../chatops/index.md) command. |
| `CHAT_INPUT` | 10.6 | all | The additional arguments passed with the [ChatOps](../chatops/index.md) command. |
+| `CHAT_USER_ID` | 14.4 | all | The chat service's user ID of the user who triggered the [ChatOps](../chatops/index.md) command. |
| `CI` | all | 0.4 | Available for all jobs executed in CI/CD. `true` when available. |
| `CI_API_V4_URL` | 11.7 | all | The GitLab API v4 root URL. |
| `CI_BUILDS_DIR` | all | 11.10 | The top-level directory where builds are executed. |
diff --git a/doc/ci/variables/where_variables_can_be_used.md b/doc/ci/variables/where_variables_can_be_used.md
index 0f9339657f..9fd1e3eb1a 100644
--- a/doc/ci/variables/where_variables_can_be_used.md
+++ b/doc/ci/variables/where_variables_can_be_used.md
@@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: reference
---
-# Where variables can be used
+# Where variables can be used **(FREE)**
As it's described in the [CI/CD variables](index.md) docs, you can
define many different variables. Some of them can be used for all GitLab CI/CD
@@ -65,9 +65,10 @@ because the expansion is done in GitLab before any runner gets the job.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48627) in GitLab 13.10. [Deployed behind the `variable_inside_variable` feature flag](../../user/feature_flags.md), disabled by default.
> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/297382) in GitLab 14.3.
+> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/297382) in GitLab 14.4.
FLAG:
-On self-managed GitLab, by default this feature is disabled. To enable the feature per project or for your entire instance, ask an administrator to [enable the `variable_inside_variable` flag](../../administration/feature_flags.md).
+On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](../../administration/feature_flags.md) named `variable_inside_variable`. On GitLab.com, this feature is available.
GitLab expands job variable values recursively before sending them to the runner. For example:
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
deleted file mode 100644
index 0e6c2f63f9..0000000000
--- a/doc/ci/yaml/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
----
-redirect_to: 'index.md'
-remove_date: '2021-09-28'
----
-
-This document was moved to [another location](index.md).
-
-
-
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index fb5748788f..aa44400fff 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -10,7 +10,7 @@ type: reference
This document lists the configuration options for your GitLab `.gitlab-ci.yml` file.
- For a quick introduction to GitLab CI/CD, follow the [quick start guide](../quick_start/index.md).
-- For a collection of examples, see [GitLab CI/CD Examples](../examples/README.md).
+- For a collection of examples, see [GitLab CI/CD Examples](../examples/index.md).
- To view a large `.gitlab-ci.yml` file used in an enterprise, see the [`.gitlab-ci.yml` file for `gitlab`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab-ci.yml).
When you are editing your `.gitlab-ci.yml` file, you can validate it with the
@@ -446,15 +446,15 @@ that proposes expanding this feature to support more variables.
#### `rules` with `include`
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276515) in GitLab 14.2.
-> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/337507) in GitLab 14.3 and is ready for production use.
-> - [Enabled with `ci_include_rules` flag](https://gitlab.com/gitlab-org/gitlab/-/issues/337507) for self-managed GitLab in GitLab 14.3 and is ready for production use.
-
-FLAG:
-On self-managed GitLab, by default this feature is available. To hide the feature per project or for your entire instance, ask an administrator to [disable the `ci_include_rules` flag](../../administration/feature_flags.md). On GitLab.com, this feature is available.
+> - Introduced in GitLab 14.2 [with a flag](../../administration/feature_flags.md) named `ci_include_rules`. Disabled by default.
+> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/337507) in GitLab 14.3.
+> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/337507) GitLab 14.3.
+> - [Feature flag `ci_include_rules` removed](https://gitlab.com/gitlab-org/gitlab/-/issues/337507) in GitLab 14.4.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/337507) in GitLab 14.4.
You can use [`rules`](#rules) with `include` to conditionally include other configuration files.
-You can only use `rules:if` in `include` with [certain variables](#variables-with-include).
+You can only use [`if` rules](#rulesif) in `include`, and only with [certain variables](#variables-with-include).
+`rules` keywords such as `changes` and `exists` are not supported.
```yaml
include:
@@ -746,9 +746,9 @@ Use `before_script` to define an array of commands that should run before each j
```yaml
job:
before_script:
- - echo "Execute this command before any `script:` commands."
+ - echo "Execute this command before any 'script:' commands."
script:
- - echo "This command executes after the job's `before_script` commands."
+ - echo "This command executes after the job's 'before_script' commands."
```
**Additional details**:
@@ -794,7 +794,7 @@ job:
Scripts you specify in `after_script` execute in a new shell, separate from any
`before_script` or `script` commands. As a result, they:
-- Have a current working directory set back to the default.
+- Have the current working directory set back to the default (according to the [variables which define how the runner processes Git requests](#configure-runner-behavior-with-variables)).
- Don't have access to changes done by commands defined in the `before_script` or `script`,
including:
- Command aliases and variables exported in `script` scripts.
@@ -1178,6 +1178,7 @@ job:
all rules. You can't mix `when` at the job-level with `when` in rules.
- Unlike variables in [`script`](../variables/index.md#use-cicd-variables-in-job-scripts)
sections, variables in rules expressions are always formatted as `$VARIABLE`.
+ - You can use `rules:if` with `include` to [conditionally include other configuration files](#rules-with-include).
**Related topics**:
@@ -1701,6 +1702,8 @@ same group or namespace, you can omit them from the `project:` keyword. For exam
The user running the pipeline must have at least `reporter` access to the group or project, or the group/project must have public visibility.
+You cannot use cross project artifact downloads in the same job as [`trigger`](#trigger).
+
##### Artifact downloads between pipelines in the same project
Use `needs` to download artifacts from different pipelines in the current project.
@@ -2220,7 +2223,7 @@ For more information, see
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27630) in GitLab 12.6.
Use the `kubernetes` keyword to configure deployments to a
-[Kubernetes cluster](../../user/project/clusters/index.md) that is associated with your project.
+[Kubernetes cluster](../../user/infrastructure/clusters/index.md) that is associated with your project.
For example:
@@ -2243,7 +2246,7 @@ For more information, see
NOTE:
Kubernetes configuration is not supported for Kubernetes clusters
-that are [managed by GitLab](../../user/project/clusters/index.md#gitlab-managed-clusters).
+that are [managed by GitLab](../../user/project/clusters/gitlab_managed_clusters.md).
To follow progress on support for GitLab-managed clusters, see the
[relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/38054).
@@ -3258,15 +3261,16 @@ job:
### `coverage`
-Use `coverage` to configure how code coverage is extracted from the
-job output.
+Use `coverage` with a custom regular expression to configure how code coverage
+is extracted from the job output. The coverage is shown in the UI if at least one
+line in the job output matches the regular expression.
-Regular expressions are the only valid kind of value expected here. So, using
-surrounding `/` is mandatory to consistently and explicitly represent
-a regular expression string. You must escape special characters if you want to
-match them literally.
+To extract the code coverage value in the matching line, GitLab uses this
+regular expression: `\d+(\.\d+)?`.
-For example:
+**Possible inputs**: A regular expression. Must start and end with `/`.
+
+**Example of `coverage`**:
```yaml
job1:
@@ -3274,14 +3278,20 @@ job1:
coverage: '/Code coverage: \d+\.\d+/'
```
-The coverage is shown in the UI if at least one line in the job output matches the regular expression.
-If there is more than one matched line in the job output, the last line is used.
-For the matched line, the first occurrence of `\d+(\.\d+)?` is the code coverage.
-Leading zeros are removed.
+In this example:
-Coverage output from [child pipelines](../pipelines/parent_child_pipelines.md) is not recorded
-or displayed. Check [the related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/280818)
-for more details.
+1. GitLab checks the job log for a line that matches the regular expression. A line
+ like `Code coverage: 67.89` would match.
+1. GitLab then checks the line to find a match to `\d+(\.\d+)?`. The sample matching
+ line above gives a code coverage of `67.89`.
+
+**Additional details**:
+
+- If there is more than one matched line in the job output, the last line is used.
+- Leading zeros are removed.
+- Coverage output from [child pipelines](../pipelines/parent_child_pipelines.md)
+ is not recorded or displayed. Check [the related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/280818)
+ for more details.
### `dast_configuration` **(ULTIMATE)**
@@ -3331,17 +3341,21 @@ to select a specific site profile and scanner profile.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3515) in GitLab 11.5, you can control which failures to retry on.
-Use `retry` to configure how many times a job is retried in
-case of a failure.
+Use `retry` to configure how many times a job is retried if it fails.
+If not defined, defaults to `0` and jobs do not retry.
-When a job fails, the job is processed again,
-until the limit specified by the `retry` keyword is reached.
+When a job fails, the job is processed up to two more times, until it succeeds or
+reaches the maximum number of retries.
-If `retry` is set to `2`, and a job succeeds in a second run (first retry), it is not retried.
-The `retry` value must be a positive integer, from `0` to `2`
-(two retries maximum, three runs in total).
+By default, all failure types cause the job to be retried. Use [`retry:when`](#retrywhen)
+to select which failures to retry on.
-The following example retries all failure cases:
+**Keyword type**: Job keyword. You can use it only as part of a job or in the
+[`default:` section](#custom-default-keyword-values).
+
+**Possible inputs**: `0` (default), `1`, or `2`.
+
+**Example of `retry`**:
```yaml
test:
@@ -3349,38 +3363,16 @@ test:
retry: 2
```
-By default, a job is retried on all failure cases. To have better control
-over which failures to retry, `retry` can be a hash with the following keys:
+#### `retry:when`
-- `max`: The maximum number of retries.
-- `when`: The failure cases to retry.
+Use `retry:when` with `retry:max` to retry jobs for only specific failure cases.
+`retry:max` is the maximum number of retries, like [`retry`](#retry), and can be
+`0`, `1`, or `2`.
-To retry only runner system failures at maximum two times:
+**Keyword type**: Job keyword. You can use it only as part of a job or in the
+[`default:` section](#custom-default-keyword-values).
-```yaml
-test:
- script: rspec
- retry:
- max: 2
- when: runner_system_failure
-```
-
-If there is another failure, other than a runner system failure, the job
-is not retried.
-
-To retry on multiple failure cases, `when` can also be an array of failures:
-
-```yaml
-test:
- script: rspec
- retry:
- max: 2
- when:
- - runner_system_failure
- - stuck_or_timeout_failure
-```
-
-Possible values for `when` are:
+**Possible inputs**: A single failure type, or an array of one or more failure types:
-
diff --git a/doc/development/adding_database_indexes.md b/doc/development/adding_database_indexes.md
index 16dd581113..9ca08ab1dc 100644
--- a/doc/development/adding_database_indexes.md
+++ b/doc/development/adding_database_indexes.md
@@ -275,7 +275,8 @@ You can verify if the MR was deployed to GitLab.com by executing
`/chatops run auto_deploy status `. To verify existence of
the index, you can:
-- Use a meta-command in #database-lab, such as: `\di `
+- Use a meta-command in #database-lab, such as: `\d `
+ - Ensure that the index is not [`invalid`](https://www.postgresql.org/docs/12/sql-createindex.html#:~:text=The%20psql%20%5Cd%20command%20will%20report%20such%20an%20index%20as%20INVALID)
- Ask someone in #database to check if the index exists
- With proper access, you can also verify directly on production or in a
production clone
diff --git a/doc/development/application_slis/index.md b/doc/development/application_slis/index.md
new file mode 100644
index 0000000000..c1d7ac9fa0
--- /dev/null
+++ b/doc/development/application_slis/index.md
@@ -0,0 +1,130 @@
+---
+stage: Platforms
+group: Scalability
+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/#assignments
+---
+
+# GitLab Application Service Level Indicators (SLIs)
+
+> [Introduced](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/525) in GitLab 14.4
+
+It is possible to define [Service Level Indicators
+(SLIs)](https://en.wikipedia.org/wiki/Service_level_indicator)
+directly in the Ruby codebase. This keeps the definition of operations
+and their success close to the implementation and allows the people
+building features to easily define how these features should be
+monitored.
+
+Defining an SLI causes 2
+[Prometheus
+counters](https://prometheus.io/docs/concepts/metric_types/#counter)
+to be emitted from the rails application:
+
+- `gitlab_sli::total`: incremented for each operation.
+- `gitlab_sli::success_total`: incremented for successful
+ operations.
+
+## Existing SLIs
+
+1. [`rails_request_apdex`](rails_request_apdex.md)
+
+## Defining a new SLI
+
+An SLI can be defined using the `Gitlab::Metrics::Sli` class.
+
+Before the first scrape, it is important to have [initialized the SLI
+with all possible
+label-combinations](https://prometheus.io/docs/practices/instrumentation/#avoid-missing-metrics). This
+avoid confusing results when using these counters in calculations.
+
+To initialize an SLI, use the `.inilialize_sli` class method, for
+example:
+
+```ruby
+Gitlab::Metrics::Sli.initialize_sli(:received_email, [
+ {
+ feature_category: :issue_tracking,
+ email_type: :create_issue
+ },
+ {
+ feature_category: :service_desk,
+ email_type: :service_desk
+ },
+ {
+ feature_category: :code_review,
+ email_type: :create_merge_request
+ }
+])
+```
+
+Metrics must be initialized before they get
+scraped for the first time. This could be done at the start time of the
+process that will emit them, in which case we need to pay attention
+not to increase application's boot time too much. This is preferable
+if possible.
+
+Alternatively, if initializing would take too long, this can be done
+during the first scrape. We need to make sure we don't do it for every
+scrape. This can be done as follows:
+
+```ruby
+def initialize_request_slis_if_needed!
+ return if Gitlab::Metrics::Sli.initialized?(:rails_request_apdex)
+ Gitlab::Metrics::Sli.initialize_sli(:rails_request_apdex, possible_request_labels)
+end
+```
+
+Also pay attention to do it for the different metrics
+endpoints we have. Currently the
+[`WebExporter`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/metrics/exporter/web_exporter.rb)
+and the
+[`HealthController`](https://gitlab.com/gitlab-org/gitlab/blob/master/app/controllers/health_controller.rb)
+for Rails and
+[`SidekiqExporter`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/metrics/exporter/sidekiq_exporter.rb)
+for Sidekiq.
+
+## Tracking operations for an SLI
+
+Tracking an operation in the newly defined SLI can be done like this:
+
+```ruby
+Gitlab::Metrics::Sli[:received_email].increment(
+ labels: {
+ feature_category: :service_desk,
+ email_type: :service_desk
+ },
+ success: issue_created?
+)
+```
+
+Calling `#increment` on this SLI will increment the total Prometheus counter
+
+```prometheus
+gitlab_sli:received_email:total{ feature_category='service_desk', email_type='service_desk' }
+```
+
+If the `success:` argument passed is truthy, then the success counter
+will also be incremented:
+
+```prometheus
+gitlab_sli:received_email:success_total{ feature_category='service_desk', email_type='service_desk' }
+```
+
+## Using the SLI in service monitoring and alerts
+
+When the application is emitting metrics for the new SLI, those need
+to be consumed in the service catalog to result in alerts, and be
+included in the error budget for stage groups and GitLab.com's overall
+availability.
+
+This is currently being worked on in [this
+project](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/573). As
+part of [this
+issue](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1307)
+we will update the documentation.
+
+For any question, please don't hesitate to createan issue in [the
+Scalability issue
+tracker](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues)
+or come find us in
+[#g_scalability](https://gitlab.slack.com/archives/CMMF8TKR9) on Slack.
diff --git a/doc/development/application_slis/rails_request_apdex.md b/doc/development/application_slis/rails_request_apdex.md
new file mode 100644
index 0000000000..e1ab536857
--- /dev/null
+++ b/doc/development/application_slis/rails_request_apdex.md
@@ -0,0 +1,234 @@
+---
+stage: Platforms
+group: Scalability
+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/#assignments
+---
+
+# Rails request apdex SLI
+
+> [Introduced](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/525) in GitLab 14.4
+
+NOTE:
+This SLI is not yet used in [error budgets for stage
+groups](../stage_group_dashboards.md#error-budget) or service
+monitoring. This is being worked on in [this
+project](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/573).
+
+The request apdex SLI is [an SLI defined in the application](index.md)
+that measures the duration of successful requests as an indicator for
+application performance. This includes the REST and GraphQL API, and the
+regular controller endpoints. It consists of these counters:
+
+1. `gitlab_sli:rails_request_apdex:total`: This counter gets
+ incremented for every request that did not result in a response
+ with a 5xx status code. This means that slow failures don't get
+ counted twice: The request is already counted in the error-SLI.
+
+1. `gitlab_sli:rails_request_apdex:success_total`: This counter gets
+ incremented for every successful request that performed faster than
+ the [defined target duration depending on the endpoint's
+ urgency](#adjusting-request-urgency).
+
+Both these counters are labeled with:
+
+1. `endpoint_id`: The identification of the Rails Controller or the
+ Grape-API endpoint
+
+1. `feature_category`: The feature category specified for that
+ controller or API endpoint.
+
+## Request Apdex SLO
+
+These counters can be combined into a success ratio, the objective for
+this ratio is defined in the service catalog per service:
+
+1. [Web: 0.998](https://gitlab.com/gitlab-com/runbooks/blob/master/metrics-catalog/services/web.jsonnet#L19)
+1. [API: 0.995](https://gitlab.com/gitlab-com/runbooks/blob/master/metrics-catalog/services/api.jsonnet#L19)
+1. [Git: 0.998](https://gitlab.com/gitlab-com/runbooks/blob/master/metrics-catalog/services/git.jsonnet#L22)
+
+This means that for this SLI to meet SLO, the ratio recorded needs to
+be higher than those defined above.
+
+For example: for the web-service, we want at least 99.8% of requests
+to be faster than their target duration.
+
+These are the targets we use for alerting and service montoring. So
+durations should be set keeping those into account. So we would not
+cause alerts. But the goal would be to set the urgency to a target
+that users would be satisfied with.
+
+Both successful measurements and unsuccessful ones have an impact on the
+error budget for stage groups.
+
+## Adjusting request urgency
+
+Not all endpoints perform the same type of work, so it is possible to
+define different urgencies for different endpoints. An endpoint with a
+lower urgency can have a longer request duration than endpoints that
+are high urgency.
+
+Long-running requests are more expensive for our
+infrastructure: while one request is being served, the thread remains
+occupied for the duration of that request. So nothing else can be handled by that
+thread. Because of Ruby's Global VM Lock, the thread might keep the
+lock and stall other requests handled by the same Puma worker
+process. The request is in fact a noisy neighbor for other requests
+handled by the worker. This is why the upper bound for a target
+duration is capped at 5 seconds.
+
+## Decreasing the urgency (setting a higher target duration)
+
+Increasing the urgency on an existing endpoint can be done on
+a case-by-case basis. Please take the following into account:
+
+1. Apdex is about perceived performance, if a user is actively waiting
+ for the result of a request, waiting 5 seconds might not be
+ acceptable. While if the endpoint is used by an automation
+ requiring a lot of data, 5 seconds could be okay.
+
+ A product manager can help to identify how an endpoint is used.
+
+1. The workload for some endpoints can sometimes differ greatly
+ depending on the parameters specified by the caller. The urgency
+ needs to accomodate that. In some cases, it might be interesting to
+ define a separate [application SLI](index.md#defining-a-new-sli)
+ for what the endpoint is doing.
+
+ When the endpoints in certain cases turn into no-ops, making them
+ very fast, we should ignore these fast requests when setting the
+ target. For example, if the `MergeRequests::DraftsController` is
+ hit for every merge request being viewed, but doesn't need to
+ render anything in most cases, then we should pick the target that
+ would still accomodate the endpoint performing work.
+
+1. Consider the dependent resources consumed by the endpoint. If the endpoint
+ loads a lot of data from Gitaly or the database and this is causing
+ it to not perform satisfactory. It could be better to optimize the
+ way the data is loaded rather than increasing the target duration
+ by lowering the urgency.
+
+ In cases like this, it might be appropriate to temporarily decrease
+ urgency to make the endpoint meet SLO, if this is bearable for the
+ infrastructure. In such cases, please link an issue from a code
+ comment.
+
+ If the endpoint consumes a lot of CPU time, we should also consider
+ this: these kinds of requests are the kind of noisy neighbors we
+ should try to keep as short as possible.
+
+1. Traffic characteristics should also be taken into account: if the
+ trafic to the endpoint is bursty, like CI traffic spinning up a
+ big batch of jobs hitting the same endpoint, then having these
+ endpoints take 5s is not acceptable from an infrastructure point of
+ view. We cannot scale up the fleet fast enough to accomodate for
+ the incoming slow requests alongside the regular traffic.
+
+When lowering the urgency for an existing endpoint, please involve a
+[Scalability team member](https://about.gitlab.com/handbook/engineering/infrastructure/team/scalability/#team-members)
+in the review. We can use request rates and durations available in the
+logs to come up with a recommendation. Picking a threshold can be done
+using the same process as for [increasing
+urgency](#increasing-urgency-setting-a-lower-target-duration), picking
+a duration that is higher than the SLO for the service.
+
+We shouldn't set the longest durations on endpoints in the merge
+requests that introduces them, since we don't yet have data to support
+the decision.
+
+## Increasing urgency (setting a lower target duration)
+
+When decreasing the target duration, we need to make sure the endpoint
+still meets SLO for the fleet that handles the request. You can use the
+information in the logs to determine this:
+
+1. Open [this table in
+ Kibana](https://log.gprd.gitlab.net/goto/bbb6465c68eb83642269e64a467df3df)
+
+1. The table loads information for the busiest endpoints by
+ default. You can speed things up by adding a filter for
+ `json.caller_id.keyword` and adding the identifier you're intersted
+ in (for example: `Projects::RawController#show`).
+
+1. Check the [appropriate percentile duration](#request-apdex-slo) for
+ the service the endpoint is handled by. The overall duration should
+ be lower than the target you intend to set.
+
+1. If the overall duration is below the intended targed. Please also
+ check the peaks over time in [this
+ graph](https://log.gprd.gitlab.net/goto/9319c4a402461d204d13f3a4924a89fc)
+ in Kibana. Here, the percentile in question should not peak above
+ the target duration we want to set.
+
+Since decreasing a threshold too much could result in alerts for the
+apdex degradation, please also involve a Scalability team member in
+the merge reqeust.
+
+## How to adjust the urgency
+
+The urgency can be specified similar to how endpoints [get a feature
+category](../feature_categorization/index.md).
+
+For endpoints that don't have a specific target, the default urgency (1s duration) will be used.
+
+The following configurations are available:
+
+| Urgency | Duration in seconds | Notes |
+|----------|---------------------|-----------------------------------------------|
+| :high | 0.25s | |
+| :medium | 0.5s | |
+| :default | 1s | This is the default when nothing is specified |
+| :low | 5s | |
+
+### Rails controller
+
+An urgency can be specified for all actions in a controller like this:
+
+```ruby
+class Boards::ListsController < ApplicationController
+ urgency :high
+end
+```
+
+To specify the urgency also for certain actions in a controller, they
+can be specified like this:
+
+```ruby
+class Boards::ListsController < ApplicationController
+ urgency :high, [:index, :show]
+end
+```
+
+### Grape endpoints
+
+To specify the urgency for an entire API class, this can be done as
+follows:
+
+```ruby
+module API
+ class Issues < ::API::Base
+ urgency :low
+ end
+end
+```
+
+To specify the urgency also for certain actions in a API class, they
+can be specified like this:
+
+```ruby
+module API
+ class Issues < ::API::Base
+ urgency :medium, [
+ '/groups/:id/issues',
+ '/groups/:id/issues_statistics'
+ ]
+ end
+end
+```
+
+Or, we can specify the urgency per endpoint:
+
+```ruby
+get 'client/features', urgency: :low do
+ # endpoint logic
+end
+```
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index fe2b621da2..9accd4a359 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -813,7 +813,7 @@ Starting with GitLab 13.0, Puma is the default web server.
- [Project page](https://gitlab.com/gitlab-org/gitlab/-/blob/master/README.md)
- Configuration:
- - [Omnibus](https://docs.gitlab.com/omnibus/settings/puma.html)
+ - [Omnibus](../administration/operations/puma.md)
- [Charts](https://docs.gitlab.com/charts/charts/gitlab/webservice/)
- [Source](../install/installation.md#configure-it)
- [GDK](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/gitlab.yml.example)
diff --git a/doc/development/cascading_settings.md b/doc/development/cascading_settings.md
index 0fa0e220ba..a85fc52d30 100644
--- a/doc/development/cascading_settings.md
+++ b/doc/development/cascading_settings.md
@@ -135,7 +135,7 @@ Renders the enforcement checkbox.
| `attribute` | Name of the setting. For example, `:delayed_project_removal`. | `String` or `Symbol` | `true` |
| `group` | Current group. | [`Group`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/group.rb) | `true` |
| `form` | [Rails FormBuilder object](https://apidock.com/rails/ActionView/Helpers/FormBuilder). | [`ActionView::Helpers::FormBuilder`](https://apidock.com/rails/ActionView/Helpers/FormBuilder) | `true` |
-| `setting_locked` | If the setting is locked by an ancestor group or admin setting. Can be calculated with [`cascading_namespace_setting_locked?`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86). | `Boolean` | `true` |
+| `setting_locked` | If the setting is locked by an ancestor group or administrator setting. Can be calculated with [`cascading_namespace_setting_locked?`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86). | `Boolean` | `true` |
| `help_text` | Text shown below the checkbox. | `String` | `false` (Subgroups cannot change this setting.) |
[`_setting_label_checkbox.html.haml`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/views/shared/namespaces/cascading_settings/_setting_label_checkbox.html.haml)
@@ -147,7 +147,7 @@ Renders the label for a checkbox setting.
| `attribute` | Name of the setting. For example, `:delayed_project_removal`. | `String` or `Symbol` | `true` |
| `group` | Current group. | [`Group`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/group.rb) | `true` |
| `form` | [Rails FormBuilder object](https://apidock.com/rails/ActionView/Helpers/FormBuilder). | [`ActionView::Helpers::FormBuilder`](https://apidock.com/rails/ActionView/Helpers/FormBuilder) | `true` |
-| `setting_locked` | If the setting is locked by an ancestor group or admin setting. Can be calculated with [`cascading_namespace_setting_locked?`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86). | `Boolean` | `true` |
+| `setting_locked` | If the setting is locked by an ancestor group or administrator setting. Can be calculated with [`cascading_namespace_setting_locked?`](https://gitlab.com/gitlab-org/gitlab/-/blob/c2736823b8e922e26fd35df4f0cd77019243c858/app/helpers/namespaces_helper.rb#L86). | `Boolean` | `true` |
| `settings_path_helper` | Lambda function that generates a path to the ancestor setting. For example, `settings_path_helper: -> (locked_ancestor) { edit_group_path(locked_ancestor, anchor: 'js-permissions-settings') }` | `Lambda` | `true` |
| `help_text` | Text shown below the checkbox. | `String` | `false` (`nil`) |
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
index be46d61eb4..2753257c94 100644
--- a/doc/development/changelog.md
+++ b/doc/development/changelog.md
@@ -62,8 +62,8 @@ The value must be the full URL of the merge request.
### GitLab Enterprise changes
-If a change is for GitLab Enterprise Edition, you must also add the trailer `EE:
-true`:
+If a change is exclusively for GitLab Enterprise Edition, **you must add** the
+trailer `EE: true`:
```plaintext
Update git vendor to gitlab
@@ -77,6 +77,8 @@ MR: https://gitlab.com/foo/bar/-/merge_requests/123
EE: true
```
+**Do not** add the trailer for changes that apply to both EE and CE.
+
## What warrants a changelog entry?
- Any change that introduces a database migration, whether it's regular, post,
diff --git a/doc/development/cicd/index.md b/doc/development/cicd/index.md
index b4e32066ba..82fd37eaca 100644
--- a/doc/development/cicd/index.md
+++ b/doc/development/cicd/index.md
@@ -157,7 +157,7 @@ On top of that, we have the following types of jobs:
- `Ci::Build` ... The job to be executed by runners.
- `Ci::Bridge` ... The job to trigger a downstream pipeline.
-- `GenericCommitStatus` ... The job to be executed in an external CI/CD system e.g. Jenkins.
+- `GenericCommitStatus` ... The job to be executed in an external CI/CD system, for example Jenkins.
When you use the "Job" terminology in codebase, readers would
assume that the class/object is any type of above.
diff --git a/doc/development/cicd/templates.md b/doc/development/cicd/templates.md
index 3fc464e661..b74a1d0d58 100644
--- a/doc/development/cicd/templates.md
+++ b/doc/development/cicd/templates.md
@@ -325,8 +325,14 @@ projects on `gitlab.com`:
After you're confident the latest template can be moved to stable:
1. Update the stable template with the content of the latest version.
+1. Remove the migration template from `Gitlab::Template::GitlabCiYmlTemplate::TEMPLATES_WITH_LATEST_VERSION` const.
1. Remove the corresponding feature flag.
+NOTE:
+Feature flags are enabled by default in RSpec, so all tests are performed
+against the latest templates. You should also test the stable templates
+with `stub_feature_flags(redirect_to_latest_template_: false)`.
+
### Further reading
There is an [open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/17716) about
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 12cc63ef56..89516c2168 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -106,6 +106,7 @@ with [domain expertise](#domain-experts).
1. If your merge request includes user-facing changes (*3*), it must be
**approved by a [Product Designer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_reviewers_UX)**,
based on assignments in the appropriate [DevOps stage group](https://about.gitlab.com/handbook/product/categories/#devops-stages).
+ See the [design and user interface guidelines](contributing/design.md) for details.
1. If your merge request includes adding a new JavaScript library (*1*)...
- If the library significantly increases the
[bundle size](https://gitlab.com/gitlab-org/frontend/playground/webpack-memory-metrics/-/blob/master/doc/report.md), it must
@@ -156,7 +157,7 @@ See the [test engineering process](https://about.gitlab.com/handbook/engineering
1. I have confirmed that this change is [backwards compatible across updates](multi_version_compatibility.md), or I have decided that this does not apply.
1. I have properly separated EE content from FOSS, or this MR is FOSS only.
- [Where should EE code go?](ee_features.md#separation-of-ee-code)
-1. If I am introducing a new expectation for existing data, I have confirmed that existing data meets this expectation or I have made this expectation optional rather than required.
+1. I have considered that existing data may be surprisingly varied. For example, a new model validation can break existing records. Consider making validation on existing data optional rather than required if you haven't confirmed that existing data will pass validation.
##### Performance, reliability, and availability
diff --git a/doc/development/contributing/design.md b/doc/development/contributing/design.md
index 9e8375fcbd..e85f5dd834 100644
--- a/doc/development/contributing/design.md
+++ b/doc/development/contributing/design.md
@@ -5,34 +5,119 @@ group: Development
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/#assignments
---
-# Implement design & UI elements
+# Design and user interface changes
-For guidance on UX implementation at GitLab, please refer to our [Design System](https://design.gitlab.com/).
+Follow these guidelines when contributing or reviewing design and user interface
+(UI) changes. Refer to our [code review guide](../code_review.md) for broader
+advice and best practices for code review in general.
-The UX team uses labels to manage their workflow.
+The basis for most of these guidelines is [Pajamas](https://design.gitlab.com/),
+GitLab design system. We encourage you to [contribute to Pajamas](https://design.gitlab.com/get-started/contribute/)
+with additions and improvements.
-The `~UX` label on an issue is a signal to the UX team that it will need UX attention.
-To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/engineering/ux/) of the handbook.
+## Merge request reviews
-Once an issue has been worked on and is ready for development, a UXer removes the `~UX` label and applies the `~"UX ready"` label to that issue.
+As a merge request (MR) author, you must include _Before_ and _After_
+screenshots (or videos) of your changes in the description, as explained in our
+[MR workflow](merge_request_workflow.md). These screenshots/videos are very helpful
+for all reviewers and can speed up the review process, especially if the changes
+are small.
-There is a special type label called `~"product discovery"` intended for UX (user experience),
-PM (product manager), FE (frontend), and BE (backend). It represents a discovery issue to discuss the problem and
-potential solutions. The final output for this issue could be a doc of
-requirements, a design artifact, or even a prototype. The solution will be
-developed in a subsequent milestone.
+## Checklist
-`~"product discovery"` issues are like any other issue and should contain a milestone label, `~Deliverable` or `~Stretch`, when scheduled in the current milestone.
+Check these aspects both when _designing_ and _reviewing_ UI changes.
-The initial issue should be about the problem we are solving. If a separate [product discovery issue](https://about.gitlab.com/handbook/engineering/ux/ux-department-workflow/#how-we-use-labels)
-is needed for additional research and design work, it will be created by a PM or UX person.
-Assign the `~UX`, `~"product discovery"` and `~Deliverable` labels, add a milestone and
-use a title that makes it clear that the scheduled issue is product discovery
-(for example, `Product discovery for XYZ`).
+### Writing
-In order to complete a product discovery issue in a release, you must complete the following:
+- Follow [Pajamas](https://design.gitlab.com/content/punctuation/) as the primary
+ guidelines for UI text and [documentation style guide](../documentation/styleguide/index.md)
+ as the secondary.
+- Use clear and consistent [terminology](https://design.gitlab.com/content/terminology/).
+- Check grammar and spelling.
+- Consider help content and follow its [guidelines](https://design.gitlab.com/usability/helping-users/).
+- Request review from the [appropriate Technical Writer](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers),
+ indicating any specific files or lines they should review, and how to preview
+ or understand the location/context of the text from the user's perspective.
-1. UXer removes the `~UX` label, adds the `~"UX ready"` label.
-1. Modify the issue description in the product discovery issue to contain the final design. If it makes sense, the original information indicating the need for the design can be moved to a lower "Original Information" section.
-1. Copy the design to the description of the delivery issue for which the product discovery issue was created. Do not simply refer to the product discovery issue as a separate source of truth.
-1. In some cases, a product discovery issue also identifies future enhancements that will not go into the issue that originated the product discovery issue. For these items, create new issues containing the designs to ensure they are not lost. Put the issues in the backlog if they are agreed upon as good ideas. Otherwise leave them for triage.
+### Patterns
+
+- Consider similar patterns used in the product and justify in the issue when diverging
+ from them.
+- Use appropriate [components](https://design.gitlab.com/components/overview/)
+ and [data visualizations](https://design.gitlab.com/data-visualization/overview/).
+
+### Visual design
+
+Check visual design properties using your browser's _elements inspector_ ([Chrome](https://developer.chrome.com/docs/devtools/css/),
+[Firefox](https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Open_the_Inspector)).
+
+- Use recommended [colors](https://design.gitlab.com/product-foundations/colors/)
+ and [typography](https://design.gitlab.com/product-foundations/type-fundamentals/).
+- Follow [layout guidelines](https://design.gitlab.com/layout/grid/).
+- Use existing [icons](http://gitlab-org.gitlab.io/gitlab-svgs/) and [illustrations](http://gitlab-org.gitlab.io/gitlab-svgs/illustrations/)
+ or propose new ones according to [iconography](https://design.gitlab.com/product-foundations/iconography/)
+ and [illustration](https://design.gitlab.com/product-foundations/illustration/)
+ guidelines.
+- _Optionally_ consider [dark mode](../../user/profile/preferences.md#dark-mode). [^1]
+
+ [^1]: You're not required to design for [dark mode](../../user/profile/preferences.md#dark-mode) while the feature is in [alpha](https://about.gitlab.com/handbook/product/gitlab-the-product/#alpha). The [UX Foundations team](https://about.gitlab.com/direction/ecosystem/foundations/) plans to improve the dark mode in the future. Until we integrate [Pajamas](https://design.gitlab.com/) components into the product and the underlying design strategy is in place to support dark mode, we cannot guarantee that we won't introduce bugs and debt to this mode. At your discretion, evaluate the need to create dark mode patches.
+
+### States
+
+Check states using your browser's _styles inspector_ to toggle CSS pseudo-classes
+like `:hover` and others ([Chrome](https://developer.chrome.com/docs/devtools/css/reference/#pseudo-class),
+[Firefox](https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_and_edit_CSS#viewing_common_pseudo-classes)).
+
+- Account for all applicable states ([error](https://design.gitlab.com/content/error-messages),
+ rest, loading, focus, hover, selected, disabled).
+- Account for states dependent on data size ([empty](https://design.gitlab.com/regions/empty-states),
+ some data, and lots of data).
+- Account for states dependent on user role, user preferences, and subscription.
+- Consider animations and transitions, and follow their [guidelines](https://design.gitlab.com/product-foundations/motion/).
+
+### Responsive
+
+Check responsive behavior using your browser's _responsive mode_ ([Chrome](https://developer.chrome.com/docs/devtools/device-mode/#viewport),
+[Firefox](https://developer.mozilla.org/en-US/docs/Tools/Responsive_Design_Mode)).
+
+- Account for resizing, collapsing, moving, or wrapping of elements across
+ all breakpoints (even if larger viewports are prioritized).
+- Provide the same information and actions in all breakpoints.
+
+### Accessibility
+
+Check accessibility using your browser's _accessibility inspector_ ([Chrome](https://developer.chrome.com/docs/devtools/accessibility/reference/),
+[Firefox](https://developer.mozilla.org/en-US/docs/Tools/Accessibility_inspector#accessing_the_accessibility_inspector)).
+
+- Conform to level AA of the World Wide Web Consortium (W3C) [Web Content Accessibility Guidelines 2.1](https://www.w3.org/TR/WCAG21/),
+ according to our [statement of compliance](https://design.gitlab.com/accessibility/a11y/).
+- Follow accessibility [best practices](https://design.gitlab.com/accessibility/best-practices/)
+ and [checklist](../fe_guide/accessibility.md#quick-checklist).
+
+### Handoff
+
+When the design is ready, _before_ starting its implementation:
+
+- Share design specifications in the related issue, preferably through a [Figma link](https://help.figma.com/hc/en-us/articles/360040531773-Share-Files-with-anyone-using-Link-Sharing#Copy_links)
+ link or [GitLab Designs feature](../../user/project/issues/design_management.md#the-design-management-section).
+ See [when you should use each tool](https://about.gitlab.com/handbook/engineering/ux/product-designer/#deliver).
+- Document user flow and states (for example, using [Mermaid flowcharts in Markdown](../../user/markdown.md#mermaid)).
+- Document animations and transitions.
+- Document responsive behaviors.
+- Document non-evident behaviors (for example, field is auto-focused).
+- Document accessibility behaviors (for example, using [accessibility annotations in Figma](https://www.figma.com/file/g7QtDbfxF3pCdWiyskIr0X/Accessibility-bluelines)).
+- Contribute new icons or illustrations to the [GitLab SVGs](https://gitlab.com/gitlab-org/gitlab-svgs)
+ project.
+
+### Follow-ups
+
+At any moment, but usually _during_ or _after_ the design's implementation:
+
+- Contribute [issues to Pajamas](https://design.gitlab.com/get-started/contribute#contribute-an-issue)
+ for additions or enhancements to the design system.
+- Create issues with the [`~UX debt`](issue_workflow.md#technical-and-ux-debt)
+ label for intentional deviations from the agreed-upon UX requirements due to
+ time or feasibility challenges, linking back to the corresponding issue(s) or
+ MR(s).
+- Create issues for [feature additions or enhancements](issue_workflow.md#feature-proposals)
+ outside the agreed-upon UX requirements to avoid scope creep.
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index 29f6eb5716..d0f107ba98 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -151,7 +151,7 @@ From the handbook's
page:
> Categories are high-level capabilities that may be a standalone product at
-another company. e.g. Portfolio Management.
+another company, such as Portfolio Management, for example.
It's highly recommended to add a category label, as it's used by our triage
automation to
@@ -182,7 +182,7 @@ From the handbook's
[Product stages, groups, and categories](https://about.gitlab.com/handbook/product/categories/#hierarchy)
page:
-> Features: Small, discrete functionalities. e.g. Issue weights. Some common
+> Features: Small, discrete functionalities, for example Issue weights. Some common
features are listed within parentheses to facilitate finding responsible PMs by keyword.
It's highly recommended to add a feature label if no category label applies, as
@@ -303,7 +303,7 @@ We automatically add the ~"Accepting merge requests" label to issues
that match the [triage policy](https://about.gitlab.com/handbook/engineering/quality/triage-operations/#accepting-merge-requests).
We recommend people that have never contributed to any open source project to
-look for issues labeled `~"Accepting merge requests"` with a [weight of 1](https://gitlab.com/groups/gitlab-org/-/issues?state=opened&label_name[]=Accepting+merge+requests&assignee_id=None&sort=weight&weight=1) or the `~"Good for new contributors"` [label](https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&state=opened&label_name[]=good%20for%20new%20contributors&assignee_id=None) attached to it.
+look for issues labeled `~"Accepting merge requests"` with a [weight of 1](https://gitlab.com/groups/gitlab-org/-/issues?state=opened&label_name[]=Accepting+merge+requests&assignee_id=None&sort=weight&weight=1) or the `~"good for new contributors"` [label](https://gitlab.com/gitlab-org/gitlab/-/issues?scope=all&state=opened&label_name[]=good%20for%20new%20contributors&assignee_id=None) attached to it.
More experienced contributors are very welcome to tackle
[any of them](https://gitlab.com/groups/gitlab-org/-/issues?state=opened&label_name[]=Accepting+merge+requests&assignee_id=None).
@@ -342,19 +342,22 @@ To create a feature proposal, open an issue on the
[issue tracker](https://gitlab.com/gitlab-org/gitlab/-/issues).
In order to help track the feature proposals, we have created a
-[`feature`](https://gitlab.com/gitlab-org/gitlab/-/issues?label_name=feature) label. For the time being, users that are not members
-of the project cannot add labels. You can instead ask one of the [core team](https://about.gitlab.com/community/core-team/)
-members to add the label ~feature to the issue or add the following
+[`feature`](https://gitlab.com/gitlab-org/gitlab/-/issues?label_name=feature) label.
+For the time being, users that are not members of the project cannot add labels.
+You can instead ask one of the [core team](https://about.gitlab.com/community/core-team/)
+members to add the label `~feature` to the issue or add the following
code snippet right after your description in a new line: `~feature`.
Please keep feature proposals as small and simple as possible, complex ones
might be edited to make them small and simple.
-Please submit Feature Proposals using the ['Feature Proposal' issue template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal%20-%20detailed.md) provided on the issue tracker.
+Please submit feature proposals using the ['Feature Proposal' issue template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20proposal%20-%20detailed.md) provided on the issue tracker.
-For changes in the interface, it is helpful to include a mockup. Issues that add to, or change, the interface should
-be given the ~"UX" label. This will allow the UX team to provide input and guidance. You may
-need to ask one of the [core team](https://about.gitlab.com/community/core-team/) members to add the label, if you do not have permissions to do it by yourself.
+For changes to the user interface (UI), follow our [design and UI guidelines](design.md),
+and include a visual example (screenshot, wireframe, or mockup). Such issues should
+be given the `~UX"` label for the Product Design team to provide input and guidance.
+You may need to ask one of the [core team](https://about.gitlab.com/community/core-team/)
+members to add the label, if you do not have permissions to do it by yourself.
If you want to create something yourself, consider opening an issue first to
discuss whether it is interesting to include this in GitLab.
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index 25561764bd..a521d89db2 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -18,8 +18,8 @@ in order to ensure the work is finished before the release date.
If you want to add a new feature that is not labeled, it is best to first create
an issue (if there isn't one already) and leave a comment asking for it
-to be marked as `Accepting Merge Requests`. Please include screenshots or
-wireframes of the proposed feature if it will also change the UI.
+to be marked as `Accepting merge requests`. See the [feature proposals](issue_workflow.md#feature-proposals)
+section.
Merge requests should be submitted to the appropriate project at GitLab.com, for example
[GitLab](https://gitlab.com/gitlab-org/gitlab/-/merge_requests),
@@ -255,14 +255,14 @@ requirements.
1. The change is tested in a review app where possible and if appropriate.
1. The new feature does not degrade the user experience of the product.
1. The change is evaluated to [limit the impact of far-reaching work](https://about.gitlab.com/handbook/engineering/development/#reducing-the-impact-of-far-reaching-work).
-1. An agreed-upon rollout plan.
+1. An agreed-upon [rollout plan](https://about.gitlab.com/handbook/engineering/development/processes/rollout-plans/).
1. Merged by a project maintainer.
### Production use
1. Confirmed to be working in staging before implementing the change in production, where possible.
1. Confirmed to be working in the production with no new [Sentry](https://about.gitlab.com/handbook/engineering/#sentry) errors after the contribution is deployed.
-1. Confirmed that the rollout plan has been completed.
+1. Confirmed that the [rollout plan](https://about.gitlab.com/handbook/engineering/development/processes/rollout-plans) has been completed.
1. If there is a performance risk in the change, I have analyzed the performance of the system before and after the change.
1. *If the merge request uses feature flags, per-project or per-group enablement, and a staged rollout:*
- Confirmed to be working on GitLab projects.
diff --git a/doc/development/dangerbot.md b/doc/development/dangerbot.md
index d9b922cb60..aca37e2182 100644
--- a/doc/development/dangerbot.md
+++ b/doc/development/dangerbot.md
@@ -141,7 +141,7 @@ at GitLab so far:
- Their availability:
- No "OOO"/"PTO"/"Parental Leave" in their GitLab or Slack status.
- No `:red_circle:`/`:palm_tree:`/`:beach:`/`:beach_umbrella:`/`:beach_with_umbrella:` emojis in GitLab or Slack status.
- - (Experimental) Their timezone: people for which the local hour is between
+ - (Experimental) Their time zone: people for which the local hour is between
6 AM and 2 PM are eligible to be picked. This is to ensure they have a good
chance to get to perform a review during their current work day. The experimentation is tracked in
[this issue](https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/563)
diff --git a/doc/development/database/database_migration_pipeline.md b/doc/development/database/database_migration_pipeline.md
index 5a8ce89a36..ce7e1801ab 100644
--- a/doc/development/database/database_migration_pipeline.md
+++ b/doc/development/database/database_migration_pipeline.md
@@ -50,6 +50,6 @@ Some additional information is included at the bottom of the comment:
| Result | Description |
|----------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Migrations pending on GitLab.com | A summary of migrations not deployed yet to GitLab.com. This info is useful when testing a migration that was merged but not deployed yet. |
+| Migrations pending on GitLab.com | A summary of migrations not deployed yet to GitLab.com. This information is useful when testing a migration that was merged but not deployed yet. |
| Clone details | A link to the `Postgres.ai` thin clone created for this testing pipeline, along with information about its expiry. This can be used to further explore the results of running the migration. Only accessible by database maintainers or with an access request. |
| Artifacts | A link to the pipeline's artifacts. Full query logs for each migration (ending in `.log`) are available there and only accessible by database maintainers or with an access request. |
diff --git a/doc/development/database/database_reviewer_guidelines.md b/doc/development/database/database_reviewer_guidelines.md
index 59653c6dde..bc18e606f2 100644
--- a/doc/development/database/database_reviewer_guidelines.md
+++ b/doc/development/database/database_reviewer_guidelines.md
@@ -71,6 +71,7 @@ topics and use cases. The most frequently required during database reviewing are
- [Migrations style guide](../migration_style_guide.md) for creating safe SQL migrations.
- [Avoiding downtime in migrations](../avoiding_downtime_in_migrations.md).
- [SQL guidelines](../sql.md) for working with SQL queries.
+- [Guidelines for JiHu contributions with database migrations](https://about.gitlab.com/handbook/ceo/chief-of-staff-team/jihu-support/jihu-database-change-process.html)
## How to apply to become a database maintainer
diff --git a/doc/development/database/efficient_in_operator_queries.md b/doc/development/database/efficient_in_operator_queries.md
index bc72bce30b..1e706890f6 100644
--- a/doc/development/database/efficient_in_operator_queries.md
+++ b/doc/development/database/efficient_in_operator_queries.md
@@ -66,9 +66,9 @@ The execution of the query can be largely broken down into three steps:
1. The database sorts the `issues` rows in memory by `created_at` and returns `LIMIT 20` rows to
the end-user. For large groups, this final step requires both large memory and CPU resources.
-
-Expand this sentence to see the execution plan for this DB query.
-
+Execution plan for this DB query:
+
+```sql
Limit (cost=90170.07..90170.12 rows=20 width=1329) (actual time=967.597..967.607 rows=20 loops=1)
Buffers: shared hit=239127 read=3060
I/O Timings: read=336.879
@@ -106,8 +106,7 @@ The execution of the query can be largely broken down into three steps:
Planning Time: 7.750 ms
Execution Time: 967.973 ms
(36 rows)
-
-
+```
The performance of the query depends on the number of rows in the database.
On average, we can say the following:
@@ -226,7 +225,12 @@ Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder.new(
- `finder_query` loads the actual record row from the database. It must also be a lambda, where
the order by column expressions is available for locating the record. In this example, the
yielded values are `created_at` and `id` SQL expressions. Finding a record is very fast via the
- primary key, so we don't use the `created_at` value.
+ primary key, so we don't use the `created_at` value. Providing the `finder_query` lambda is optional.
+ If it's not given, the IN operator optimization will only make the ORDER BY columns available to
+ the end-user and not the full database row.
+
+ If it's not given, the IN operator optimization will only make the ORDER BY columns available to
+ the end-user and not the full database row.
The following database index on the `issues` table must be present
to make the query execute efficiently:
@@ -235,9 +239,9 @@ to make the query execute efficiently:
"idx_issues_on_project_id_and_created_at_and_id" btree (project_id, created_at, id)
```
-
-Expand this sentence to see the SQL query.
-
+The SQL query:
+
+```sql
SELECT "issues".*
FROM
(WITH RECURSIVE "array_cte" AS MATERIALIZED
@@ -348,8 +352,7 @@ SELECT (records).*
FROM "recursive_keyset_cte" AS "issues"
WHERE (COUNT <> 0)) issues -- filtering out the initializer row
LIMIT 20
-
-
+```
### Using the `IN` query optimization
@@ -461,9 +464,9 @@ Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder.new(
).execute.limit(20)
```
-
-Expand this sentence to see the SQL query.
-
+The SQL query:
+
+```sql
SELECT "issues".*
FROM
(WITH RECURSIVE "array_cte" AS MATERIALIZED
@@ -581,9 +584,7 @@ FROM
FROM "recursive_keyset_cte" AS "issues"
WHERE (COUNT <> 0)) issues
LIMIT 20
-
-
-
+```
NOTE:
To make the query efficient, the following columns need to be covered with an index: `project_id`, `issue_type`, `created_at`, and `id`.
@@ -611,6 +612,32 @@ Gitlab::Pagination::Keyset::Iterator.new(scope: scope, **opts).each_batch(of: 10
end
```
+NOTE:
+The query loads complete database rows from the disk. This may cause increased I/O and slower
+database queries. Depending on the use case, the primary key is often only
+needed for the batch query to invoke additional statements. For example, `UPDATE` or `DELETE`. The
+`id` column is included in the `ORDER BY` columns (`created_at` and `id`) and is already
+loaded. In this case, you can omit the `finder_query` parameter.
+
+Example for loading the `ORDER BY` columns only:
+
+```ruby
+scope = Issue.order(:created_at, :id)
+array_scope = Group.find(9970).all_projects.select(:id)
+array_mapping_scope = -> (id_expression) { Issue.where(Issue.arel_table[:project_id].eq(id_expression)) }
+
+opts = {
+ in_operator_optimization_options: {
+ array_scope: array_scope,
+ array_mapping_scope: array_mapping_scope
+ }
+}
+
+Gitlab::Pagination::Keyset::Iterator.new(scope: scope, **opts).each_batch(of: 100) do |records|
+ puts records.select(:id).map { |r| [r.id] } # only id and created_at are available
+end
+```
+
#### Keyset pagination
The optimization works out of the box with GraphQL and the `keyset_paginate` helper method.
diff --git a/doc/development/database/keyset_pagination.md b/doc/development/database/keyset_pagination.md
index fd62c36b75..4f0b353a37 100644
--- a/doc/development/database/keyset_pagination.md
+++ b/doc/development/database/keyset_pagination.md
@@ -169,7 +169,7 @@ Consider the following scope:
scope = Issue.where(project_id: 10).order(Gitlab::Database.nulls_last_order('relative_position', 'DESC'))
# SELECT "issues".* FROM "issues" WHERE "issues"."project_id" = 10 ORDER BY relative_position DESC NULLS LAST
-scope.keyset_paginate # raises: Gitlab::Pagination::Keyset::Paginator::UnsupportedScopeOrder: The order on the scope does not support keyset pagination
+scope.keyset_paginate # raises: Gitlab::Pagination::Keyset::UnsupportedScopeOrder: The order on the scope does not support keyset pagination
```
The `keyset_paginate` method raises an error because the order value on the query is a custom SQL string and not an [`Arel`](https://www.rubydoc.info/gems/arel) AST node. The keyset library cannot automatically infer configuration values from these kinds of queries.
diff --git a/doc/development/database/multiple_databases.md b/doc/development/database/multiple_databases.md
index 0fd9f821fa..0ba752ba3a 100644
--- a/doc/development/database/multiple_databases.md
+++ b/doc/development/database/multiple_databases.md
@@ -6,16 +6,14 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Multiple Databases
-In order to scale GitLab, the GitLab application database
-will be [decomposed into multiple
-databases](https://gitlab.com/groups/gitlab-org/-/epics/6168).
+To scale GitLab, the we are
+[decomposing the GitLab application database into multiple databases](https://gitlab.com/groups/gitlab-org/-/epics/6168).
-## CI Database
+## CI/CD Database
-Support for configuring the GitLab Rails application to use a distinct
-database for CI tables was added in [GitLab
-14.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64289). This
-feature is still under development, and is not ready for production use.
+> Support for configuring the GitLab Rails application to use a distinct
+database for CI/CD tables was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64289)
+in GitLab 14.1. This feature is still under development, and is not ready for production use.
By default, GitLab is configured to use only one main database. To
opt-in to use a main database, and CI database, modify the
@@ -92,8 +90,8 @@ test: &test
### Migrations
-Any migrations that affect `Ci::CiDatabaseRecord` models
-and their tables must be placed in two directories for now:
+Place any migrations that affect `Ci::CiDatabaseRecord` models
+and their tables in two directories:
- `db/migrate`
- `db/ci_migrate`
@@ -394,7 +392,8 @@ You can see a real example of using this method for fixing a cross-join in
#### Allowlist for existing cross-joins
A cross-join across databases can be explicitly allowed by wrapping the code in the
-`::Gitlab::Database.allow_cross_joins_across_databases` helper method.
+`::Gitlab::Database.allow_cross_joins_across_databases` helper method. Alternative
+way is to mark a given relation as `relation.allow_cross_joins_across_databases`.
This method should only be used:
@@ -405,16 +404,113 @@ This method should only be used:
The `allow_cross_joins_across_databases` helper method can be used as follows:
```ruby
+# Scope the block executing a object from database
::Gitlab::Database.allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/336590') do
subject.perform(1, 4)
end
```
+```ruby
+# Mark a relation as allowed to cross-join databases
+def find_actual_head_pipeline
+ all_pipelines
+ .allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/336891')
+ .for_sha_or_source_sha(diff_head_sha)
+ .first
+end
+```
+
The `url` parameter should point to an issue with a milestone for when we intend
to fix the cross-join. If the cross-join is being used in a migration, we do not
need to fix the code. See
for more details.
+### Removing cross-database transactions
+
+When dealing with multiple databases, it's important to pay close attention to data modification
+that affects more than one database.
+[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339811) GitLab 14.4, an automated check
+prevents cross-database modifications.
+
+When at least two different databases are modified during a transaction initiated on any database
+server, the application triggers a cross-database modification error (only in test environment).
+
+Example:
+
+```ruby
+# Open transaction on Main DB
+ApplicationRecord.transaction do
+ ci_build.update!(updated_at: Time.current) # UPDATE on CI DB
+ ci_build.project.update!(updated_at: Time.current) # UPDATE on Main DB
+end
+# raises error: Cross-database data modification of 'main, ci' were detected within
+# a transaction modifying the 'ci_build, projects' tables
+```
+
+The code example above updates the timestamp for two records within a transaction. With the
+ongoing work on the CI database decomposition, we cannot ensure the schematics of a database
+transaction.
+If the second update query fails, the first update query will not be
+rolled back because the `ci_build` record is located on a different database server. For
+more information, look at the
+[transaction guidelines](transaction_guidelines.md#dangerous-example-third-party-api-calls)
+page.
+
+#### Fixing cross-database errors
+
+##### Removing the transaction block
+
+Without an open transaction, the cross-database modification check cannot raise an error.
+By making this change, we sacrifice consistency. In case of an application failure after the
+first `UPDATE` query, the second `UPDATE` query will never execute.
+
+The same code without the `transaction` block:
+
+```ruby
+ci_build.update!(updated_at: Time.current) # CI DB
+ci_build.project.update!(updated_at: Time.current) # Main DB
+```
+
+##### Async processing
+
+If we need more guarantee that an operation finishes the work consistently we can execute it
+within a background job. A background job is scheduled asynchronously and retried several times
+in case of an error. There is still a very small chance of introducing inconsistency.
+
+Example:
+
+```ruby
+current_time = Time.current
+
+MyAsyncConsistencyJob.perform_async(cu_build.id)
+
+ci_build.update!(updated_at: current_time)
+ci_build.project.update!(updated_at: current_time)
+```
+
+The `MyAsyncConsistencyJob` would also attempt to update the timestamp if they differ.
+
+##### Aiming for perfect consistency
+
+At this point, we don't have the tooling (we might not even need it) to ensure similar consistency
+characteristics as we had with one database. If you think that the code you're working on requires
+these properties, then you can disable the cross-database modification check by wrapping to
+offending database queries with a block and create a follow-up issue mentioning the sharding group
+(`gitlab-org/sharding-group`).
+
+```ruby
+Gitlab::Database.allow_cross_joins_across_databases(url: 'gitlab issue URL') do
+ ApplicationRecord.transaction do
+ ci_build.update!(updated_at: Time.current) # UPDATE on CI DB
+ ci_build.project.update!(updated_at: Time.current) # UPDATE on Main DB
+ end
+end
+```
+
+Don't hesitate to reach out to the
+[sharding group](https://about.gitlab.com/handbook/engineering/development/enablement/sharding/)
+for advice.
+
## `config/database.yml`
GitLab will support running multiple databases in the future, for example to [separate tables for the continuous integration features](https://gitlab.com/groups/gitlab-org/-/epics/6167) from the main database. In order to prepare for this change, we [validate the structure of the configuration](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67877) in `database.yml` to ensure that only known databases are used.
diff --git a/doc/development/database/transaction_guidelines.md b/doc/development/database/transaction_guidelines.md
index 4c58613501..2806bd217d 100644
--- a/doc/development/database/transaction_guidelines.md
+++ b/doc/development/database/transaction_guidelines.md
@@ -8,17 +8,21 @@ info: To determine the technical writer assigned to the Stage/Group associated w
This document gives a few examples of the usage of database transactions in application code.
-For further reference please check PostgreSQL documentation about [transactions](https://www.postgresql.org/docs/current/tutorial-transactions.html).
+For further reference, check PostgreSQL documentation about [transactions](https://www.postgresql.org/docs/current/tutorial-transactions.html).
## Database decomposition and sharding
-The [sharding group](https://about.gitlab.com/handbook/engineering/development/enablement/sharding/) plans to split the main GitLab database and move some of the database tables to other database servers.
+The [sharding group](https://about.gitlab.com/handbook/engineering/development/enablement/sharding/) plans
+to split the main GitLab database and move some of the database tables to other database servers.
-The group will start decomposing the `ci_*` related database tables first. To maintain the current application development experience, tooling and static analyzers will be added to the codebase to ensure correct data access and data modification methods. By using the correct form for defining database transactions, we can save significant refactoring work in the future.
+We'll start decomposing the `ci_*`-related database tables first. To maintain the current application
+development experience, we'll add tooling and static analyzers to the codebase to ensure correct
+data access and data modification methods. By using the correct form for defining database transactions,
+we can save significant refactoring work in the future.
## The transaction block
-The `ActiveRecord` library provides a convenient way to group database statements into a transaction.
+The `ActiveRecord` library provides a convenient way to group database statements into a transaction:
```ruby
issue = Issue.find(10)
@@ -30,16 +34,19 @@ ApplicationRecord.transaction do
end
```
-This transaction involves two database tables, in case of an error, each `UPDATE` statement will be rolled back to the previous, consistent state.
+This transaction involves two database tables. In case of an error, each `UPDATE`
+statement rolls back to the previous consistent state.
NOTE:
Avoid referencing the `ActiveRecord::Base` class and use `ApplicationRecord` instead.
## Transaction and database locks
-When a transaction block is opened, the database will try to acquire the necessary locks on the resources. The type of locks will depend on the actual database statements.
+When a transaction block is opened, the database tries to acquire the necessary
+locks on the resources. The type of locks depend on the actual database statements.
-Consider a concurrent update scenario where the following code is executed at the same time from two different processes:
+Consider a concurrent update scenario where the following code is executed at the
+same time from two different processes:
```ruby
issue = Issue.find(10)
@@ -51,15 +58,22 @@ ApplicationRecord.transaction do
end
```
-The database will try to acquire the `FOR UPDATE` lock for the referenced `issue` and `project` records. In our case, we have two competing transactions for these locks, one of them will successfully acquire them. The other transaction will have to wait in the lock queue until the first transaction finishes. The execution of the second transaction is blocked at this point.
+The database tries to acquire the `FOR UPDATE` lock for the referenced `issue` and
+`project` records. In our case, we have two competing transactions for these locks,
+and only one of them will successfully acquire them. The other transaction will have
+to wait in the lock queue until the first transaction finishes. The execution of the
+second transaction is blocked at this point.
## Transaction speed
-To prevent lock contention and maintain stable application performance, the transaction block should finish as fast as possible. When a transaction acquires locks, it will hold on to them until the transaction finishes.
+To prevent lock contention and maintain stable application performance, the transaction
+block should finish as fast as possible. When a transaction acquires locks, it holds
+on to them until the transaction finishes.
-Apart from application performance, long-running transactions can also affect the application upgrade processes by blocking database migrations.
+Apart from application performance, long-running transactions can also affect application
+upgrade processes by blocking database migrations.
-### Dangerous example: 3rd party API calls
+### Dangerous example: third-party API calls
Consider the following example:
@@ -73,20 +87,29 @@ Member.transaction do
end
```
-Here, we ensure that the `notification_email_sent` column is updated only when the `send_notification_email` method succeeds. The `send_notification_email` method executes a network request to an email sending service. If the underlying infrastructure does not specify timeouts or the network call takes too long time, the database transaction will stay open.
+Here, we ensure that the `notification_email_sent` column is updated only when the
+`send_notification_email` method succeeds. The `send_notification_email` method
+executes a network request to an email sending service. If the underlying infrastructure
+does not specify timeouts or the network call takes too long time, the database transaction
+stays open.
Ideally, a transaction should only contain database statements.
Avoid doing in a `transaction` block:
-- External network requests such as: triggering Sidekiq jobs, sending emails, HTTP API calls and running database statements using a different connection.
+- External network requests such as:
+ - Triggering Sidekiq jobs.
+ - Sending emails.
+ - HTTP API calls.
+ - Running database statements using a different connection.
- File system operations.
- Long, CPU intensive computation.
- Calling `sleep(n)`.
## Explicit model referencing
-If a transaction modifies records from the same database table, it's advised to use the `Model.transaction` block:
+If a transaction modifies records from the same database table, we advise to use the
+`Model.transaction` block:
```ruby
build_1 = Ci::Build.find(1)
@@ -98,7 +121,8 @@ Ci::Build.transaction do
end
```
-The transaction above will use the same database connection for the transaction as the models in the `transaction` block. In a multi-database environment the following example would be dangerous:
+The transaction above uses the same database connection for the transaction as the models
+in the `transaction` block. In a multi-database environment the following example is dangerous:
```ruby
# `ci_builds` table is located on another database
@@ -114,4 +138,6 @@ ActiveRecord::Base.transaction do
end
```
-The `ActiveRecord::Base` class uses a different database connection than the `Ci::Build` records. The two statements in the transaction block will not be part of the transaction and will not be rolled back in case something goes wrong. They act as 3rd part calls.
+The `ActiveRecord::Base` class uses a different database connection than the `Ci::Build` records.
+The two statements in the transaction block will not be part of the transaction and will not be
+rolled back in case something goes wrong. They act as 3rd part calls.
diff --git a/doc/development/database_debugging.md b/doc/development/database_debugging.md
index b1c8508c88..7c17a39746 100644
--- a/doc/development/database_debugging.md
+++ b/doc/development/database_debugging.md
@@ -72,8 +72,8 @@ bundle exec rails db -e development
## Access the database with a GUI
-Most GUIs (DataGrid, RubyMine, DBeaver) require a TCP connection to the database, but by default
-the database runs on a UNIX socket. To be able to access the database from these tools, some steps
+Most GUIs (DataGrid, RubyMine, DBeaver) require a TCP connection to the database, but by default
+the database runs on a UNIX socket. To be able to access the database from these tools, some steps
are needed:
1. On the GDK root directory, run:
diff --git a/doc/development/database_review.md b/doc/development/database_review.md
index 42bfa656a6..dcd5baab17 100644
--- a/doc/development/database_review.md
+++ b/doc/development/database_review.md
@@ -38,13 +38,24 @@ migration only.
### Required
-The following artifacts are required prior to submitting for a ~database review.
+You must provide the following artifacts when you request a ~database review.
If your merge request description does not include these items, the review will be reassigned back to the author.
+#### Migrations
+
If new migrations are introduced, in the MR **you are required to provide**:
- The output of both migrating (`db:migrate`) and rolling back (`db:rollback`) for all migrations.
+Note that we have automated tooling for
+[GitLab](https://gitlab.com/gitlab-org/gitlab) (provided by the
+`db:check-migrations` pipeline job) that provides this output for migrations on
+~database merge requests. You do not need to provide this information manually
+if the bot can do it for you. The bot also checks that migrations are correctly
+reversible.
+
+#### Queries
+
If new queries have been introduced or existing queries have been updated, **you are required to provide**:
- [Query plans](#query-plans) for each raw SQL query included in the merge request along with the link to the query plan following each raw SQL snippet.
diff --git a/doc/development/distributed_tracing.md b/doc/development/distributed_tracing.md
index f8184a562e..1e85abf585 100644
--- a/doc/development/distributed_tracing.md
+++ b/doc/development/distributed_tracing.md
@@ -57,7 +57,7 @@ on non-Go GitLab subsystems.
## Enabling distributed tracing
GitLab uses the `GITLAB_TRACING` environment variable to configure distributed tracing. The same
-configuration is used for all components (e.g., Workhorse, Rails, etc).
+configuration is used for all components (for example, Workhorse, Rails, etc).
When `GITLAB_TRACING` is not set, the application isn't instrumented, meaning that there is
no overhead at all.
diff --git a/doc/development/documentation/feature_flags.md b/doc/development/documentation/feature_flags.md
index 5a4d365ed2..c169af1958 100644
--- a/doc/development/documentation/feature_flags.md
+++ b/doc/development/documentation/feature_flags.md
@@ -37,14 +37,14 @@ FLAG:
| If the feature is... | Use this text |
|--------------------------|---------------|
-| Available | `On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the flag](/administration/feature_flags.md).` |
-| Unavailable | `On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the flag](/administration/feature_flags.md).` |
-| Available, per-group | `On self-managed GitLab, by default this feature is available. To hide the feature per group, ask an administrator to [disable the flag](/administration/feature_flags.md).` |
-| Unavailable, per-group | `On self-managed GitLab, by default this feature is not available. To make it available per group, ask an administrator to [enable the flag](/administration/feature_flags.md).` |
-| Available, per-project | `On self-managed GitLab, by default this feature is available. To hide the feature per project or for your entire instance, ask an administrator to [disable the flag](/administration/feature_flags.md).` |
-| Unavailable, per-project | `On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, ask an administrator to [enable the flag](/administration/feature_flags.md).` |
-| Available, per-user | `On self-managed GitLab, by default this feature is available. To hide the feature per user, ask an administrator to [disable the flag](/administration/feature_flags.md).` |
-| Unavailable, per-user | `On self-managed GitLab, by default this feature is not available. To make it available per user, ask an administrator to [enable the flag](/administration/feature_flags.md).` |
+| Available | `On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](/administration/feature_flags.md) named .` |
+| Unavailable | `On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](/administration/feature_flags.md) named .` |
+| Available, per-group | `On self-managed GitLab, by default this feature is available. To hide the feature per group, ask an administrator to [disable the feature flag](/administration/feature_flags.md) named .` |
+| Unavailable, per-group | `On self-managed GitLab, by default this feature is not available. To make it available per group, ask an administrator to [enable the feature flag](/administration/feature_flags.md) named .` |
+| Available, per-project | `On self-managed GitLab, by default this feature is available. To hide the feature per project or for your entire instance, ask an administrator to [disable the feature flag](/administration/feature_flags.md) named .` |
+| Unavailable, per-project | `On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, ask an administrator to [enable the feature flag](/administration/feature_flags.md) named .` |
+| Available, per-user | `On self-managed GitLab, by default this feature is available. To hide the feature per user, ask an administrator to [disable the feature flag](/administration/feature_flags.md) named .` |
+| Unavailable, per-user | `On self-managed GitLab, by default this feature is not available. To make it available per user, ask an administrator to [enable the feature flag](/administration/feature_flags.md) named .` |
### GitLab.com availability information
@@ -67,7 +67,7 @@ When the state of a flag changes (for example, disabled by default to enabled by
Possible version history entries are:
```markdown
-> - [Introduced](issue-link) in GitLab X.X. [Deployed behind the flag](../../administration/feature_flags.md), disabled by default.
+> - [Introduced](issue-link) in GitLab X.X [with a flag](../../administration/feature_flags.md) named . Disabled by default.
> - [Enabled on GitLab.com](issue-link) in GitLab X.X.
> - [Enabled on GitLab.com](issue-link) in GitLab X.X. Available to GitLab.com administrators only.
> - [Enabled on self-managed](issue-link) in GitLab X.X.
@@ -80,31 +80,31 @@ Possible version history entries are:
The following examples show the progression of a feature flag.
```markdown
-> Introduced in GitLab 13.7. [Deployed behind the `forti_token_cloud` flag](../../administration/feature_flags.md), disabled by default.
+> Introduced in GitLab 13.7 [with a flag](../../administration/feature_flags.md) named `forti_token_cloud`. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available,
-ask an administrator to [enable the `forti_token_cloud` flag](../administration/feature_flags.md).`
+ask an administrator to [enable the featured flag](../administration/feature_flags.md) named `forti_token_cloud`.
The feature is not ready for production use.
```
When the feature is enabled in production, you can update the version history:
```markdown
-> - Introduced in GitLab 13.7. [Deployed behind the `forti_token_cloud` flag](../../administration/feature_flags.md), disabled by default.
+> - Introduced in GitLab 13.7 [with a flag](../../administration/feature_flags.md) named `forti_token_cloud`. Disabled by default.
> - [Enabled on self-managed](https://gitlab.com/issue/etc) GitLab 13.8.
FLAG:
On self-managed GitLab, by default this feature is available. To hide the feature per user,
-ask an administrator to [disable the `forti_token_cloud` flag](../administration/feature_flags.md).
+ask an administrator to [disable the feature flag](../administration/feature_flags.md) named `forti_token_cloud`.
```
And, when the feature is done and fully available to all users:
```markdown
-> - Introduced in GitLab 13.7. [Deployed behind the `forti_token_cloud` flag](../../administration/feature_flags.md), disabled by default.
+> - Introduced in GitLab 13.7 [with a flag](../../administration/feature_flags.md) named `forti_token_cloud`. Disabled by default.
> - [Enabled on self-managed](https://gitlab.com/issue/etc) GitLab 13.8.
> - [Enabled on GitLab.com](https://gitlab.com/issue/etc) in GitLab 13.9.
-> - [Feature flag `forti_token_cloud`](https://gitlab.com/issue/etc) removed in GitLab 14.0.
+> - [Feature flag removed](https://gitlab.com/issue/etc) in GitLab 14.0.
> - [Generally available](issue-link) in GitLab 14.0.
```
diff --git a/doc/development/documentation/img/manual_build_docs.png b/doc/development/documentation/img/manual_build_docs_v14_3.png
similarity index 100%
rename from doc/development/documentation/img/manual_build_docs.png
rename to doc/development/documentation/img/manual_build_docs_v14_3.png
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index a597ea512c..75538fe1fe 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -108,23 +108,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
If you need help determining the correct stage, read [Ask for help](workflow.md#ask-for-help).
-### Document type metadata
-
-Originally discussed in [this epic](https://gitlab.com/groups/gitlab-org/-/epics/1280),
-each page should have a metadata tag called `type`. It can be one or more of the
-following:
-
-- `index`: It consists mostly of a list of links to other pages.
- [Example page](../../index.md).
-- `concepts`: The background or context of a subject.
- [Example page](../../topics/autodevops/index.md).
-- `howto`: Specific use case instructions.
- [Example page](../../ssh/index.md).
-- `tutorial`: Learn a process/concept by doing.
- [Example page](../../gitlab-basics/start-using-git.md).
-- `reference`: A collection of information used as a reference to use a feature
- or a functionality. [Example page](../../ci/yaml/index.md).
-
### Redirection metadata
The following metadata should be added when a page is moved to another location:
@@ -154,7 +137,12 @@ Each page can have additional, optional metadata (set in the
[default.html](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/fc3577921343173d589dfa43d837b4307e4e620f/layouts/default.html#L30-52)
Nanoc layout), which is displayed at the top of the page if defined.
-## Move or rename a page
+### Deprecated metadata
+
+The `type` metadata parameter is deprecated but still exists in documentation
+pages. You can safely remove the `type` metadata parameter and its values.
+
+## Move, rename, or delete a page
See [redirects](redirects.md).
@@ -214,8 +202,10 @@ with the following conventions:
The help text follows the [Pajamas guidelines](https://design.gitlab.com/usability/helping-users/#formatting-help-content).
-Use the following special cases depending on the context, ensuring all links
-are inside `_()` so they can be translated:
+#### Linking to `/help` in HAML
+
+Use the following special cases depending on the context, ensuring all link text
+is inside `_()` so it can be translated:
- Linking to a doc page. In its most basic form, the HAML code to generate a
link to the `/help` page is:
@@ -260,6 +250,27 @@ helpPagePath('user/permissions', { anchor: 'anchor-link' })
This is preferred over static paths, as the helper also works on instances installed under a [relative URL](../../install/relative_url.md).
+#### Linking to `/help` in Ruby
+
+To link to the documentation from within Ruby code, use the following code block as a guide, ensuring all link text is inside `_()` so it can
+be translated:
+
+```ruby
+docs_link = link_to _('Learn more.'), help_page_url('user/permissions', anchor: 'anchor-link'), target: '_blank', rel: 'noopener noreferrer'
+_('This is a text describing the option/feature in a sentence. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
+```
+
+In cases where you need to generate a link from outside of views/helpers, where the `link_to` and `help_page_url` methods are not available, use the following code block
+as a guide where the methods are fully qualified:
+
+```ruby
+docs_link = ActionController::Base.helpers.link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/permissions', anchor: 'anchor-link'), target: '_blank', rel: 'noopener noreferrer'
+_('This is a text describing the option/feature in a sentence. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
+```
+
+Do not use `include ActionView::Helpers::UrlHelper` just to make the `link_to` method available as you might see in some existing code. Read more in
+[issue 340567](https://gitlab.com/gitlab-org/gitlab/-/issues/340567).
+
### GitLab `/help` tests
Several [RSpec tests](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/features/help_pages_spec.rb)
diff --git a/doc/development/documentation/redirects.md b/doc/development/documentation/redirects.md
index eb6878f587..ef94c33a27 100644
--- a/doc/development/documentation/redirects.md
+++ b/doc/development/documentation/redirects.md
@@ -15,13 +15,26 @@ description: Learn how to contribute to GitLab Documentation.
# Redirects in GitLab documentation
-Moving or renaming a document is the same as changing its location. Be sure
-to assign a technical writer to any merge request that renames or moves a page.
-Technical Writers can help with any questions and can review your change.
+When you move, rename, or delete a page, you must add a redirect. Redirects reduce
+how often users get 404s when visiting the documentation site from out-of-date links, like:
-When moving or renaming a page, you must redirect browsers to the new page.
-This ensures users find the new page, and have the opportunity to update their
-bookmarks.
+- Bookmarks
+- Links from external sites
+- Links from old blog posts
+- Links in the documentation site global navigation
+
+Add a redirect to ensure:
+
+- Users see the new page and can update or delete their bookmark.
+- External sites can update their links, especially sites that have automation that
+ check for redirecting links.
+- The documentation site global navigation does not link to a missing page.
+
+ The links in the global navigation are already tested in the `gitlab-docs` project.
+ If the redirect is missing, the `gitlab-docs` project's `main` branch might break.
+
+Be sure to assign a technical writer to any merge request that moves, renames, or deletes a page.
+Technical Writers can help with any questions and can review your change.
There are two types of redirects:
diff --git a/doc/development/documentation/review_apps.md b/doc/development/documentation/review_apps.md
index 2b8c412f16..a5094ea87f 100644
--- a/doc/development/documentation/review_apps.md
+++ b/doc/development/documentation/review_apps.md
@@ -26,7 +26,7 @@ to render and preview the documentation locally.
If a merge request has documentation changes, use the `review-docs-deploy` manual job
to deploy the documentation review app for your merge request.
-![Manual trigger a documentation review app](img/manual_build_docs.png)
+![Manual trigger a documentation review app](img/manual_build_docs_v14_3.png)
The `review-docs-deploy*` job triggers a cross project pipeline and builds the
docs site with your changes. When the pipeline finishes, the review app URL
diff --git a/doc/development/documentation/site_architecture/index.md b/doc/development/documentation/site_architecture/index.md
index cd69154217..d1736e1000 100644
--- a/doc/development/documentation/site_architecture/index.md
+++ b/doc/development/documentation/site_architecture/index.md
@@ -230,7 +230,7 @@ reports.
## Monthly release process (versions)
The docs website supports versions and each month we add the latest one to the list.
-For more information, read about the [monthly release process](https://about.gitlab.com/handbook/engineering/ux/technical-writing/workflow/#monthly-documentation-releases).
+For more information, read about the [monthly release process](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/doc/releases.md).
## Review Apps for documentation merge requests
diff --git a/doc/development/documentation/structure.md b/doc/development/documentation/structure.md
index a9b9399790..229dbb077f 100644
--- a/doc/development/documentation/structure.md
+++ b/doc/development/documentation/structure.md
@@ -149,6 +149,8 @@ For the heading:
If you do not put the full error in the title, include it in the body text.
+If multiple causes or workarounds exist, consider putting them into a table format.
+
## Other types of content
There are other types of content in the GitLab documentation that don't
diff --git a/doc/development/documentation/styleguide/img/admin_access_level.png b/doc/development/documentation/styleguide/img/admin_access_level.png
new file mode 100644
index 0000000000..191ba78cd6
Binary files /dev/null and b/doc/development/documentation/styleguide/img/admin_access_level.png differ
diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md
index 2cbecc91b2..72491ab3a3 100644
--- a/doc/development/documentation/styleguide/index.md
+++ b/doc/development/documentation/styleguide/index.md
@@ -1091,47 +1091,51 @@ However, they should be used sparingly because:
- They are difficult and expensive to localize.
- They cannot be read by screen readers.
-If you do include an image in the documentation, ensure it provides value.
-Don't use `lorem ipsum` text. Try to replicate how the feature would be
-used in a real-world scenario, and [use realistic text](#fake-user-information).
+When needed, use images to help the reader understand:
+
+- Where they are in a complicated process.
+- How they should interact with the application.
### Capture the image
-Use images to help the reader understand where they are in a process, or how
-they need to interact with the application.
-
When you take screenshots:
-- **Capture the most relevant area of the page.** Don't include unnecessary white
- space or areas of the page that don't help illustrate the point. The left
- sidebar of the GitLab user interface can change, so don't include the sidebar
- if it's not necessary.
+- **Ensure it provides value.** Don't use `lorem ipsum` text.
+ Try to replicate how the feature would be used in a real-world scenario, and
+ [use realistic text](#fake-user-information).
+- **Capture only the relevant UI.** Don't include unnecessary white
+ space or areas of the UI that don't help illustrate the point. The
+ sidebars in GitLab can change, so don't include
+ them in screenshots unless absolutely necessary.
- **Keep it small.** If you don't need to show the full width of the screen, don't.
- A value of 1000 pixels is a good maximum width for your screenshot image.
+ Reduce the size of your browser window as much as possible to keep elements close
+ together and reduce empty space. Try to keep the screenshot dimensions as small as possible.
+- **Review how the image renders on the page.** Preview the image locally or use the
+review app in the merge request. Make sure the image isn't blurry or overwhelming.
- **Be consistent.** Coordinate screenshots with the other screenshots already on
- a documentation page. For example, if other screenshots include the left
- sidebar, include the sidebar in all screenshots.
+ a documentation page for a consistent reading experience.
### Save the image
+- Resize any wide or tall screenshots if needed, but make sure the screenshot is
+ still clear after being resized and compressed.
+- All images **must** be [compressed](#compress-images) to 100KB or less.
+ In many cases, 25-50KB or less is often possible without reducing image quality.
- Save the image with a lowercase filename that's descriptive of the feature
- or concept in the image. If the image is of the GitLab interface, append the
- GitLab version to the filename, based on this format:
- `image_name_vX_Y.png`. For example, for a screenshot taken from the pipelines
- page of GitLab 11.1, a valid name is `pipelines_v11_1.png`. If you're adding an
- illustration that doesn't include parts of the user interface, add the release
- number corresponding to the release the image was added to; for an MR added to
- 11.1's milestone, a valid name for an illustration is `devops_diagram_v11_1.png`.
+ or concept in the image:
+ - If the image is of the GitLab interface, append the GitLab version to the filename,
+ based on this format: `image_name_vX_Y.png`. For example, for a screenshot taken
+ from the pipelines page of GitLab 11.1, a valid name is `pipelines_v11_1.png`.
+ - If you're adding an illustration that doesn't include parts of the user interface,
+ add the release number corresponding to the release the image was added to.
+ For an MR added to 11.1's milestone, a valid name for an illustration is `devops_diagram_v11_1.png`.
- Place images in a separate directory named `img/` in the same directory where
the `.md` document that you're working on is located.
- Consider using PNG images instead of JPEG.
-- [Compress all PNG images](#compress-images).
- Compress GIFs with or similar tool.
- Images should be used (only when necessary) to illustrate the description
of a process, not to replace it.
-- Max image size: 100KB (GIFs included).
-- See also how to link and embed [videos](#videos) to illustrate the
- documentation.
+- See also how to link and embed [videos](#videos) to illustrate the documentation.
### Add the image link to content
@@ -1152,8 +1156,11 @@ known tool is [`pngquant`](https://pngquant.org/), which is cross-platform and
open source. Install it by visiting the official website and following the
instructions for your OS.
+If you use macOS and want all screenshots to be compressed automatically, read
+[One simple trick to make your screenshots 80% smaller](https://about.gitlab.com/blog/2020/01/30/simple-trick-for-smaller-screenshots/).
+
GitLab has a [Ruby script](https://gitlab.com/gitlab-org/gitlab/-/blob/master/bin/pngquant)
-that you can use to automate the process. In the root directory of your local
+that you can use to simplify the manual process. In the root directory of your local
copy of `https://gitlab.com/gitlab-org/gitlab`, run in a terminal:
- Before compressing, if you want, check that all documentation PNG images have
@@ -1360,13 +1367,15 @@ Do not use words to describe the icon:
## Alert boxes
-Use alert boxes to call attention to information.
+Use alert boxes to call attention to information. Use them sparingly, and never have an alert box immediately follow another alert box.
Alert boxes are generated when one of these words is followed by a line break:
- `FLAG:`
- `NOTE:`
- `WARNING:`
+- `INFO:` (Marketing only)
+- `DISCLAIMER:`
For example:
@@ -1423,6 +1432,58 @@ It renders on the GitLab documentation site as:
WARNING:
This is something to be warned about.
+### Info
+
+The Marketing team uses the `INFO` alert to add information relating
+to sales and marketing efforts.
+
+The text in an `INFO:` alert always renders in a floating text box to the right of the text around it.
+To view the rendered GitLab docs site, check the review app in the MR. You might need to move the text up or down
+in the surrounding text, depending on where you'd like to floating box to appear.
+
+For example, if your page has text like this:
+
+```markdown
+This is an introductory paragraph. GitLab uses the SSH protocol to securely communicate with Git.
+When you use SSH keys to authenticate to the GitLab remote server,
+you don't need to supply your username and password each time.
+
+INFO:
+Here is some information. This information is an important addition to how you
+work with GitLab and you might want to consider it.
+
+And here is another paragraph. GitLab uses the SSH protocol to securely communicate with Git.
+When you use SSH keys to authenticate to the GitLab remote server,
+you don't need to supply your username and password each time.
+
+And here is another paragraph. GitLab uses the SSH protocol to securely communicate with Git.
+When you use SSH keys to authenticate to the GitLab remote server,
+you don't need to supply your username and password each time.
+```
+
+It renders on the GitLab documentation site as:
+
+This is an introductory paragraph. GitLab uses the SSH protocol to securely communicate with Git.
+When you use SSH keys to authenticate to the GitLab remote server,
+you don't need to supply your username and password each time.
+
+INFO:
+Here is some information. This information is an important addition to how you
+work with GitLab and you might want to consider it.
+
+And here is another paragraph. GitLab uses the SSH protocol to securely communicate with Git.
+When you use SSH keys to authenticate to the GitLab remote server,
+you don't need to supply your username and password each time.
+
+And here is another paragraph. GitLab uses the SSH protocol to securely communicate with Git.
+When you use SSH keys to authenticate to the GitLab remote server,
+you don't need to supply your username and password each time.
+
+### Disclaimer
+
+Use to describe future functionality only.
+For more information, see [Legal disclaimer for future features](#legal-disclaimer-for-future-features).
+
## Blockquotes
For highlighting a text inside a blockquote, use this format:
@@ -1616,6 +1677,34 @@ For example:
You can say that we plan to remove a feature.
+#### Legal disclaimer for future features
+
+If you **must** write about features we have not yet delivered, put this exact disclaimer near the content it applies to.
+
+```markdown
+DISCLAIMER:
+This page contains information related to upcoming products, features, and functionality.
+It is important to note that the information presented is for informational purposes only.
+Please do not rely on this information for purchasing or planning purposes.
+As with all projects, the items mentioned on this page are subject to change or delay.
+The development, release, and timing of any products, features, or functionality remain at the
+sole discretion of GitLab Inc.
+```
+
+It renders on the GitLab documentation site as:
+
+DISCLAIMER:
+This page contains information related to upcoming products, features, and functionality.
+It is important to note that the information presented is for informational purposes only.
+Please do not rely on this information for purchasing or planning purposes.
+As with all projects, the items mentioned on this page are subject to change or delay.
+The development, release, and timing of any products, features, or functionality remain at the
+sole discretion of GitLab Inc.
+
+If all of the content on the page is not available, use the disclaimer once at the top of the page.
+
+If the content in a topic is not ready, use the disclaimer in the topic.
+
### Removing versions after each major release
Whenever a major GitLab release occurs, we remove all version references
diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md
index eafe0e7a1c..f1e6a14757 100644
--- a/doc/development/documentation/styleguide/word_list.md
+++ b/doc/development/documentation/styleguide/word_list.md
@@ -29,9 +29,39 @@ Try to avoid using **above** when referring to an example or table in a document
- In the previous example, the dog had fleas.
-## admin, admin area
+Do not use **above** when referring to versions of the product. Use [**later**](#later) instead.
-Use **administration**, **administrator**, **administer**, or **Admin Area** instead. ([Vale](../testing.md#vale) rule: [`Admin.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/Admin.yml))
+- Do: In GitLab 14.4 and later...
+- Do not: In GitLab 14.4 and above...
+- Do not: In GitLab 14.4 and higher...
+
+## access level
+
+Access levels are different than [roles](#roles) or [permissions](#permissions).
+When you create a user, you choose an access level: **Regular**, **Auditor**, or **Admin**.
+
+Capitalize these words when you refer to the UI. Otherwise use lowercase.
+
+## administrator
+
+Use **administrator** instead of **admin** when talking about a user's access level.
+Use lowercase unless you are referring to the **Admin** access level you select in the UI.
+
+To view the administrator access type, in the GitLab UI, go to the Admin Area and select
+**Users**. Then select **New user**.
+
+![admin access level](img/admin_access_level.png)
+
+An **administrator** is not a [role](#roles) or [permission](#permissions).
+
+- Do: To do this thing, you must be an administrator.
+- Do: To do this thing, you must have the administrator access level.
+- Do not: To do this thing, you must have the Admin role.
+
+## Admin Area
+
+Use title case **Admin Area** to refer to the area of the UI that you access when you select **Menu > Admin**.
+This area of the UI says **Admin Area** at the top of the page and on the menu.
## allow, enable
@@ -54,9 +84,14 @@ in the handbook when writing about Alpha features.
Instead of **and/or**, use **or** or rewrite the sentence to spell out both options.
+## and so on
+
+Do not use **and so on**. Instead, be more specific. For details, see
+[the Microsoft style guide](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/a/and-so-on).
+
## area
-Use [**section**](#section) instead of **area**. The only exception is [the Admin Area](#admin-admin-area).
+Use [**section**](#section) instead of **area**. The only exception is [the Admin Area](#admin-area).
## below
@@ -150,8 +185,8 @@ When writing about the Developer role:
- Do not use the phrase, **if you are a developer** to mean someone who is assigned the Developer
role. Instead, write it out. For example, **if you are assigned the Developer role**.
- To describe a situation where the Developer role is the minimum required:
- - Avoid: the Developer role or higher
- - Use instead: at least the Developer role
+ - Do: at least the Developer role
+ - Do not: the Developer role or higher
Do not use **Developer permissions**. A user who is assigned the Developer role has a set of associated permissions.
@@ -207,7 +242,8 @@ Use lowercase for **epic board**.
## etc.
-Try to avoid **etc.**. Be as specific as you can. Do not use **and so on** as a replacement.
+Try to avoid **etc.**. Be as specific as you can. Do not use
+[**and so on**](#and-so-on) as a replacement.
- Do: You can update objects, like merge requests and issues.
- Do not: You can update objects, like merge requests, issues, etc.
@@ -220,8 +256,8 @@ Use **expand** instead of **open** when you are talking about expanding or colla
Use **box** instead of **field** or **text box**.
-- Avoid: In the **Variable name** field, enter `my text`.
-- Use instead: In the **Variable name** box, enter `my text`.
+- Do: In the **Variable name** box, enter `my text`.
+- Do not: In the **Variable name** field, enter `my text`.
## foo
@@ -265,8 +301,8 @@ When writing about the Guest role:
- Do not use the phrase, **if you are a guest** to mean someone who is assigned the Guest
role. Instead, write it out. For example, **if you are assigned the Guest role**.
- To describe a situation where the Guest role is the minimum required:
- - Avoid: the Guest role or higher
- - Use instead: at least the Guest role
+ - Do: at least the Guest role
+ - Do not: the Guest role or higher
Do not use **Guest permissions**. A user who is assigned the Guest role has a set of associated permissions.
@@ -282,15 +318,16 @@ Do not use **high availability** or **HA**. Instead, direct readers to the GitLa
Do not use **higher** when talking about version numbers.
-- Do: In GitLab 14.1 and later.
-- Do not: In GitLab 14.1 and higher.
+- Do: In GitLab 14.4 and later...
+- Do not: In GitLab 14.4 and higher...
+- Do not: In GitLab 14.4 and above...
## hit
Don't use **hit** to mean **press**.
-- Avoid: Hit the **ENTER** button.
-- Use instead: Press **ENTER**.
+- Do: Press **ENTER**.
+- Do not: Hit the **ENTER** button.
## I
@@ -326,8 +363,9 @@ If you want to use **CI** with the word **job**, use **CI/CD job** rather than *
Use **later** when talking about version numbers.
-- Avoid: In GitLab 14.1 and higher.
-- Use instead: In GitLab 14.1 and later.
+- Do: In GitLab 14.1 and later...
+- Do not: In GitLab 14.1 and higher...
+- Do not: In GitLab 14.1 and above...
## list
@@ -354,8 +392,8 @@ When writing about the Maintainer role:
- Do not use the phrase, **if you are a maintainer** to mean someone who is assigned the Maintainer
role. Instead, write it out. For example, **if you are assigned the Maintainer role**.
- To describe a situation where the Maintainer role is the minimum required:
- - Avoid: the Maintainer role or higher
- - Use instead: at least the Maintainer role
+ - Do: at least the Maintainer role
+ - Do not: the Maintainer role or higher
Do not use **Maintainer permissions**. A user who is assigned the Maintainer role has a set of associated permissions.
@@ -416,6 +454,13 @@ Do not use **note that** because it's wordy.
- Do: You can change the settings.
- Do not: Note that you can change the settings.
+## once
+
+The word **once** means **one time**. Don't use it to mean **after** or **when**.
+
+- Do: When the process is complete...
+- Do not: Once the process is complete...
+
## Owner
When writing about the Owner role:
@@ -429,7 +474,9 @@ Do not use **Owner permissions**. A user who is assigned the Owner role has a se
## permissions
-Do not use **roles** and **permissions** interchangeably. Each user is assigned a role. Each role includes a set of permissions.
+Do not use [**roles**](#roles) and **permissions** interchangeably. Each user is assigned a role. Each role includes a set of permissions.
+
+Permissions are not the same as [**access levels**](#access-level).
## please
@@ -454,8 +501,8 @@ When writing about the Reporter role:
- Do not use the phrase, **if you are a reporter** to mean someone who is assigned the Reporter
role. Instead, write it out. For example, **if you are assigned the Reporter role**.
- To describe a situation where the Reporter role is the minimum required:
- - Avoid: the Reporter role or higher
- - Use instead: at least the Reporter role
+ - Do: at least the Reporter role
+ - Do not: the Reporter role or higher
Do not use **Reporter permissions**. A user who is assigned the Reporter role has a set of associated permissions.
@@ -465,12 +512,23 @@ Use title case for **Repository Mirroring**.
## roles
-Do not use **roles** and **permissions** interchangeably. Each user is assigned a role. Each role includes a set of permissions.
+Do not use **roles** and [**permissions**](#permissions) interchangeably. Each user is assigned a role. Each role includes a set of permissions.
+
+Roles are not the same as [**access levels**](#access-level).
## runner, runners
Use lowercase for **runners**. These are the agents that run CI/CD jobs. See also [GitLab Runner](#gitlab-runner) and [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/233529).
+## (s)
+
+Do not use **(s)** to make a word optionally plural. It can slow down comprehension. For example:
+
+Do: Select the jobs you want.
+Do not: Select the job(s) you want.
+
+If you can select multiples of something, then write the word as plural.
+
## sanity check
Do not use **sanity check**. Use **check for completeness** instead. ([Vale](../testing.md#vale) rule: [`InclusionAbleism.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/InclusionAbleism.yml))
@@ -514,6 +572,13 @@ You can use **single sign-on**.
Do not use **simply** or **simple**. If the user doesn't find the process to be simple, we lose their trust. ([Vale](../testing.md#vale) rule: [`Simplicity.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/Simplicity.yml))
+## since
+
+The word **since** indicates a timeframe. For example, **Since 1984, Bon Jovi has existed**. Don't use **since** to mean **because**.
+
+- Do: Because you have the Developer role, you can delete the widget.
+- Do not: Since you have the Developer role, you can delete the widget.
+
## slashes
Instead of **and/or**, use **or** or re-write the sentence. This rule also applies to other slashes, like **follow/unfollow**. Some exceptions (like **CI/CD**) are allowed.
diff --git a/doc/development/documentation/testing.md b/doc/development/documentation/testing.md
index dfa2f3ed55..13648a7c71 100644
--- a/doc/development/documentation/testing.md
+++ b/doc/development/documentation/testing.md
@@ -206,7 +206,6 @@ Vale returns three types of results: `suggestion`, `warning`, and `error`:
(after the Technical Writing team completes its cleanup). Warnings don't break CI. See a list of
[warning-level rules](https://gitlab.com/search?utf8=✓&snippets=false&scope=&repository_ref=master&search=path%3Adoc%2F.vale%2Fgitlab+Warning%3A&group_id=9970&project_id=278964).
- **Error**-level results are Style Guide violations, and should contain clear explanations
- about how to resolve the error. Errors break CI and are displayed in CI job output.
of how to resolve the error. Errors break CI and are displayed in CI job output. See a list of
[error-level rules](https://gitlab.com/search?utf8=✓&snippets=false&scope=&repository_ref=master&search=path%3Adoc%2F.vale%2Fgitlab+Error%3A&group_id=9970&project_id=278964).
diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md
index 31c38bc144..90c1137e5c 100644
--- a/doc/development/documentation/workflow.md
+++ b/doc/development/documentation/workflow.md
@@ -99,7 +99,7 @@ The process involves the following:
- Primary Reviewer. Review by a [code reviewer](https://about.gitlab.com/handbook/engineering/projects/)
or other appropriate colleague to confirm accuracy, clarity, and completeness. This can be skipped
for minor fixes without substantive content changes.
-- Technical Writer (Optional). If not completed for a merge request prior to merging, must be scheduled
+- Technical Writer (Optional). If not completed for a merge request before merging, must be scheduled
post-merge. Schedule post-merge reviews only if an urgent merge is required. To request a:
- Pre-merge review, assign the Technical Writer listed for the applicable
[DevOps stage group](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments).
@@ -111,7 +111,7 @@ The process involves the following:
- Ensure the appropriate labels are applied, including any required to pick a merge request into
a release.
- Ensure that, if there has not been a Technical Writer review completed or scheduled, they
- [create the required issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Doc%20Review), assign to the Technical Writer of the given stage group,
+ [create the required issue](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Doc%20Review), assign it to the Technical Writer of the given stage group,
and link it from the merge request.
The process is reflected in the **Documentation**
@@ -130,10 +130,10 @@ immediately after merge by the developer or maintainer. For this,
create an issue using the [Doc Review description template](https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_template=Doc%20Review)
and link to it from the merged merge request that introduced the documentation change.
-Circumstances where a regular pre-merge Technical Writer review might be skipped include:
+Circumstances, where a regular pre-merge Technical Writer review might be skipped, include:
-- There is a short amount of time left before the milestone release. If there are less than three days
- remaining, seek a post-merge review and ping the writer via Slack to ensure the review is
+- There is a short amount of time left before the milestone release. If less than three
+ days are remaining, seek a post-merge review and ping the writer via Slack to ensure the review is
completed as soon as possible.
- The size of the change is small and you have a high degree of confidence
that early users of the feature (for example, GitLab.com users) can easily
@@ -156,15 +156,15 @@ Remember:
Ensure the following if skipping an initial Technical Writer review:
-- That [product badges](styleguide/index.md#product-tier-badges) are applied.
-- That the GitLab [version](styleguide/index.md#gitlab-versions) that
- introduced the feature has been included.
-- That changes to headings don't affect in-app hyperlinks.
+- [Product badges](styleguide/index.md#product-tier-badges) are applied.
+- The GitLab [version](styleguide/index.md#gitlab-versions) that
+ introduced the feature is included.
+- Changes to headings don't affect in-app hyperlinks.
- Specific [user permissions](../../user/permissions.md) are documented.
-- That new documents are linked from higher-level indexes, for discoverability.
-- Style guide is followed:
+- New documents are linked from higher-level indexes, for discoverability.
+- The style guide is followed:
- For [directories and files](styleguide/index.md#work-with-directories-and-files).
- For [images](styleguide/index.md#images).
Merge requests that change the location of documentation must always be reviewed by a Technical
-Writer prior to merging.
+Writer before merging.
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index 42fb9fd42f..7f74d9660e 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -40,7 +40,7 @@ By default, merge request pipelines for development run in an EE-context only. I
developing features that differ between FOSS and EE, you may wish to run pipelines in a
FOSS context as well.
-To run pipelines in both contexts, include `RUN AS-IF-FOSS` in the merge request title.
+To run pipelines in both contexts, add the `~"pipeline:run-as-if-foss"` label to the merge request.
See the [As-if-FOSS jobs](pipelines.md#as-if-foss-jobs) pipelines documentation for more information.
diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md
index bba4e1cda2..68d8b42433 100644
--- a/doc/development/elasticsearch.md
+++ b/doc/development/elasticsearch.md
@@ -233,11 +233,11 @@ Any data or index cleanup needed to support migration retries should be handled
will re-enqueue itself with a delay which is set using the `throttle_delay` option described below. The batching
must be handled within the `migrate` method, this setting controls the re-enqueuing only.
-- `batch_size` - Sets the number of documents modified during a `batched!` migration run. This size should be set to a value which allows the updates
- enough time to finish. This can be tuned in combination with the `throttle_delay` option described below. The batching
- must be handled within a custom `migrate` method or by using the [`Elastic::MigrationBackfillHelper`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/workers/concerns/elastic/migration_backfill_helper.rb)
- `migrate` method which uses this setting. Default value is 1000 documents.
-
+- `batch_size` - Sets the number of documents modified during a `batched!` migration run. This size should be set to a value which allows the updates
+enough time to finish. This can be tuned in combination with the `throttle_delay` option described below. The batching
+must be handled within a custom `migrate` method or by using the [`Elastic::MigrationBackfillHelper`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/workers/concerns/elastic/migration_backfill_helper.rb)
+`migrate` method which uses this setting. Default value is 1000 documents.
+
- `throttle_delay` - Sets the wait time in between batch runs. This time should be set high enough to allow each migration batch
enough time to finish. Additionally, the time should be less than 30 minutes since that is how often the
[`Elastic::MigrationWorker`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/workers/elastic/migration_worker.rb)
diff --git a/doc/development/experiment_guide/index.md b/doc/development/experiment_guide/index.md
index 4de272fec2..1b1f756d4c 100644
--- a/doc/development/experiment_guide/index.md
+++ b/doc/development/experiment_guide/index.md
@@ -61,7 +61,7 @@ Therefore, you should postpone this effort until the [experiment cleanup process
We recommend the following workflow:
-1. Review the Pajamas guidelines for [icons](https://design.gitlab.com/product-foundations/iconography) and [illustrations](https://design.gitlab.com/product-foundations/illustration).
+1. Review the Pajamas guidelines for [icons](https://design.gitlab.com/product-foundations/iconography/) and [illustrations](https://design.gitlab.com/product-foundations/illustration/).
1. Add an icon or illustration as an `.svg` file in the `/app/assets/images` (or EE) path in the GitLab repository.
1. Use `image_tag` or `image_path` to render it via the asset pipeline.
1. **If the experiment is a success**, designers add the new icon or illustration to the Pajamas UI kit as part of the cleanup process.
diff --git a/doc/development/fe_guide/accessibility.md b/doc/development/fe_guide/accessibility.md
index 7c870de9a6..c4ebef4c28 100644
--- a/doc/development/fe_guide/accessibility.md
+++ b/doc/development/fe_guide/accessibility.md
@@ -334,7 +334,7 @@ Keep in mind that:
- When you add `:hover` styles, in most cases you should add `:focus` styles too so that the styling is applied for both mouse **and** keyboard users.
- If you remove an interactive element's `outline`, make sure you maintain visual focus state in another way such as with `box-shadow`.
-See the [Pajamas Keyboard-only page](https://design.gitlab.com/accessibility-audits/keyboard-only) for more detail.
+See the [Pajamas Keyboard-only page](https://design.gitlab.com/accessibility-audits/keyboard-only/) for more detail.
## Tabindex
diff --git a/doc/development/fe_guide/content_editor.md b/doc/development/fe_guide/content_editor.md
index 956e7d0d56..139825655e 100644
--- a/doc/development/fe_guide/content_editor.md
+++ b/doc/development/fe_guide/content_editor.md
@@ -11,7 +11,7 @@ experience for [GitLab Flavored Markdown](../../user/markdown.md) in the GitLab
It also serves as the foundation for implementing Markdown-focused editors
that target other engines, like static site generators.
-We use [tiptap 2.0](https://www.tiptap.dev/) and [ProseMirror](https://prosemirror.net/)
+We use [tiptap 2.0](https://tiptap.dev/) and [ProseMirror](https://prosemirror.net/)
to build the Content Editor. These frameworks provide a level of abstraction on top of
the native
[`contenteditable`](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content) web technology.
@@ -143,7 +143,7 @@ The Content Editor is composed of three main layers:
### Editing tools UI
The editing tools UI are Vue components that display the editor's state and
-dispatch [commands](https://www.tiptap.dev/api/commands/#commands) to mutate it.
+dispatch [commands](https://tiptap.dev/api/commands/#commands) to mutate it.
They are located in the `~/content_editor/components` directory. For example,
the **Bold** toolbar button displays the editor's state by becoming active when
the user selects bold text. This button also dispatches the `toggleBold` command
@@ -159,7 +159,7 @@ sequenceDiagram
#### Node views
-We implement [node views](https://www.tiptap.dev/guide/node-views/vue/#node-views-with-vue)
+We implement [node views](https://tiptap.dev/guide/node-views/vue/#node-views-with-vue)
to provide inline editing tools for some content types, like tables and images. Node views
allow separating the presentation of a content type from its
[model](https://prosemirror.net/docs/guide/#doc.data_structures). Using a Vue component in
@@ -209,7 +209,7 @@ the following events:
- `blur`
- `error`.
-Learn more about these events in [Tiptap's event guide](https://www.tiptap.dev/api/events/).
+Learn more about these events in [Tiptap's event guide](https://tiptap.dev/api/events/).
```html