Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-12-03 18:11:11 +00:00
parent 78aaabb87f
commit 5b0916450c
46 changed files with 296 additions and 182 deletions

View file

@ -1 +1 @@
2247949bf06c7e8b28ef629dccb347868ee3abf4
7034511c6f456177e023d7b418ceaa391deee79a

View file

@ -67,7 +67,7 @@ export default {
:class="cssClassJobName"
class="dropdown-menu-toggle gl-pipeline-job-width! gl-pr-4!"
>
<div class="gl-display-flex gl-align-items-center gl-justify-content-space-between">
<div class="gl-display-flex gl-align-items-stretch gl-justify-content-space-between">
<job-item
:type="$options.jobItemTypes.jobDropdown"
:group-tooltip="tooltipText"

View file

@ -203,7 +203,7 @@ export default {
<template>
<div
:id="computedJobId"
class="ci-job-component gl-display-flex gl-align-items-center gl-justify-content-space-between gl-w-full"
class="ci-job-component gl-display-flex gl-justify-content-space-between gl-pipeline-job-width"
data-qa-selector="job_item_container"
>
<component
@ -223,12 +223,12 @@ export default {
>
<div class="ci-job-name-component gl-display-flex gl-align-items-center">
<ci-icon :size="24" :status="job.status" class="gl-line-height-0" />
<div class="gl-pl-3 gl-display-flex gl-flex-direction-column gl-w-full">
<div class="gl-text-truncate gl-w-70p gl-line-height-normal">{{ job.name }}</div>
<div class="gl-pl-3 gl-pr-3 gl-display-flex gl-flex-direction-column gl-pipeline-job-width">
<div class="gl-text-truncate gl-pr-9 gl-line-height-normal">{{ job.name }}</div>
<div
v-if="showStageName"
data-testid="stage-name-in-job"
class="gl-text-truncate gl-w-70p gl-font-sm gl-text-gray-500 gl-line-height-normal"
class="gl-text-truncate gl-pr-9 gl-font-sm gl-text-gray-500 gl-line-height-normal"
>
{{ stageName }}
</div>

View file

@ -124,7 +124,7 @@ export default {
<div
ref="linkedPipeline"
v-gl-tooltip
class="gl-pipeline-job-width"
class="gl-downstream-pipeline-job-width"
:title="tooltipText"
data-qa-selector="child_pipeline"
@mouseover="onDownstreamHovered"
@ -134,7 +134,7 @@ export default {
class="gl-relative gl-bg-white gl-p-3 gl-border-solid gl-border-gray-100 gl-border-1"
:class="{ 'gl-pl-9': isUpstream }"
>
<div class="gl-display-flex">
<div class="gl-display-flex gl-pr-7 gl-pipeline-job-width">
<ci-status
v-if="!pipelineIsLoading"
:status="pipelineStatus"
@ -142,7 +142,9 @@ export default {
css-classes="gl-top-0 gl-pr-2"
/>
<div v-else class="gl-pr-2"><gl-loading-icon size="sm" inline /></div>
<div class="gl-display-flex gl-flex-direction-column gl-w-13">
<div
class="gl-display-flex gl-flex-direction-column gl-pipeline-job-width gl-text-truncate"
>
<span class="gl-text-truncate" data-testid="downstream-title">
{{ downstreamTitle }}
</span>

View file

@ -25,7 +25,7 @@ export default {
// The max width and the width make sure the ellipsis to work and the min width
// is for when there is less text than the stage column width (which the width 100% does not fix)
jobWrapperClasses:
'gl-display-flex gl-flex-direction-column gl-align-items-center gl-w-full gl-px-8 gl-min-w-full gl-max-w-15',
'gl-display-flex gl-flex-direction-column gl-align-items-stretch gl-w-full gl-px-8 gl-min-w-full gl-max-w-15',
props: {
pipelineData: {
required: true,

View file

@ -1,10 +1,19 @@
<script>
import { GlLink } from '@gitlab/ui';
import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format';
import { s__, n__ } from '~/locale';
const defaultPrecision = 2;
export default {
components: {
GlLink,
},
inject: {
failedPipelinesLink: {
default: '',
},
},
props: {
counts: {
type: Object,
@ -27,6 +36,7 @@ export default {
{
title: s__('PipelineCharts|Failed:'),
value: n__('1 pipeline', '%d pipelines', this.counts.failed),
link: this.failedPipelinesLink,
},
{
title: s__('PipelineCharts|Success ratio:'),
@ -39,10 +49,13 @@ export default {
</script>
<template>
<ul>
<template v-for="({ title, value }, index) in statistics">
<template v-for="({ title, value, link }, index) in statistics">
<li :key="index">
<span>{{ title }}</span>
<strong>{{ value }}</strong>
<gl-link v-if="link" :href="link">
{{ value }}
</gl-link>
<strong v-else>{{ value }}</strong>
</li>
</template>
</ul>

View file

@ -11,7 +11,7 @@ const apolloProvider = new VueApollo({
});
const mountPipelineChartsApp = (el) => {
const { projectPath } = el.dataset;
const { projectPath, failedPipelinesLink } = el.dataset;
const shouldRenderDoraCharts = parseBoolean(el.dataset.shouldRenderDoraCharts);
const shouldRenderQualitySummary = parseBoolean(el.dataset.shouldRenderQualitySummary);
@ -25,6 +25,7 @@ const mountPipelineChartsApp = (el) => {
apolloProvider,
provide: {
projectPath,
failedPipelinesLink,
shouldRenderDoraCharts,
shouldRenderQualitySummary,
},

View file

@ -113,8 +113,8 @@
- mini graph in Commit widget pipeline
*/
@mixin pipeline-graph-dropdown-menu() {
width: 240px;
max-width: 240px;
width: auto;
max-width: 400px;
// override dropdown.scss
&.dropdown-menu li button,
@ -185,8 +185,6 @@
}
.ci-status-icon {
@include gl-mr-3;
position: relative;
> svg {

View file

@ -129,11 +129,17 @@
}
.gl-pipeline-job-width {
width: 186px;
width: 100%;
max-width: 400px;
}
.gl-pipeline-job-width\! {
width: 186px !important;
width: 100% !important;
max-width: 400px !important;
}
.gl-downstream-pipeline-job-width {
width: 240px;
}
.gl-linked-pipeline-padding {
@ -199,7 +205,6 @@
.big-pipeline-graph-dropdown-menu {
@include pipeline-graph-dropdown-menu();
width: 195px;
min-width: 195px;
left: 100%;
top: -10px;

View file

@ -5,18 +5,18 @@ module Emails
def new_issue_email(recipient_id, issue_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id, reason))
mail_new_thread(@issue, issue_thread_options(@issue.author_id, reason))
end
def issue_due_email(recipient_id, issue_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
mail_answer_thread(@issue, issue_thread_options(@issue.author_id, recipient_id, reason))
mail_answer_thread(@issue, issue_thread_options(@issue.author_id, reason))
end
def new_mention_in_issue_email(recipient_id, issue_id, updated_by_user_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, reason))
end
# rubocop: disable CodeReuse/ActiveRecord
@ -26,7 +26,7 @@ module Emails
@previous_assignees = []
@previous_assignees = User.where(id: previous_assignee_ids) if previous_assignee_ids.any?
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, reason))
end
# rubocop: enable CodeReuse/ActiveRecord
@ -34,9 +34,8 @@ module Emails
setup_issue_mail(issue_id, recipient_id, closed_via: closed_via)
@updated_by = User.find(updated_by_user_id)
@recipient = User.find(recipient_id)
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, reason))
end
def relabeled_issue_email(recipient_id, issue_id, label_names, updated_by_user_id, reason = nil)
@ -44,13 +43,13 @@ module Emails
@label_names = label_names
@labels_url = project_labels_url(@project)
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, reason))
end
def removed_milestone_issue_email(recipient_id, issue_id, updated_by_user_id, reason = nil)
setup_issue_mail(issue_id, recipient_id)
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, reason))
end
def changed_milestone_issue_email(recipient_id, issue_id, milestone, updated_by_user_id, reason = nil)
@ -58,7 +57,7 @@ module Emails
@milestone = milestone
@milestone_url = milestone_url(@milestone)
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason).merge({
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, reason).merge({
template_name: 'changed_milestone_email'
}))
end
@ -68,7 +67,7 @@ module Emails
@issue_status = status
@updated_by = User.find(updated_by_user_id)
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, reason))
end
def issue_moved_email(recipient, issue, new_issue, updated_by_user, reason = nil)
@ -77,7 +76,7 @@ module Emails
@new_issue = new_issue
@new_project = new_issue.project
@can_access_project = recipient.can?(:read_project, @new_project)
mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id, reason))
mail_answer_thread(issue, issue_thread_options(updated_by_user.id, reason))
end
def issue_cloned_email(recipient, issue, new_issue, updated_by_user, reason = nil)
@ -87,7 +86,7 @@ module Emails
@issue = issue
@new_issue = new_issue
@can_access_project = recipient.can?(:read_project, @new_issue.project)
mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id, reason))
mail_answer_thread(issue, issue_thread_options(updated_by_user.id, reason))
end
def import_issues_csv_email(user_id, project_id, results)
@ -124,14 +123,15 @@ module Emails
@project = @issue.project
@target_url = project_issue_url(@project, @issue)
@closed_via = closed_via
@recipient = User.find(recipient_id)
@sent_notification = SentNotification.record(@issue, recipient_id, reply_key)
end
def issue_thread_options(sender_id, recipient_id, reason)
def issue_thread_options(sender_id, reason)
{
from: sender(sender_id),
to: User.find(recipient_id).notification_email_for(@project.group),
to: @recipient.notification_email_for(@project.group),
subject: subject("#{@issue.title} (##{@issue.iid})"),
'X-GitLab-NotificationReason' => reason
}

View file

@ -5,13 +5,13 @@ module Emails
def new_merge_request_email(recipient_id, merge_request_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id, present: true)
mail_new_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason))
mail_new_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, reason))
end
def new_mention_in_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id, present: true)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
def push_to_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil, new_commits: [], existing_commits: [])
@ -20,7 +20,7 @@ module Emails
@existing_commits = existing_commits
@updated_by_user = User.find(updated_by_user_id)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
def change_in_merge_request_draft_status_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
@ -28,7 +28,7 @@ module Emails
@updated_by_user = User.find(updated_by_user_id)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
# rubocop: disable CodeReuse/ActiveRecord
@ -38,7 +38,7 @@ module Emails
@previous_assignees = []
@previous_assignees = User.where(id: previous_assignee_ids) if previous_assignee_ids.any?
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
# rubocop: enable CodeReuse/ActiveRecord
@ -49,7 +49,7 @@ module Emails
@previous_reviewers = []
@previous_reviewers = User.where(id: previous_reviewer_ids) if previous_reviewer_ids.any?
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
# rubocop: enable CodeReuse/ActiveRecord
@ -58,13 +58,13 @@ module Emails
@label_names = label_names
@labels_url = project_labels_url(@project)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
def removed_milestone_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
def changed_milestone_merge_request_email(recipient_id, merge_request_id, milestone, updated_by_user_id, reason = nil)
@ -72,7 +72,7 @@ module Emails
@milestone = milestone
@milestone_url = milestone_url(@milestone)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason).merge({
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason).merge({
template_name: 'changed_milestone_email'
}))
end
@ -81,27 +81,27 @@ module Emails
setup_merge_request_mail(merge_request_id, recipient_id)
@updated_by = User.find(updated_by_user_id)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason: nil, closed_via: nil)
setup_merge_request_mail(merge_request_id, recipient_id)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
def request_review_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@updated_by = User.find(updated_by_user_id)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
def attention_requested_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@updated_by = User.find(updated_by_user_id)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
def merge_request_status_email(recipient_id, merge_request_id, status, updated_by_user_id, reason = nil)
@ -109,27 +109,27 @@ module Emails
@mr_status = status
@updated_by = User.find(updated_by_user_id)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, reason))
end
def merge_request_unmergeable_email(recipient_id, merge_request_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
mail_answer_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, reason))
end
def resolved_all_discussions_email(recipient_id, merge_request_id, resolved_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@resolved_by = User.find(resolved_by_user_id)
mail_answer_thread(@merge_request, merge_request_thread_options(resolved_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(resolved_by_user_id, reason))
end
def merge_when_pipeline_succeeds_email(recipient_id, merge_request_id, mwps_set_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
@mwps_set_by = ::User.find(mwps_set_by_user_id)
mail_answer_thread(@merge_request, merge_request_thread_options(mwps_set_by_user_id, recipient_id, reason))
mail_answer_thread(@merge_request, merge_request_thread_options(mwps_set_by_user_id, reason))
end
def merge_requests_csv_email(user, project, csv_data, export_status)
@ -154,19 +154,19 @@ module Emails
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request)
@recipient = User.find(recipient_id)
if present
recipient = User.find(recipient_id)
@mr_presenter = @merge_request.present(current_user: recipient)
@mr_presenter = @merge_request.present(current_user: @recipient)
end
@sent_notification = SentNotification.record(@merge_request, recipient_id, reply_key)
end
def merge_request_thread_options(sender_id, recipient_id, reason = nil)
def merge_request_thread_options(sender_id, reason = nil)
{
from: sender(sender_id),
to: User.find(recipient_id).notification_email_for(@project.group),
to: @recipient.notification_email_for(@project.group),
subject: subject("#{@merge_request.title} (#{@merge_request.to_reference})"),
'X-GitLab-NotificationReason' => reason
}

View file

@ -7,7 +7,7 @@ module Emails
@commit = @note.noteable
@target_url = project_commit_url(*note_target_url_options)
mail_answer_note_thread(@commit, @note, note_thread_options(recipient_id, reason))
mail_answer_note_thread(@commit, @note, note_thread_options(reason))
end
def note_issue_email(recipient_id, note_id, reason = nil)
@ -15,7 +15,7 @@ module Emails
@issue = @note.noteable
@target_url = project_issue_url(*note_target_url_options)
mail_answer_note_thread(@issue, @note, note_thread_options(recipient_id, reason))
mail_answer_note_thread(@issue, @note, note_thread_options(reason))
end
def note_merge_request_email(recipient_id, note_id, reason = nil)
@ -23,7 +23,7 @@ module Emails
@merge_request = @note.noteable
@target_url = project_merge_request_url(*note_target_url_options)
mail_answer_note_thread(@merge_request, @note, note_thread_options(recipient_id, reason))
mail_answer_note_thread(@merge_request, @note, note_thread_options(reason))
end
def note_snippet_email(recipient_id, note_id, reason = nil)
@ -37,7 +37,7 @@ module Emails
@target_url = gitlab_snippet_url(@note.noteable)
end
mail_answer_note_thread(@snippet, @note, note_thread_options(recipient_id, reason))
mail_answer_note_thread(@snippet, @note, note_thread_options(reason))
end
def note_design_email(recipient_id, note_id, reason = nil)
@ -49,7 +49,7 @@ module Emails
design.issue,
note_target_url_query_params.merge(vueroute: design.filename)
)
mail_answer_note_thread(design, @note, note_thread_options(recipient_id, reason))
mail_answer_note_thread(design, @note, note_thread_options(reason))
end
private
@ -62,10 +62,10 @@ module Emails
{ anchor: "note_#{@note.id}" }
end
def note_thread_options(recipient_id, reason)
def note_thread_options(reason)
{
from: sender(@note.author_id),
to: User.find(recipient_id).notification_email_for(@project&.group || @group),
to: @recipient.notification_email_for(@project&.group || @group),
subject: subject("#{@note.noteable.title} (#{@note.noteable.reference_link_text})"),
'X-GitLab-NotificationReason' => reason
}
@ -76,6 +76,7 @@ module Emails
@note = note_id.is_a?(Note) ? note_id : Note.find(note_id)
@project = @note.project
@group = @note.noteable.try(:group)
@recipient = User.find(recipient_id)
if (@project || @group) && @note.persisted?
@sent_notification = SentNotification.record_note(@note, recipient_id, reply_key)

View file

@ -9,11 +9,10 @@ module Emails
namespace_id: @project.namespace,
project_id: @project
)
user = User.find(user_id)
@recipient = User.find(user_id)
mail(
to: user.notification_email_for(@project.group),
to: @recipient.notification_email_for(@project.group),
subject: subject(release_email_subject)
)
end

