Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
78aaabb87f
commit
5b0916450c
46 changed files with 296 additions and 182 deletions
|
@ -1 +1 @@
|
|||
2247949bf06c7e8b28ef629dccb347868ee3abf4
|
||||
7034511c6f456177e023d7b418ceaa391deee79a
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
43
app/services/todos/destroy/unauthorized_features_service.rb
Normal file
43
app/services/todos/destroy/unauthorized_features_service.rb
Normal 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
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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') } }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
1
db/schema_migrations/20211125120444
Normal file
1
db/schema_migrations/20211125120444
Normal file
|
@ -0,0 +1 @@
|
|||
19062282d022e5d93cd525cff44c67f1fbc5557f1201e523a57725dc0b6ecd70
|
1
db/schema_migrations/20211202145237
Normal file
1
db/schema_migrations/20211202145237
Normal file
|
@ -0,0 +1 @@
|
|||
d109142aa838faedcd307f6cd235c969ca265813493eef50d63cbc2fe5d203b3
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue