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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-10-20 15:08:54 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-10-20 15:08:54 +0300
commitbe2142bf5e03af8ba9bb96e4bf0b4781184ae01b (patch)
treeb40aab281b29a56bd398757564d271c8b3aa8662
parentbc12365ae0254332f97299138f019bea3ff12351 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitattributes1
-rw-r--r--.haml-lint_todo.yml1
-rw-r--r--.rubocop_todo.yml2
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/behaviors/copy_to_clipboard.js2
-rw-r--r--app/assets/javascripts/pages/users/index.js5
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue5
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue7
-rw-r--r--app/assets/stylesheets/mailer.scss17
-rw-r--r--app/assets/stylesheets/page_bundles/pipeline.scss24
-rw-r--r--app/assets/stylesheets/page_bundles/pipelines.scss4
-rw-r--r--app/assets/stylesheets/pages/builds.scss17
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss58
-rw-r--r--app/controllers/invites_controller.rb16
-rw-r--r--app/graphql/resolvers/user_resolver.rb2
-rw-r--r--app/mailers/emails/members.rb51
-rw-r--r--app/models/alert_management/alert.rb17
-rw-r--r--app/views/dashboard/todos/_todo.html.haml6
-rw-r--r--app/views/dashboard/todos/index.html.haml2
-rw-r--r--app/views/invites/show.html.haml2
-rw-r--r--app/views/layouts/_mailer.html.haml8
-rw-r--r--app/views/layouts/experiment_mailer.html.haml48
-rw-r--r--app/views/layouts/mailer.html.haml9
-rw-r--r--app/views/layouts/unknown_user_mailer.html.haml8
-rw-r--r--app/views/layouts/unknown_user_mailer.text.erb9
-rw-r--r--app/views/notify/member_invited_email.html.haml20
-rw-r--r--app/views/notify/member_invited_email.text.erb11
-rw-r--r--app/views/notify/member_invited_email_experiment.html.haml12
-rw-r--r--app/views/notify/member_invited_email_experiment.text.erb10
-rw-r--r--app/views/users/_groups.html.haml5
-rw-r--r--changelogs/unreleased/268144-mr-widget-copy-branch-name-button-tooltip-occasionally-shows-undef.yml5
-rw-r--r--changelogs/unreleased/nicolasdular-new-invite-mail-design.yml5
-rw-r--r--changelogs/unreleased/sy-truncate-alert-fields.yml5
-rw-r--r--changelogs/unreleased/update-cluster-applications-34.yml5
-rw-r--r--config/routes.rb9
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql2
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json2
-rw-r--r--doc/api/templates/gitlab_ci_ymls.md2
-rw-r--r--doc/user/application_security/sast/index.md2
-rw-r--r--doc/user/application_security/secret_detection/index.md2
-rw-r--r--lib/gitlab/alert_management/payload/base.rb22
-rw-r--r--lib/gitlab/ci/templates/Crystal.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Django.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Elixir.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Laravel.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/PHP.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Ruby.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Rust.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/trace/metrics.rb2
-rw-r--r--lib/gitlab/import_export/repo_restorer.rb14
-rw-r--r--locale/gitlab.pot25
-rw-r--r--package.json2
-rw-r--r--qa/qa.rb2
-rw-r--r--qa/qa/flow/login.rb1
-rw-r--r--qa/qa/flow/merge_request.rb15
-rw-r--r--qa/qa/page/dashboard/todos.rb33
-rw-r--r--spec/controllers/invites_controller_spec.rb69
-rw-r--r--spec/factories/prometheus_alert.rb4
-rw-r--r--spec/features/profiles/account_spec.rb2
-rw-r--r--spec/features/projects/navbar_spec.rb2
-rw-r--r--spec/lib/gitlab/alert_management/payload/base_spec.rb103
-rw-r--r--spec/lib/gitlab/import_export/repo_restorer_spec.rb21
-rw-r--r--spec/mailers/notify_spec.rb196
-rw-r--r--spec/models/alert_management/alert_spec.rb6
-rw-r--r--spec/services/alert_management/process_prometheus_alert_service_spec.rb8
-rw-r--r--spec/support/shared_examples/mailers/notify_shared_examples.rb9
-rw-r--r--yarn.lock8
70 files changed, 473 insertions, 517 deletions
diff --git a/.gitattributes b/.gitattributes
index ec47d175c55..8d21784ed11 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,4 @@
VERSION merge=ours
Dangerfile gitlab-language=ruby
*.pdf filter=lfs diff=lfs merge=lfs -text
+*.rb diff=ruby
diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml
index 57c0e014a03..78b5f3fb88b 100644
--- a/.haml-lint_todo.yml
+++ b/.haml-lint_todo.yml
@@ -119,7 +119,6 @@ linters:
- 'app/views/invites/show.html.haml'
- 'app/views/jira_connect/subscriptions/index.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/_new_dropdown.haml'
- 'app/views/layouts/jira_connect.html.haml'
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 21599a74a56..2c103a9998c 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -1298,7 +1298,6 @@ Graphql/IDType:
- 'app/graphql/resolvers/error_tracking/sentry_error_stack_trace_resolver.rb'
- 'app/graphql/resolvers/snippets_resolver.rb'
- 'app/graphql/resolvers/user_merge_requests_resolver.rb'
- - 'app/graphql/resolvers/user_resolver.rb'
# Offense count: 86
# Cop supports --auto-correct.
@@ -1328,7 +1327,6 @@ FactoryBot/InlineAssociation:
- 'spec/factories/notes.rb'
- 'spec/factories/packages.rb'
- 'spec/factories/packages/package_file.rb'
- - 'spec/factories/prometheus_alert.rb'
- 'spec/factories/resource_label_events.rb'
- 'spec/factories/resource_milestone_event.rb'
- 'spec/factories/resource_state_event.rb'
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 7805ebc228e..6ebe641f7d7 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-9bb23d81e20244cb4f9635295ae4615e8ff12f8c
+60aaf7cdd17b4efdf4dab23eb83aa86fe596a55b
diff --git a/app/assets/javascripts/behaviors/copy_to_clipboard.js b/app/assets/javascripts/behaviors/copy_to_clipboard.js
index a492b95d1d9..430a8c38387 100644
--- a/app/assets/javascripts/behaviors/copy_to_clipboard.js
+++ b/app/assets/javascripts/behaviors/copy_to_clipboard.js
@@ -10,7 +10,7 @@ function showTooltip(target, title) {
setTimeout(() => {
target.setAttribute('title', originalTitle);
fixTitle(target);
- }, 300);
+ }, 100);
};
target.setAttribute('title', title);
diff --git a/app/assets/javascripts/pages/users/index.js b/app/assets/javascripts/pages/users/index.js
index cfc6dc61f9f..8adbc2a8168 100644
--- a/app/assets/javascripts/pages/users/index.js
+++ b/app/assets/javascripts/pages/users/index.js
@@ -4,11 +4,6 @@ import UserCallout from '~/user_callout';
import UserTabs from './user_tabs';
function initUserProfile(action) {
- // place profile avatars to top
- $('.profile-groups-avatars').tooltip({
- placement: 'top',
- });
-
// eslint-disable-next-line no-new
new UserTabs({ parentEl: '.user-profile', action });
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
index 33a20d41bde..b9f268629fb 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
@@ -56,6 +56,9 @@ export default {
// Note: Realtime is only available on issues right now, future support for MR wil be built later.
return this.glFeatures.realTimeIssueSidebar && this.issuableType === 'issue';
},
+ relativeUrlRoot() {
+ return gon.relative_url_root ?? '';
+ },
},
created() {
this.removeAssignee = this.store.removeAssignee.bind(this.store);
@@ -119,7 +122,7 @@ export default {
/>
<assignees
v-if="!store.isFetching.assignees"
- :root-path="store.rootPath"
+ :root-path="relativeUrlRoot"
:users="store.assignees"
:editable="store.editable"
:issuable-type="issuableType"
diff --git a/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue b/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue
index 5d8a2e6fa65..aee94a55134 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/sidebar_reviewers.vue
@@ -50,6 +50,11 @@ export default {
loading: false,
};
},
+ computed: {
+ relativeUrlRoot() {
+ return gon.relative_url_root ?? '';
+ },
+ },
created() {
this.removeReviewer = this.store.removeReviewer.bind(this.store);
this.addReviewer = this.store.addReviewer.bind(this.store);
@@ -97,7 +102,7 @@ export default {
/>
<reviewers
v-if="!store.isFetching.reviewers"
- :root-path="store.rootPath"
+ :root-path="relativeUrlRoot"
:users="store.reviewers"
:editable="store.editable"
:issuable-type="issuableType"
diff --git a/app/assets/stylesheets/mailer.scss b/app/assets/stylesheets/mailer.scss
index b2050c0e73f..27c6ef20269 100644
--- a/app/assets/stylesheets/mailer.scss
+++ b/app/assets/stylesheets/mailer.scss
@@ -143,4 +143,21 @@ tr.footer td {
color: $mailer-link-color;
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;
+ }
}
diff --git a/app/assets/stylesheets/page_bundles/pipeline.scss b/app/assets/stylesheets/page_bundles/pipeline.scss
index 8e7be629481..2ba0feb5126 100644
--- a/app/assets/stylesheets/page_bundles/pipeline.scss
+++ b/app/assets/stylesheets/page_bundles/pipeline.scss
@@ -482,3 +482,27 @@
@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;
+}
diff --git a/app/assets/stylesheets/page_bundles/pipelines.scss b/app/assets/stylesheets/page_bundles/pipelines.scss
index 6ff07017d2e..e0e56893afc 100644
--- a/app/assets/stylesheets/page_bundles/pipelines.scss
+++ b/app/assets/stylesheets/page_bundles/pipelines.scss
@@ -5,6 +5,10 @@
* Pipelines Bundle: Pipeline lists and Mini Pipelines
*/
+.pipelines-container .top-area .nav-controls > .btn:last-child {
+ float: none;
+}
+
// Pipelines list
// Should affect pipelines table components rendered by:
// - app/assets/javascripts/commit/pipelines/pipelines_bundle.js
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index d7b4db3840e..a086ea1d6e4 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -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;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 2df43b861b2..06911cbab0f 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -1,11 +1,17 @@
-@include media-breakpoint-down(md) {
- .content-list {
- &.builds-content-list {
- width: 100%;
- overflow: auto;
- }
- }
-}
+/**
+ * !! NOTE: Do not add more code in this file:
+ *
+ * https://gitlab.com/gitlab-org/gitlab/-/issues/267602
+ *
+ * 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 {
.avatar {
@@ -80,39 +86,3 @@
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;
-}
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index c7b8486d1c9..26fc1c11f6d 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -15,13 +15,11 @@ class InvitesController < ApplicationController
feature_category :authentication_and_authorization
def show
- track_new_user_invite_experiment('opened')
accept if skip_invitation_prompt?
end
def accept
if member.accept_invite!(current_user)
- track_new_user_invite_experiment('accepted')
track_invitation_reminders_experiment('accepted')
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] }
@@ -110,25 +108,13 @@ class InvitesController < ApplicationController
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)
return unless Gitlab::Experimentation.enabled?(:invitation_reminders)
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::Experimentation.experiment(experiment_key).tracking_category,
+ Gitlab::Experimentation.experiment(:invitation_reminders).tracking_category,
action,
property: property,
label: Digest::MD5.hexdigest(member.to_global_id.to_s)
diff --git a/app/graphql/resolvers/user_resolver.rb b/app/graphql/resolvers/user_resolver.rb
index a34cecba491..06c1f0cb42d 100644
--- a/app/graphql/resolvers/user_resolver.rb
+++ b/app/graphql/resolvers/user_resolver.rb
@@ -6,7 +6,7 @@ module Resolvers
type Types::UserType, null: true
- argument :id, GraphQL::ID_TYPE,
+ argument :id, Types::GlobalIDType[User],
required: false,
description: 'ID of the User'
diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb
index 57e4c7df440..0b5a8dfdc24 100644
--- a/app/mailers/emails/members.rb
+++ b/app/mailers/emails/members.rb
@@ -51,34 +51,20 @@ module Emails
return unless member_exists?
- subject_line = subject("Invitation to join the #{member_source.human_name} #{member_source.model_name.singular}")
-
- if member.invite_to_unknown_user? && Feature.enabled?(:invite_email_experiment)
- subject_line = subject("#{member.created_by.name} invited you to join GitLab") if member.created_by
- @invite_url_params = { new_user_invite: 'experiment' }
-
- 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')
+ subject_line =
+ if member.created_by
+ subject(s_("MemberInviteEmail|%{member_name} invited you to join GitLab") % { member_name: member.created_by.name })
+ else
+ 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 })
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::Experimentation.experiment(:invitation_reminders).tracking_category,
'sent',
@@ -105,7 +91,7 @@ module Emails
subject_line = subjects[reminder_index] % { inviter: member.created_by.name }
member_email_with_layout(
- layout: 'experiment_mailer',
+ layout: 'unknown_user_mailer',
to: member.invite_email,
subject: subject(subject_line)
)
@@ -162,15 +148,10 @@ module Emails
@member_source_type.classify.constantize
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|
- if template
- format.html { render template, layout: layout }
- format.text { render template, layout: layout }
- else
- format.html { render layout: layout }
- format.text { render layout: layout }
- end
+ format.html { render layout: layout }
+ format.text { render layout: layout }
end
end
end
diff --git a/app/models/alert_management/alert.rb b/app/models/alert_management/alert.rb
index 71d09830d56..61cc15a522e 100644
--- a/app/models/alert_management/alert.rb
+++ b/app/models/alert_management/alert.rb
@@ -38,12 +38,16 @@ module AlertManagement
sha_attribute :fingerprint
+ TITLE_MAX_LENGTH = 200
+ DESCRIPTION_MAX_LENGTH = 1_000
+ SERVICE_MAX_LENGTH = 100
+ TOOL_MAX_LENGTH = 100
HOSTS_MAX_LENGTH = 255
- validates :title, length: { maximum: 200 }, presence: true
- validates :description, length: { maximum: 1_000 }
- validates :service, length: { maximum: 100 }
- validates :monitoring_tool, length: { maximum: 100 }
+ validates :title, length: { maximum: TITLE_MAX_LENGTH }, presence: true
+ validates :description, length: { maximum: DESCRIPTION_MAX_LENGTH }
+ validates :service, length: { maximum: SERVICE_MAX_LENGTH }
+ validates :monitoring_tool, length: { maximum: TOOL_MAX_LENGTH }
validates :project, presence: true
validates :events, presence: true
validates :severity, presence: true
@@ -54,7 +58,7 @@ module AlertManagement
conditions: -> { not_resolved },
message: -> (object, data) { _('Cannot have multiple unresolved alerts') }
}, unless: :resolved?
- validate :hosts_length
+ validate :hosts_format
enum severity: {
critical: 0,
@@ -251,10 +255,11 @@ module AlertManagement
Gitlab::DataBuilder::Alert.build(self)
end
- def hosts_length
+ def hosts_format
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 cannot be nested") if hosts.flatten != hosts
end
end
end
diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml
index 0cf5ef93e96..6fb387ecca3 100644
--- a/app/views/dashboard/todos/_todo.html.haml
+++ b/app/views/dashboard/todos/_todo.html.haml
@@ -2,7 +2,7 @@
.todo-avatar
= 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
- if todo_author_display?(todo)
= todo_target_state_pill(todo)
@@ -13,7 +13,7 @@
- else
(removed)
- %span.title-item.action-name
+ %span.title-item.action-name{ data: { qa_selector: "todo_action_name_content" } }
= todo_action_name(todo)
%span.title-item.todo-label.todo-target-link
@@ -22,7 +22,7 @@
- else
= _("(removed)")
- %span.title-item.todo-target-title
+ %span.title-item.todo-target-title{ data: { qa_selector: "todo_target_title_content" } }
= todo_target_title(todo)
%span.title-item.todo-project.todo-label
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 44d968ae26d..56506370ee0 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -83,7 +83,7 @@
.todos-list-container.js-todos-all
- 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 } }
.card.card-without-border.card-without-margin
%ul.content-list.todos-list
diff --git a/app/views/invites/show.html.haml b/app/views/invites/show.html.haml
index 37143799132..1492fea7fb2 100644
--- a/app/views/invites/show.html.haml
+++ b/app/views/invites/show.html.haml
@@ -25,5 +25,5 @@
- if !member?
.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"
diff --git a/app/views/layouts/_mailer.html.haml b/app/views/layouts/_mailer.html.haml
index 24b8138078d..74d05be7f95 100644
--- a/app/views/layouts/_mailer.html.haml
+++ b/app/views/layouts/_mailer.html.haml
@@ -34,13 +34,7 @@
= render_if_exists 'layouts/mailer/additional_text'
- %tr.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} &middot; %{help_link}").html_safe % { host: Gitlab.config.gitlab.host, manage_notifications_link: manage_notifications_link, help_link: help_link }
+ = yield :footer
= yield :additional_footer
%tr
diff --git a/app/views/layouts/experiment_mailer.html.haml b/app/views/layouts/experiment_mailer.html.haml
deleted file mode 100644
index 5a342c400d6..00000000000
--- a/app/views/layouts/experiment_mailer.html.haml
+++ /dev/null
@@ -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
diff --git a/app/views/layouts/mailer.html.haml b/app/views/layouts/mailer.html.haml
index 28dcbce7183..c2eb6b68024 100644
--- a/app/views/layouts/mailer.html.haml
+++ b/app/views/layouts/mailer.html.haml
@@ -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} &middot; %{help_link}").html_safe % { host: Gitlab.config.gitlab.host, manage_notifications_link: manage_notifications_link, help_link: help_link }
+
= render 'layouts/mailer'
diff --git a/app/views/layouts/unknown_user_mailer.html.haml b/app/views/layouts/unknown_user_mailer.html.haml
new file mode 100644
index 00000000000..2eb7b400604
--- /dev/null
+++ b/app/views/layouts/unknown_user_mailer.html.haml
@@ -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'
diff --git a/app/views/layouts/unknown_user_mailer.text.erb b/app/views/layouts/unknown_user_mailer.text.erb
new file mode 100644
index 00000000000..f3d8f13b7bf
--- /dev/null
+++ b/app/views/layouts/unknown_user_mailer.text.erb
@@ -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 %>
diff --git a/app/views/notify/member_invited_email.html.haml b/app/views/notify/member_invited_email.html.haml
index 4fcd2936d25..5ff1e2393c9 100644
--- a/app/views/notify/member_invited_email.html.haml
+++ b/app/views/notify/member_invited_email.html.haml
@@ -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
%td.text-content
+ %h2.invite-header
+ = s_('InviteEmail|You are invited!')
%p
- You have been invited
- if member.created_by
- by
- = link_to member.created_by.name, user_url(member.created_by)
- to join the
- = link_to member_source.human_name, member_source.public? ? member_source.web_url : invite_url(@token), class: :highlight
- #{member_source.model_name.singular} as #{content_tag :span, member.human_access, class: :highlight}.
-
- %p
- = link_to 'Accept invitation', invite_url(@token, @invite_url_params)
- or
- = link_to 'decline', decline_invite_url(@token)
-
+ = 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 })
+ - else
+ = 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
+ %p.invite-actions
+ = link_to s_('InviteEmail|Join now'), invite_url(@token), class: 'invite-btn-join'
diff --git a/app/views/notify/member_invited_email.text.erb b/app/views/notify/member_invited_email.text.erb
index e6e6a685f92..e58dfc10810 100644
--- a/app/views/notify/member_invited_email.text.erb
+++ b/app/views/notify/member_invited_email.text.erb
@@ -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) %>
-Decline invitation: <%= decline_invite_url(@token) %>
+<% if member.created_by %>
+<%= 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) %>
diff --git a/app/views/notify/member_invited_email_experiment.html.haml b/app/views/notify/member_invited_email_experiment.html.haml
deleted file mode 100644
index 5cfb6acee05..00000000000
--- a/app/views/notify/member_invited_email_experiment.html.haml
+++ /dev/null
@@ -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'
diff --git a/app/views/notify/member_invited_email_experiment.text.erb b/app/views/notify/member_invited_email_experiment.text.erb
deleted file mode 100644
index 6843cea4df7..00000000000
--- a/app/views/notify/member_invited_email_experiment.text.erb
+++ /dev/null
@@ -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) %>
diff --git a/app/views/users/_groups.html.haml b/app/views/users/_groups.html.haml
deleted file mode 100644
index 6d7a52c7688..00000000000
--- a/app/views/users/_groups.html.haml
+++ /dev/null
@@ -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')
diff --git a/changelogs/unreleased/268144-mr-widget-copy-branch-name-button-tooltip-occasionally-shows-undef.yml b/changelogs/unreleased/268144-mr-widget-copy-branch-name-button-tooltip-occasionally-shows-undef.yml
new file mode 100644
index 00000000000..412c62c7ce4
--- /dev/null
+++ b/changelogs/unreleased/268144-mr-widget-copy-branch-name-button-tooltip-occasionally-shows-undef.yml
@@ -0,0 +1,5 @@
+---
+title: Fix undefined tooltip text flashing on clipboard icon
+merge_request: 45482
+author:
+type: fixed
diff --git a/changelogs/unreleased/nicolasdular-new-invite-mail-design.yml b/changelogs/unreleased/nicolasdular-new-invite-mail-design.yml
new file mode 100644
index 00000000000..d4d8b4fbabd
--- /dev/null
+++ b/changelogs/unreleased/nicolasdular-new-invite-mail-design.yml
@@ -0,0 +1,5 @@
+---
+title: New group and project invite mail design
+merge_request: 44940
+author:
+type: changed
diff --git a/changelogs/unreleased/sy-truncate-alert-fields.yml b/changelogs/unreleased/sy-truncate-alert-fields.yml
new file mode 100644
index 00000000000..e929cde65f9
--- /dev/null
+++ b/changelogs/unreleased/sy-truncate-alert-fields.yml
@@ -0,0 +1,5 @@
+---
+title: Truncate over-long alert fields instead of return error response
+merge_request: 45099
+author:
+type: changed
diff --git a/changelogs/unreleased/update-cluster-applications-34.yml b/changelogs/unreleased/update-cluster-applications-34.yml
new file mode 100644
index 00000000000..5a7a17c2fae
--- /dev/null
+++ b/changelogs/unreleased/update-cluster-applications-34.yml
@@ -0,0 +1,5 @@
+---
+title: Bump cluster applications CI template
+merge_request: 45472
+author:
+type: other
diff --git a/config/routes.rb b/config/routes.rb
index 87d32e3d89a..ef718c7618a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -180,6 +180,7 @@ Rails.application.routes.draw do
get 'jwks' => 'doorkeeper/openid_connect/discovery#keys'
draw :snippets
+ draw :profile
# Product analytics collector
match '/collector/i', to: ProductAnalytics::CollectorApp.new, via: :all
@@ -266,7 +267,6 @@ Rails.application.routes.draw do
draw :uploads
draw :explore
draw :admin
- draw :profile
draw :dashboard
draw :user
draw :project
@@ -274,13 +274,6 @@ Rails.application.routes.draw do
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/210024
scope as: 'deprecated' do
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
end
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index a44f8f70311..57bd91bc75b 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -15681,7 +15681,7 @@ type Query {
"""
ID of the User
"""
- id: ID
+ id: UserID
"""
Username of the User
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 6914ba29c57..74aad595bcb 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -45328,7 +45328,7 @@
"description": "ID of the User",
"type": {
"kind": "SCALAR",
- "name": "ID",
+ "name": "UserID",
"ofType": null
},
"defaultValue": null
diff --git a/doc/api/templates/gitlab_ci_ymls.md b/doc/api/templates/gitlab_ci_ymls.md
index dfe22fc453e..45bc0f55095 100644
--- a/doc/api/templates/gitlab_ci_ymls.md
+++ b/doc/api/templates/gitlab_ci_ymls.md
@@ -135,7 +135,7 @@ Example response:
```json
{
"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"
}
```
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 9e4d4112ae8..cd90da7adb1 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -205,7 +205,7 @@ spotbugs-sast:
FAIL_NEVER: 1
```
-### Custom rulesets
+### Custom rulesets **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235382) in GitLab 13.5.
diff --git a/doc/user/application_security/secret_detection/index.md b/doc/user/application_security/secret_detection/index.md
index bb10e9d7315..988c9297480 100644
--- a/doc/user/application_security/secret_detection/index.md
+++ b/doc/user/application_security/secret_detection/index.md
@@ -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_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.
diff --git a/lib/gitlab/alert_management/payload/base.rb b/lib/gitlab/alert_management/payload/base.rb
index 74e47e5226e..0fd593a3780 100644
--- a/lib/gitlab/alert_management/payload/base.rb
+++ b/lib/gitlab/alert_management/payload/base.rb
@@ -88,19 +88,19 @@ module Gitlab
# AlertManagement::Alert directly for read operations.
def alert_params
{
- description: description,
+ description: description&.truncate(::AlertManagement::Alert::DESCRIPTION_MAX_LENGTH),
ended_at: ends_at,
environment: environment,
fingerprint: gitlab_fingerprint,
- hosts: Array(hosts),
- monitoring_tool: monitoring_tool,
+ hosts: truncate_hosts(Array(hosts).flatten),
+ monitoring_tool: monitoring_tool&.truncate(::AlertManagement::Alert::TOOL_MAX_LENGTH),
payload: payload,
project_id: project.id,
prometheus_alert: gitlab_alert,
- service: service,
+ service: service&.truncate(::AlertManagement::Alert::SERVICE_MAX_LENGTH),
severity: severity,
started_at: starts_at,
- title: title
+ title: title&.truncate(::AlertManagement::Alert::TITLE_MAX_LENGTH)
}.transform_values(&:presence).compact
end
@@ -135,6 +135,18 @@ module Gitlab
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)
target_path = paths.find { |path| payload&.dig(*path) }
diff --git a/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml b/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml
index e9301a2638d..538f96c4084 100644
--- a/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml
@@ -4,7 +4,7 @@ image: "crystallang/crystal:latest"
# Pick zero or more services to be used on all builds.
# 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:
# - mysql:latest
# - redis:latest
diff --git a/lib/gitlab/ci/templates/Django.gitlab-ci.yml b/lib/gitlab/ci/templates/Django.gitlab-ci.yml
index d35fcb0f807..c657c7e8eb1 100644
--- a/lib/gitlab/ci/templates/Django.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Django.gitlab-ci.yml
@@ -4,7 +4,7 @@ image: python:latest
# Pick zero or more services to be used on all builds.
# 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:
- mysql:latest
- postgres:latest
@@ -13,7 +13,7 @@ variables:
POSTGRES_DB: database_name
# 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:
paths:
- ~/.cache/pip/
diff --git a/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml b/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml
index 4d4c6a64cd5..7271526ab1b 100644
--- a/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml
@@ -2,7 +2,7 @@ image: elixir:latest
# Pick zero or more services to be used on all builds.
# 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:
- mysql:latest
- redis:latest
diff --git a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
index 9bde04dff19..5d2c8024524 100644
--- a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
@@ -4,7 +4,7 @@ image: php:latest
# Pick zero or more services to be used on all builds.
# 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:
- mysql:latest
@@ -13,7 +13,7 @@ variables:
MYSQL_ROOT_PASSWORD: secret
# 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:
paths:
- vendor/
diff --git a/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml b/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml
index 7050b41e045..a9638f564f3 100644
--- a/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml
@@ -1,6 +1,6 @@
apply:
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:
name: production
variables:
diff --git a/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml b/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml
index b87178141a1..92379ded77c 100644
--- a/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml
@@ -4,14 +4,14 @@ image: node:latest
# Pick zero or more services to be used on all builds.
# 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:
- mysql:latest
- redis:latest
- postgres:latest
# 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:
paths:
- node_modules/
diff --git a/lib/gitlab/ci/templates/PHP.gitlab-ci.yml b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml
index 25ea20e454f..84e8223e69b 100644
--- a/lib/gitlab/ci/templates/PHP.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml
@@ -19,7 +19,7 @@ before_script:
- 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
-# 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:
- mysql:5.7
diff --git a/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml
index a683561a455..3a6eac63892 100644
--- a/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml
@@ -1,7 +1,7 @@
image: node:latest
# 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:
paths:
- node_modules/
diff --git a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
index b3cad8b858a..275364afae4 100644
--- a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
@@ -4,7 +4,7 @@ image: "ruby:2.5"
# Pick zero or more services to be used on all builds.
# 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:
- mysql:latest
- redis:latest
diff --git a/lib/gitlab/ci/templates/Rust.gitlab-ci.yml b/lib/gitlab/ci/templates/Rust.gitlab-ci.yml
index f35470367cc..94117a79d1c 100644
--- a/lib/gitlab/ci/templates/Rust.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Rust.gitlab-ci.yml
@@ -4,7 +4,7 @@ image: "rust:latest"
# Optional: Pick zero or more services to be used on all builds.
# 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:
# - mysql:latest
# - redis:latest
diff --git a/lib/gitlab/ci/trace/metrics.rb b/lib/gitlab/ci/trace/metrics.rb
index cdc474d06dc..097436d84ea 100644
--- a/lib/gitlab/ci/trace/metrics.rb
+++ b/lib/gitlab/ci/trace/metrics.rb
@@ -59,7 +59,7 @@ module Gitlab
strong_memoize(:finalize_histogram) do
name = :gitlab_ci_trace_finalize_duration_seconds
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 = {}
::Gitlab::Metrics.histogram(name, comment, labels, buckets)
diff --git a/lib/gitlab/import_export/repo_restorer.rb b/lib/gitlab/import_export/repo_restorer.rb
index 9e10e7aea13..f808e30bd6e 100644
--- a/lib/gitlab/import_export/repo_restorer.rb
+++ b/lib/gitlab/import_export/repo_restorer.rb
@@ -14,10 +14,10 @@ module Gitlab
def restore
return true unless File.exist?(path_to_bundle)
+ ensure_repository_does_not_exist!
+
repository.create_from_bundle(path_to_bundle)
rescue => e
- Repositories::DestroyService.new(repository).execute
-
shared.error(e)
false
end
@@ -25,6 +25,16 @@ module Gitlab
private
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
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 2eff8d1365f..9af7ada2e8f 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -12303,6 +12303,12 @@ msgstr ""
msgid "GitLab for Slack"
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."
msgstr ""
@@ -14389,25 +14395,22 @@ msgstr ""
msgid "Invite teammates (optional)"
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 ""
-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 ""
msgid "InviteEmail|Join now"
msgstr ""
-msgid "InviteEmail|You are invited!"
-msgstr ""
-
-msgid "InviteEmail|You have been invited"
+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|to join the %{project_or_group_name} %{project_or_group} as a %{role}"
+msgid "InviteEmail|You are invited!"
msgstr ""
-msgid "InviteEmail|to join the %{strong_start}%{project_or_group_name}%{strong_end}"
+msgid "InviteEmail|You have been invited to join the %{project_or_group_name} %{project_or_group} as a %{role}"
msgstr ""
msgid "InviteMembersBanner|Collaborate with your team"
@@ -16208,6 +16211,12 @@ msgstr ""
msgid "Member since %{date}"
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"
msgstr ""
diff --git a/package.json b/package.json
index eed43d4eb53..276b10f0819 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,7 @@
"@babel/preset-env": "^7.10.1",
"@gitlab/at.js": "1.5.5",
"@gitlab/svgs": "1.171.0",
- "@gitlab/ui": "21.33.0",
+ "@gitlab/ui": "21.34.1",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-3",
"@rails/ujs": "^6.0.3-2",
diff --git a/qa/qa.rb b/qa/qa.rb
index 8564b8e04a7..f281a4b6ef4 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -18,6 +18,7 @@ module QA
autoload :Project, 'qa/flow/project'
autoload :Saml, 'qa/flow/saml'
autoload :User, 'qa/flow/user'
+ autoload :MergeRequest, 'qa/flow/merge_request'
end
##
@@ -191,6 +192,7 @@ module QA
autoload :Projects, 'qa/page/dashboard/projects'
autoload :Groups, 'qa/page/dashboard/groups'
autoload :Welcome, 'qa/page/dashboard/welcome'
+ autoload :Todos, 'qa/page/dashboard/todos'
module Snippet
autoload :New, 'qa/page/dashboard/snippet/new'
diff --git a/qa/qa/flow/login.rb b/qa/qa/flow/login.rb
index d4d5cc2dcfc..d23d8eaf097 100644
--- a/qa/qa/flow/login.rb
+++ b/qa/qa/flow/login.rb
@@ -23,6 +23,7 @@ module QA
end
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)
Page::Main::Login.perform { |login| login.sign_in_using_credentials(user: as, skip_page_validation: skip_page_validation) }
end
diff --git a/qa/qa/flow/merge_request.rb b/qa/qa/flow/merge_request.rb
new file mode 100644
index 00000000000..c26140000fe
--- /dev/null
+++ b/qa/qa/flow/merge_request.rb
@@ -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
diff --git a/qa/qa/page/dashboard/todos.rb b/qa/qa/page/dashboard/todos.rb
new file mode 100644
index 00000000000..d8baadcf73d
--- /dev/null
+++ b/qa/qa/page/dashboard/todos.rb
@@ -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
diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb
index 75a972d2f95..2d13b942c31 100644
--- a/spec/controllers/invites_controller_spec.rb
+++ b/spec/controllers/invites_controller_spec.rb
@@ -9,13 +9,6 @@ RSpec.describe InvitesController, :snowplow do
let(:project_members) { member.source.users }
let(:md5_member_global_id) { Digest::MD5.hexdigest(member.to_global_id.to_s) }
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
context 'when invite token is not valid' do
@@ -94,38 +87,6 @@ RSpec.describe InvitesController, :snowplow do
expect(flash[:notice]).to be_nil
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 'invalid token'
end
@@ -158,36 +119,6 @@ RSpec.describe InvitesController, :snowplow do
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 'invalid token'
end
diff --git a/spec/factories/prometheus_alert.rb b/spec/factories/prometheus_alert.rb
index 18cf1a20e0d..ad3868c38ed 100644
--- a/spec/factories/prometheus_alert.rb
+++ b/spec/factories/prometheus_alert.rb
@@ -7,11 +7,11 @@ FactoryBot.define do
threshold { 1 }
environment do |alert|
- build(:environment, project: alert.project)
+ association(:environment, project: alert.project)
end
prometheus_metric do |alert|
- build(:prometheus_metric, project: alert.project)
+ association(:prometheus_metric, project: alert.project)
end
trait :with_runbook_url do
diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb
index e8caa2159a4..13ec12c50ea 100644
--- a/spec/features/profiles/account_spec.rb
+++ b/spec/features/profiles/account_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe 'Profile > Account', :js do
end
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
it 'shows active for a provider that is not allowed to unlink' do
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index dcb901bcf11..4ff3827b240 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -12,8 +12,6 @@ RSpec.describe 'Project navbar' do
let_it_be(:project) { create(:project, :repository) }
before do
- stub_feature_flags(project_iterations: false)
-
insert_package_nav(_('Operations'))
project.add_maintainer(user)
diff --git a/spec/lib/gitlab/alert_management/payload/base_spec.rb b/spec/lib/gitlab/alert_management/payload/base_spec.rb
index e0f63bad05d..0c26e94e596 100644
--- a/spec/lib/gitlab/alert_management/payload/base_spec.rb
+++ b/spec/lib/gitlab/alert_management/payload/base_spec.rb
@@ -120,14 +120,107 @@ RSpec.describe Gitlab::AlertManagement::Payload::Base do
end
describe '#alert_params' do
- before do
- allow(parsed_payload).to receive(:title).and_return('title')
- allow(parsed_payload).to receive(:description).and_return('description')
+ subject { parsed_payload.alert_params }
+
+ 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
- subject { parsed_payload.alert_params }
+ 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
- it { is_expected.to eq({ description: 'description', project_id: project.id, title: 'title' }) }
+ 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
describe '#gitlab_fingerprint' do
diff --git a/spec/lib/gitlab/import_export/repo_restorer_spec.rb b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
index ace4449042e..b32ae60fbcc 100644
--- a/spec/lib/gitlab/import_export/repo_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/repo_restorer_spec.rb
@@ -36,21 +36,20 @@ RSpec.describe Gitlab::ImportExport::RepoRestorer do
expect(subject.restore).to be_truthy
end
- context 'when the repository creation fails' do
- before do
- allow_next_instance_of(Repositories::DestroyService) do |instance|
+ context 'when the repository already exists' do
+ it 'deletes the existing repository before importing' do
+ 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
end
- end
-
- it 'logs the error' do
- allow(project.repository)
- .to receive(:create_from_bundle)
- .and_raise('9:CreateRepositoryFromBundle: target directory is non-empty')
- expect(shared).to receive(:error).and_call_original
+ expect(shared.logger).to receive(:info).with(
+ message: 'Deleting existing "repository_path" to re-import it.'
+ )
- expect(subject.restore).to be_falsey
+ expect(subject.restore).to be_truthy
end
end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 8604939ead9..fa91bbc36b2 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -887,96 +887,30 @@ RSpec.describe Notify do
subject { described_class.member_invited_email('project', project_member.id, project_member.invite_token) }
- context 'when invite_email_experiment is disabled' do
- before do
- stub_feature_flags(invite_email_experiment: false)
- 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"
- it_behaves_like 'appearance header and footer enabled'
- it_behaves_like 'appearance header and footer not enabled'
+ 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 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+ it_behaves_like 'does not render a manage notifications link'
+ context 'when there is an inviter' 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 "#{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
+ is_expected.to have_body_text project_member.human_access.downcase
is_expected.to have_body_text project_member.invite_token
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 no inviter' do
+ let(:inviter) { nil }
- 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
+ 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
end
@@ -1547,95 +1481,31 @@ RSpec.describe Notify do
end
end
- context 'when invite_email_experiment is disabled' do
- before do
- stub_feature_flags(invite_email_experiment: false)
- 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"
- 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 '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 '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'
+ context 'when there is an inviter' 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 "#{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.web_url
- is_expected.to have_body_text group_member.human_access
+ is_expected.to have_body_text group_member.human_access.downcase
is_expected.to have_body_text group_member.invite_token
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 no inviter' do
+ let(:inviter) { nil }
- 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
+ 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
end
diff --git a/spec/models/alert_management/alert_spec.rb b/spec/models/alert_management/alert_spec.rb
index c80697e1af3..b57062b5fc1 100644
--- a/spec/models/alert_management/alert_spec.rb
+++ b/spec/models/alert_management/alert_spec.rb
@@ -170,6 +170,12 @@ RSpec.describe AlertManagement::Alert do
it { is_expected.to be_valid }
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
diff --git a/spec/services/alert_management/process_prometheus_alert_service_spec.rb b/spec/services/alert_management/process_prometheus_alert_service_spec.rb
index ad4ab26c198..ae0b8d6d7ac 100644
--- a/spec/services/alert_management/process_prometheus_alert_service_spec.rb
+++ b/spec/services/alert_management/process_prometheus_alert_service_spec.rb
@@ -117,15 +117,19 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
end
context 'when alert cannot be created' do
+ let(:errors) { double(messages: { hosts: ['hosts array is over 255 chars'] })}
+
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
it 'writes a warning to the log' do
expect(Gitlab::AppLogger).to receive(:warn).with(
message: 'Unable to create AlertManagement::Alert',
project_id: project.id,
- alert_errors: { title: ["is too long (maximum is 200 characters)"] }
+ alert_errors: { hosts: ['hosts array is over 255 chars'] }
)
execute
diff --git a/spec/support/shared_examples/mailers/notify_shared_examples.rb b/spec/support/shared_examples/mailers/notify_shared_examples.rb
index 7ce7b2161f6..0143bf693c7 100644
--- a/spec/support/shared_examples/mailers/notify_shared_examples.rb
+++ b/spec/support/shared_examples/mailers/notify_shared_examples.rb
@@ -273,3 +273,12 @@ RSpec.shared_examples 'no email is sent' do
expect(subject.message).to be_a_kind_of(ActionMailer::Base::NullMail)
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
diff --git a/yarn.lock b/yarn.lock
index 731828e3049..714035006e6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -866,10 +866,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.171.0.tgz#abc3092bf804f0898301626130e0f3231834924a"
integrity sha512-TPfdqIxQDda+0CQHhb9XdF50lmqDmADu6yT8R4oZi6BoUtWLdiHbyFt+RnVU6t7EmjIKicNAii7Ga+f2ljCfUA==
-"@gitlab/ui@21.33.0":
- version "21.33.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.33.0.tgz#83dd7e4d65557f7b82ce1f9d7d6e7a1c54cb3dbc"
- integrity sha512-UBLTz5A1z1Usxw2yfjFNuQ1Ccgx7dYD3M+lDfewDYlU48s0PY7Hm/VXsd543XIuIf+4GEJ1b43kptEZI1wtrbQ==
+"@gitlab/ui@21.34.1":
+ version "21.34.1"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-21.34.1.tgz#50c21bb751e88c12a5c7491d3c980a239a0dbbc3"
+ integrity sha512-S60A7vZcc9ZcXJrT6lCOUbzbPo+Yzx3HOaSIq335iee9DED5WMVCCYvbjm7f7Rn3CoTmDAVC4akNVTzhiuyQYQ==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0"