View file

@ -434,8 +434,6 @@ class Issue < ApplicationRecord
# Returns `true` if the current issue can be viewed by either a logged in User
# or an anonymous user.
def visible_to_user?(user = nil)
return false unless project && project.feature_available?(:issues, user)
return publicly_visible? unless user
return false unless readable_by?(user)
@ -563,10 +561,10 @@ class Issue < ApplicationRecord
project.team.member?(user, Gitlab::Access::REPORTER)
elsif hidden?
false
elsif project.public? || (project.internal? && !user.external?)
project.feature_available?(:issues, user)
else
project.public? ||
project.internal? && !user.external? ||
project.team.member?(user)
project.team.member?(user)
end
end

View file

@ -3,6 +3,7 @@
class Todo < ApplicationRecord
include Sortable
include FromUnion
include EachBatch
# Time to wait for todos being removed when not visible for user anymore.
# Prevents TODOs being removed by mistake, for example, removing access from a user

View file

@ -1,46 +0,0 @@
# frozen_string_literal: true
module Todos
module Destroy
class PrivateFeaturesService < ::Todos::Destroy::BaseService
attr_reader :project_ids, :user_id
def initialize(project_ids, user_id = nil)
@project_ids = project_ids
@user_id = user_id
end
# rubocop: disable CodeReuse/ActiveRecord
def execute
ProjectFeature.where(project_id: project_ids).each do |project_features|
target_types = []
target_types << Issue.name if private?(project_features.issues_access_level)
target_types << MergeRequest.name if private?(project_features.merge_requests_access_level)
target_types << Commit.name if private?(project_features.repository_access_level)
next if target_types.empty?
remove_todos(project_features.project_id, target_types)
end
end
# rubocop: enable CodeReuse/ActiveRecord
private
def private?(feature_level)
feature_level == ProjectFeature::PRIVATE
end
# rubocop: disable CodeReuse/ActiveRecord
def remove_todos(project_id, target_types)
items = Todo.where(project_id: project_id)
items = items.where(user_id: user_id) if user_id
items.where.not(user_id: authorized_users)
.where(target_type: target_types)
.delete_all
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end

