Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
bc12365ae0
commit
be2142bf5e
|
@ -1,3 +1,4 @@
|
||||||
VERSION merge=ours
|
VERSION merge=ours
|
||||||
Dangerfile gitlab-language=ruby
|
Dangerfile gitlab-language=ruby
|
||||||
*.pdf filter=lfs diff=lfs merge=lfs -text
|
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.rb diff=ruby
|
||||||
|
|
|
@ -119,7 +119,6 @@ linters:
|
||||||
- 'app/views/invites/show.html.haml'
|
- 'app/views/invites/show.html.haml'
|
||||||
- 'app/views/jira_connect/subscriptions/index.html.haml'
|
- 'app/views/jira_connect/subscriptions/index.html.haml'
|
||||||
- 'app/views/layouts/_mailer.html.haml'
|
- 'app/views/layouts/_mailer.html.haml'
|
||||||
- 'app/views/layouts/experiment_mailer.html.haml'
|
|
||||||
- 'app/views/layouts/header/_default.html.haml'
|
- 'app/views/layouts/header/_default.html.haml'
|
||||||
- 'app/views/layouts/header/_new_dropdown.haml'
|
- 'app/views/layouts/header/_new_dropdown.haml'
|
||||||
- 'app/views/layouts/jira_connect.html.haml'
|
- 'app/views/layouts/jira_connect.html.haml'
|
||||||
|
|
|
@ -1298,7 +1298,6 @@ Graphql/IDType:
|
||||||
- 'app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb'
|
- 'app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb'
|
||||||
- 'app/graphql/resolvers/snippets_resolver.rb'
|
- 'app/graphql/resolvers/snippets_resolver.rb'
|
||||||
- 'app/graphql/resolvers/user_merge_requests_resolver.rb'
|
- 'app/graphql/resolvers/user_merge_requests_resolver.rb'
|
||||||
- 'app/graphql/resolvers/user_resolver.rb'
|
|
||||||
|
|
||||||
# Offense count: 86
|
# Offense count: 86
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
|
@ -1328,7 +1327,6 @@ FactoryBot/InlineAssociation:
|
||||||
- 'spec/factories/notes.rb'
|
- 'spec/factories/notes.rb'
|
||||||
- 'spec/factories/packages.rb'
|
- 'spec/factories/packages.rb'
|
||||||
- 'spec/factories/packages/package_file.rb'
|
- 'spec/factories/packages/package_file.rb'
|
||||||
- 'spec/factories/prometheus_alert.rb'
|
|
||||||
- 'spec/factories/resource_label_events.rb'
|
- 'spec/factories/resource_label_events.rb'
|
||||||
- 'spec/factories/resource_milestone_event.rb'
|
- 'spec/factories/resource_milestone_event.rb'
|
||||||
- 'spec/factories/resource_state_event.rb'
|
- 'spec/factories/resource_state_event.rb'
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
9bb23d81e20244cb4f9635295ae4615e8ff12f8c
|
60aaf7cdd17b4efdf4dab23eb83aa86fe596a55b
|
||||||
|
|
|
@ -10,7 +10,7 @@ function showTooltip(target, title) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
target.setAttribute('title', originalTitle);
|
target.setAttribute('title', originalTitle);
|
||||||
fixTitle(target);
|
fixTitle(target);
|
||||||
}, 300);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
target.setAttribute('title', title);
|
target.setAttribute('title', title);
|
||||||
|
|
|
@ -4,11 +4,6 @@ import UserCallout from '~/user_callout';
|
||||||
import UserTabs from './user_tabs';
|
import UserTabs from './user_tabs';
|
||||||
|
|
||||||
function initUserProfile(action) {
|
function initUserProfile(action) {
|
||||||
// place profile avatars to top
|
|
||||||
$('.profile-groups-avatars').tooltip({
|
|
||||||
placement: 'top',
|
|
||||||
});
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new UserTabs({ parentEl: '.user-profile', action });
|
new UserTabs({ parentEl: '.user-profile', action });
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,9 @@ export default {
|
||||||
// Note: Realtime is only available on issues right now, future support for MR wil be built later.
|
// Note: Realtime is only available on issues right now, future support for MR wil be built later.
|
||||||
return this.glFeatures.realTimeIssueSidebar && this.issuableType === 'issue';
|
return this.glFeatures.realTimeIssueSidebar && this.issuableType === 'issue';
|
||||||
},
|
},
|
||||||
|
relativeUrlRoot() {
|
||||||
|
return gon.relative_url_root ?? '';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.removeAssignee = this.store.removeAssignee.bind(this.store);
|
this.removeAssignee = this.store.removeAssignee.bind(this.store);
|
||||||
|
@ -119,7 +122,7 @@ export default {
|
||||||
/>
|
/>
|
||||||
<assignees
|
<assignees
|
||||||
v-if="!store.isFetching.assignees"
|
v-if="!store.isFetching.assignees"
|
||||||
:root-path="store.rootPath"
|
:root-path="relativeUrlRoot"
|
||||||
:users="store.assignees"
|
:users="store.assignees"
|
||||||
:editable="store.editable"
|
:editable="store.editable"
|
||||||
:issuable-type="issuableType"
|
:issuable-type="issuableType"
|
||||||
|
|
|
@ -50,6 +50,11 @@ export default {
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
relativeUrlRoot() {
|
||||||
|
return gon.relative_url_root ?? '';
|
||||||
|
},
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
this.removeReviewer = this.store.removeReviewer.bind(this.store);
|
this.removeReviewer = this.store.removeReviewer.bind(this.store);
|
||||||
this.addReviewer = this.store.addReviewer.bind(this.store);
|
this.addReviewer = this.store.addReviewer.bind(this.store);
|
||||||
|
@ -97,7 +102,7 @@ export default {
|
||||||
/>
|
/>
|
||||||
<reviewers
|
<reviewers
|
||||||
v-if="!store.isFetching.reviewers"
|
v-if="!store.isFetching.reviewers"
|
||||||
:root-path="store.rootPath"
|
:root-path="relativeUrlRoot"
|
||||||
:users="store.reviewers"
|
:users="store.reviewers"
|
||||||
:editable="store.editable"
|
:editable="store.editable"
|
||||||
:issuable-type="issuableType"
|
:issuable-type="issuableType"
|
||||||
|
|
|
@ -143,4 +143,21 @@ tr.footer td {
|
||||||
color: $mailer-link-color;
|
color: $mailer-link-color;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gitlab-info {
|
||||||
|
padding: $gl-padding-24 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gitlab-info-text {
|
||||||
|
max-width: 640px;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
color: $gray-400;
|
||||||
|
font-size: $gl-font-size-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-logo {
|
||||||
|
width: 90px;
|
||||||
|
height: 33px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -482,3 +482,27 @@
|
||||||
@include build-trace();
|
@include build-trace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-bar.bg-primary {
|
||||||
|
background-color: $blue-500 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-stage-pill {
|
||||||
|
width: 10rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pipeline-job-pill {
|
||||||
|
width: 8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stage-rounded {
|
||||||
|
border-radius: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stage-left-rounded {
|
||||||
|
border-radius: 2rem 0 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stage-right-rounded {
|
||||||
|
border-radius: 0 2rem 2rem 0;
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
* Pipelines Bundle: Pipeline lists and Mini Pipelines
|
* Pipelines Bundle: Pipeline lists and Mini Pipelines
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.pipelines-container .top-area .nav-controls > .btn:last-child {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
// Pipelines list
|
// Pipelines list
|
||||||
// Should affect pipelines table components rendered by:
|
// Should affect pipelines table components rendered by:
|
||||||
// - app/assets/javascripts/commit/pipelines/pipelines_bundle.js
|
// - app/assets/javascripts/commit/pipelines/pipelines_bundle.js
|
||||||
|
|
|
@ -347,3 +347,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-down(md) {
|
||||||
|
.content-list {
|
||||||
|
&.builds-content-list {
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-page='admin:jobs:index'] {
|
||||||
|
.admin-builds-table {
|
||||||
|
td:last-child {
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
@include media-breakpoint-down(md) {
|
/**
|
||||||
.content-list {
|
* !! NOTE: Do not add more code in this file:
|
||||||
&.builds-content-list {
|
*
|
||||||
width: 100%;
|
* https://gitlab.com/gitlab-org/gitlab/-/issues/267602
|
||||||
overflow: auto;
|
*
|
||||||
}
|
* For new pipeline CSS please consider:
|
||||||
}
|
*
|
||||||
}
|
* For pipelines tables and lists:
|
||||||
|
* - `app/assets/stylesheets/page_bundles/pipelines.scss`
|
||||||
|
*
|
||||||
|
* For individual pipelines and mini-pipelines:
|
||||||
|
* - `app/assets/stylesheets/page_bundles/pipeline.scss`
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
|
||||||
.ci-table {
|
.ci-table {
|
||||||
.avatar {
|
.avatar {
|
||||||
|
@ -80,39 +86,3 @@
|
||||||
color: $gl-text-color;
|
color: $gl-text-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-page='admin:jobs:index'] {
|
|
||||||
.admin-builds-table {
|
|
||||||
td:last-child {
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.pipelines-container .top-area .nav-controls > .btn:last-child {
|
|
||||||
float: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar.bg-primary {
|
|
||||||
background-color: $blue-500 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pipeline-stage-pill {
|
|
||||||
width: 10rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pipeline-job-pill {
|
|
||||||
width: 8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stage-rounded {
|
|
||||||
border-radius: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stage-left-rounded {
|
|
||||||
border-radius: 2rem 0 0 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stage-right-rounded {
|
|
||||||
border-radius: 0 2rem 2rem 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,13 +15,11 @@ class InvitesController < ApplicationController
|
||||||
feature_category :authentication_and_authorization
|
feature_category :authentication_and_authorization
|
||||||
|
|
||||||
def show
|
def show
|
||||||
track_new_user_invite_experiment('opened')
|
|
||||||
accept if skip_invitation_prompt?
|
accept if skip_invitation_prompt?
|
||||||
end
|
end
|
||||||
|
|
||||||
def accept
|
def accept
|
||||||
if member.accept_invite!(current_user)
|
if member.accept_invite!(current_user)
|
||||||
track_new_user_invite_experiment('accepted')
|
|
||||||
track_invitation_reminders_experiment('accepted')
|
track_invitation_reminders_experiment('accepted')
|
||||||
redirect_to invite_details[:path], notice: _("You have been granted %{member_human_access} access to %{title} %{name}.") %
|
redirect_to invite_details[:path], notice: _("You have been granted %{member_human_access} access to %{title} %{name}.") %
|
||||||
{ member_human_access: member.human_access, title: invite_details[:title], name: invite_details[:name] }
|
{ member_human_access: member.human_access, title: invite_details[:title], name: invite_details[:name] }
|
||||||
|
@ -110,25 +108,13 @@ class InvitesController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def track_new_user_invite_experiment(action)
|
|
||||||
return unless params[:new_user_invite]
|
|
||||||
|
|
||||||
property = params[:new_user_invite] == 'experiment' ? 'experiment_group' : 'control_group'
|
|
||||||
|
|
||||||
track_experiment(:invite_email, action, property)
|
|
||||||
end
|
|
||||||
|
|
||||||
def track_invitation_reminders_experiment(action)
|
def track_invitation_reminders_experiment(action)
|
||||||
return unless Gitlab::Experimentation.enabled?(:invitation_reminders)
|
return unless Gitlab::Experimentation.enabled?(:invitation_reminders)
|
||||||
|
|
||||||
property = Gitlab::Experimentation.enabled_for_attribute?(:invitation_reminders, member.invite_email) ? 'experimental_group' : 'control_group'
|
property = Gitlab::Experimentation.enabled_for_attribute?(:invitation_reminders, member.invite_email) ? 'experimental_group' : 'control_group'
|
||||||
|
|
||||||
track_experiment(:invitation_reminders, action, property)
|
|
||||||
end
|
|
||||||
|
|
||||||
def track_experiment(experiment_key, action, property)
|
|
||||||
Gitlab::Tracking.event(
|
Gitlab::Tracking.event(
|
||||||
Gitlab::Experimentation.experiment(experiment_key).tracking_category,
|
Gitlab::Experimentation.experiment(:invitation_reminders).tracking_category,
|
||||||
action,
|
action,
|
||||||
property: property,
|
property: property,
|
||||||
label: Digest::MD5.hexdigest(member.to_global_id.to_s)
|
label: Digest::MD5.hexdigest(member.to_global_id.to_s)
|
||||||
|
|
|
@ -6,7 +6,7 @@ module Resolvers
|
||||||
|
|
||||||
type Types::UserType, null: true
|
type Types::UserType, null: true
|
||||||
|
|
||||||
argument :id, GraphQL::ID_TYPE,
|
argument :id, Types::GlobalIDType[User],
|
||||||
required: false,
|
required: false,
|
||||||
description: 'ID of the User'
|
description: 'ID of the User'
|
||||||
|
|
||||||
|
|
|
@ -51,34 +51,20 @@ module Emails
|
||||||
|
|
||||||
return unless member_exists?
|
return unless member_exists?
|
||||||
|
|
||||||
subject_line = subject("Invitation to join the #{member_source.human_name} #{member_source.model_name.singular}")
|
subject_line =
|
||||||
|
if member.created_by
|
||||||
if member.invite_to_unknown_user? && Feature.enabled?(:invite_email_experiment)
|
subject(s_("MemberInviteEmail|%{member_name} invited you to join GitLab") % { member_name: member.created_by.name })
|
||||||
subject_line = subject("#{member.created_by.name} invited you to join GitLab") if member.created_by
|
else
|
||||||
@invite_url_params = { new_user_invite: 'experiment' }
|
subject(s_("MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}") % { project_or_group: member_source.human_name, project_or_group_name: member_source.model_name.singular })
|
||||||
|
|
||||||
member_email_with_layout(
|
|
||||||
to: member.invite_email,
|
|
||||||
subject: subject_line,
|
|
||||||
template: 'member_invited_email_experiment',
|
|
||||||
layout: 'experiment_mailer'
|
|
||||||
)
|
|
||||||
|
|
||||||
Gitlab::Tracking.event(Gitlab::Experimentation::EXPERIMENTS[:invite_email][:tracking_category], 'sent', property: 'experiment_group')
|
|
||||||
else
|
|
||||||
@invite_url_params = member.invite_to_unknown_user? ? { new_user_invite: 'control' } : {}
|
|
||||||
|
|
||||||
member_email_with_layout(
|
|
||||||
to: member.invite_email,
|
|
||||||
subject: subject_line
|
|
||||||
)
|
|
||||||
|
|
||||||
if member.invite_to_unknown_user?
|
|
||||||
Gitlab::Tracking.event(Gitlab::Experimentation::EXPERIMENTS[:invite_email][:tracking_category], 'sent', property: 'control_group')
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if member.invite_to_unknown_user? && Gitlab::Experimentation.enabled?(:invitation_reminders)
|
member_email_with_layout(
|
||||||
|
to: member.invite_email,
|
||||||
|
subject: subject_line,
|
||||||
|
layout: 'unknown_user_mailer'
|
||||||
|
)
|
||||||
|
|
||||||
|
if Gitlab::Experimentation.enabled?(:invitation_reminders)
|
||||||
Gitlab::Tracking.event(
|
Gitlab::Tracking.event(
|
||||||
Gitlab::Experimentation.experiment(:invitation_reminders).tracking_category,
|
Gitlab::Experimentation.experiment(:invitation_reminders).tracking_category,
|
||||||
'sent',
|
'sent',
|
||||||
|
@ -105,7 +91,7 @@ module Emails
|
||||||
subject_line = subjects[reminder_index] % { inviter: member.created_by.name }
|
subject_line = subjects[reminder_index] % { inviter: member.created_by.name }
|
||||||
|
|
||||||
member_email_with_layout(
|
member_email_with_layout(
|
||||||
layout: 'experiment_mailer',
|
layout: 'unknown_user_mailer',
|
||||||
to: member.invite_email,
|
to: member.invite_email,
|
||||||
subject: subject(subject_line)
|
subject: subject(subject_line)
|
||||||
)
|
)
|
||||||
|
@ -162,15 +148,10 @@ module Emails
|
||||||
@member_source_type.classify.constantize
|
@member_source_type.classify.constantize
|
||||||
end
|
end
|
||||||
|
|
||||||
def member_email_with_layout(to:, subject:, template: nil, layout: 'mailer')
|
def member_email_with_layout(to:, subject:, layout: 'mailer')
|
||||||
mail(to: to, subject: subject) do |format|
|
mail(to: to, subject: subject) do |format|
|
||||||
if template
|
format.html { render layout: layout }
|
||||||
format.html { render template, layout: layout }
|
format.text { render layout: layout }
|
||||||
format.text { render template, layout: layout }
|
|
||||||
else
|
|
||||||
format.html { render layout: layout }
|
|
||||||
format.text { render layout: layout }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,12 +38,16 @@ module AlertManagement
|
||||||
|
|
||||||
sha_attribute :fingerprint
|
sha_attribute :fingerprint
|
||||||
|
|
||||||
|
TITLE_MAX_LENGTH = 200
|
||||||
|
DESCRIPTION_MAX_LENGTH = 1_000
|
||||||
|
SERVICE_MAX_LENGTH = 100
|
||||||
|
TOOL_MAX_LENGTH = 100
|
||||||
HOSTS_MAX_LENGTH = 255
|
HOSTS_MAX_LENGTH = 255
|
||||||
|
|
||||||
validates :title, length: { maximum: 200 }, presence: true
|
validates :title, length: { maximum: TITLE_MAX_LENGTH }, presence: true
|
||||||
validates :description, length: { maximum: 1_000 }
|
validates :description, length: { maximum: DESCRIPTION_MAX_LENGTH }
|
||||||
validates :service, length: { maximum: 100 }
|
validates :service, length: { maximum: SERVICE_MAX_LENGTH }
|
||||||
validates :monitoring_tool, length: { maximum: 100 }
|
validates :monitoring_tool, length: { maximum: TOOL_MAX_LENGTH }
|
||||||
validates :project, presence: true
|
validates :project, presence: true
|
||||||
validates :events, presence: true
|
validates :events, presence: true
|
||||||
validates :severity, presence: true
|
validates :severity, presence: true
|
||||||
|
@ -54,7 +58,7 @@ module AlertManagement
|
||||||
conditions: -> { not_resolved },
|
conditions: -> { not_resolved },
|
||||||
message: -> (object, data) { _('Cannot have multiple unresolved alerts') }
|
message: -> (object, data) { _('Cannot have multiple unresolved alerts') }
|
||||||
}, unless: :resolved?
|
}, unless: :resolved?
|
||||||
validate :hosts_length
|
validate :hosts_format
|
||||||
|
|
||||||
enum severity: {
|
enum severity: {
|
||||||
critical: 0,
|
critical: 0,
|
||||||
|
@ -251,10 +255,11 @@ module AlertManagement
|
||||||
Gitlab::DataBuilder::Alert.build(self)
|
Gitlab::DataBuilder::Alert.build(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
def hosts_length
|
def hosts_format
|
||||||
return unless hosts
|
return unless hosts
|
||||||
|
|
||||||
errors.add(:hosts, "hosts array is over #{HOSTS_MAX_LENGTH} chars") if hosts.join.length > HOSTS_MAX_LENGTH
|
errors.add(:hosts, "hosts array is over #{HOSTS_MAX_LENGTH} chars") if hosts.join.length > HOSTS_MAX_LENGTH
|
||||||
|
errors.add(:hosts, "hosts array cannot be nested") if hosts.flatten != hosts
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
.todo-avatar
|
.todo-avatar
|
||||||
= author_avatar(todo, size: 40)
|
= author_avatar(todo, size: 40)
|
||||||
|
|
||||||
.todo-item.todo-block.align-self-center
|
.todo-item.todo-block.align-self-center{ data: { qa_selector: "todo_item_container" } }
|
||||||
.todo-title
|
.todo-title
|
||||||
- if todo_author_display?(todo)
|
- if todo_author_display?(todo)
|
||||||
= todo_target_state_pill(todo)
|
= todo_target_state_pill(todo)
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
- else
|
- else
|
||||||
(removed)
|
(removed)
|
||||||
|
|
||||||
%span.title-item.action-name
|
%span.title-item.action-name{ data: { qa_selector: "todo_action_name_content" } }
|
||||||
= todo_action_name(todo)
|
= todo_action_name(todo)
|
||||||
|
|
||||||
%span.title-item.todo-label.todo-target-link
|
%span.title-item.todo-label.todo-target-link
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
- else
|
- else
|
||||||
= _("(removed)")
|
= _("(removed)")
|
||||||
|
|
||||||
%span.title-item.todo-target-title
|
%span.title-item.todo-target-title{ data: { qa_selector: "todo_target_title_content" } }
|
||||||
= todo_target_title(todo)
|
= todo_target_title(todo)
|
||||||
|
|
||||||
%span.title-item.todo-project.todo-label
|
%span.title-item.todo-project.todo-label
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
|
|
||||||
.todos-list-container.js-todos-all
|
.todos-list-container.js-todos-all
|
||||||
- if @todos.any?
|
- if @todos.any?
|
||||||
.js-todos-list-container
|
.js-todos-list-container{ data: { qa_selector: "todos_list_container" } }
|
||||||
.js-todos-options{ data: { per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages } }
|
.js-todos-options{ data: { per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages } }
|
||||||
.card.card-without-border.card-without-margin
|
.card.card-without-border.card-without-margin
|
||||||
%ul.content-list.todos-list
|
%ul.content-list.todos-list
|
||||||
|
|
|
@ -25,5 +25,5 @@
|
||||||
|
|
||||||
- if !member?
|
- if !member?
|
||||||
.actions
|
.actions
|
||||||
= link_to _("Accept invitation"), accept_invite_url(@token, new_user_invite: params[:new_user_invite]), method: :post, class: "btn gl-button btn-success"
|
= link_to _("Accept invitation"), accept_invite_url(@token), method: :post, class: "btn gl-button btn-success"
|
||||||
= link_to _("Decline"), decline_invite_url(@token), method: :post, class: "btn gl-button btn-danger gl-ml-3"
|
= link_to _("Decline"), decline_invite_url(@token), method: :post, class: "btn gl-button btn-danger gl-ml-3"
|
||||||
|
|
|
@ -34,13 +34,7 @@
|
||||||
|
|
||||||
= render_if_exists 'layouts/mailer/additional_text'
|
= render_if_exists 'layouts/mailer/additional_text'
|
||||||
|
|
||||||
%tr.footer
|
= yield :footer
|
||||||
%td
|
|
||||||
%img{ alt: "GitLab", height: "33", width: "90", src: image_url('mailers/gitlab_footer_logo.gif') }
|
|
||||||
%div
|
|
||||||
- manage_notifications_link = link_to(_("Manage all notifications"), profile_notifications_url, class: 'mng-notif-link')
|
|
||||||
- help_link = link_to(_("Help"), help_url, class: 'help-link')
|
|
||||||
= _("You're receiving this email because of your account on %{host}. %{manage_notifications_link} · %{help_link}").html_safe % { host: Gitlab.config.gitlab.host, manage_notifications_link: manage_notifications_link, help_link: help_link }
|
|
||||||
|
|
||||||
= yield :additional_footer
|
= yield :additional_footer
|
||||||
%tr
|
%tr
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
%html{ lang: "en" }
|
|
||||||
%head
|
|
||||||
%meta{ content: "text/html; charset=UTF-8", "http-equiv" => "Content-Type" }/
|
|
||||||
%meta{ content: "width=device-width, initial-scale=1", name: "viewport" }/
|
|
||||||
%meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/
|
|
||||||
%title= message.subject
|
|
||||||
|
|
||||||
-# Avoid premailer processing of client-specific styles (@media tag not supported)
|
|
||||||
-# We need to inline the contents here because mail clients (e.g. iOS Mail, Outlook)
|
|
||||||
-# do not support linked stylesheets.
|
|
||||||
%style{ type: 'text/css', 'data-premailer': 'ignore' }
|
|
||||||
= asset_to_string('mailer_client_specific.css').html_safe
|
|
||||||
|
|
||||||
= stylesheet_link_tag 'mailer.css'
|
|
||||||
%body
|
|
||||||
%table#body{ border: "0", cellpadding: "0", cellspacing: "0" }
|
|
||||||
%tbody
|
|
||||||
%tr.line
|
|
||||||
%td
|
|
||||||
%tr.header
|
|
||||||
%td
|
|
||||||
= html_header_message
|
|
||||||
= header_logo
|
|
||||||
%tr
|
|
||||||
%td
|
|
||||||
%table.wrapper{ border: "0", cellpadding: "0", cellspacing: "0" }
|
|
||||||
%tbody
|
|
||||||
%tr
|
|
||||||
%td.wrapper-cell{ style: "padding: 0" }
|
|
||||||
%table.content{ border: "0", cellpadding: "0", cellspacing: "0" }
|
|
||||||
%tbody
|
|
||||||
= yield
|
|
||||||
|
|
||||||
= render_if_exists 'layouts/mailer/additional_text'
|
|
||||||
|
|
||||||
%tr.footer
|
|
||||||
%td{ style: "padding: 24px 0" }
|
|
||||||
%img{ alt: "GitLab", height: "33", width: "90", src: image_url('mailers/gitlab_footer_logo.gif') }
|
|
||||||
%p{ style: "color: #949ba5; max-width: 640px; margin: 0 auto; text-align: left; font-size: 12px;" }
|
|
||||||
GitLab is a complete DevOps platform, delivered as a single application, fundamentally changing the way
|
|
||||||
%br
|
|
||||||
Development, Security, and Ops teams collaborate.
|
|
||||||
|
|
||||||
= yield :additional_footer
|
|
||||||
%tr
|
|
||||||
%td.footer-message
|
|
||||||
= html_footer_message
|
|
|
@ -1 +1,10 @@
|
||||||
|
= content_for :footer do
|
||||||
|
%tr.footer
|
||||||
|
%td
|
||||||
|
%img.footer-logo{ alt: "GitLab", src: image_url('mailers/gitlab_footer_logo.gif') }
|
||||||
|
%div
|
||||||
|
- manage_notifications_link = link_to(_("Manage all notifications"), profile_notifications_url, class: 'mng-notif-link')
|
||||||
|
- help_link = link_to(_("Help"), help_url, class: 'help-link')
|
||||||
|
= _("You're receiving this email because of your account on %{host}. %{manage_notifications_link} · %{help_link}").html_safe % { host: Gitlab.config.gitlab.host, manage_notifications_link: manage_notifications_link, help_link: help_link }
|
||||||
|
|
||||||
= render 'layouts/mailer'
|
= render 'layouts/mailer'
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
= content_for :footer do
|
||||||
|
%tr.footer
|
||||||
|
%td.gitlab-info
|
||||||
|
%img.footer-logo{ alt: "GitLab", src: image_url('mailers/gitlab_footer_logo.gif') }
|
||||||
|
%p.gitlab-info-text
|
||||||
|
= html_escape(_("GitLab is a complete DevOps platform, delivered as a single application, fundamentally changing the way%{br_tag}Development, Security, and Ops teams collaborate")) % { br_tag: '<br/>'.html_safe }
|
||||||
|
|
||||||
|
= render 'layouts/mailer'
|
|
@ -0,0 +1,9 @@
|
||||||
|
<%= text_header_message %>
|
||||||
|
|
||||||
|
<%= yield -%>
|
||||||
|
|
||||||
|
-- <%# signature marker %>
|
||||||
|
<%= _("GitLab is a complete DevOps platform, delivered as a single application, fundamentally changing the way Development, Security, and Ops teams collaborate") %>
|
||||||
|
<%= render_if_exists 'layouts/mailer/additional_text' %>
|
||||||
|
|
||||||
|
<%= text_footer_message %>
|
|
@ -1,16 +1,12 @@
|
||||||
|
- placeholders = { strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe, project_or_group_name: member_source.human_name, project_or_group: member_source.model_name.singular, br_tag: '<br/>'.html_safe, role: member.human_access.downcase }
|
||||||
%tr
|
%tr
|
||||||
%td.text-content
|
%td.text-content
|
||||||
|
%h2.invite-header
|
||||||
|
= s_('InviteEmail|You are invited!')
|
||||||
%p
|
%p
|
||||||
You have been invited
|
|
||||||
- if member.created_by
|
- if member.created_by
|
||||||
by
|
= html_escape(s_("InviteEmail|%{inviter} invited you to join the %{strong_start}%{project_or_group_name}%{strong_end}%{br_tag}%{project_or_group} as a %{role}")) % placeholders.merge({ inviter: (link_to member.created_by.name, user_url(member.created_by)).html_safe })
|
||||||
= link_to member.created_by.name, user_url(member.created_by)
|
- else
|
||||||
to join the
|
= html_escape(s_("InviteEmail|You are invited to join the %{strong_start}%{project_or_group_name}%{strong_end}%{br_tag}%{project_or_group} as a %{role}")) % placeholders
|
||||||
= link_to member_source.human_name, member_source.public? ? member_source.web_url : invite_url(@token), class: :highlight
|
%p.invite-actions
|
||||||
#{member_source.model_name.singular} as #{content_tag :span, member.human_access, class: :highlight}.
|
= link_to s_('InviteEmail|Join now'), invite_url(@token), class: 'invite-btn-join'
|
||||||
|
|
||||||
%p
|
|
||||||
= link_to 'Accept invitation', invite_url(@token, @invite_url_params)
|
|
||||||
or
|
|
||||||
= link_to 'decline', decline_invite_url(@token)
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
You have been invited <%= "by #{sanitize_name(member.created_by.name)} " if member.created_by %>to join the <%= member_source.human_name %> <%= member_source.model_name.singular %> as <%= member.human_access %>.
|
<% placeholders = { project_or_group_name: member_source.human_name, project_or_group: member_source.model_name.singular, role: member.human_access.downcase } %>
|
||||||
|
|
||||||
Accept invitation: <%= invite_url(@token, @invite_url_params) %>
|
<% if member.created_by %>
|
||||||
Decline invitation: <%= decline_invite_url(@token) %>
|
<%= s_('InviteEmail|%{inviter} invited you to join the %{project_or_group_name} %{project_or_group} as a %{role}') % placeholders.merge({ inviter: sanitize_name(member.created_by.name) }) %>
|
||||||
|
<% else %>
|
||||||
|
<%= s_('InviteEmail|You have been invited to join the %{project_or_group_name} %{project_or_group} as a %{role}') % placeholders %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= s_('InviteEmail|Join now') %>: <%= invite_url(@token) %>
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
%tr
|
|
||||||
%td.text-content
|
|
||||||
%h2.invite-header
|
|
||||||
= s_('InviteEmail|You are invited!')
|
|
||||||
%p
|
|
||||||
- if member.created_by
|
|
||||||
= html_escape(s_("InviteEmail|%{inviter} invited you")) % { inviter: (link_to member.created_by.name, user_url(member.created_by)).html_safe }
|
|
||||||
= html_escape(s_("InviteEmail|to join the %{strong_start}%{project_or_group_name}%{strong_end}")) % { strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe, project_or_group_name: member_source.human_name }
|
|
||||||
%br
|
|
||||||
= s_("InviteEmail|%{project_or_group} as a %{role}") % { project_or_group: member_source.model_name.singular, role: member.human_access.downcase }
|
|
||||||
%p.invite-actions
|
|
||||||
= link_to s_('InviteEmail|Join now'), invite_url(@token, @invite_url_params), class: 'invite-btn-join'
|
|
|
@ -1,10 +0,0 @@
|
||||||
<% project_and_role = s_('InviteEmail|to join the %{project_or_group_name} %{project_or_group} as a %{role}') \
|
|
||||||
% { project_or_group_name: member_source.human_name, project_or_group: member_source.model_name.singular, role: member.human_access.downcase } %>
|
|
||||||
|
|
||||||
<% if member.created_by %>
|
|
||||||
<%= s_('InviteEmail|%{inviter} invited you') % { inviter: sanitize_name(member.created_by.name) } %> <%= project_and_role %>
|
|
||||||
<% else %>
|
|
||||||
<%= s_('InviteEmail|You have been invited') %> <%= project_and_role %>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
Join now: <%= invite_url(@token, @invite_url_params) %>
|
|
|
@ -1,5 +0,0 @@
|
||||||
.clearfix
|
|
||||||
- groups.each do |group|
|
|
||||||
= link_to group, class: 'profile-groups-avatars inline', title: group.name do
|
|
||||||
.avatar-container.rect-avatar.s40
|
|
||||||
= group_icon(group, class: 'avatar group-avatar s40')
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fix undefined tooltip text flashing on clipboard icon
|
||||||
|
merge_request: 45482
|
||||||
|
author:
|
||||||
|
type: fixed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: New group and project invite mail design
|
||||||
|
merge_request: 44940
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Truncate over-long alert fields instead of return error response
|
||||||
|
merge_request: 45099
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Bump cluster applications CI template
|
||||||
|
merge_request: 45472
|
||||||
|
author:
|
||||||
|
type: other
|
|
@ -180,6 +180,7 @@ Rails.application.routes.draw do
|
||||||
get 'jwks' => 'doorkeeper/openid_connect/discovery#keys'
|
get 'jwks' => 'doorkeeper/openid_connect/discovery#keys'
|
||||||
|
|
||||||
draw :snippets
|
draw :snippets
|
||||||
|
draw :profile
|
||||||
|
|
||||||
# Product analytics collector
|
# Product analytics collector
|
||||||
match '/collector/i', to: ProductAnalytics::CollectorApp.new, via: :all
|
match '/collector/i', to: ProductAnalytics::CollectorApp.new, via: :all
|
||||||
|
@ -266,7 +267,6 @@ Rails.application.routes.draw do
|
||||||
draw :uploads
|
draw :uploads
|
||||||
draw :explore
|
draw :explore
|
||||||
draw :admin
|
draw :admin
|
||||||
draw :profile
|
|
||||||
draw :dashboard
|
draw :dashboard
|
||||||
draw :user
|
draw :user
|
||||||
draw :project
|
draw :project
|
||||||
|
@ -274,13 +274,6 @@ Rails.application.routes.draw do
|
||||||
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/210024
|
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/210024
|
||||||
scope as: 'deprecated' do
|
scope as: 'deprecated' do
|
||||||
draw :snippets
|
draw :snippets
|
||||||
end
|
|
||||||
|
|
||||||
# Serve profile routes under /-/ scope.
|
|
||||||
# To ensure an old unscoped routing is used for the UI we need to
|
|
||||||
# add prefix 'as' to the scope routing and place it below original routing.
|
|
||||||
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/210024
|
|
||||||
scope '-', as: :scoped do
|
|
||||||
draw :profile
|
draw :profile
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -15681,7 +15681,7 @@ type Query {
|
||||||
"""
|
"""
|
||||||
ID of the User
|
ID of the User
|
||||||
"""
|
"""
|
||||||
id: ID
|
id: UserID
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Username of the User
|
Username of the User
|
||||||
|
|
|
@ -45328,7 +45328,7 @@
|
||||||
"description": "ID of the User",
|
"description": "ID of the User",
|
||||||
"type": {
|
"type": {
|
||||||
"kind": "SCALAR",
|
"kind": "SCALAR",
|
||||||
"name": "ID",
|
"name": "UserID",
|
||||||
"ofType": null
|
"ofType": null
|
||||||
},
|
},
|
||||||
"defaultValue": null
|
"defaultValue": null
|
||||||
|
|
|
@ -135,7 +135,7 @@ Example response:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "Ruby",
|
"name": "Ruby",
|
||||||
"content": "# This file is a template, and might need editing before it works on your project.\n# Official language image. Look for the different tagged releases at:\n# https://hub.docker.com/r/library/ruby/tags/\nimage: \"ruby:2.5\"\n\n# Pick zero or more services to be used on all builds.\n# Only needed when using a docker container to run your tests in.\n# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service\nservices:\n - mysql:latest\n - redis:latest\n - postgres:latest\n\nvariables:\n POSTGRES_DB: database_name\n\n# Cache gems in between builds\ncache:\n paths:\n - vendor/ruby\n\n# This is a basic example for a gem or script which doesn't use\n# services such as redis or postgres\nbefore_script:\n - ruby -v # Print out ruby version for debugging\n # Uncomment next line if your rails app needs a JS runtime:\n # - apt-get update -q && apt-get install nodejs -yqq\n - bundle install -j $(nproc) --path vendor # Install dependencies into ./vendor/ruby\n\n# Optional - Delete if not using `rubocop`\nrubocop:\n script:\n - rubocop\n\nrspec:\n script:\n - rspec spec\n\nrails:\n variables:\n DATABASE_URL: \"postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB\"\n script:\n - rails db:migrate\n - rails db:seed\n - rails test\n\n# This deploy job uses a simple deploy flow to Heroku, other providers, e.g. AWS Elastic Beanstalk\n# are supported too: https://github.com/travis-ci/dpl\ndeploy:\n type: deploy\n environment: production\n script:\n - gem install dpl\n - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_PRODUCTION_KEY\n"
|
"content": "# This file is a template, and might need editing before it works on your project.\n# Official language image. Look for the different tagged releases at:\n# https://hub.docker.com/r/library/ruby/tags/\nimage: \"ruby:2.5\"\n\n# Pick zero or more services to be used on all builds.\n# Only needed when using a docker container to run your tests in.\n# Check out: http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service\nservices:\n - mysql:latest\n - redis:latest\n - postgres:latest\n\nvariables:\n POSTGRES_DB: database_name\n\n# Cache gems in between builds\ncache:\n paths:\n - vendor/ruby\n\n# This is a basic example for a gem or script which doesn't use\n# services such as redis or postgres\nbefore_script:\n - ruby -v # Print out ruby version for debugging\n # Uncomment next line if your rails app needs a JS runtime:\n # - apt-get update -q && apt-get install nodejs -yqq\n - bundle install -j $(nproc) --path vendor # Install dependencies into ./vendor/ruby\n\n# Optional - Delete if not using `rubocop`\nrubocop:\n script:\n - rubocop\n\nrspec:\n script:\n - rspec spec\n\nrails:\n variables:\n DATABASE_URL: \"postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB\"\n script:\n - rails db:migrate\n - rails db:seed\n - rails test\n\n# This deploy job uses a simple deploy flow to Heroku, other providers, e.g. AWS Elastic Beanstalk\n# are supported too: https://github.com/travis-ci/dpl\ndeploy:\n type: deploy\n environment: production\n script:\n - gem install dpl\n - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_PRODUCTION_KEY\n"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -205,7 +205,7 @@ spotbugs-sast:
|
||||||
FAIL_NEVER: 1
|
FAIL_NEVER: 1
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom rulesets
|
### Custom rulesets **(ULTIMATE)**
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235382) in GitLab 13.5.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235382) in GitLab 13.5.
|
||||||
|
|
||||||
|
|
|
@ -142,7 +142,7 @@ Secret Detection can be customized by defining available variables:
|
||||||
| `SECRET_DETECTION_EXCLUDED_PATHS` | "" | Exclude vulnerabilities from output based on the paths. This is a comma-separated list of patterns. Patterns can be globs, or file or folder paths (for example, `doc,spec` ). Parent directories also match patterns. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225273) in GitLab 13.3. |
|
| `SECRET_DETECTION_EXCLUDED_PATHS` | "" | Exclude vulnerabilities from output based on the paths. This is a comma-separated list of patterns. Patterns can be globs, or file or folder paths (for example, `doc,spec` ). Parent directories also match patterns. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225273) in GitLab 13.3. |
|
||||||
| `SECRET_DETECTION_HISTORIC_SCAN` | false | Flag to enable a historic Gitleaks scan. |
|
| `SECRET_DETECTION_HISTORIC_SCAN` | false | Flag to enable a historic Gitleaks scan. |
|
||||||
|
|
||||||
### Custom rulesets
|
### Custom rulesets **(ULTIMATE)**
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211387) in GitLab 13.5.
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211387) in GitLab 13.5.
|
||||||
|
|
||||||
|
|
|
@ -88,19 +88,19 @@ module Gitlab
|
||||||
# AlertManagement::Alert directly for read operations.
|
# AlertManagement::Alert directly for read operations.
|
||||||
def alert_params
|
def alert_params
|
||||||
{
|
{
|
||||||
description: description,
|
description: description&.truncate(::AlertManagement::Alert::DESCRIPTION_MAX_LENGTH),
|
||||||
ended_at: ends_at,
|
ended_at: ends_at,
|
||||||
environment: environment,
|
environment: environment,
|
||||||
fingerprint: gitlab_fingerprint,
|
fingerprint: gitlab_fingerprint,
|
||||||
hosts: Array(hosts),
|
hosts: truncate_hosts(Array(hosts).flatten),
|
||||||
monitoring_tool: monitoring_tool,
|
monitoring_tool: monitoring_tool&.truncate(::AlertManagement::Alert::TOOL_MAX_LENGTH),
|
||||||
payload: payload,
|
payload: payload,
|
||||||
project_id: project.id,
|
project_id: project.id,
|
||||||
prometheus_alert: gitlab_alert,
|
prometheus_alert: gitlab_alert,
|
||||||
service: service,
|
service: service&.truncate(::AlertManagement::Alert::SERVICE_MAX_LENGTH),
|
||||||
severity: severity,
|
severity: severity,
|
||||||
started_at: starts_at,
|
started_at: starts_at,
|
||||||
title: title
|
title: title&.truncate(::AlertManagement::Alert::TITLE_MAX_LENGTH)
|
||||||
}.transform_values(&:presence).compact
|
}.transform_values(&:presence).compact
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -135,6 +135,18 @@ module Gitlab
|
||||||
|
|
||||||
def plain_gitlab_fingerprint; end
|
def plain_gitlab_fingerprint; end
|
||||||
|
|
||||||
|
def truncate_hosts(hosts)
|
||||||
|
return hosts if hosts.join.length <= ::AlertManagement::Alert::HOSTS_MAX_LENGTH
|
||||||
|
|
||||||
|
hosts.inject([]) do |new_hosts, host|
|
||||||
|
remaining_length = ::AlertManagement::Alert::HOSTS_MAX_LENGTH - new_hosts.join.length
|
||||||
|
|
||||||
|
break new_hosts unless remaining_length > 0
|
||||||
|
|
||||||
|
new_hosts << host.to_s.truncate(remaining_length, omission: '')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def value_for_paths(paths)
|
def value_for_paths(paths)
|
||||||
target_path = paths.find { |path| payload&.dig(*path) }
|
target_path = paths.find { |path| payload&.dig(*path) }
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ image: "crystallang/crystal:latest"
|
||||||
|
|
||||||
# Pick zero or more services to be used on all builds.
|
# Pick zero or more services to be used on all builds.
|
||||||
# Only needed when using a docker container to run your tests in.
|
# Only needed when using a docker container to run your tests in.
|
||||||
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
|
# Check out: http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
||||||
# services:
|
# services:
|
||||||
# - mysql:latest
|
# - mysql:latest
|
||||||
# - redis:latest
|
# - redis:latest
|
||||||
|
|
|
@ -4,7 +4,7 @@ image: python:latest
|
||||||
|
|
||||||
# Pick zero or more services to be used on all builds.
|
# Pick zero or more services to be used on all builds.
|
||||||
# Only needed when using a docker container to run your tests in.
|
# Only needed when using a docker container to run your tests in.
|
||||||
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
|
# Check out: http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
||||||
services:
|
services:
|
||||||
- mysql:latest
|
- mysql:latest
|
||||||
- postgres:latest
|
- postgres:latest
|
||||||
|
@ -13,7 +13,7 @@ variables:
|
||||||
POSTGRES_DB: database_name
|
POSTGRES_DB: database_name
|
||||||
|
|
||||||
# This folder is cached between builds
|
# This folder is cached between builds
|
||||||
# http://docs.gitlab.com/ce/ci/yaml/README.html#cache
|
# http://docs.gitlab.com/ee/ci/yaml/README.html#cache
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
- ~/.cache/pip/
|
- ~/.cache/pip/
|
||||||
|
|
|
@ -2,7 +2,7 @@ image: elixir:latest
|
||||||
|
|
||||||
# Pick zero or more services to be used on all builds.
|
# Pick zero or more services to be used on all builds.
|
||||||
# Only needed when using a docker container to run your tests in.
|
# Only needed when using a docker container to run your tests in.
|
||||||
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
|
# Check out: http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
||||||
services:
|
services:
|
||||||
- mysql:latest
|
- mysql:latest
|
||||||
- redis:latest
|
- redis:latest
|
||||||
|
|
|
@ -4,7 +4,7 @@ image: php:latest
|
||||||
|
|
||||||
# Pick zero or more services to be used on all builds.
|
# Pick zero or more services to be used on all builds.
|
||||||
# Only needed when using a docker container to run your tests in.
|
# Only needed when using a docker container to run your tests in.
|
||||||
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
|
# Check out: http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
||||||
services:
|
services:
|
||||||
- mysql:latest
|
- mysql:latest
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ variables:
|
||||||
MYSQL_ROOT_PASSWORD: secret
|
MYSQL_ROOT_PASSWORD: secret
|
||||||
|
|
||||||
# This folder is cached between builds
|
# This folder is cached between builds
|
||||||
# http://docs.gitlab.com/ce/ci/yaml/README.html#cache
|
# http://docs.gitlab.com/ee/ci/yaml/README.html#cache
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
- vendor/
|
- vendor/
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
apply:
|
apply:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
image: "registry.gitlab.com/gitlab-org/cluster-integration/cluster-applications:v0.29.0"
|
image: "registry.gitlab.com/gitlab-org/cluster-integration/cluster-applications:v0.33.0"
|
||||||
environment:
|
environment:
|
||||||
name: production
|
name: production
|
||||||
variables:
|
variables:
|
||||||
|
|
|
@ -4,14 +4,14 @@ image: node:latest
|
||||||
|
|
||||||
# Pick zero or more services to be used on all builds.
|
# Pick zero or more services to be used on all builds.
|
||||||
# Only needed when using a docker container to run your tests in.
|
# Only needed when using a docker container to run your tests in.
|
||||||
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
|
# Check out: http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
||||||
services:
|
services:
|
||||||
- mysql:latest
|
- mysql:latest
|
||||||
- redis:latest
|
- redis:latest
|
||||||
- postgres:latest
|
- postgres:latest
|
||||||
|
|
||||||
# This folder is cached between builds
|
# This folder is cached between builds
|
||||||
# http://docs.gitlab.com/ce/ci/yaml/README.html#cache
|
# http://docs.gitlab.com/ee/ci/yaml/README.html#cache
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
- node_modules/
|
- node_modules/
|
||||||
|
|
|
@ -19,7 +19,7 @@ before_script:
|
||||||
- php composer.phar install
|
- php composer.phar install
|
||||||
|
|
||||||
# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
||||||
# See http://docs.gitlab.com/ce/ci/services/README.html for examples.
|
# See http://docs.gitlab.com/ee/ci/services/README.html for examples.
|
||||||
services:
|
services:
|
||||||
- mysql:5.7
|
- mysql:5.7
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
image: node:latest
|
image: node:latest
|
||||||
|
|
||||||
# This folder is cached between builds
|
# This folder is cached between builds
|
||||||
# http://docs.gitlab.com/ce/ci/yaml/README.html#cache
|
# http://docs.gitlab.com/ee/ci/yaml/README.html#cache
|
||||||
cache:
|
cache:
|
||||||
paths:
|
paths:
|
||||||
- node_modules/
|
- node_modules/
|
||||||
|
|
|
@ -4,7 +4,7 @@ image: "ruby:2.5"
|
||||||
|
|
||||||
# Pick zero or more services to be used on all builds.
|
# Pick zero or more services to be used on all builds.
|
||||||
# Only needed when using a docker container to run your tests in.
|
# Only needed when using a docker container to run your tests in.
|
||||||
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
|
# Check out: http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
||||||
services:
|
services:
|
||||||
- mysql:latest
|
- mysql:latest
|
||||||
- redis:latest
|
- redis:latest
|
||||||
|
|
|
@ -4,7 +4,7 @@ image: "rust:latest"
|
||||||
|
|
||||||
# Optional: Pick zero or more services to be used on all builds.
|
# Optional: Pick zero or more services to be used on all builds.
|
||||||
# Only needed when using a docker container to run your tests in.
|
# Only needed when using a docker container to run your tests in.
|
||||||
# Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service
|
# Check out: http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service
|
||||||
# services:
|
# services:
|
||||||
# - mysql:latest
|
# - mysql:latest
|
||||||
# - redis:latest
|
# - redis:latest
|
||||||
|
|
|
@ -59,7 +59,7 @@ module Gitlab
|
||||||
strong_memoize(:finalize_histogram) do
|
strong_memoize(:finalize_histogram) do
|
||||||
name = :gitlab_ci_trace_finalize_duration_seconds
|
name = :gitlab_ci_trace_finalize_duration_seconds
|
||||||
comment = 'Duration of build trace chunks migration to object storage'
|
comment = 'Duration of build trace chunks migration to object storage'
|
||||||
buckets = [0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 10.0, 30.0, 60.0, 300.0]
|
buckets = [0.1, 0.5, 1.0, 2.0, 3.0, 5.0, 7.0, 10.0, 20.0, 30.0, 60.0, 300.0]
|
||||||
labels = {}
|
labels = {}
|
||||||
|
|
||||||
::Gitlab::Metrics.histogram(name, comment, labels, buckets)
|
::Gitlab::Metrics.histogram(name, comment, labels, buckets)
|
||||||
|
|
|
@ -14,10 +14,10 @@ module Gitlab
|
||||||
def restore
|
def restore
|
||||||
return true unless File.exist?(path_to_bundle)
|
return true unless File.exist?(path_to_bundle)
|
||||||
|
|
||||||
|
ensure_repository_does_not_exist!
|
||||||
|
|
||||||
repository.create_from_bundle(path_to_bundle)
|
repository.create_from_bundle(path_to_bundle)
|
||||||
rescue => e
|
rescue => e
|
||||||
Repositories::DestroyService.new(repository).execute
|
|
||||||
|
|
||||||
shared.error(e)
|
shared.error(e)
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -25,6 +25,16 @@ module Gitlab
|
||||||
private
|
private
|
||||||
|
|
||||||
attr_accessor :repository, :path_to_bundle, :shared
|
attr_accessor :repository, :path_to_bundle, :shared
|
||||||
|
|
||||||
|
def ensure_repository_does_not_exist!
|
||||||
|
if repository.exists?
|
||||||
|
shared.logger.info(
|
||||||
|
message: %Q{Deleting existing "#{repository.path}" to re-import it.}
|
||||||
|
)
|
||||||
|
|
||||||
|
Repositories::DestroyService.new(repository).execute
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12303,6 +12303,12 @@ msgstr ""
|
||||||
msgid "GitLab for Slack"
|
msgid "GitLab for Slack"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "GitLab is a complete DevOps platform, delivered as a single application, fundamentally changing the way Development, Security, and Ops teams collaborate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "GitLab is a complete DevOps platform, delivered as a single application, fundamentally changing the way%{br_tag}Development, Security, and Ops teams collaborate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "GitLab is a single application for the entire software development lifecycle. From project planning and source code management to CI/CD, monitoring, and security."
|
msgid "GitLab is a single application for the entire software development lifecycle. From project planning and source code management to CI/CD, monitoring, and security."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -14389,25 +14395,22 @@ msgstr ""
|
||||||
msgid "Invite teammates (optional)"
|
msgid "Invite teammates (optional)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "InviteEmail|%{inviter} invited you"
|
msgid "InviteEmail|%{inviter} invited you to join the %{project_or_group_name} %{project_or_group} as a %{role}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "InviteEmail|%{project_or_group} as a %{role}"
|
msgid "InviteEmail|%{inviter} invited you to join the %{strong_start}%{project_or_group_name}%{strong_end}%{br_tag}%{project_or_group} as a %{role}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "InviteEmail|Join now"
|
msgid "InviteEmail|Join now"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "InviteEmail|You are invited to join the %{strong_start}%{project_or_group_name}%{strong_end}%{br_tag}%{project_or_group} as a %{role}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "InviteEmail|You are invited!"
|
msgid "InviteEmail|You are invited!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "InviteEmail|You have been invited"
|
msgid "InviteEmail|You have been invited to join the %{project_or_group_name} %{project_or_group} as a %{role}"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "InviteEmail|to join the %{project_or_group_name} %{project_or_group} as a %{role}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "InviteEmail|to join the %{strong_start}%{project_or_group_name}%{strong_end}"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "InviteMembersBanner|Collaborate with your team"
|
msgid "InviteMembersBanner|Collaborate with your team"
|
||||||
|
@ -16208,6 +16211,12 @@ msgstr ""
|
||||||
msgid "Member since %{date}"
|
msgid "Member since %{date}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "MemberInviteEmail|%{member_name} invited you to join GitLab"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "MemberInviteEmail|Invitation to join the %{project_or_group} %{project_or_group_name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Members"
|
msgid "Members"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
"@babel/preset-env": "^7.10.1",
|
"@babel/preset-env": "^7.10.1",
|
||||||
"@gitlab/at.js": "1.5.5",
|
"@gitlab/at.js": "1.5.5",
|
||||||
"@gitlab/svgs": "1.171.0",
|
"@gitlab/svgs": "1.171.0",
|
||||||
"@gitlab/ui": "21.33.0",
|
"@gitlab/ui": "21.34.1",
|
||||||
"@gitlab/visual-review-tools": "1.6.1",
|
"@gitlab/visual-review-tools": "1.6.1",
|
||||||
"@rails/actioncable": "^6.0.3-3",
|
"@rails/actioncable": "^6.0.3-3",
|
||||||
"@rails/ujs": "^6.0.3-2",
|
"@rails/ujs": "^6.0.3-2",
|
||||||
|
|
2
qa/qa.rb
2
qa/qa.rb
|
@ -18,6 +18,7 @@ module QA
|
||||||
autoload :Project, 'qa/flow/project'
|
autoload :Project, 'qa/flow/project'
|
||||||
autoload :Saml, 'qa/flow/saml'
|
autoload :Saml, 'qa/flow/saml'
|
||||||
autoload :User, 'qa/flow/user'
|
autoload :User, 'qa/flow/user'
|
||||||
|
autoload :MergeRequest, 'qa/flow/merge_request'
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -191,6 +192,7 @@ module QA
|
||||||
autoload :Projects, 'qa/page/dashboard/projects'
|
autoload :Projects, 'qa/page/dashboard/projects'
|
||||||
autoload :Groups, 'qa/page/dashboard/groups'
|
autoload :Groups, 'qa/page/dashboard/groups'
|
||||||
autoload :Welcome, 'qa/page/dashboard/welcome'
|
autoload :Welcome, 'qa/page/dashboard/welcome'
|
||||||
|
autoload :Todos, 'qa/page/dashboard/todos'
|
||||||
|
|
||||||
module Snippet
|
module Snippet
|
||||||
autoload :New, 'qa/page/dashboard/snippet/new'
|
autoload :New, 'qa/page/dashboard/snippet/new'
|
||||||
|
|
|
@ -23,6 +23,7 @@ module QA
|
||||||
end
|
end
|
||||||
|
|
||||||
def sign_in(as: nil, address: :gitlab, skip_page_validation: false)
|
def sign_in(as: nil, address: :gitlab, skip_page_validation: false)
|
||||||
|
Page::Main::Menu.perform(&:sign_out) if Page::Main::Menu.perform(&:signed_in?)
|
||||||
Runtime::Browser.visit(address, Page::Main::Login)
|
Runtime::Browser.visit(address, Page::Main::Login)
|
||||||
Page::Main::Login.perform { |login| login.sign_in_using_credentials(user: as, skip_page_validation: skip_page_validation) }
|
Page::Main::Login.perform { |login| login.sign_in_using_credentials(user: as, skip_page_validation: skip_page_validation) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module QA
|
||||||
|
module Flow
|
||||||
|
module MergeRequest
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def enable_merge_trains
|
||||||
|
Page::Project::Menu.perform(&:go_to_general_settings)
|
||||||
|
Page::Project::Settings::Main.perform(&:expand_merge_requests_settings)
|
||||||
|
Page::Project::Settings::MergeRequest.perform(&:enable_merge_train)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,33 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module QA
|
||||||
|
module Page
|
||||||
|
module Dashboard
|
||||||
|
class Todos < Page::Base
|
||||||
|
include Page::Component::Snippet
|
||||||
|
|
||||||
|
view 'app/views/dashboard/todos/index.html.haml' do
|
||||||
|
element :todos_list_container, required: true
|
||||||
|
end
|
||||||
|
|
||||||
|
view 'app/views/dashboard/todos/_todo.html.haml' do
|
||||||
|
element :todo_item_container
|
||||||
|
element :todo_action_name_content
|
||||||
|
element :todo_target_title_content
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_todo_list?
|
||||||
|
has_element? :todo_item_container
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_latest_todo_item_with_content?(action, title)
|
||||||
|
within_element(:todos_list_container) do
|
||||||
|
within_element_by_index(:todo_item_container, 0) do
|
||||||
|
has_element?(:todo_action_name_content, text: action) && has_element?(:todo_target_title_content, text: title)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,13 +9,6 @@ RSpec.describe InvitesController, :snowplow do
|
||||||
let(:project_members) { member.source.users }
|
let(:project_members) { member.source.users }
|
||||||
let(:md5_member_global_id) { Digest::MD5.hexdigest(member.to_global_id.to_s) }
|
let(:md5_member_global_id) { Digest::MD5.hexdigest(member.to_global_id.to_s) }
|
||||||
let(:params) { { id: raw_invite_token } }
|
let(:params) { { id: raw_invite_token } }
|
||||||
let(:snowplow_event) do
|
|
||||||
{
|
|
||||||
category: 'Growth::Acquisition::Experiment::InviteEmail',
|
|
||||||
label: md5_member_global_id,
|
|
||||||
property: group_type
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'invalid token' do
|
shared_examples 'invalid token' do
|
||||||
context 'when invite token is not valid' do
|
context 'when invite token is not valid' do
|
||||||
|
@ -94,38 +87,6 @@ RSpec.describe InvitesController, :snowplow do
|
||||||
expect(flash[:notice]).to be_nil
|
expect(flash[:notice]).to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when new_user_invite is not set' do
|
|
||||||
it 'does not track the user as experiment group' do
|
|
||||||
request
|
|
||||||
|
|
||||||
expect_no_snowplow_event
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when new_user_invite is experiment' do
|
|
||||||
let(:params) { { id: raw_invite_token, new_user_invite: 'experiment' } }
|
|
||||||
let(:group_type) { 'experiment_group' }
|
|
||||||
|
|
||||||
it 'tracks the user as experiment group' do
|
|
||||||
request
|
|
||||||
|
|
||||||
expect_snowplow_event(**snowplow_event.merge(action: 'opened'))
|
|
||||||
expect_snowplow_event(**snowplow_event.merge(action: 'accepted'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when new_user_invite is control' do
|
|
||||||
let(:params) { { id: raw_invite_token, new_user_invite: 'control' } }
|
|
||||||
let(:group_type) { 'control_group' }
|
|
||||||
|
|
||||||
it 'tracks the user as control group' do
|
|
||||||
request
|
|
||||||
|
|
||||||
expect_snowplow_event(**snowplow_event.merge(action: 'opened'))
|
|
||||||
expect_snowplow_event(**snowplow_event.merge(action: 'accepted'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like "tracks the 'accepted' event for the invitation reminders experiment"
|
it_behaves_like "tracks the 'accepted' event for the invitation reminders experiment"
|
||||||
it_behaves_like 'invalid token'
|
it_behaves_like 'invalid token'
|
||||||
end
|
end
|
||||||
|
@ -158,36 +119,6 @@ RSpec.describe InvitesController, :snowplow do
|
||||||
|
|
||||||
subject(:request) { post :accept, params: params }
|
subject(:request) { post :accept, params: params }
|
||||||
|
|
||||||
context 'when new_user_invite is not set' do
|
|
||||||
it 'does not track an event' do
|
|
||||||
request
|
|
||||||
|
|
||||||
expect_no_snowplow_event
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when new_user_invite is experiment' do
|
|
||||||
let(:params) { { id: raw_invite_token, new_user_invite: 'experiment' } }
|
|
||||||
let(:group_type) { 'experiment_group' }
|
|
||||||
|
|
||||||
it 'tracks the user as experiment group' do
|
|
||||||
request
|
|
||||||
|
|
||||||
expect_snowplow_event(**snowplow_event.merge(action: 'accepted'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when new_user_invite is control' do
|
|
||||||
let(:params) { { id: raw_invite_token, new_user_invite: 'control' } }
|
|
||||||
let(:group_type) { 'control_group' }
|
|
||||||
|
|
||||||
it 'tracks the user as control group' do
|
|
||||||
request
|
|
||||||
|
|
||||||
expect_snowplow_event(**snowplow_event.merge(action: 'accepted'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like "tracks the 'accepted' event for the invitation reminders experiment"
|
it_behaves_like "tracks the 'accepted' event for the invitation reminders experiment"
|
||||||
it_behaves_like 'invalid token'
|
it_behaves_like 'invalid token'
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,11 +7,11 @@ FactoryBot.define do
|
||||||
threshold { 1 }
|
threshold { 1 }
|
||||||
|
|
||||||
environment do |alert|
|
environment do |alert|
|
||||||
build(:environment, project: alert.project)
|
association(:environment, project: alert.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
prometheus_metric do |alert|
|
prometheus_metric do |alert|
|
||||||
build(:prometheus_metric, project: alert.project)
|
association(:prometheus_metric, project: alert.project)
|
||||||
end
|
end
|
||||||
|
|
||||||
trait :with_runbook_url do
|
trait :with_runbook_url do
|
||||||
|
|
|
@ -33,7 +33,7 @@ RSpec.describe 'Profile > Account', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows the user to disconnect when there is an existing identity' do
|
it 'allows the user to disconnect when there is an existing identity' do
|
||||||
expect(page).to have_link('Disconnect Twitter', href: '/profile/account/unlink?provider=twitter')
|
expect(page).to have_link('Disconnect Twitter', href: '/-/profile/account/unlink?provider=twitter')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows active for a provider that is not allowed to unlink' do
|
it 'shows active for a provider that is not allowed to unlink' do
|
||||||
|
|
|
@ -12,8 +12,6 @@ RSpec.describe 'Project navbar' do
|
||||||
let_it_be(:project) { create(:project, :repository) }
|
let_it_be(:project) { create(:project, :repository) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(project_iterations: false)
|
|
||||||
|
|
||||||
insert_package_nav(_('Operations'))
|
insert_package_nav(_('Operations'))
|
||||||
|
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
|
|
|
@ -120,14 +120,107 @@ RSpec.describe Gitlab::AlertManagement::Payload::Base do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#alert_params' do
|
describe '#alert_params' do
|
||||||
before do
|
|
||||||
allow(parsed_payload).to receive(:title).and_return('title')
|
|
||||||
allow(parsed_payload).to receive(:description).and_return('description')
|
|
||||||
end
|
|
||||||
|
|
||||||
subject { parsed_payload.alert_params }
|
subject { parsed_payload.alert_params }
|
||||||
|
|
||||||
it { is_expected.to eq({ description: 'description', project_id: project.id, title: 'title' }) }
|
context 'with every key' do
|
||||||
|
let_it_be(:raw_payload) { { 'key' => 'value' } }
|
||||||
|
let_it_be(:stubs) do
|
||||||
|
{
|
||||||
|
description: 'description',
|
||||||
|
ends_at: Time.current,
|
||||||
|
environment: create(:environment, project: project),
|
||||||
|
gitlab_fingerprint: 'gitlab_fingerprint',
|
||||||
|
hosts: 'hosts',
|
||||||
|
monitoring_tool: 'monitoring_tool',
|
||||||
|
gitlab_alert: create(:prometheus_alert, project: project),
|
||||||
|
service: 'service',
|
||||||
|
severity: 'critical',
|
||||||
|
starts_at: Time.current,
|
||||||
|
title: 'title'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:expected_result) do
|
||||||
|
{
|
||||||
|
description: stubs[:description],
|
||||||
|
ended_at: stubs[:ends_at],
|
||||||
|
environment: stubs[:environment],
|
||||||
|
fingerprint: stubs[:gitlab_fingerprint],
|
||||||
|
hosts: [stubs[:hosts]],
|
||||||
|
monitoring_tool: stubs[:monitoring_tool],
|
||||||
|
payload: raw_payload,
|
||||||
|
project_id: project.id,
|
||||||
|
prometheus_alert: stubs[:gitlab_alert],
|
||||||
|
service: stubs[:service],
|
||||||
|
severity: stubs[:severity],
|
||||||
|
started_at: stubs[:starts_at],
|
||||||
|
title: stubs[:title]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(parsed_payload).to receive_messages(stubs)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq(expected_result) }
|
||||||
|
|
||||||
|
it 'can generate a valid new alert' do
|
||||||
|
expect(::AlertManagement::Alert.new(subject.except(:ended_at))).to be_valid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with too-long strings' do
|
||||||
|
let_it_be(:stubs) do
|
||||||
|
{
|
||||||
|
description: 'a' * (::AlertManagement::Alert::DESCRIPTION_MAX_LENGTH + 1),
|
||||||
|
hosts: 'b' * (::AlertManagement::Alert::HOSTS_MAX_LENGTH + 1),
|
||||||
|
monitoring_tool: 'c' * (::AlertManagement::Alert::TOOL_MAX_LENGTH + 1),
|
||||||
|
service: 'd' * (::AlertManagement::Alert::SERVICE_MAX_LENGTH + 1),
|
||||||
|
title: 'e' * (::AlertManagement::Alert::TITLE_MAX_LENGTH + 1)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(parsed_payload).to receive_messages(stubs)
|
||||||
|
end
|
||||||
|
|
||||||
|
it do
|
||||||
|
is_expected.to eq({
|
||||||
|
description: stubs[:description].truncate(AlertManagement::Alert::DESCRIPTION_MAX_LENGTH),
|
||||||
|
hosts: ['b' * ::AlertManagement::Alert::HOSTS_MAX_LENGTH],
|
||||||
|
monitoring_tool: stubs[:monitoring_tool].truncate(::AlertManagement::Alert::TOOL_MAX_LENGTH),
|
||||||
|
service: stubs[:service].truncate(::AlertManagement::Alert::SERVICE_MAX_LENGTH),
|
||||||
|
project_id: project.id,
|
||||||
|
title: stubs[:title].truncate(::AlertManagement::Alert::TITLE_MAX_LENGTH)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with too-long hosts array' do
|
||||||
|
let(:hosts) { %w(abc def ghij) }
|
||||||
|
let(:shortened_hosts) { %w(abc def ghi) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
stub_const('::AlertManagement::Alert::HOSTS_MAX_LENGTH', 9)
|
||||||
|
allow(parsed_payload).to receive(:hosts).and_return(hosts)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to eq(hosts: shortened_hosts, project_id: project.id) }
|
||||||
|
|
||||||
|
context 'with host cut off between elements' do
|
||||||
|
let(:hosts) { %w(abcde fghij) }
|
||||||
|
let(:shortened_hosts) { %w(abcde fghi) }
|
||||||
|
|
||||||
|
it { is_expected.to eq({ hosts: shortened_hosts, project_id: project.id }) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with nested hosts' do
|
||||||
|
let(:hosts) { ['abc', ['de', 'f'], 'g', 'hij'] } # rubocop:disable Style/WordArray
|
||||||
|
let(:shortened_hosts) { %w(abc de f g hi) }
|
||||||
|
|
||||||
|
it { is_expected.to eq({ hosts: shortened_hosts, project_id: project.id }) }
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#gitlab_fingerprint' do
|
describe '#gitlab_fingerprint' do
|
||||||
|
|
|
@ -36,21 +36,20 @@ RSpec.describe Gitlab::ImportExport::RepoRestorer do
|
||||||
expect(subject.restore).to be_truthy
|
expect(subject.restore).to be_truthy
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the repository creation fails' do
|
context 'when the repository already exists' do
|
||||||
before do
|
it 'deletes the existing repository before importing' do
|
||||||
allow_next_instance_of(Repositories::DestroyService) do |instance|
|
allow(project.repository).to receive(:exists?).and_return(true)
|
||||||
|
allow(project.repository).to receive(:path).and_return('repository_path')
|
||||||
|
|
||||||
|
expect_next_instance_of(Repositories::DestroyService) do |instance|
|
||||||
expect(instance).to receive(:execute).and_call_original
|
expect(instance).to receive(:execute).and_call_original
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
it 'logs the error' do
|
expect(shared.logger).to receive(:info).with(
|
||||||
allow(project.repository)
|
message: 'Deleting existing "repository_path" to re-import it.'
|
||||||
.to receive(:create_from_bundle)
|
)
|
||||||
.and_raise('9:CreateRepositoryFromBundle: target directory is non-empty')
|
|
||||||
|
|
||||||
expect(shared).to receive(:error).and_call_original
|
expect(subject.restore).to be_truthy
|
||||||
|
|
||||||
expect(subject.restore).to be_falsey
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -887,97 +887,31 @@ RSpec.describe Notify do
|
||||||
|
|
||||||
subject { described_class.member_invited_email('project', project_member.id, project_member.invite_token) }
|
subject { described_class.member_invited_email('project', project_member.id, project_member.invite_token) }
|
||||||
|
|
||||||
context 'when invite_email_experiment is disabled' do
|
it_behaves_like 'an email sent from GitLab'
|
||||||
before do
|
it_behaves_like 'it should not have Gmail Actions links'
|
||||||
stub_feature_flags(invite_email_experiment: false)
|
it_behaves_like "a user cannot unsubscribe through footer link"
|
||||||
end
|
it_behaves_like 'appearance header and footer enabled'
|
||||||
|
it_behaves_like 'appearance header and footer not enabled'
|
||||||
|
it_behaves_like 'does not render a manage notifications link'
|
||||||
|
|
||||||
it_behaves_like 'an email sent from GitLab'
|
context 'when there is an inviter' do
|
||||||
it_behaves_like 'it should not have Gmail Actions links'
|
it 'contains all the useful information' do
|
||||||
it_behaves_like "a user cannot unsubscribe through footer link"
|
is_expected.to have_subject "#{inviter.name} invited you to join GitLab"
|
||||||
it_behaves_like 'appearance header and footer enabled'
|
is_expected.to have_body_text project.full_name
|
||||||
it_behaves_like 'appearance header and footer not enabled'
|
is_expected.to have_body_text project_member.human_access.downcase
|
||||||
|
is_expected.to have_body_text project_member.invite_token
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there is no inviter' do
|
||||||
|
let(:inviter) { nil }
|
||||||
|
|
||||||
it 'contains all the useful information' do
|
it 'contains all the useful information' do
|
||||||
is_expected.to have_subject "Invitation to join the #{project.full_name} project"
|
is_expected.to have_subject "Invitation to join the #{project.full_name} project"
|
||||||
is_expected.to have_body_text project.full_name
|
is_expected.to have_body_text project.full_name
|
||||||
is_expected.to have_body_text project_member.human_access
|
is_expected.to have_body_text project_member.human_access.downcase
|
||||||
is_expected.to have_body_text project_member.invite_token
|
is_expected.to have_body_text project_member.invite_token
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when member is invited via an email address' do
|
|
||||||
it 'does add a param to the invite link' do
|
|
||||||
is_expected.to have_body_text 'new_user_invite=control'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'tracks an event' do
|
|
||||||
expect(Gitlab::Tracking).to receive(:event).with(
|
|
||||||
'Growth::Acquisition::Experiment::InviteEmail',
|
|
||||||
'sent',
|
|
||||||
property: 'control_group'
|
|
||||||
)
|
|
||||||
|
|
||||||
subject.deliver_now
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when member is already a user' do
|
|
||||||
let(:project_member) { invite_to_project(project, inviter: maintainer, user: create(:user)) }
|
|
||||||
|
|
||||||
it 'does not add a param to the invite link' do
|
|
||||||
is_expected.not_to have_body_text 'new_user_invite'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not track an event' do
|
|
||||||
expect(Gitlab::Tracking).not_to receive(:event)
|
|
||||||
|
|
||||||
subject.deliver_now
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when invite_email_experiment is enabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(invite_email_experiment: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'an email sent from GitLab'
|
|
||||||
it_behaves_like 'it should not have Gmail Actions links'
|
|
||||||
it_behaves_like "a user cannot unsubscribe through footer link"
|
|
||||||
|
|
||||||
context 'when there is no inviter' do
|
|
||||||
let(:inviter) { nil }
|
|
||||||
|
|
||||||
it 'contains all the useful information' do
|
|
||||||
is_expected.to have_subject "Invitation to join the #{project.full_name} project"
|
|
||||||
is_expected.to have_body_text project.full_name
|
|
||||||
is_expected.to have_body_text project_member.human_access.downcase
|
|
||||||
is_expected.to have_body_text project_member.invite_token
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when there is an inviter' do
|
|
||||||
it 'contains all the useful information' do
|
|
||||||
is_expected.to have_subject "#{inviter.name} invited you to join GitLab"
|
|
||||||
is_expected.to have_body_text project.full_name
|
|
||||||
is_expected.to have_body_text project_member.human_access.downcase
|
|
||||||
is_expected.to have_body_text project_member.invite_token
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'adds a param to the invite link' do
|
|
||||||
is_expected.to have_body_text 'new_user_invite=experiment'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'tracks an event' do
|
|
||||||
expect(Gitlab::Tracking).to receive(:event).with(
|
|
||||||
'Growth::Acquisition::Experiment::InviteEmail',
|
|
||||||
'sent',
|
|
||||||
property: 'experiment_group'
|
|
||||||
)
|
|
||||||
|
|
||||||
subject.deliver_now
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1547,96 +1481,32 @@ RSpec.describe Notify do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when invite_email_experiment is disabled' do
|
it_behaves_like 'an email sent from GitLab'
|
||||||
before do
|
it_behaves_like 'it should not have Gmail Actions links'
|
||||||
stub_feature_flags(invite_email_experiment: false)
|
it_behaves_like "a user cannot unsubscribe through footer link"
|
||||||
end
|
it_behaves_like 'appearance header and footer enabled'
|
||||||
|
it_behaves_like 'appearance header and footer not enabled'
|
||||||
|
it_behaves_like 'it requires a group'
|
||||||
|
it_behaves_like 'does not render a manage notifications link'
|
||||||
|
|
||||||
it_behaves_like 'an email sent from GitLab'
|
context 'when there is an inviter' do
|
||||||
it_behaves_like 'it should not have Gmail Actions links'
|
it 'contains all the useful information' do
|
||||||
it_behaves_like "a user cannot unsubscribe through footer link"
|
is_expected.to have_subject "#{group_member.created_by.name} invited you to join GitLab"
|
||||||
it_behaves_like 'appearance header and footer enabled'
|
is_expected.to have_body_text group.name
|
||||||
it_behaves_like 'appearance header and footer not enabled'
|
is_expected.to have_body_text group_member.human_access.downcase
|
||||||
it_behaves_like 'it requires a group'
|
is_expected.to have_body_text group_member.invite_token
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there is no inviter' do
|
||||||
|
let(:inviter) { nil }
|
||||||
|
|
||||||
it 'contains all the useful information' do
|
it 'contains all the useful information' do
|
||||||
is_expected.to have_subject "Invitation to join the #{group.name} group"
|
is_expected.to have_subject "Invitation to join the #{group.name} group"
|
||||||
is_expected.to have_body_text group.name
|
is_expected.to have_body_text group.name
|
||||||
is_expected.to have_body_text group.web_url
|
is_expected.to have_body_text group_member.human_access.downcase
|
||||||
is_expected.to have_body_text group_member.human_access
|
|
||||||
is_expected.to have_body_text group_member.invite_token
|
is_expected.to have_body_text group_member.invite_token
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when member is invited via an email address' do
|
|
||||||
it 'does add a param to the invite link' do
|
|
||||||
is_expected.to have_body_text 'new_user_invite=control'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'tracks an event' do
|
|
||||||
expect(Gitlab::Tracking).to receive(:event).with(
|
|
||||||
'Growth::Acquisition::Experiment::InviteEmail',
|
|
||||||
'sent',
|
|
||||||
property: 'control_group'
|
|
||||||
)
|
|
||||||
|
|
||||||
subject.deliver_now
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when member is already a user' do
|
|
||||||
let(:group_member) { invite_to_group(group, inviter: owner, user: create(:user)) }
|
|
||||||
|
|
||||||
it 'does not add a param to the invite link' do
|
|
||||||
is_expected.not_to have_body_text 'new_user_invite'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not track an event' do
|
|
||||||
expect(Gitlab::Tracking).not_to receive(:event)
|
|
||||||
|
|
||||||
subject.deliver_now
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when invite_email_experiment is enabled' do
|
|
||||||
it_behaves_like 'an email sent from GitLab'
|
|
||||||
it_behaves_like 'it should not have Gmail Actions links'
|
|
||||||
it_behaves_like "a user cannot unsubscribe through footer link"
|
|
||||||
it_behaves_like 'it requires a group'
|
|
||||||
|
|
||||||
context 'when there is no inviter' do
|
|
||||||
let(:inviter) { nil }
|
|
||||||
|
|
||||||
it 'contains all the useful information' do
|
|
||||||
is_expected.to have_subject "Invitation to join the #{group.name} group"
|
|
||||||
is_expected.to have_body_text group.name
|
|
||||||
is_expected.to have_body_text group_member.human_access.downcase
|
|
||||||
is_expected.to have_body_text group_member.invite_token
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when there is an inviter' do
|
|
||||||
it 'contains all the useful information' do
|
|
||||||
is_expected.to have_subject "#{group_member.created_by.name} invited you to join GitLab"
|
|
||||||
is_expected.to have_body_text group.name
|
|
||||||
is_expected.to have_body_text group_member.human_access.downcase
|
|
||||||
is_expected.to have_body_text group_member.invite_token
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does add a param to the invite link' do
|
|
||||||
is_expected.to have_body_text 'new_user_invite'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'tracks an event' do
|
|
||||||
expect(Gitlab::Tracking).to receive(:event).with(
|
|
||||||
'Growth::Acquisition::Experiment::InviteEmail',
|
|
||||||
'sent',
|
|
||||||
property: 'experiment_group'
|
|
||||||
)
|
|
||||||
|
|
||||||
subject.deliver_now
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,12 @@ RSpec.describe AlertManagement::Alert do
|
||||||
|
|
||||||
it { is_expected.to be_valid }
|
it { is_expected.to be_valid }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'nested array' do
|
||||||
|
let(:hosts) { ['111.111.111.111', ['111.111.111.111']] }
|
||||||
|
|
||||||
|
it { is_expected.not_to be_valid }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -117,15 +117,19 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when alert cannot be created' do
|
context 'when alert cannot be created' do
|
||||||
|
let(:errors) { double(messages: { hosts: ['hosts array is over 255 chars'] })}
|
||||||
|
|
||||||
before do
|
before do
|
||||||
payload['annotations']['title'] = 'description' * 50
|
allow(service).to receive(:alert).and_call_original
|
||||||
|
allow(service).to receive_message_chain(:alert, :save).and_return(false)
|
||||||
|
allow(service).to receive_message_chain(:alert, :errors).and_return(errors)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'writes a warning to the log' do
|
it 'writes a warning to the log' do
|
||||||
expect(Gitlab::AppLogger).to receive(:warn).with(
|
expect(Gitlab::AppLogger).to receive(:warn).with(
|
||||||
message: 'Unable to create AlertManagement::Alert',
|
message: 'Unable to create AlertManagement::Alert',
|
||||||
project_id: project.id,
|
project_id: project.id,
|
||||||
alert_errors: { title: ["is too long (maximum is 200 characters)"] }
|
alert_errors: { hosts: ['hosts array is over 255 chars'] }
|
||||||
)
|
)
|
||||||
|
|
||||||
execute
|
execute
|
||||||
|
|
|
@ -273,3 +273,12 @@ RSpec.shared_examples 'no email is sent' do
|
||||||
expect(subject.message).to be_a_kind_of(ActionMailer::Base::NullMail)
|
expect(subject.message).to be_a_kind_of(ActionMailer::Base::NullMail)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
RSpec.shared_examples 'does not render a manage notifications link' do
|
||||||
|
it do
|
||||||
|
aggregate_failures do
|
||||||
|
expect(subject).not_to have_body_text("Manage all notifications")
|
||||||
|
expect(subject).not_to have_body_text(profile_notifications_url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -866,10 +866,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.171.0.tgz#abc3092bf804f0898301626130e0f3231834924a"
|
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.171.0.tgz#abc3092bf804f0898301626130e0f3231834924a"
|
||||||
integrity sha512-TPfdqIxQDda+0CQHhb9XdF50lmqDmADu6yT8R4oZi6BoUtWLdiHbyFt+RnVU6t7EmjIKicNAii7Ga+f2ljCfUA==
|
integrity sha512-TPfdqIxQDda+0CQHhb9XdF50lmqDmADu6yT8R4oZi6BoUtWLdiHbyFt+RnVU6t7EmjIKicNAii7Ga+f2ljCfUA==
|
||||||
|
|
||||||
"@gitlab/ui@21.33.0":
|
"@gitlab/ui@21.34.1":
|
||||||
version "21.33.0"
|
version "21.34.1"
|
||||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.33.0.tgz#83dd7e4d65557f7b82ce1f9d7d6e7a1c54cb3dbc"
|
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.34.1.tgz#50c21bb751e88c12a5c7491d3c980a239a0dbbc3"
|
||||||
integrity sha512-UBLTz5A1z1Usxw2yfjFNuQ1Ccgx7dYD3M+lDfewDYlU48s0PY7Hm/VXsd543XIuIf+4GEJ1b43kptEZI1wtrbQ==
|
integrity sha512-S60A7vZcc9ZcXJrT6lCOUbzbPo+Yzx3HOaSIq335iee9DED5WMVCCYvbjm7f7Rn3CoTmDAVC4akNVTzhiuyQYQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/standalone" "^7.0.0"
|
"@babel/standalone" "^7.0.0"
|
||||||
"@gitlab/vue-toasted" "^1.3.0"
|
"@gitlab/vue-toasted" "^1.3.0"
|
||||||
|
|
Loading…
Reference in New Issue