View file

@ -0,0 +1,43 @@
# frozen_string_literal: true
module Todos
module Destroy
class UnauthorizedFeaturesService < ::Todos::Destroy::BaseService
attr_reader :project_id, :user_id
BATCH_SIZE = 1000
def initialize(project_id, user_id = nil)
@project_id = project_id
@user_id = user_id
end
# rubocop: disable CodeReuse/ActiveRecord
def execute
return if user_id && authorized_users.where(user_id: user_id).exists?
related_todos.each_batch(of: BATCH_SIZE) do |batch|
pending_delete = without_authorized(batch).includes(:target, :user).reject do |todo|
Ability.allowed?(todo.user, :read_todo, todo, scope: :user)
end
Todo.where(id: pending_delete).delete_all if pending_delete.present?
end
end
# rubocop: enable CodeReuse/ActiveRecord
private
def related_todos
base_scope = Todo.for_project(project_id)
base_scope = base_scope.for_user(user_id) if user_id
base_scope
end
# Compatibility for #authorized_users in this class we always work
# with 1 project for queries efficiency
def project_ids
[project_id]
end
end
end
end

View file

@ -34,4 +34,4 @@
email: true }
%div{ style: note_style }
= markdown(note.note, pipeline: :email, author: note.author)
= markdown(note.note, pipeline: :email, author: note.author, current_user: @recipient, issuable_reference_expansion_enabled: true)

View file

@ -9,4 +9,4 @@
- if @issue.description
%div
= markdown(@issue.description, pipeline: :email, author: @issue.author)
= markdown(@issue.description, pipeline: :email, author: @issue.author, current_user: @recipient, issuable_reference_expansion_enabled: true)

View file

@ -8,4 +8,4 @@
- if @issue.description
%div
= markdown(@issue.description, pipeline: :email, author: @issue.author)
= markdown(@issue.description, pipeline: :email, author: @issue.author, current_user: @recipient, issuable_reference_expansion_enabled: true)

View file

@ -16,4 +16,4 @@
- if @merge_request.description
%div
= markdown(@merge_request.description, pipeline: :email, author: @merge_request.author)
= markdown(@merge_request.description, pipeline: :email, author: @merge_request.author, current_user: @recipient, issuable_reference_expansion_enabled: true)

View file

@ -15,4 +15,4 @@
%p
%h4= _("Release notes:")
= markdown(@release.description, pipeline: :email, author: @release.author)
= markdown(@release.description, pipeline: :email, author: @release.author, current_user: @recipient)

View file

@ -2,4 +2,4 @@
%div
= _("%{author_link} wrote:").html_safe % { author_link: link_to(@note.author_name, user_url(@note.author)) }
%div
= markdown(@note.note, pipeline: :email, author: @note.author)
= markdown(@note.note, pipeline: :email, author: @note.author, issuable_reference_expansion_enabled: true)

View file

@ -2,4 +2,5 @@
#js-project-pipelines-charts-app{ data: { project_path: @project.full_path,
should_render_dora_charts: should_render_dora_charts.to_s,
should_render_quality_summary: should_render_quality_summary.to_s } }
should_render_quality_summary: should_render_quality_summary.to_s,
failed_pipelines_link: project_pipelines_path(@project, page: '1', scope: 'all', status: 'failed') } }

View file

@ -10,7 +10,7 @@ module TodosDestroyer
include TodosDestroyerQueue
def perform(project_id, user_id = nil)
::Todos::Destroy::PrivateFeaturesService.new(project_id, user_id).execute
::Todos::Destroy::UnauthorizedFeaturesService.new(project_id, user_id).execute
end
end
end

View file

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345261
milestone: '14.5'
type: development
group: group::source code
default_enabled: false
default_enabled: true

View file

@ -1,8 +0,0 @@
---
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

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
class AddIndexTodosProjectIdUserId < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
INDEX_NAME = 'index_todos_on_project_id_and_user_id_and_id'
def up
add_concurrent_index :todos, [:project_id, :user_id, :id], name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :todos, INDEX_NAME
end
end

View file

@ -0,0 +1,32 @@
# frozen_string_literal: true
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddTodosProjectAndIdIndex < Gitlab::Database::Migration[1.0]
# When using the methods "add_concurrent_index" or "remove_concurrent_index"
# you must disable the use of transactions
# as these methods can not run in an existing transaction.
# When using "add_concurrent_index" or "remove_concurrent_index" methods make sure
# that either of them is the _only_ method called in the migration,
# any other changes should go in a separate migration.
# This ensures that upon failure _only_ the index creation or removing fails
# and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
disable_ddl_transaction!
NEW_INDEX_NAME = 'index_todos_on_project_id_and_id'
OLD_INDEX_NAME = 'index_todos_on_project_id'
def up
add_concurrent_index :todos, [:project_id, :id], name: NEW_INDEX_NAME
remove_concurrent_index_by_name :todos, OLD_INDEX_NAME
end
def down
add_concurrent_index :todos, :project_id, name: OLD_INDEX_NAME
remove_concurrent_index_by_name :todos, NEW_INDEX_NAME
end
end

View file

@ -0,0 +1 @@
19062282d022e5d93cd525cff44c67f1fbc5557f1201e523a57725dc0b6ecd70

View file

@ -0,0 +1 @@
d109142aa838faedcd307f6cd235c969ca265813493eef50d63cbc2fe5d203b3

View file

@ -27543,7 +27543,9 @@ CREATE INDEX index_todos_on_group_id ON todos USING btree (group_id);
CREATE INDEX index_todos_on_note_id ON todos USING btree (note_id);
CREATE INDEX index_todos_on_project_id ON todos USING btree (project_id);
CREATE INDEX index_todos_on_project_id_and_id ON todos USING btree (project_id, id);
CREATE INDEX index_todos_on_project_id_and_user_id_and_id ON todos USING btree (project_id, user_id, id);
CREATE INDEX index_todos_on_target_type_and_target_id ON todos USING btree (target_type, target_id);

View file

@ -257,7 +257,7 @@ 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/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.
defaults to 300 seconds (5 minutes). For example, a pull refresh only runs once in a given 300 second period, regardless of how many times you 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/mirror/pull.md).
@ -739,7 +739,7 @@ See [Environment Dashboard](../ci/environments/environments_dashboard.md#adding-
[Deploy boards](../user/project/deploy_boards.md) load information from Kubernetes about
Pods and Deployments. However, data over 10 MB for a certain environment read from
Kubernetes won't be shown.
Kubernetes aren't shown.
## Merge requests
@ -762,7 +762,7 @@ prevent any more changes from rendering. For more information about these limits
### Merge request reports size limit
Reports that go over the 20 MB limit won't be loaded. Affected reports:
Reports that go over the 20 MB limit aren't loaded. Affected reports:
- [Merge request security reports](../user/project/merge_requests/testing_and_reports_in_merge_requests.md#security-reports)
- [CI/CD parameter `artifacts:expose_as`](../ci/yaml/index.md#artifactsexpose_as)
@ -826,7 +826,7 @@ See the [Design Management Limitations](../user/project/issues/design_management
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31009) in GitLab 12.4.
Total number of changes (branches or tags) in a single push. If changes are more
than the specified limit, hooks won't be executed.
than the specified limit, hooks are not executed.
More information can be found in these docs:

View file

@ -20,31 +20,31 @@ 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).
Starting with GitLab version 14.0, support for NFS to store Git repository data is deprecated. Technical customer support and engineering support is available for the 14.x releases. Engineering is fixing 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.
At the end of the 14.12 milestone (tentatively 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:
For the 14.x releases, we 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:
Assistance is 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:
GitLab support is 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.
If the issue is reproducible, or if it happens intermittently but regularly, GitLab Support can 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
@ -438,7 +438,7 @@ the file system access GitLab requires. Workloads where many small files are wri
a serialized manner, like `git`, are not well suited to cloud-based file systems.
If you do choose to use these, avoid storing GitLab log files (for example, those in `/var/log/gitlab`)
there because this will also affect performance. We recommend that the log files be
there because this also affects performance. We recommend that the log files be
stored on a local volume.
For more details on the experience of using a cloud-based file systems with GitLab,
@ -447,12 +447,12 @@ see this [Commit Brooklyn 2019 video](https://youtu.be/K6OS8WodRBQ?t=313).
### Avoid using CephFS and GlusterFS
GitLab strongly recommends against using CephFS and GlusterFS.
These distributed file systems are not well-suited for the GitLab input/output access patterns because Git uses many small files and access times and file locking times to propagate will make Git activity very slow.
These distributed file systems are not well-suited for the GitLab input/output access patterns because Git uses many small files and access times and file locking times to propagate makes Git activity very slow.
### Avoid using PostgreSQL with NFS
GitLab strongly recommends against running your PostgreSQL database
across NFS. The GitLab support team will not be able to assist on performance issues related to
across NFS. The GitLab support team is not able to assist on performance issues related to
this configuration.
Additionally, this configuration is specifically warned against in the

View file

@ -98,6 +98,10 @@ The Enterprise Edition retains some group data that isn't part of the Community
## Importing the group
WARNING:
This feature will be [deprecated](https://gitlab.com/groups/gitlab-org/-/epics/4619)
in GitLab 14.8 and replaced by [GitLab Migration](../import/).
1. Create a new group:
- On the top bar, select **New** (**{plus}**) and then **New group**.
- On an existing group's page, select the **New subgroup** button.

View file

@ -316,19 +316,19 @@ a merge request or an issue.
The following table lists all GitLab-specific email headers:
| Header | Description |
|------------------------------------|-------------------------------------------------------------------------|
| `List-Id` | The path of the project in an RFC 2919 mailing list identifier. You can use it for email organization with filters. |
| `X-GitLab-(Resource)-ID` | The ID of the resource the notification is for. The resource, for example, can be `Issue`, `MergeRequest`, `Commit`, or another such resource. |
| `X-GitLab-Discussion-ID` | The ID of the thread the comment belongs to, in notification emails for comments. |
| `X-GitLab-Group-Id` **(PREMIUM)** | The group's ID. Only present on notification emails for epics. |
| `X-GitLab-Group-Path` **(PREMIUM)** | The group's path. Only present on notification emails for epics. |
| [`X-GitLab-NotificationReason`](#x-gitlab-notificationreason) | The reason for the notification. This can be `mentioned`, `assigned`, or `own_activity`. |
| `X-GitLab-Pipeline-Id` | The ID of the pipeline the notification is for, in notification emails for pipelines. |
| `X-GitLab-Project-Id` | The project's ID. |
| `X-GitLab-Project-Path` | The project's path. |
| `X-GitLab-Project` | The name of the project the notification belongs to. |
| `X-GitLab-Reply-Key` | A unique token to support reply by email. |
| Header | Description |
|---------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
| `List-Id` | The path of the project in an RFC 2919 mailing list identifier. You can use it for email organization with filters. |
| `X-GitLab-(Resource)-ID` | The ID of the resource the notification is for. The resource, for example, can be `Issue`, `MergeRequest`, `Commit`, or another such resource. |
| `X-GitLab-Discussion-ID` | The ID of the thread the comment belongs to, in notification emails for comments. |
| `X-GitLab-Group-Id` | The group's ID. Only present on notification emails for [epics](../group/epics/index.md). |
| `X-GitLab-Group-Path` | The group's path. Only present on notification emails for [epics](../group/epics/index.md) |
| [`X-GitLab-NotificationReason`](#x-gitlab-notificationreason) | The reason for the notification. This can be `mentioned`, `assigned`, or `own_activity`. |
| `X-GitLab-Pipeline-Id` | The ID of the pipeline the notification is for, in notification emails for pipelines. |
| `X-GitLab-Project-Id` | The project's ID. |
| `X-GitLab-Project-Path` | The project's path. |
| `X-GitLab-Project` | The name of the project the notification belongs to. |
| `X-GitLab-Reply-Key` | A unique token to support reply by email. |
### X-GitLab-NotificationReason

View file

@ -107,7 +107,11 @@ module Gitlab
hosts.any? || service_discovery_enabled?
end
# This is disabled for Rake tasks to ensure e.g. database migrations
# always produce consistent results.
def service_discovery_enabled?
return false if Gitlab::Runtime.rake?
service_discovery[:record].present?
end

View file

@ -4,14 +4,11 @@
category: importer
redis_slot: import
aggregation: weekly
feature_flag: track_importer_activity
- name: github_import_project_success
category: importer
redis_slot: import
aggregation: weekly
feature_flag: track_importer_activity
- name: github_import_project_failure
category: importer
redis_slot: import
aggregation: weekly
feature_flag: track_importer_activity

View file

@ -25,9 +25,13 @@ exports[`StatisticsList displays the counts data with labels 1`] = `
Failed:
</span>
<strong>
2 pipelines
</strong>
<gl-link-stub
href="/flightjs/Flight/-/pipelines?page=1&scope=all&status=failed"
>
2 pipelines
</gl-link-stub>
</li>
<li>
<span>

View file

@ -1,3 +1,4 @@
import { GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Component from '~/projects/pipelines/charts/components/statistics_list.vue';
import { counts } from '../mock_data';
@ -5,8 +6,15 @@ import { counts } from '../mock_data';
describe('StatisticsList', () => {
let wrapper;
const failedPipelinesLink = '/flightjs/Flight/-/pipelines?page=1&scope=all&status=failed';
const findFailedPipelinesLink = () => wrapper.findComponent(GlLink);
beforeEach(() => {
wrapper = shallowMount(Component, {
provide: {
failedPipelinesLink,
},
propsData: {
counts,
},
@ -15,10 +23,13 @@ describe('StatisticsList', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('displays the counts data with labels', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('displays failed pipelines link', () => {
expect(findFailedPipelinesLink().attributes('href')).toBe(failedPipelinesLink);
});
});

View file

@ -140,6 +140,15 @@ RSpec.describe Gitlab::Database::LoadBalancing::Configuration do
end
describe '#service_discovery_enabled?' do
it 'returns false when running inside a Rake task' do
allow(Gitlab::Runtime).to receive(:rake?).and_return(true)
config = described_class.new(ActiveRecord::Base)
config.service_discovery[:record] = 'foo'
expect(config.service_discovery_enabled?).to eq(false)
end
it 'returns true when a record is configured' do
config = described_class.new(ActiveRecord::Base)
config.service_discovery[:record] = 'foo'

View file

@ -4,9 +4,10 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery do
let(:load_balancer) do
Gitlab::Database::LoadBalancing::LoadBalancer.new(
Gitlab::Database::LoadBalancing::Configuration.new(ActiveRecord::Base)
)
configuration = Gitlab::Database::LoadBalancing::Configuration.new(ActiveRecord::Base)
configuration.service_discovery[:record] = 'localhost'
Gitlab::Database::LoadBalancing::LoadBalancer.new(configuration)
end
let(:service) do
@ -86,6 +87,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::ServiceDiscovery do
service.perform_service_discovery
end
end
context 'with failures' do
before do
allow(Gitlab::ErrorTracking).to receive(:track_exception)

View file

@ -613,6 +613,28 @@ RSpec.describe Notify do
it 'has References header including the notes and issue of the discussion' do
expect(subject.header['References'].message_ids).to include("issue_#{note.noteable.id}@#{host}")
end
context 'with private references accessible to the recipient' do
let_it_be(:private_project) { create(:project, :private) }
let_it_be(:private_issue) { create(:issue, :closed, project: private_project) }
before_all do
private_project.add_guest(recipient)
note.update!(note: "#{private_issue.to_reference(full: true)}")
end
let(:html_part) { subject.body.parts.last.to_s }
it 'does not redact the reference' do
expect(html_part).to include("data-reference-type=\"issue\"")
expect(html_part).to include("title=\"#{private_issue.title}\"")
end
it 'renders expanded issue references' do
expect(html_part).to include("#{private_issue.to_reference(full: true)} (closed)")
end
end
end
end

View file

@ -987,6 +987,7 @@ RSpec.describe Issue do
issue = build(:issue, project: project)
user = build(:user)
allow(::Gitlab::ExternalAuthorization).to receive(:access_allowed?).with(user, 'a-label', project.full_path).and_call_original
expect(::Gitlab::ExternalAuthorization).to receive(:access_allowed?).with(user, 'a-label') { false }
expect(issue.visible_to_user?(user)).to be_falsy
end
@ -1020,6 +1021,7 @@ RSpec.describe Issue do
issue = build(:issue, project: project)
user = build(:admin)
allow(::Gitlab::ExternalAuthorization).to receive(:access_allowed?).with(user, 'a-label', project.full_path).and_call_original
expect(::Gitlab::ExternalAuthorization).to receive(:access_allowed?).with(user, 'a-label') { false }
expect(issue.visible_to_user?(user)).to be_falsy
end

View file

@ -2,13 +2,17 @@
require 'spec_helper'
RSpec.describe Todos::Destroy::PrivateFeaturesService do
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
let(:another_user) { create(:user) }
let(:project_member) { create(:user) }
let(:issue) { create(:issue, project: project) }
let(:mr) { create(:merge_request, source_project: project) }
RSpec.describe Todos::Destroy::UnauthorizedFeaturesService do
let_it_be(:project, reload: true) { create(:project, :public, :repository) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:mr) { create(:merge_request, source_project: project) }
let_it_be(:user) { create(:user) }
let_it_be(:another_user) { create(:user) }
let_it_be(:project_member) do
create(:user).tap do |user|
project.add_developer(user)
end
end
let!(:todo_mr_non_member) { create(:todo, user: user, target: mr, project: project) }
let!(:todo_mr_non_member2) { create(:todo, user: another_user, target: mr, project: project) }
@ -20,10 +24,6 @@ RSpec.describe Todos::Destroy::PrivateFeaturesService do
let!(:commit_todo_non_member2) { create(:on_commit_todo, user: another_user, project: project) }
let!(:commit_todo_member) { create(:on_commit_todo, user: project_member, project: project) }
before do
project.add_developer(project_member)
end
context 'when user_id is provided' do
subject { described_class.new(project.id, user.id).execute }

View file

@ -6,7 +6,7 @@ RSpec.describe TodosDestroyer::PrivateFeaturesWorker do
it "calls the Todos::Destroy::PrivateFeaturesService with the params it was given" do
service = double
expect(::Todos::Destroy::PrivateFeaturesService).to receive(:new).with(100, nil).and_return(service)
expect(::Todos::Destroy::UnauthorizedFeaturesService).to receive(:new).with(100, nil).and_return(service)
expect(service).to receive(:execute)
described_class.new.perform(100)