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:
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock6
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/pikaday.vue48
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue49
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue148
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue37
-rw-r--r--app/assets/javascripts/vue_shared/components/svg_gradient.vue34
-rw-r--r--app/assets/stylesheets/framework/buttons.scss23
-rw-r--r--app/assets/stylesheets/framework/header.scss43
-rw-r--r--app/assets/stylesheets/page_bundles/milestone.scss6
-rw-r--r--app/assets/stylesheets/pages/issuable.scss8
-rw-r--r--app/assets/stylesheets/startup/startup-dark.scss84
-rw-r--r--app/assets/stylesheets/startup/startup-general.scss80
-rw-r--r--app/controllers/admin/users_controller.rb2
-rw-r--r--app/finders/autocomplete/users_finder.rb8
-rw-r--r--app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb2
-rw-r--r--app/models/member.rb2
-rw-r--r--app/models/user.rb47
-rw-r--r--app/models/users_star_project.rb2
-rw-r--r--app/services/ci/retry_build_service.rb4
-rw-r--r--app/services/merge_requests/rebase_service.rb4
-rw-r--r--app/services/merge_requests/squash_service.rb4
-rw-r--r--app/views/layouts/header/_default.html.haml14
-rw-r--r--config/feature_flags/development/dast_sharded_cloned_ci_builds.yml8
-rw-r--r--config/sidekiq_queues.yml4
-rw-r--r--danger/sidekiq_queues/Dangerfile2
-rw-r--r--doc/administration/job_artifacts.md2
-rw-r--r--doc/api/graphql/reference/index.md28
-rw-r--r--doc/api/job_artifacts.md2
-rw-r--r--doc/api/project_import_export.md4
-rw-r--r--doc/api/visual_review_discussions.md3
-rw-r--r--doc/ci/cloud_services/aws/index.md4
-rw-r--r--doc/ci/cloud_services/index.md4
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/index.md2
-rw-r--r--doc/ci/metrics_reports.md3
-rw-r--r--doc/ci/pipelines/job_artifacts.md3
-rw-r--r--doc/ci/pipelines/pipeline_artifacts.md3
-rw-r--r--doc/ci/review_apps/index.md3
-rw-r--r--doc/ci/unit_test_reports.md3
-rw-r--r--doc/ci/yaml/artifacts_reports.md2
-rw-r--r--doc/install/installation.md4
-rw-r--r--doc/user/clusters/agent/repository.md10
-rw-r--r--doc/user/group/repositories_analytics/index.md3
-rw-r--r--doc/user/project/merge_requests/accessibility_testing.md3
-rw-r--r--doc/user/project/merge_requests/browser_performance_testing.md3
-rw-r--r--doc/user/project/merge_requests/fail_fast_testing.md3
-rw-r--r--doc/user/project/merge_requests/load_performance_testing.md3
-rw-r--r--doc/user/project/merge_requests/test_coverage_visualization.md3
-rw-r--r--doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md3
-rw-r--r--lib/api/helpers/members_helpers.rb2
-rw-r--r--lib/gitlab/application_context.rb16
-rw-r--r--lib/gitlab/import_export/command_line_util.rb4
-rw-r--r--lib/gitlab/sidekiq_queue.rb4
-rw-r--r--lib/system_check/app/git_config_check.rb44
-rw-r--r--lib/system_check/app/git_version_check.rb31
-rw-r--r--lib/system_check/rake_task/app_task.rb2
-rw-r--r--lib/tasks/gitlab/info.rake4
-rw-r--r--locale/gitlab.pot38
-rw-r--r--qa/qa/resource/user.rb3
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb6
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb6
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb2
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb2
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb4
-rw-r--r--spec/features/dashboard/issuables_counter_spec.rb15
-rw-r--r--spec/features/dashboard/merge_requests_spec.rb4
-rw-r--r--spec/features/issues/todo_spec.rb8
-rw-r--r--spec/finders/autocomplete/users_finder_spec.rb47
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/merge_request.json260
-rw-r--r--spec/frontend/vue_shared/components/multiselect_dropdown_spec.js35
-rw-r--r--spec/frontend/vue_shared/components/pikaday_spec.js41
-rw-r--r--spec/frontend/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js69
-rw-r--r--spec/frontend/vue_shared/components/sidebar/date_picker_spec.js126
-rw-r--r--spec/lib/gitlab/application_context_spec.rb11
-rw-r--r--spec/models/user_spec.rb202
-rw-r--r--spec/services/ci/retry_build_service_spec.rb3
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb2
-rw-r--r--spec/services/merge_requests/rebase_service_spec.rb2
-rw-r--r--spec/services/merge_requests/squash_service_spec.rb6
-rw-r--r--spec/support/matchers/schema_matcher.rb34
-rw-r--r--spec/tasks/gitlab/info_rake_spec.rb39
84 files changed, 486 insertions, 1366 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 8bcb41edfe9..d4cc38bf846 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-08c42adfc68c06661cf52363dacf52e2c347adff
+87a19146fdc01d0132a278b661017705514207cf
diff --git a/Gemfile b/Gemfile
index 19dd3e49b78..5cf83abef64 100644
--- a/Gemfile
+++ b/Gemfile
@@ -310,7 +310,7 @@ gem 'pg_query', '~> 2.1'
gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
-gem 'gitlab-labkit', '~> 0.21.3'
+gem 'gitlab-labkit', '~> 0.22.0'
# Thrift is a dependency of gitlab-labkit, we want a version higher than 0.14.0
# because of https://gitlab.com/gitlab-org/gitlab/-/issues/321900
gem 'thrift', '>= 0.14.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index eb19d981a4a..edfa7ac3679 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -470,11 +470,11 @@ GEM
fog-json (~> 1.2.0)
mime-types
ms_rest_azure (~> 0.12.0)
- gitlab-labkit (0.21.3)
+ gitlab-labkit (0.22.0)
actionpack (>= 5.0.0, < 7.0.0)
activesupport (>= 5.0.0, < 7.0.0)
grpc (>= 1.37)
- jaeger-client (~> 1.1)
+ jaeger-client (~> 1.1.0)
opentracing (~> 0.4)
pg_query (~> 2.1)
redis (> 3.0.0, < 5.0.0)
@@ -1470,7 +1470,7 @@ DEPENDENCIES
gitlab-dangerfiles (~> 2.8.0)
gitlab-experiment (~> 0.7.0)
gitlab-fog-azure-rm (~> 1.2.0)
- gitlab-labkit (~> 0.21.3)
+ gitlab-labkit (~> 0.22.0)
gitlab-license (~> 2.1.0)
gitlab-license_finder (~> 6.0)
gitlab-mail_room (~> 0.0.9)
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index 25468d4a697..4664bb56958 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -1,4 +1,4 @@
-import Store from 'ee_else_ce/sidebar/stores/sidebar_store';
+import Store from '~/sidebar/stores/sidebar_store';
import createFlash from '~/flash';
import { __, sprintf } from '~/locale';
import toast from '~/vue_shared/plugins/global_toast';
diff --git a/app/assets/javascripts/vue_shared/components/pikaday.vue b/app/assets/javascripts/vue_shared/components/pikaday.vue
deleted file mode 100644
index 3c0ac32e512..00000000000
--- a/app/assets/javascripts/vue_shared/components/pikaday.vue
+++ /dev/null
@@ -1,48 +0,0 @@
-<script>
-import { GlDatepicker } from '@gitlab/ui';
-import { pikadayToString } from '~/lib/utils/datetime_utility';
-
-export default {
- name: 'DatePicker',
- components: {
- GlDatepicker,
- },
- props: {
- selectedDate: {
- type: Date,
- required: false,
- default: null,
- },
- minDate: {
- type: Date,
- required: false,
- default: null,
- },
- maxDate: {
- type: Date,
- required: false,
- default: null,
- },
- },
- methods: {
- selected(date) {
- this.$emit('newDateSelected', pikadayToString(date));
- },
- toggled() {
- this.$emit('hidePicker');
- },
- },
-};
-</script>
-
-<template>
- <gl-datepicker
- :value="selectedDate"
- :min-date="minDate"
- :max-date="maxDate"
- start-opened
- @close="toggled"
- @click="toggled"
- @input="selected"
- />
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue b/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue
deleted file mode 100644
index 460a10e08ed..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon.vue
+++ /dev/null
@@ -1,49 +0,0 @@
-<script>
-import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-
-export default {
- name: 'CollapsedCalendarIcon',
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- components: {
- GlIcon,
- },
- props: {
- containerClass: {
- type: String,
- required: false,
- default: '',
- },
- text: {
- type: String,
- required: false,
- default: '',
- },
- showIcon: {
- type: Boolean,
- required: false,
- default: true,
- },
- tooltipText: {
- type: String,
- required: false,
- default: '',
- },
- },
- methods: {
- click() {
- this.$emit('click');
- },
- },
-};
-</script>
-
-<template>
- <div v-gl-tooltip.left.viewport="tooltipText" :class="containerClass" @click="click">
- <gl-icon v-if="showIcon" name="calendar" />
- <slot>
- <span> {{ text }} </span>
- </slot>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
deleted file mode 100644
index 4531fafbf72..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
+++ /dev/null
@@ -1,148 +0,0 @@
-<script>
-import { GlLoadingIcon } from '@gitlab/ui';
-import { __ } from '~/locale';
-import { dateInWords } from '../../../lib/utils/datetime_utility';
-import datePicker from '../pikaday.vue';
-import collapsedCalendarIcon from './collapsed_calendar_icon.vue';
-import toggleSidebar from './toggle_sidebar.vue';
-
-export default {
- name: 'SidebarDatePicker',
- components: {
- datePicker,
- toggleSidebar,
- collapsedCalendarIcon,
- GlLoadingIcon,
- },
- props: {
- blockClass: {
- type: String,
- required: false,
- default: '',
- },
- collapsed: {
- type: Boolean,
- required: false,
- default: true,
- },
- showToggleSidebar: {
- type: Boolean,
- required: false,
- default: false,
- },
- isLoading: {
- type: Boolean,
- required: false,
- default: false,
- },
- editable: {
- type: Boolean,
- required: false,
- default: false,
- },
- label: {
- type: String,
- required: false,
- default: __('Date picker'),
- },
- selectedDate: {
- type: Date,
- required: false,
- default: null,
- },
- minDate: {
- type: Date,
- required: false,
- default: null,
- },
- maxDate: {
- type: Date,
- required: false,
- default: null,
- },
- },
- data() {
- return {
- editing: false,
- };
- },
- computed: {
- selectedAndEditable() {
- return this.selectedDate && this.editable;
- },
- selectedDateWords() {
- return dateInWords(this.selectedDate, true);
- },
- collapsedText() {
- return this.selectedDateWords ? this.selectedDateWords : __('None');
- },
- },
- methods: {
- stopEditing() {
- this.editing = false;
- },
- toggleDatePicker() {
- this.editing = !this.editing;
- },
- newDateSelected(date = null) {
- this.date = date;
- this.editing = false;
- this.$emit('saveDate', date);
- },
- toggleSidebar() {
- this.$emit('toggleCollapse');
- },
- },
-};
-</script>
-
-<template>
- <div :class="blockClass" class="block">
- <div class="issuable-sidebar-header">
- <toggle-sidebar :collapsed="collapsed" @toggle="toggleSidebar" />
- </div>
- <collapsed-calendar-icon :text="collapsedText" class="sidebar-collapsed-icon" />
- <div class="title">
- {{ label }}
- <gl-loading-icon v-if="isLoading" size="sm" :inline="true" />
- <div class="float-right">
- <button
- v-if="editable && !editing"
- type="button"
- class="btn-blank btn-link btn-primary-hover-link btn-sidebar-action"
- @click="toggleDatePicker"
- >
- {{ __('Edit') }}
- </button>
- <toggle-sidebar v-if="showToggleSidebar" :collapsed="collapsed" @toggle="toggleSidebar" />
- </div>
- </div>
- <div class="value">
- <date-picker
- v-if="editing"
- :selected-date="selectedDate"
- :min-date="minDate"
- :max-date="maxDate"
- :label="label"
- @newDateSelected="newDateSelected"
- @hidePicker="stopEditing"
- />
- <span v-else class="value-content">
- <template v-if="selectedDate">
- <strong>{{ selectedDateWords }}</strong>
- <span v-if="selectedAndEditable" class="no-value">
- -
- <button
- type="button"
- class="btn-blank btn-link btn-secondary-hover-link"
- @click="newDateSelected(null)"
- >
- {{ __('remove') }}
- </button>
- </span>
- </template>
- <span v-else class="no-value">{{ __('None') }}</span>
- </span>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue b/app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue
deleted file mode 100644
index 17904f20341..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue
+++ /dev/null
@@ -1,37 +0,0 @@
-<script>
-import { GlDropdown, GlDropdownForm, GlDropdownDivider } from '@gitlab/ui';
-
-export default {
- components: {
- GlDropdownForm,
- GlDropdown,
- GlDropdownDivider,
- },
- props: {
- headerText: {
- type: String,
- required: true,
- },
- text: {
- type: String,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <gl-dropdown class="show" :text="text" @toggle="$emit('toggle')">
- <template #header>
- <p class="gl-font-weight-bold gl-text-center gl-mt-2 gl-mb-4">{{ headerText }}</p>
- <gl-dropdown-divider />
- <slot name="search"></slot>
- </template>
- <gl-dropdown-form>
- <slot name="items"></slot>
- </gl-dropdown-form>
- <template #footer>
- <slot name="footer"></slot>
- </template>
- </gl-dropdown>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/svg_gradient.vue b/app/assets/javascripts/vue_shared/components/svg_gradient.vue
deleted file mode 100644
index 5ce45d492f9..00000000000
--- a/app/assets/javascripts/vue_shared/components/svg_gradient.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<script>
-export default {
- props: {
- colors: {
- type: Array,
- required: true,
- validator(value) {
- return value.length === 2;
- },
- },
- opacity: {
- type: Array,
- required: true,
- validator(value) {
- return value.length === 2;
- },
- },
- identifierName: {
- type: String,
- required: true,
- },
- },
-};
-</script>
-<template>
- <svg height="0" width="0">
- <defs>
- <linearGradient :id="identifierName">
- <stop :stop-color="colors[0]" :stop-opacity="opacity[0]" offset="0%" />
- <stop :stop-color="colors[1]" :stop-opacity="opacity[1]" offset="100%" />
- </linearGradient>
- </defs>
- </svg>
-</template>
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 1d7457e3c56..9cebd4f49a4 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -366,29 +366,6 @@
background-color: transparent;
border-color: transparent;
}
-
- &.btn-secondary-hover-link,
- &.btn-default-hover-link {
- color: $gl-text-color-secondary;
-
- &:hover,
- &:active,
- &:focus {
- color: $blue-600;
- text-decoration: none;
- }
- }
-
- &.btn-primary-hover-link {
- color: inherit;
-
- &:hover,
- &:active,
- &:focus {
- color: $blue-600;
- text-decoration: none;
- }
- }
}
// The .btn-svg class is available for legacy icon buttons to
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 89d59587cba..1004383cfd3 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -415,49 +415,6 @@ $top-nav-hover-bg: var(--indigo-900-alpha-008, $indigo-900-alpha-008) !important
}
}
-.title-container,
-.navbar-nav {
- .badge.badge-pill:not(.gl-badge) {
- position: inherit;
- font-weight: $gl-font-weight-normal;
- margin-left: -6px;
- font-size: 11px;
- color: var(--gray-950, $white);
- padding: 0 5px;
- line-height: 12px;
- border-radius: 7px;
- box-shadow: 0 1px 0 rgba($gl-header-color, 0.2);
-
- &.green-badge {
- background-color: var(--green-400, $green-400);
- }
-
- &.merge-requests-count {
- background-color: var(--orange-400, $orange-400);
- }
-
- &.todos-count {
- background-color: var(--blue-400, $blue-400);
- }
- }
-
- .canary-badge {
- .badge {
- font-size: $gl-font-size-small;
- line-height: $gl-line-height;
- padding: 0 $grid-size;
- }
-
- &:hover {
- text-decoration: none;
-
- .badge {
- text-decoration: none;
- }
- }
- }
-}
-
@include media-breakpoint-down(xs) {
.navbar-gitlab .container-fluid {
font-size: 18px;
diff --git a/app/assets/stylesheets/page_bundles/milestone.scss b/app/assets/stylesheets/page_bundles/milestone.scss
index 08d9d24d246..989219552a6 100644
--- a/app/assets/stylesheets/page_bundles/milestone.scss
+++ b/app/assets/stylesheets/page_bundles/milestone.scss
@@ -42,12 +42,6 @@ $status-box-line-height: 26px;
}
.milestone-content {
- .issues-count {
- margin-right: 17px;
- float: right;
- width: 105px;
- }
-
.issuable-row {
span {
a {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index ad05cbd1f0e..d2a3d17e1bb 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -274,16 +274,10 @@
font-weight: $gl-font-weight-normal;
}
- .no-value,
- .btn-default-hover-link,
- .btn-secondary-hover-link {
+ .no-value {
color: $gl-text-color-secondary;
}
- .btn-secondary-hover-link:hover {
- color: $blue-600;
- }
-
.sidebar-collapsed-icon {
display: none;
}
diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss
index f14298c2cf7..e4a1a6dc016 100644
--- a/app/assets/stylesheets/startup/startup-dark.scss
+++ b/app/assets/stylesheets/startup/startup-dark.scss
@@ -7,12 +7,8 @@ body.gl-dark {
--gray-100: #404040;
--gray-600: #bfbfbf;
--gray-900: #fafafa;
- --gray-950: #fff;
--green-100: #0d532a;
- --green-400: #108548;
--green-700: #91d4a8;
- --blue-400: #1f75cb;
- --orange-400: #ab6100;
--indigo-900-alpha-008: rgba(235, 235, 250, 0.08);
--gl-text-color: #fafafa;
--border-color: #4f4f4f;
@@ -314,10 +310,18 @@ h1 {
padding-left: 0.6em;
border-radius: 10rem;
}
+.badge-success {
+ color: #fff;
+ background-color: #2da160;
+}
.badge-info {
color: #fff;
background-color: #428fdc;
}
+.badge-warning {
+ color: #fff;
+ background-color: #c17d10;
+}
.bg-transparent {
background-color: transparent !important;
}
@@ -394,6 +398,34 @@ a.gl-badge.badge-info:active {
0 0 0 1px rgba(51, 51, 51, 0.4), 0 0 0 4px rgba(66, 143, 220, 0.48);
outline: none;
}
+.gl-badge.badge-success {
+ background-color: #0d532a;
+ color: #91d4a8;
+}
+a.gl-badge.badge-success.active,
+a.gl-badge.badge-success:active {
+ color: #ecf4ee;
+ background-color: #24663b;
+}
+a.gl-badge.badge-success:active {
+ box-shadow: inset 0 0 0 1px rgba(51, 51, 51, 0.8),
+ 0 0 0 1px rgba(51, 51, 51, 0.4), 0 0 0 4px rgba(66, 143, 220, 0.48);
+ outline: none;
+}
+.gl-badge.badge-warning {
+ background-color: #703800;
+ color: #e9be74;
+}
+a.gl-badge.badge-warning.active,
+a.gl-badge.badge-warning:active {
+ color: #fdf1dd;
+ background-color: #8f4700;
+}
+a.gl-badge.badge-warning:active {
+ box-shadow: inset 0 0 0 1px rgba(51, 51, 51, 0.8),
+ 0 0 0 1px rgba(51, 51, 51, 0.4), 0 0 0 4px rgba(66, 143, 220, 0.48);
+ outline: none;
+}
.gl-button .gl-badge {
top: 0;
}
@@ -920,36 +952,6 @@ input {
line-height: 18px;
margin: 4px 0 4px 2px;
}
-.title-container .badge.badge-pill:not(.gl-badge),
-.navbar-nav .badge.badge-pill:not(.gl-badge) {
- position: inherit;
- font-weight: 400;
- margin-left: -6px;
- font-size: 11px;
- color: var(--gray-950, #333);
- padding: 0 5px;
- line-height: 12px;
- border-radius: 7px;
- box-shadow: 0 1px 0 rgba(76, 78, 84, 0.2);
-}
-.title-container .badge.badge-pill:not(.gl-badge).green-badge,
-.navbar-nav .badge.badge-pill:not(.gl-badge).green-badge {
- background-color: var(--green-400, #108548);
-}
-.title-container .badge.badge-pill:not(.gl-badge).merge-requests-count,
-.navbar-nav .badge.badge-pill:not(.gl-badge).merge-requests-count {
- background-color: var(--orange-400, #ab6100);
-}
-.title-container .badge.badge-pill:not(.gl-badge).todos-count,
-.navbar-nav .badge.badge-pill:not(.gl-badge).todos-count {
- background-color: var(--blue-400, #1f75cb);
-}
-.title-container .canary-badge .badge,
-.navbar-nav .canary-badge .badge {
- font-size: 12px;
- line-height: 16px;
- padding: 0 0.5rem;
-}
@media (max-width: 575.98px) {
.navbar-gitlab .container-fluid {
font-size: 18px;
@@ -2022,18 +2024,9 @@ body.gl-dark {
white-space: nowrap;
width: 1px;
}
-.gl-bg-green-500 {
- background-color: #2da160;
-}
.gl-border-none\! {
border-style: none !important;
}
-.gl-rounded-pill {
- border-radius: 0.75rem;
-}
-.gl-text-white {
- color: #333;
-}
.gl-display-none {
display: none;
}
@@ -2055,9 +2048,8 @@ body.gl-dark {
.gl-pr-2 {
padding-right: 0.25rem;
}
-.gl-py-1 {
- padding-top: 0.125rem;
- padding-bottom: 0.125rem;
+.gl-ml-n2 {
+ margin-left: -0.25rem;
}
.gl-ml-3 {
margin-left: 0.5rem;
diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss
index 7eced0f3b07..fb4ca0dc736 100644
--- a/app/assets/stylesheets/startup/startup-general.scss
+++ b/app/assets/stylesheets/startup/startup-general.scss
@@ -295,10 +295,18 @@ h1 {
padding-left: 0.6em;
border-radius: 10rem;
}
+.badge-success {
+ color: #fff;
+ background-color: #108548;
+}
.badge-info {
color: #fff;
background-color: #1f75cb;
}
+.badge-warning {
+ color: #fff;
+ background-color: #ab6100;
+}
.bg-transparent {
background-color: transparent !important;
}
@@ -375,6 +383,34 @@ a.gl-badge.badge-info:active {
0 0 0 1px rgba(255, 255, 255, 0.4), 0 0 0 4px rgba(31, 117, 203, 0.48);
outline: none;
}
+.gl-badge.badge-success {
+ background-color: #c3e6cd;
+ color: #24663b;
+}
+a.gl-badge.badge-success.active,
+a.gl-badge.badge-success:active {
+ color: #0a4020;
+ background-color: #91d4a8;
+}
+a.gl-badge.badge-success:active {
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.8),
+ 0 0 0 1px rgba(255, 255, 255, 0.4), 0 0 0 4px rgba(31, 117, 203, 0.48);
+ outline: none;
+}
+.gl-badge.badge-warning {
+ background-color: #f5d9a8;
+ color: #8f4700;
+}
+a.gl-badge.badge-warning.active,
+a.gl-badge.badge-warning:active {
+ color: #5c2900;
+ background-color: #e9be74;
+}
+a.gl-badge.badge-warning:active {
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.8),
+ 0 0 0 1px rgba(255, 255, 255, 0.4), 0 0 0 4px rgba(31, 117, 203, 0.48);
+ outline: none;
+}
.gl-button .gl-badge {
top: 0;
}
@@ -901,36 +937,6 @@ input {
line-height: 18px;
margin: 4px 0 4px 2px;
}
-.title-container .badge.badge-pill:not(.gl-badge),
-.navbar-nav .badge.badge-pill:not(.gl-badge) {
- position: inherit;
- font-weight: 400;
- margin-left: -6px;
- font-size: 11px;
- color: var(--gray-950, #fff);
- padding: 0 5px;
- line-height: 12px;
- border-radius: 7px;
- box-shadow: 0 1px 0 rgba(76, 78, 84, 0.2);
-}
-.title-container .badge.badge-pill:not(.gl-badge).green-badge,
-.navbar-nav .badge.badge-pill:not(.gl-badge).green-badge {
- background-color: var(--green-400, #2da160);
-}
-.title-container .badge.badge-pill:not(.gl-badge).merge-requests-count,
-.navbar-nav .badge.badge-pill:not(.gl-badge).merge-requests-count {
- background-color: var(--orange-400, #c17d10);
-}
-.title-container .badge.badge-pill:not(.gl-badge).todos-count,
-.navbar-nav .badge.badge-pill:not(.gl-badge).todos-count {
- background-color: var(--blue-400, #428fdc);
-}
-.title-container .canary-badge .badge,
-.navbar-nav .canary-badge .badge {
- font-size: 12px;
- line-height: 16px;
- padding: 0 0.5rem;
-}
@media (max-width: 575.98px) {
.navbar-gitlab .container-fluid {
font-size: 18px;
@@ -1691,18 +1697,9 @@ svg.s16 {
white-space: nowrap;
width: 1px;
}
-.gl-bg-green-500 {
- background-color: #108548;
-}
.gl-border-none\! {
border-style: none !important;
}
-.gl-rounded-pill {
- border-radius: 0.75rem;
-}
-.gl-text-white {
- color: #fff;
-}
.gl-display-none {
display: none;
}
@@ -1724,9 +1721,8 @@ svg.s16 {
.gl-pr-2 {
padding-right: 0.25rem;
}
-.gl-py-1 {
- padding-top: 0.125rem;
- padding-bottom: 0.125rem;
+.gl-ml-n2 {
+ margin-left: -0.25rem;
}
.gl-ml-3 {
margin-left: 0.5rem;
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index b40e2affcee..cd3f65cf044 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -16,7 +16,7 @@ class Admin::UsersController < Admin::ApplicationController
return redirect_to admin_cohorts_path if params[:tab] == 'cohorts'
@users = User.filter_items(params[:filter]).order_name_asc
- @users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present?
+ @users = @users.search(params[:search_query], with_private_emails: true) if params[:search_query].present?
@users = users_with_included_associations(@users)
@users = @users.sort_by_attribute(@sort = params[:sort])
@users = @users.page(params[:page])
diff --git a/app/finders/autocomplete/users_finder.rb b/app/finders/autocomplete/users_finder.rb
index a9fffd3f411..33d9a8a3dbc 100644
--- a/app/finders/autocomplete/users_finder.rb
+++ b/app/finders/autocomplete/users_finder.rb
@@ -62,7 +62,7 @@ module Autocomplete
find_users
.active
.reorder_by_name
- .optionally_search(search)
+ .optionally_search(search, use_minimum_char_limit: use_minimum_char_limit)
.where_not_in(skip_users)
.limit_to_todo_authors(
user: current_user,
@@ -99,6 +99,12 @@ module Autocomplete
ActiveRecord::Associations::Preloader.new.preload(items, :status)
end
# rubocop: enable CodeReuse/ActiveRecord
+
+ def use_minimum_char_limit
+ return if project.blank? && group.blank? # We return nil so that we use the default defined in the User model
+
+ false
+ end
end
end
diff --git a/app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb b/app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb
index c4f91d0c15c..b1db355aa40 100644
--- a/app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb
+++ b/app/graphql/mutations/admin/sidekiq_queues/delete_jobs.rb
@@ -8,7 +8,7 @@ module Mutations
ADMIN_MESSAGE = 'You must be an admin to use this mutation'
- ::Gitlab::ApplicationContext::KNOWN_KEYS.each do |key|
+ ::Gitlab::ApplicationContext.known_keys.each do |key|
argument key,
GraphQL::Types::String,
required: false,
diff --git a/app/models/member.rb b/app/models/member.rb
index 1c1b603b4c7..b46a497dd69 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -204,7 +204,7 @@ class Member < ApplicationRecord
class << self
def search(query)
- joins(:user).merge(User.search(query))
+ joins(:user).merge(User.search(query, use_minimum_char_limit: false))
end
def search_invite_email(query)
diff --git a/app/models/user.rb b/app/models/user.rb
index 65061305c02..313c1726429 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -668,7 +668,8 @@ class User < ApplicationRecord
sanitized_order_sql = Arel.sql(sanitize_sql_array([order, query: query]))
- scope = options[:with_private_emails] ? search_with_secondary_emails(query) : search_with_public_emails(query)
+ scope = options[:with_private_emails] ? with_primary_or_secondary_email(query) : with_public_email(query)
+ scope = scope.or(search_by_name_or_username(query, use_minimum_char_limit: options[:use_minimum_char_limit]))
scope.reorder(sanitized_order_sql, :name)
end
@@ -685,50 +686,32 @@ class User < ApplicationRecord
reorder(:name)
end
- def search_with_public_emails(query)
- return none if query.blank?
-
- query = query.downcase
+ # searches user by given pattern
+ # it compares name and username fields with given pattern
+ # This method uses ILIKE on PostgreSQL.
+ def search_by_name_or_username(query, use_minimum_char_limit: nil)
+ use_minimum_char_limit = user_search_minimum_char_limit if use_minimum_char_limit.nil?
where(
- fuzzy_arel_match(:name, query, use_minimum_char_limit: user_search_minimum_char_limit)
- .or(fuzzy_arel_match(:username, query, use_minimum_char_limit: user_search_minimum_char_limit))
- .or(arel_table[:public_email].eq(query))
+ fuzzy_arel_match(:name, query, use_minimum_char_limit: use_minimum_char_limit)
+ .or(fuzzy_arel_match(:username, query, use_minimum_char_limit: use_minimum_char_limit))
)
end
- def search_without_secondary_emails(query)
- return none if query.blank?
-
- query = query.downcase
-
- where(
- fuzzy_arel_match(:name, query, lower_exact_match: true)
- .or(fuzzy_arel_match(:username, query, lower_exact_match: true))
- .or(arel_table[:email].eq(query))
- )
+ def with_public_email(email_address)
+ where(public_email: email_address)
end
- # searches user by given pattern
- # it compares name, email, username fields and user's secondary emails with given pattern
- # This method uses ILIKE on PostgreSQL.
-
- def search_with_secondary_emails(query)
- return none if query.blank?
-
- query = query.downcase
-
+ def with_primary_or_secondary_email(email_address)
email_table = Email.arel_table
matched_by_email_user_id = email_table
.project(email_table[:user_id])
- .where(email_table[:email].eq(query))
+ .where(email_table[:email].eq(email_address))
.take(1) # at most 1 record as there is a unique constraint
where(
- fuzzy_arel_match(:name, query, use_minimum_char_limit: user_search_minimum_char_limit)
- .or(fuzzy_arel_match(:username, query, use_minimum_char_limit: user_search_minimum_char_limit))
- .or(arel_table[:email].eq(query))
- .or(arel_table[:id].eq(matched_by_email_user_id))
+ arel_table[:email].eq(email_address)
+ .or(arel_table[:id].eq(matched_by_email_user_id))
)
end
diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb
index c633e2d8b3d..1549c099a64 100644
--- a/app/models/users_star_project.rb
+++ b/app/models/users_star_project.rb
@@ -32,7 +32,7 @@ class UsersStarProject < ApplicationRecord
end
def search(query)
- joins(:user).merge(User.search(query))
+ joins(:user).merge(User.search(query, use_minimum_char_limit: false))
end
end
end
diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb
index 2371b3615ee..71588f714e8 100644
--- a/app/services/ci/retry_build_service.rb
+++ b/app/services/ci/retry_build_service.rb
@@ -70,7 +70,9 @@ module Ci
def check_assignable_runners!(build); end
def clone_build(build)
- project.builds.new(build_attributes(build))
+ project.builds.new(build_attributes(build)).tap do |new_build|
+ yield(new_build) if block_given?
+ end
end
def build_attributes(build)
diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb
index d1f45b4b49c..1c4e1784b34 100644
--- a/app/services/merge_requests/rebase_service.rb
+++ b/app/services/merge_requests/rebase_service.rb
@@ -2,7 +2,7 @@
module MergeRequests
class RebaseService < MergeRequests::BaseService
- REBASE_ERROR = 'Rebase failed. Please rebase locally'
+ REBASE_ERROR = 'Rebase failed: Rebase locally, resolve all conflicts, then push the branch.'
attr_reader :merge_request, :rebase_error
@@ -35,7 +35,7 @@ module MergeRequests
def set_rebase_error(exception)
@rebase_error =
if exception.is_a?(Gitlab::Git::PreReceiveError)
- "Something went wrong during the rebase pre-receive hook: #{exception.message}."
+ "The rebase pre-receive hook failed: #{exception.message}."
else
REBASE_ERROR
end
diff --git a/app/services/merge_requests/squash_service.rb b/app/services/merge_requests/squash_service.rb
index 69b9740c2a5..f04682bf08a 100644
--- a/app/services/merge_requests/squash_service.rb
+++ b/app/services/merge_requests/squash_service.rb
@@ -9,9 +9,9 @@ module MergeRequests
return success(squash_sha: merge_request.diff_head_sha)
end
- return error(s_('MergeRequests|This project does not allow squashing commits when merge requests are accepted.')) if squash_forbidden?
+ return error(s_("MergeRequests|Squashing not allowed: This project doesn't allow you to squash commits when merging.")) if squash_forbidden?
- squash! || error(s_('MergeRequests|Failed to squash. Should be done manually.'))
+ squash! || error(s_('MergeRequests|Squashing failed: Squash the commits locally, resolve any conflicts, then push the branch.'))
end
private
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 246a31f86c9..0a3cdb5b5cd 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -16,7 +16,7 @@
= logo_text
- if Gitlab.com_and_canary?
= link_to Gitlab::Saas.canary_toggle_com_url, class: 'canary-badge bg-transparent', data: { qa_selector: 'canary_badge_link' }, target: :_blank, rel: 'noopener noreferrer' do
- %span.gl-badge.gl-bg-green-500.gl-text-white.gl-rounded-pill.gl-font-weight-bold.gl-py-1
+ = gl_badge_tag({ variant: :success }) do
= _('Next')
- if current_user
@@ -64,7 +64,7 @@
container: 'body' } do
= sprite_icon('issues')
- issues_count = assigned_issuables_count(:issues)
- %span.badge.badge-pill.issues-count.green-badge{ class: ('hidden' if issues_count == 0) }
+ = gl_badge_tag({ size: :sm, variant: :success }, { class: "gl-ml-n2 #{(' gl-display-none' if issues_count == 0)}", "aria-label": n_("%d assigned issue", "%d assigned issues", issues_count) % issues_count }) do
= number_with_delimiter(issues_count)
- if header_link?(:merge_requests)
= nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter dropdown" }) do
@@ -77,7 +77,7 @@
track_property: 'navigation',
container: 'body' } do
= sprite_icon('git-merge')
- %span.badge.badge-pill.merge-requests-count.js-merge-requests-count{ class: ('hidden' if user_merge_requests_counts[:total] == 0) }
+ = gl_badge_tag({ size: :sm, variant: :warning }, { class: "js-merge-requests-count gl-ml-n2#{(' gl-display-none' if user_merge_requests_counts[:total] == 0)}", "aria-label": n_("%d merge request", "%d merge requests", user_merge_requests_counts[:total]) % user_merge_requests_counts[:total] }) do
= number_with_delimiter(user_merge_requests_counts[:total])
= sprite_icon('chevron-down', css_class: 'caret-down gl-mx-0!')
.dropdown-menu.dropdown-menu-right
@@ -87,12 +87,12 @@
%li
= link_to assigned_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center js-prefetch-document' do
= _('Assigned to you')
- %span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-assigned-mr-count{ class: "" }
+ = gl_badge_tag({ variant: :neutral, size: :sm }, { class: "js-assigned-mr-count gl-ml-auto" }) do
= user_merge_requests_counts[:assigned]
%li
= link_to reviewer_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center js-prefetch-document' do
= _('Review requests for you')
- %span.badge.gl-badge.badge-pill.badge-muted.merge-request-badge.gl-ml-auto.js-reviewer-mr-count{ class: "" }
+ = gl_badge_tag({ variant: :neutral, size: :sm }, { class: "js-reviewer-mr-count gl-ml-auto" }) do
= user_merge_requests_counts[:review_requested]
- if header_link?(:todos)
= nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do
@@ -103,7 +103,9 @@
track_property: 'navigation',
container: 'body' } do
= sprite_icon('todo-done')
- %span.badge.badge-pill.todos-count.js-todos-count{ class: ('hidden' if todos_pending_count == 0) }
+ -# The todos' counter badge's visibility is being toggled by adding or removing the .hidden class in Js.
+ -# We'll eventually migrate to .gl-display-none: https://gitlab.com/gitlab-org/gitlab/-/issues/351792.
+ = gl_badge_tag({ size: :sm, variant: :info }, { class: "js-todos-count gl-ml-n2#{(' hidden' if todos_pending_count == 0)}", "aria-label": _("Todos count") }) do
= todos_count_format(todos_pending_count)
%li.nav-item.header-help.dropdown.d-none.d-md-block{ **tracking_attrs('main_navigation', 'click_question_mark_link', 'navigation') }
= link_to help_path, class: 'header-help-dropdown-toggle gl-relative', data: { toggle: "dropdown" } do
diff --git a/config/feature_flags/development/dast_sharded_cloned_ci_builds.yml b/config/feature_flags/development/dast_sharded_cloned_ci_builds.yml
new file mode 100644
index 00000000000..f7d3f57403f
--- /dev/null
+++ b/config/feature_flags/development/dast_sharded_cloned_ci_builds.yml
@@ -0,0 +1,8 @@
+---
+name: dast_sharded_cloned_ci_builds
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78164
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351980
+milestone: '14.8'
+type: development
+group: group::dynamic analysis
+default_enabled: true
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index c197e77fbf7..d52659d80e7 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -35,8 +35,12 @@
- 1
- - analytics_usage_trends_counter_job
- 1
+- - app_sec_dast_scanner_profiles_builds_consistency
+ - 1
- - app_sec_dast_scans_consistency
- 1
+- - app_sec_dast_site_profiles_builds_consistency
+ - 1
- - approval_rules_external_approval_rule_payload
- 1
- - approve_blocked_pending_approval_users
diff --git a/danger/sidekiq_queues/Dangerfile b/danger/sidekiq_queues/Dangerfile
index b40be7a486e..de98af3cab2 100644
--- a/danger/sidekiq_queues/Dangerfile
+++ b/danger/sidekiq_queues/Dangerfile
@@ -3,7 +3,7 @@
SCALABILITY_REVIEW_MESSAGE = <<~MSG
## Sidekiq queue changes
-This merge request contains changes to Sidekiq queues. Please follow the [documentation on changing a queue's urgency](https://docs.gitlab.com/ee/development/sidekiq_style_guide.html#changing-a-queues-urgency).
+This merge request contains changes to Sidekiq queues. Please follow the [documentation on changing a queue's urgency](https://docs.gitlab.com/ee/development/sidekiq/worker_attributes.html#job-urgency).
MSG
ADDED_QUEUES_MESSAGE = <<~MSG
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 62c403bfe43..9d6f646e984 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -1,6 +1,6 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 03bc80c5b83..d7b878fb1a9 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -8810,6 +8810,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="boardepicancestorsauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="boardepicancestorsconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
+| <a id="boardepicancestorscreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
+| <a id="boardepicancestorscreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="boardepicancestorsenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="boardepicancestorsiid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="boardepicancestorsiidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
@@ -8826,6 +8828,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="boardepicancestorsstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="boardepicancestorsstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="boardepicancestorstimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
+| <a id="boardepicancestorsupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
+| <a id="boardepicancestorsupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `BoardEpic.children`
@@ -8843,6 +8847,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="boardepicchildrenauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="boardepicchildrenconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
+| <a id="boardepicchildrencreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
+| <a id="boardepicchildrencreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="boardepicchildrenenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="boardepicchildreniid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="boardepicchildreniidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
@@ -8859,6 +8865,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="boardepicchildrenstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="boardepicchildrenstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="boardepicchildrentimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
+| <a id="boardepicchildrenupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
+| <a id="boardepicchildrenupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `BoardEpic.currentUserTodos`
@@ -10326,6 +10334,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="epicancestorsauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="epicancestorsconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
+| <a id="epicancestorscreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
+| <a id="epicancestorscreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="epicancestorsenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="epicancestorsiid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="epicancestorsiidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
@@ -10342,6 +10352,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="epicancestorsstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="epicancestorsstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="epicancestorstimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
+| <a id="epicancestorsupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
+| <a id="epicancestorsupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `Epic.children`
@@ -10359,6 +10371,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="epicchildrenauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="epicchildrenconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
+| <a id="epicchildrencreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
+| <a id="epicchildrencreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="epicchildrenenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="epicchildreniid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="epicchildreniidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
@@ -10375,6 +10389,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="epicchildrenstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="epicchildrenstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="epicchildrentimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
+| <a id="epicchildrenupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
+| <a id="epicchildrenupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `Epic.currentUserTodos`
@@ -11049,6 +11065,8 @@ Returns [`Epic`](#epic).
| ---- | ---- | ----------- |
| <a id="groupepicauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="groupepicconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
+| <a id="groupepiccreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
+| <a id="groupepiccreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="groupepicenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="groupepiciid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="groupepiciidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
@@ -11065,6 +11083,8 @@ Returns [`Epic`](#epic).
| <a id="groupepicstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="groupepicstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="groupepictimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
+| <a id="groupepicupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
+| <a id="groupepicupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `Group.epicBoard`
@@ -11094,6 +11114,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="groupepicsauthorusername"></a>`authorUsername` | [`String`](#string) | Filter epics by author. |
| <a id="groupepicsconfidential"></a>`confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. |
+| <a id="groupepicscreatedafter"></a>`createdAfter` | [`Time`](#time) | Epics created after this date. |
+| <a id="groupepicscreatedbefore"></a>`createdBefore` | [`Time`](#time) | Epics created before this date. |
| <a id="groupepicsenddate"></a>`endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. |
| <a id="groupepicsiid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="groupepicsiidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
@@ -11110,6 +11132,8 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupepicsstartdate"></a>`startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. |
| <a id="groupepicsstate"></a>`state` | [`EpicState`](#epicstate) | Filter epics by state. |
| <a id="groupepicstimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
+| <a id="groupepicsupdatedafter"></a>`updatedAfter` | [`Time`](#time) | Epics updated after this date. |
+| <a id="groupepicsupdatedbefore"></a>`updatedBefore` | [`Time`](#time) | Epics updated before this date. |
##### `Group.groupMembers`
@@ -17041,12 +17065,16 @@ Roadmap sort values.
| Value | Description |
| ----- | ----------- |
+| <a id="epicsortcreated_at_asc"></a>`CREATED_AT_ASC` | Sort by created_at by ascending order. |
+| <a id="epicsortcreated_at_desc"></a>`CREATED_AT_DESC` | Sort by created_at by descending order. |
| <a id="epicsortend_date_asc"></a>`END_DATE_ASC` | Sort by end date in ascending order. |
| <a id="epicsortend_date_desc"></a>`END_DATE_DESC` | Sort by end date in descending order. |
| <a id="epicsortstart_date_asc"></a>`START_DATE_ASC` | Sort by start date in ascending order. |
| <a id="epicsortstart_date_desc"></a>`START_DATE_DESC` | Sort by start date in descending order. |
| <a id="epicsorttitle_asc"></a>`TITLE_ASC` | Sort by title in ascending order. |
| <a id="epicsorttitle_desc"></a>`TITLE_DESC` | Sort by title in descending order. |
+| <a id="epicsortupdated_at_asc"></a>`UPDATED_AT_ASC` | Sort by updated_at by ascending order. |
+| <a id="epicsortupdated_at_desc"></a>`UPDATED_AT_DESC` | Sort by updated_at by descending order. |
| <a id="epicsortend_date_asc"></a>`end_date_asc` **{warning-solid}** | **Deprecated** in 13.11. Use END_DATE_ASC. |
| <a id="epicsortend_date_desc"></a>`end_date_desc` **{warning-solid}** | **Deprecated** in 13.11. Use END_DATE_DESC. |
| <a id="epicsortstart_date_asc"></a>`start_date_asc` **{warning-solid}** | **Deprecated** in 13.11. Use START_DATE_ASC. |
diff --git a/doc/api/job_artifacts.md b/doc/api/job_artifacts.md
index 01ffd479acc..d272f259ddf 100644
--- a/doc/api/job_artifacts.md
+++ b/doc/api/job_artifacts.md
@@ -1,6 +1,6 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md
index 6e9e9f0a81f..218036b1ee0 100644
--- a/doc/api/project_import_export.md
+++ b/doc/api/project_import_export.md
@@ -243,8 +243,8 @@ curl --request POST \
}
```
-The `ContentType` header must return a valid number. The maximum file size is 10 gigabytes.
-The `ContentLength` header must be `application/gzip`.
+The `Content-Length` header must return a valid number. The maximum file size is 10 gigabytes.
+The `Content-Type` header must be `application/gzip`.
## Import status
diff --git a/doc/api/visual_review_discussions.md b/doc/api/visual_review_discussions.md
index 5b7aec3785b..7f91b829061 100644
--- a/doc/api/visual_review_discussions.md
+++ b/doc/api/visual_review_discussions.md
@@ -1,8 +1,7 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: reference, api
---
# Visual Review discussions API **(PREMIUM)**
diff --git a/doc/ci/cloud_services/aws/index.md b/doc/ci/cloud_services/aws/index.md
index 51aa08fecbe..aa38562c866 100644
--- a/doc/ci/cloud_services/aws/index.md
+++ b/doc/ci/cloud_services/aws/index.md
@@ -1,6 +1,6 @@
---
-stage: Configure
-group: Configure
+stage: Verify
+group: Pipeline Authoring
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/ci/cloud_services/index.md b/doc/ci/cloud_services/index.md
index a0395730680..a80231a04c2 100644
--- a/doc/ci/cloud_services/index.md
+++ b/doc/ci/cloud_services/index.md
@@ -1,6 +1,6 @@
---
-stage: Configure
-group: Configure
+stage: Verify
+group: Pipeline Authoring
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/index.md b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
index 9881c9657bc..4d247a4ff74 100644
--- a/doc/ci/examples/end_to_end_testing_webdriverio/index.md
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -1,6 +1,6 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
author: Vincent Tunru
author_gitlab: Vinnl
diff --git a/doc/ci/metrics_reports.md b/doc/ci/metrics_reports.md
index eb302b9ed7f..5b472eec7b5 100644
--- a/doc/ci/metrics_reports.md
+++ b/doc/ci/metrics_reports.md
@@ -1,8 +1,7 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: reference
---
# Metrics Reports **(PREMIUM)**
diff --git a/doc/ci/pipelines/job_artifacts.md b/doc/ci/pipelines/job_artifacts.md
index 8afe6243460..fa8041671dc 100644
--- a/doc/ci/pipelines/job_artifacts.md
+++ b/doc/ci/pipelines/job_artifacts.md
@@ -1,9 +1,8 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
disqus_identifier: 'https://docs.gitlab.com/ee/user/project/pipelines/job_artifacts.html'
-type: reference, howto
---
# Job artifacts **(FREE)**
diff --git a/doc/ci/pipelines/pipeline_artifacts.md b/doc/ci/pipelines/pipeline_artifacts.md
index b174b6af9f9..e9dd1b2a942 100644
--- a/doc/ci/pipelines/pipeline_artifacts.md
+++ b/doc/ci/pipelines/pipeline_artifacts.md
@@ -1,8 +1,7 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: reference, howto
---
# Pipeline artifacts **(FREE)**
diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md
index 2450939b12b..ed29b33bc3a 100644
--- a/doc/ci/review_apps/index.md
+++ b/doc/ci/review_apps/index.md
@@ -1,8 +1,7 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: reference
---
# Review Apps **(FREE)**
diff --git a/doc/ci/unit_test_reports.md b/doc/ci/unit_test_reports.md
index c2b285bb017..4e43d8792fb 100644
--- a/doc/ci/unit_test_reports.md
+++ b/doc/ci/unit_test_reports.md
@@ -1,8 +1,7 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: reference
---
# Unit test reports **(FREE)**
diff --git a/doc/ci/yaml/artifacts_reports.md b/doc/ci/yaml/artifacts_reports.md
index 25fb861cfb7..e010dd21b9e 100644
--- a/doc/ci/yaml/artifacts_reports.md
+++ b/doc/ci/yaml/artifacts_reports.md
@@ -1,6 +1,6 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 8217be02f45..878e9e3416c 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -560,10 +560,6 @@ sudo -u git -H cp config/puma.rb.example config/puma.rb
# cores you have available. You can get that number via the `nproc` command.
sudo -u git -H editor config/puma.rb
-# Configure Git global settings for git user
-# 'autocrlf' is needed for the web editor
-sudo -u git -H git config --global core.autocrlf input
-
# Disable 'git gc --auto' because GitLab already runs 'git gc' when needed
sudo -u git -H git config --global gc.auto 0
diff --git a/doc/user/clusters/agent/repository.md b/doc/user/clusters/agent/repository.md
index d8d30f2e681..8c1a4450598 100644
--- a/doc/user/clusters/agent/repository.md
+++ b/doc/user/clusters/agent/repository.md
@@ -202,7 +202,7 @@ any Kubernetes-specific commands from the authorized project.
First, configure your Agent:
-1. Go to you your Agent's configuration repository.
+1. Go to your Agent's configuration repository.
1. Edit your Agent's `config.yaml` file authorizing the [project](#authorize-projects-to-use-an-agent) or [group](#authorize-groups-to-use-an-agent) you want to run Kubernetes commands from.
Then, configure the other project:
@@ -231,6 +231,14 @@ To get the list of available contexts:
1. Open your terminal and connect to your cluster.
1. Run `kubectl config get-contexts`.
+### `kubectl` commands not supported
+
+The commands `kubectl exec`, `kubectl cp`, and `kubectl attach` are not supported by the CI/CD tunnel.
+Anything else that uses the same API endpoints does not work either as they use the deprecated
+SPDY protocol.
+We [plan to add support for these features](https://gitlab.com/gitlab-org/gitlab/-/issues/346248)
+in a future version of GitLab.
+
## Use impersonation to restrict project and group access **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345014) in GitLab 14.5.
diff --git a/doc/user/group/repositories_analytics/index.md b/doc/user/group/repositories_analytics/index.md
index 2487ab188e8..6e3ea7d6c0f 100644
--- a/doc/user/group/repositories_analytics/index.md
+++ b/doc/user/group/repositories_analytics/index.md
@@ -1,7 +1,6 @@
---
-type: reference
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/user/project/merge_requests/accessibility_testing.md b/doc/user/project/merge_requests/accessibility_testing.md
index e67af8dc936..612f499bb65 100644
--- a/doc/user/project/merge_requests/accessibility_testing.md
+++ b/doc/user/project/merge_requests/accessibility_testing.md
@@ -1,8 +1,7 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: reference, howto
---
# Accessibility testing **(FREE)**
diff --git a/doc/user/project/merge_requests/browser_performance_testing.md b/doc/user/project/merge_requests/browser_performance_testing.md
index a1caea39d30..9c7d9e2bf19 100644
--- a/doc/user/project/merge_requests/browser_performance_testing.md
+++ b/doc/user/project/merge_requests/browser_performance_testing.md
@@ -1,8 +1,7 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: reference, howto
---
# Browser Performance Testing **(PREMIUM)**
diff --git a/doc/user/project/merge_requests/fail_fast_testing.md b/doc/user/project/merge_requests/fail_fast_testing.md
index 20ef1746ca3..355661516a7 100644
--- a/doc/user/project/merge_requests/fail_fast_testing.md
+++ b/doc/user/project/merge_requests/fail_fast_testing.md
@@ -1,8 +1,7 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: reference, howto
---
# Fail Fast Testing **(PREMIUM)**
diff --git a/doc/user/project/merge_requests/load_performance_testing.md b/doc/user/project/merge_requests/load_performance_testing.md
index ba120080ef8..cc4ad186771 100644
--- a/doc/user/project/merge_requests/load_performance_testing.md
+++ b/doc/user/project/merge_requests/load_performance_testing.md
@@ -1,8 +1,7 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: reference, howto
---
# Load Performance Testing **(PREMIUM)**
diff --git a/doc/user/project/merge_requests/test_coverage_visualization.md b/doc/user/project/merge_requests/test_coverage_visualization.md
index 728d28d293c..bd54eda42f5 100644
--- a/doc/user/project/merge_requests/test_coverage_visualization.md
+++ b/doc/user/project/merge_requests/test_coverage_visualization.md
@@ -1,8 +1,7 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: reference, howto
---
# Test coverage visualization **(FREE)**
diff --git a/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md b/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md
index 3e68c92e6f0..741ac325cca 100644
--- a/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md
+++ b/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md
@@ -1,8 +1,7 @@
---
stage: Verify
-group: Testing
+group: Pipeline Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: index
description: "Test your code and display reports in merge requests"
---
diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb
index 6c20993431d..f26ac1318b1 100644
--- a/lib/api/helpers/members_helpers.rb
+++ b/lib/api/helpers/members_helpers.rb
@@ -23,7 +23,7 @@ module API
def retrieve_members(source, params:, deep: false)
members = deep ? find_all_members(source) : source_members(source).connected_to_user
members = members.includes(:user)
- members = members.references(:user).merge(User.search(params[:query])) if params[:query].present?
+ members = members.references(:user).merge(User.search(params[:query], use_minimum_char_limit: false)) if params[:query].present?
members = members.where(user_id: params[:user_ids]) if params[:user_ids].present?
members
end
diff --git a/lib/gitlab/application_context.rb b/lib/gitlab/application_context.rb
index c3415c45b28..d93067c7e2f 100644
--- a/lib/gitlab/application_context.rb
+++ b/lib/gitlab/application_context.rb
@@ -9,7 +9,17 @@ module Gitlab
Attribute = Struct.new(:name, :type)
LOG_KEY = Labkit::Context::LOG_KEY
- KNOWN_KEYS = Labkit::Context::KNOWN_KEYS
+ KNOWN_KEYS = [
+ :user,
+ :project,
+ :root_namespace,
+ :client_id,
+ :caller_id,
+ :remote_ip,
+ :related_class,
+ :feature_category
+ ].freeze
+ private_constant :KNOWN_KEYS
APPLICATION_ATTRIBUTES = [
Attribute.new(:project, Project),
@@ -22,6 +32,10 @@ module Gitlab
Attribute.new(:feature_category, String)
].freeze
+ def self.known_keys
+ KNOWN_KEYS
+ end
+
def self.with_context(args, &block)
application_context = new(**args)
application_context.use(&block)
diff --git a/lib/gitlab/import_export/command_line_util.rb b/lib/gitlab/import_export/command_line_util.rb
index f9ebefdd974..e520cade517 100644
--- a/lib/gitlab/import_export/command_line_util.rb
+++ b/lib/gitlab/import_export/command_line_util.rb
@@ -95,10 +95,6 @@ module Gitlab
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
- def git_bin_path
- Gitlab.config.git.bin_path
- end
-
def copy_files(source, destination)
# if we are copying files, create the destination folder
destination_folder = File.file?(source) ? File.dirname(destination) : destination
diff --git a/lib/gitlab/sidekiq_queue.rb b/lib/gitlab/sidekiq_queue.rb
index 67a9d8120d8..e22f09b4976 100644
--- a/lib/gitlab/sidekiq_queue.rb
+++ b/lib/gitlab/sidekiq_queue.rb
@@ -8,7 +8,7 @@ module Gitlab
InvalidQueueError = Class.new(StandardError)
WORKER_KEY = 'worker_class'
- ALLOWED_KEYS = Gitlab::ApplicationContext::KNOWN_KEYS + [WORKER_KEY]
+ ALLOWED_KEYS = Gitlab::ApplicationContext.known_keys.map(&:to_s) + [WORKER_KEY]
attr_reader :queue_name
@@ -53,7 +53,7 @@ module Gitlab
private
def transform_key(key)
- if Gitlab::ApplicationContext::KNOWN_KEYS.include?(key)
+ if Gitlab::ApplicationContext.known_keys.include?(key.to_sym)
"meta.#{key}"
elsif key == WORKER_KEY
'class'
diff --git a/lib/system_check/app/git_config_check.rb b/lib/system_check/app/git_config_check.rb
deleted file mode 100644
index d0b64b8bfeb..00000000000
--- a/lib/system_check/app/git_config_check.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-module SystemCheck
- module App
- class GitConfigCheck < SystemCheck::BaseCheck
- OPTIONS = {
- 'core.autocrlf' => 'input'
- }.freeze
-
- set_name 'Git configured correctly?'
-
- def check?
- correct_options = OPTIONS.map do |name, value|
- run_command(%W(#{Gitlab.config.git.bin_path} config --global --get #{name})).try(:squish) == value
- end
-
- correct_options.all?
- end
-
- # Tries to configure git itself
- #
- # Returns true if all subcommands were successful (according to their exit code)
- # Returns false if any or all subcommands failed.
- def repair!
- return false unless gitlab_user?
-
- command_success = OPTIONS.map do |name, value|
- system(*%W(#{Gitlab.config.git.bin_path} config --global #{name} #{value}))
- end
-
- command_success.all?
- end
-
- def show_error
- try_fixing_it(
- sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global core.autocrlf \"#{OPTIONS['core.autocrlf']}\"")
- )
- for_more_information(
- see_installation_guide_section('GitLab')
- )
- end
- end
- end
-end
diff --git a/lib/system_check/app/git_version_check.rb b/lib/system_check/app/git_version_check.rb
deleted file mode 100644
index 6512b142969..00000000000
--- a/lib/system_check/app/git_version_check.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-module SystemCheck
- module App
- class GitVersionCheck < SystemCheck::BaseCheck
- set_name -> { "Git version >= #{self.required_version} ?" }
- set_check_pass -> { "yes (#{self.current_version})" }
-
- def self.required_version
- @required_version ||= Gitlab::VersionInfo.parse('2.33.0')
- end
-
- def self.current_version
- @current_version ||= Gitlab::VersionInfo.parse(Gitlab::TaskHelpers.run_command(%W(#{Gitlab.config.git.bin_path} --version)))
- end
-
- def check?
- self.class.current_version.valid? && self.class.required_version <= self.class.current_version
- end
-
- def show_error
- $stdout.puts "Your git bin path is \"#{Gitlab.config.git.bin_path}\""
-
- try_fixing_it(
- "Update your git to a version >= #{self.class.required_version} from #{self.class.current_version}"
- )
- fix_and_rerun
- end
- end
- end
-end
diff --git a/lib/system_check/rake_task/app_task.rb b/lib/system_check/rake_task/app_task.rb
index 892417d67ec..1eb7a35b40a 100644
--- a/lib/system_check/rake_task/app_task.rb
+++ b/lib/system_check/rake_task/app_task.rb
@@ -12,7 +12,6 @@ module SystemCheck
def self.checks
[
- SystemCheck::App::GitConfigCheck,
SystemCheck::App::DatabaseConfigExistsCheck,
SystemCheck::App::MigrationsAreUpCheck,
SystemCheck::App::OrphanedGroupMembersCheck,
@@ -28,7 +27,6 @@ module SystemCheck
SystemCheck::App::ProjectsHaveNamespaceCheck,
SystemCheck::App::RedisVersionCheck,
SystemCheck::App::RubyVersionCheck,
- SystemCheck::App::GitVersionCheck,
SystemCheck::App::GitUserDefaultSSHConfigCheck,
SystemCheck::App::ActiveUsersCheck,
SystemCheck::App::AuthorizedKeysPermissionCheck,
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index 02764b5d46f..6f42bf8c946 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -22,8 +22,6 @@ namespace :gitlab do
proxies = Gitlab::Proxy.detect_proxy.map {|k, v| "#{k}: #{v}"}.join("\n\t\t")
end
- # check Git version
- git_version = run_and_match([Gitlab.config.git.bin_path, '--version'], /git version ([\d\.]+)/).to_a
# check Go version
go_version = run_and_match(%w(go version), /go version (.+)/).to_a
@@ -43,7 +41,6 @@ namespace :gitlab do
puts "Bundler Version:#{bunder_version || "unknown".color(:red)}"
puts "Rake Version:\t#{rake_version || "unknown".color(:red)}"
puts "Redis Version:\t#{redis_version[1] || "unknown".color(:red)}"
- puts "Git Version:\t#{git_version[1] || "unknown".color(:red)}"
puts "Sidekiq Version:#{Sidekiq::VERSION}"
puts "Go Version:\t#{go_version[1] || "unknown".color(:red)}"
@@ -95,7 +92,6 @@ namespace :gitlab do
end
end
puts "GitLab Shell path:\t\t#{Gitlab.config.gitlab_shell.path}"
- puts "Git:\t\t#{Gitlab.config.git.bin_path}"
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0d87a7df42b..0f766d48a35 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -146,6 +146,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d assigned issue"
+msgid_plural "%d assigned issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d changed file"
msgid_plural "%d changed files"
msgstr[0] ""
@@ -11382,9 +11387,6 @@ msgstr ""
msgid "Date merged"
msgstr ""
-msgid "Date picker"
-msgstr ""
-
msgid "Date range"
msgstr ""
@@ -14123,9 +14125,6 @@ msgstr ""
msgid "Error occurred while updating the issue status"
msgstr ""
-msgid "Error occurred while updating the issue weight"
-msgstr ""
-
msgid "Error occurred. A blocked user cannot be deactivated"
msgstr ""
@@ -22680,13 +22679,13 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
-msgid "MergeRequests|Failed to squash. Should be done manually."
+msgid "MergeRequests|Saving the comment failed"
msgstr ""
-msgid "MergeRequests|Saving the comment failed"
+msgid "MergeRequests|Squashing failed: Squash the commits locally, resolve any conflicts, then push the branch."
msgstr ""
-msgid "MergeRequests|This project does not allow squashing commits when merge requests are accepted."
+msgid "MergeRequests|Squashing not allowed: This project doesn't allow you to squash commits when merging."
msgstr ""
msgid "MergeRequests|Thread stays resolved"
@@ -27082,6 +27081,18 @@ msgstr ""
msgid "Policy project doesn't exist"
msgstr ""
+msgid "PolicyRuleMultiSelect|%{firstLabel} +%{numberOfAdditionalLabels} more"
+msgstr ""
+
+msgid "PolicyRuleMultiSelect|All %{itemTypeName}"
+msgstr ""
+
+msgid "PolicyRuleMultiSelect|Select %{itemTypeName}"
+msgstr ""
+
+msgid "PolicyRuleMultiSelect|Select all"
+msgstr ""
+
msgid "Polling interval multiplier"
msgstr ""
@@ -33402,12 +33413,6 @@ msgstr ""
msgid "Sidebar|None"
msgstr ""
-msgid "Sidebar|Only numeral characters allowed"
-msgstr ""
-
-msgid "Sidebar|Weight"
-msgstr ""
-
msgid "Sidekiq job compression threshold (bytes)"
msgstr ""
@@ -37796,6 +37801,9 @@ msgstr ""
msgid "Today"
msgstr ""
+msgid "Todos count"
+msgstr ""
+
msgid "Todos|Are you looking for things to do? Take a look at %{strongStart}%{openIssuesLinkStart}open issues%{openIssuesLinkEnd}%{strongEnd}, contribute to %{strongStart}%{mergeRequestLinkStart}a merge request%{mergeRequestLinkEnd}%{mergeRequestLinkEnd}%{strongEnd}, or mention someone in a comment to automatically assign them a new to-do item."
msgstr ""
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index cf8e0564041..eab428a61e7 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -100,8 +100,7 @@ module QA
def exists?
api_get
- # TODO: remove 'InternalServerError' from below line after https://gitlab.com/gitlab-org/gitlab/-/issues/349337 has been resolved
- rescue ResourceNotFoundError, InternalServerError
+ rescue ResourceNotFoundError
false
end
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb
index 847b9b7ff71..a6655471591 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_group_spec.rb
@@ -1,11 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Manage', :requires_admin, quarantine: {
- only: { subdomain: :staging },
- type: :investigating,
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/350965'
- } do
+ RSpec.describe 'Manage', :requires_admin do
describe 'Gitlab migration' do
let(:import_wait_duration) { { max_duration: 300, sleep_interval: 2 } }
let(:admin_api_client) { Runtime::API::Client.as_admin }
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb
index dc981ebceab..bb4b0472398 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_issue_spec.rb
@@ -3,11 +3,7 @@
require_relative 'gitlab_project_migration_common'
module QA
- RSpec.describe 'Manage', :requires_admin, quarantine: {
- only: { subdomain: :staging },
- type: :investigating,
- issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/351596'
- } do
+ RSpec.describe 'Manage' do
describe 'Gitlab migration' do
include_context 'with gitlab project migration'
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb
index 332133b6a4e..d656ea4dea5 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_mr_spec.rb
@@ -3,7 +3,7 @@
require_relative 'gitlab_project_migration_common'
module QA
- RSpec.describe 'Manage', :requires_admin do
+ RSpec.describe 'Manage' do
describe 'Gitlab migration' do
include_context 'with gitlab project migration'
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb
index 07ae8ae1372..421dbe56a99 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_project_spec.rb
@@ -3,7 +3,7 @@
require_relative 'gitlab_project_migration_common'
module QA
- RSpec.describe 'Manage', :requires_admin do
+ RSpec.describe 'Manage' do
describe 'Gitlab migration' do
include_context 'with gitlab project migration'
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb
index fea68114ae0..b7f0a10c525 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module QA
- RSpec.shared_context 'with gitlab project migration', quarantine: {
+ # Disable on staging until bulk_import_projects toggle is on by default
+ # Otherwise tests running in parallel can disable feature in the middle of other test
+ RSpec.shared_context 'with gitlab project migration', :requires_admin, except: { subdomain: :staging }, quarantine: {
only: { job: 'praefect' },
type: :investigating,
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/348999'
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
index 098c0b3ba63..5487ecff028 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
@@ -64,7 +64,9 @@ module QA
Page::Profile::Accounts::Show.perform do |show|
show.delete_account(user.password)
end
- Support::Waiter.wait_until { !user.exists? }
+
+ # TODO: Remove retry_on_exception once https://gitlab.com/gitlab-org/gitlab/-/issues/24294 is resolved
+ Support::Waiter.wait_until(retry_on_exception: true, sleep_interval: 3) { !user.exists? }
end
it 'allows recreating with same credentials', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347868' do
diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb
index 8e938fef155..6700ec07765 100644
--- a/spec/features/dashboard/issuables_counter_spec.rb
+++ b/spec/features/dashboard/issuables_counter_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d
it 'reflects dashboard issues count' do
visit issues_path
- expect_counters('issues', '1')
+ expect_counters('issues', '1', n_("%d assigned issue", "%d assigned issues", 1) % 1)
issue.assignees = []
@@ -26,14 +26,14 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d
travel_to(3.minutes.from_now) do
visit issues_path
- expect_counters('issues', '0')
+ expect_counters('issues', '0', n_("%d assigned issue", "%d assigned issues", 0) % 0)
end
end
it 'reflects dashboard merge requests count' do
visit merge_requests_path
- expect_counters('merge_requests', '1')
+ expect_counters('merge_requests', '1', n_("%d merge request", "%d merge requests", 1) % 1)
merge_request.update!(assignees: [])
@@ -42,7 +42,7 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d
travel_to(3.minutes.from_now) do
visit merge_requests_path
- expect_counters('merge_requests', '0')
+ expect_counters('merge_requests', '0', n_("%d merge request", "%d merge requests", 0) % 0)
end
end
@@ -54,13 +54,14 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d
merge_requests_dashboard_path(assignee_username: user.username)
end
- def expect_counters(issuable_type, count)
+ def expect_counters(issuable_type, count, badge_label)
dashboard_count = find('.gl-tabs-nav li a.active')
nav_count = find(".dashboard-shortcuts-#{issuable_type}")
- header_count = find(".header-content .#{issuable_type.tr('_', '-')}-count")
expect(dashboard_count).to have_content(count)
expect(nav_count).to have_content(count)
- expect(header_count).to have_content(count)
+ within("span[aria-label='#{badge_label}']") do
+ expect(page).to have_content(count)
+ end
end
end
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index 6239702edde..7507ef4e453 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -112,7 +112,9 @@ RSpec.describe 'Dashboard Merge Requests' do
end
it 'includes assigned and reviewers in badge' do
- expect(find('.merge-requests-count')).to have_content('3')
+ within("span[aria-label='#{n_("%d merge request", "%d merge requests", 3) % 3}']") do
+ expect(page).to have_content('3')
+ end
expect(find('.js-assigned-mr-count')).to have_content('2')
expect(find('.js-reviewer-mr-count')).to have_content('1')
end
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
index 315d1c911a2..d63d21353e5 100644
--- a/spec/features/issues/todo_spec.rb
+++ b/spec/features/issues/todo_spec.rb
@@ -19,13 +19,13 @@ RSpec.describe 'Manually create a todo item from issue', :js do
expect(page).to have_content 'Mark as done'
end
- page.within '.header-content .todos-count' do
+ page.within ".header-content span[aria-label='#{_('Todos count')}']" do
expect(page).to have_content '1'
end
visit project_issue_path(project, issue)
- page.within '.header-content .todos-count' do
+ page.within ".header-content span[aria-label='#{_('Todos count')}']" do
expect(page).to have_content '1'
end
end
@@ -36,10 +36,10 @@ RSpec.describe 'Manually create a todo item from issue', :js do
click_button 'Mark as done'
end
- expect(page).to have_selector('.todos-count', visible: false)
+ expect(page).to have_selector(".header-content span[aria-label='#{_('Todos count')}']", visible: false)
visit project_issue_path(project, issue)
- expect(page).to have_selector('.todos-count', visible: false)
+ expect(page).to have_selector(".header-content span[aria-label='#{_('Todos count')}']", visible: false)
end
end
diff --git a/spec/finders/autocomplete/users_finder_spec.rb b/spec/finders/autocomplete/users_finder_spec.rb
index df7a14d440e..9b3421d1b4f 100644
--- a/spec/finders/autocomplete/users_finder_spec.rb
+++ b/spec/finders/autocomplete/users_finder_spec.rb
@@ -7,15 +7,16 @@ RSpec.describe Autocomplete::UsersFinder do
# https://gitlab.com/gitlab-org/gitlab/-/issues/21432
describe '#execute' do
- let!(:user1) { create(:user, username: 'johndoe') }
- let!(:user2) { create(:user, :blocked, username: 'notsorandom') }
- let!(:external_user) { create(:user, :external) }
- let!(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
+ let_it_be(:user1) { create(:user, name: 'zzzzzname', username: 'johndoe') }
+ let_it_be(:user2) { create(:user, :blocked, username: 'notsorandom') }
+ let_it_be(:external_user) { create(:user, :external) }
+ let_it_be(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
+
let(:current_user) { create(:user) }
let(:params) { {} }
- let(:project) { nil }
- let(:group) { nil }
+ let_it_be(:project) { nil }
+ let_it_be(:group) { nil }
subject { described_class.new(params: params, current_user: current_user, project: project, group: group).execute.to_a }
@@ -26,7 +27,7 @@ RSpec.describe Autocomplete::UsersFinder do
end
context 'when project passed' do
- let(:project) { create(:project) }
+ let_it_be(:project) { create(:project) }
it { is_expected.to match_array([project.first_owner]) }
@@ -43,16 +44,36 @@ RSpec.describe Autocomplete::UsersFinder do
it { is_expected.to match_array([project.first_owner]) }
end
end
+
+ context 'searching with less than 3 characters' do
+ let(:params) { { search: 'zz' } }
+
+ before do
+ project.add_guest(user1)
+ end
+
+ it 'allows partial matches' do
+ expect(subject).to contain_exactly(user1)
+ end
+ end
end
context 'when group passed and project not passed' do
- let(:group) { create(:group, :public) }
+ let_it_be(:group) { create(:group, :public) }
- before do
+ before_all do
group.add_users([user1], GroupMember::DEVELOPER)
end
it { is_expected.to match_array([user1]) }
+
+ context 'searching with less than 3 characters' do
+ let(:params) { { search: 'zz' } }
+
+ it 'allows partial matches' do
+ expect(subject).to contain_exactly(user1)
+ end
+ end
end
context 'when passed a subgroup' do
@@ -76,6 +97,14 @@ RSpec.describe Autocomplete::UsersFinder do
let(:params) { { search: 'johndoe' } }
it { is_expected.to match_array([user1]) }
+
+ context 'searching with less than 3 characters' do
+ let(:params) { { search: 'zz' } }
+
+ it 'does not allow partial matches' do
+ expect(subject).to be_empty
+ end
+ end
end
context 'when filtered by skip_users' do
diff --git a/spec/fixtures/api/schemas/public_api/v4/merge_request.json b/spec/fixtures/api/schemas/public_api/v4/merge_request.json
index a55c4b8974b..66f894e9c5c 100644
--- a/spec/fixtures/api/schemas/public_api/v4/merge_request.json
+++ b/spec/fixtures/api/schemas/public_api/v4/merge_request.json
@@ -1,152 +1,140 @@
{
"type": "object",
"properties" : {
- "properties" : {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": "integer" },
- "title": { "type": "string" },
- "description": { "type": ["string", "null"] },
- "state": { "type": "string" },
- "merged_by": {
- "type": ["object", "null"],
- "properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
- },
- "additionalProperties": false
+ "id": { "type": "integer" },
+ "iid": { "type": "integer" },
+ "project_id": { "type": "integer" },
+ "title": { "type": "string" },
+ "description": { "type": ["string", "null"] },
+ "state": { "type": "string" },
+ "merged_by": {
+ "type": ["object", "null"],
+ "properties": {
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "uri" },
+ "web_url": { "type": "uri" }
},
- "merge_user": {
- "type": ["object", "null"],
- "properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
- },
- "additionalProperties": false
- },
- "merged_at": { "type": ["string", "null"] },
- "closed_by": {
- "type": ["object", "null"],
- "properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
- },
- "additionalProperties": false
- },
- "closed_at": { "type": ["string", "null"], "format": "date-time" },
- "created_at": { "type": "string", "format": "date-time" },
- "updated_at": { "type": "string", "format": "date-time" },
- "target_branch": { "type": "string" },
- "source_branch": { "type": "string" },
- "upvotes": { "type": "integer" },
- "downvotes": { "type": "integer" },
- "author": {
- "type": "object",
- "properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
- },
- "additionalProperties": false
- },
- "assignee": {
- "type": ["object", "null"],
- "properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
- },
- "additionalProperties": false
- },
- "assignees": {
- "items": {
- "$ref": "./merge_request.json"
- }
+ "additionalProperties": false
+ },
+ "merge_user": {
+ "type": ["object", "null"],
+ "properties": {
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "uri" },
+ "web_url": { "type": "uri" }
},
- "source_project_id": { "type": "integer" },
- "target_project_id": { "type": "integer" },
- "labels": {
- "type": "array",
- "items": {
- "type": "string"
- }
+ "additionalProperties": false
+ },
+ "merged_at": { "type": ["string", "null"] },
+ "closed_by": {
+ "type": ["object", "null"],
+ "properties": {
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "uri" },
+ "web_url": { "type": "uri" }
},
- "work_in_progress": { "type": "boolean" },
- "milestone": {
- "type": ["object", "null"],
- "properties": {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": ["integer", "null"] },
- "group_id": { "type": ["integer", "null"] },
- "title": { "type": "string" },
- "description": { "type": ["string", "null"] },
- "state": { "type": "string" },
- "created_at": { "type": "string", "format": "date-time" },
- "updated_at": { "type": "string", "format": "date-time" },
- "due_date": { "type": "string", "format": "date-time" },
- "start_date": { "type": "string", "format": "date-time" }
- },
- "additionalProperties": false
+ "additionalProperties": false
+ },
+ "closed_at": { "type": ["string", "null"], "format": "date-time" },
+ "created_at": { "type": "string", "format": "date-time" },
+ "updated_at": { "type": "string", "format": "date-time" },
+ "target_branch": { "type": "string" },
+ "source_branch": { "type": "string" },
+ "upvotes": { "type": "integer" },
+ "downvotes": { "type": "integer" },
+ "author": {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "uri" },
+ "web_url": { "type": "uri" }
},
- "merge_when_pipeline_succeeds": { "type": "boolean" },
- "merge_status": { "type": "string" },
- "sha": { "type": "string" },
- "merge_commit_sha": { "type": ["string", "null"] },
- "user_notes_count": { "type": "integer" },
- "changes_count": { "type": "string" },
- "should_remove_source_branch": { "type": ["boolean", "null"] },
- "force_remove_source_branch": { "type": ["boolean", "null"] },
- "discussion_locked": { "type": ["boolean", "null"] },
- "web_url": { "type": "uri" },
- "squash": { "type": "boolean" },
- "time_stats": {
- "time_estimate": { "type": "integer" },
- "total_time_spent": { "type": "integer" },
- "human_time_estimate": { "type": ["string", "null"] },
- "human_total_time_spent": { "type": ["string", "null"] }
+ "additionalProperties": false
+ },
+ "assignee": {
+ "type": ["object", "null"],
+ "properties": {
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "uri" },
+ "web_url": { "type": "uri" }
},
- "allow_collaboration": { "type": ["boolean", "null"] },
- "allow_maintainer_to_push": { "type": ["boolean", "null"] },
- "references": {
- "short": {"type": "string"},
- "relative": {"type": "string"},
- "full": {"type": "string"}
+ "additionalProperties": false
+ },
+ "assignees": {
+ "type": "array",
+ "items": {
+ "$ref": "user/basic.json"
+ }
+ },
+ "source_project_id": { "type": "integer" },
+ "target_project_id": { "type": "integer" },
+ "labels": {
+ "type": "array",
+ "items": {
+ "type": "string"
}
},
- "required": [
- "id", "iid", "project_id", "title", "description",
- "state", "created_at", "updated_at", "target_branch",
- "source_branch", "upvotes", "downvotes", "author",
- "assignee", "source_project_id", "target_project_id",
- "labels", "work_in_progress", "milestone", "merge_when_pipeline_succeeds",
- "merge_status", "sha", "merge_commit_sha", "user_notes_count",
- "should_remove_source_branch", "force_remove_source_branch",
- "web_url", "squash"
- ],
- "head_pipeline": {
+ "work_in_progress": { "type": "boolean" },
+ "milestone": {
"oneOf": [
{ "type": "null" },
- { "$ref": "pipeline/detail.json" }
+ { "$ref": "milestone.json" }
]
+ },
+ "merge_when_pipeline_succeeds": { "type": "boolean" },
+ "merge_status": { "type": "string" },
+ "sha": { "type": "string" },
+ "merge_commit_sha": { "type": ["string", "null"] },
+ "user_notes_count": { "type": "integer" },
+ "changes_count": { "type": "string" },
+ "should_remove_source_branch": { "type": ["boolean", "null"] },
+ "force_remove_source_branch": { "type": ["boolean", "null"] },
+ "discussion_locked": { "type": ["boolean", "null"] },
+ "web_url": { "type": "uri" },
+ "squash": { "type": "boolean" },
+ "time_stats": {
+ "time_estimate": { "type": "integer" },
+ "total_time_spent": { "type": "integer" },
+ "human_time_estimate": { "type": ["string", "null"] },
+ "human_total_time_spent": { "type": ["string", "null"] }
+ },
+ "allow_collaboration": { "type": ["boolean", "null"] },
+ "allow_maintainer_to_push": { "type": ["boolean", "null"] },
+ "references": {
+ "short": {"type": "string"},
+ "relative": {"type": "string"},
+ "full": {"type": "string"}
}
+ },
+ "required": [
+ "id", "iid", "project_id", "title", "description",
+ "state", "created_at", "updated_at", "target_branch",
+ "source_branch", "upvotes", "downvotes", "author",
+ "assignee", "source_project_id", "target_project_id",
+ "labels", "work_in_progress", "milestone", "merge_when_pipeline_succeeds",
+ "merge_status", "sha", "merge_commit_sha", "user_notes_count",
+ "should_remove_source_branch", "force_remove_source_branch",
+ "web_url", "squash"
+ ],
+ "head_pipeline": {
+ "oneOf": [
+ { "type": "null" },
+ { "$ref": "pipeline/detail.json" }
+ ]
}
}
diff --git a/spec/frontend/vue_shared/components/multiselect_dropdown_spec.js b/spec/frontend/vue_shared/components/multiselect_dropdown_spec.js
deleted file mode 100644
index 566ca1817f2..00000000000
--- a/spec/frontend/vue_shared/components/multiselect_dropdown_spec.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import { GlDropdown } from '@gitlab/ui';
-import { getByText } from '@testing-library/dom';
-import { shallowMount } from '@vue/test-utils';
-import MultiSelectDropdown from '~/vue_shared/components/sidebar/multiselect_dropdown.vue';
-
-describe('MultiSelectDropdown Component', () => {
- it('renders items slot', () => {
- const wrapper = shallowMount(MultiSelectDropdown, {
- propsData: {
- text: '',
- headerText: '',
- },
- slots: {
- items: '<p>Test</p>',
- },
- });
- expect(getByText(wrapper.element, 'Test')).toBeDefined();
- });
-
- it('renders search slot', () => {
- const wrapper = shallowMount(MultiSelectDropdown, {
- propsData: {
- text: '',
- headerText: '',
- },
- slots: {
- search: '<p>Search</p>',
- },
- stubs: {
- GlDropdown,
- },
- });
- expect(getByText(wrapper.element, 'Search')).toBeDefined();
- });
-});
diff --git a/spec/frontend/vue_shared/components/pikaday_spec.js b/spec/frontend/vue_shared/components/pikaday_spec.js
deleted file mode 100644
index fed4ce5e696..00000000000
--- a/spec/frontend/vue_shared/components/pikaday_spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { GlDatepicker } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import datePicker from '~/vue_shared/components/pikaday.vue';
-
-describe('datePicker', () => {
- let wrapper;
-
- const buildWrapper = (propsData = {}) => {
- wrapper = shallowMount(datePicker, {
- propsData,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
- it('should emit newDateSelected when GlDatePicker emits the input event', () => {
- const minDate = new Date();
- const maxDate = new Date();
- const selectedDate = new Date();
- const theDate = selectedDate.toISOString().slice(0, 10);
-
- buildWrapper({ minDate, maxDate, selectedDate });
-
- expect(wrapper.find(GlDatepicker).props()).toMatchObject({
- minDate,
- maxDate,
- value: selectedDate,
- });
- wrapper.find(GlDatepicker).vm.$emit('input', selectedDate);
- expect(wrapper.emitted('newDateSelected')[0][0]).toBe(theDate);
- });
- it('should emit the hidePicker event when GlDatePicker emits the close event', () => {
- buildWrapper();
-
- wrapper.find(GlDatepicker).vm.$emit('close');
-
- expect(wrapper.emitted('hidePicker')).toHaveLength(1);
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js b/spec/frontend/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js
deleted file mode 100644
index d4b43caa7b2..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlIcon } from '@gitlab/ui';
-import { nextTick } from 'vue';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-
-import CollapsedCalendarIcon from '~/vue_shared/components/sidebar/collapsed_calendar_icon.vue';
-
-describe('CollapsedCalendarIcon', () => {
- let wrapper;
-
- const defaultProps = {
- containerClass: 'test-class',
- text: 'text',
- tooltipText: 'tooltip text',
- showIcon: false,
- };
-
- const createComponent = ({ props = {} } = {}) => {
- wrapper = shallowMount(CollapsedCalendarIcon, {
- propsData: { ...defaultProps, ...props },
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findGlIcon = () => wrapper.findComponent(GlIcon);
- const getTooltip = () => getBinding(wrapper.element, 'gl-tooltip');
-
- it('adds class to container', () => {
- expect(wrapper.classes()).toContain(defaultProps.containerClass);
- });
-
- it('does not render calendar icon when showIcon is false', () => {
- expect(findGlIcon().exists()).toBe(false);
- });
-
- it('renders calendar icon when showIcon is true', () => {
- createComponent({
- props: { showIcon: true },
- });
-
- expect(findGlIcon().exists()).toBe(true);
- });
-
- it('renders text', () => {
- expect(wrapper.text()).toBe(defaultProps.text);
- });
-
- it('renders tooltipText as tooltip', () => {
- expect(getTooltip().value).toBe(defaultProps.tooltipText);
- });
-
- it('emits click event when container is clicked', async () => {
- wrapper.trigger('click');
-
- await nextTick();
-
- expect(wrapper.emitted('click')[0]).toBeDefined();
- });
-});
diff --git a/spec/frontend/vue_shared/components/sidebar/date_picker_spec.js b/spec/frontend/vue_shared/components/sidebar/date_picker_spec.js
deleted file mode 100644
index ae05d85f74f..00000000000
--- a/spec/frontend/vue_shared/components/sidebar/date_picker_spec.js
+++ /dev/null
@@ -1,126 +0,0 @@
-import { GlLoadingIcon } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import DatePicker from '~/vue_shared/components/pikaday.vue';
-import SidebarDatePicker from '~/vue_shared/components/sidebar/date_picker.vue';
-
-describe('SidebarDatePicker', () => {
- let wrapper;
-
- const createComponent = (propsData = {}, data = {}) => {
- wrapper = mount(SidebarDatePicker, {
- propsData,
- data: () => data,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findDatePicker = () => wrapper.findComponent(DatePicker);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findEditButton = () => wrapper.find('.title .btn-blank');
- const findRemoveButton = () => wrapper.find('.value-content .btn-blank');
- const findSidebarToggle = () => wrapper.find('.title .gutter-toggle');
- const findValueContent = () => wrapper.find('.value-content');
-
- it('should emit toggleCollapse when collapsed toggle sidebar is clicked', () => {
- createComponent();
-
- wrapper.find('.issuable-sidebar-header .gutter-toggle').trigger('click');
-
- expect(wrapper.emitted('toggleCollapse')).toEqual([[]]);
- });
-
- it('should render collapsed-calendar-icon', () => {
- createComponent();
-
- expect(wrapper.find('.sidebar-collapsed-icon').exists()).toBe(true);
- });
-
- it('should render value when not editing', () => {
- createComponent();
-
- expect(findValueContent().exists()).toBe(true);
- });
-
- it('should render None if there is no selectedDate', () => {
- createComponent();
-
- expect(findValueContent().text()).toBe('None');
- });
-
- it('should render date-picker when editing', () => {
- createComponent({}, { editing: true });
-
- expect(findDatePicker().exists()).toBe(true);
- });
-
- it('should render label', () => {
- const label = 'label';
- createComponent({ label });
- expect(wrapper.find('.title').text()).toBe(label);
- });
-
- it('should render loading-icon when isLoading', () => {
- createComponent({ isLoading: true });
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- describe('editable', () => {
- beforeEach(() => {
- createComponent({ editable: true });
- });
-
- it('should render edit button', () => {
- expect(findEditButton().text()).toBe('Edit');
- });
-
- it('should enable editing when edit button is clicked', async () => {
- findEditButton().trigger('click');
-
- await nextTick();
-
- expect(wrapper.vm.editing).toBe(true);
- });
- });
-
- it('should render date if selectedDate', () => {
- createComponent({ selectedDate: new Date('07/07/2017') });
-
- expect(wrapper.find('.value-content strong').text()).toBe('Jul 7, 2017');
- });
-
- describe('selectedDate and editable', () => {
- beforeEach(() => {
- createComponent({ selectedDate: new Date('07/07/2017'), editable: true });
- });
-
- it('should render remove button if selectedDate and editable', () => {
- expect(findRemoveButton().text()).toBe('remove');
- });
-
- it('should emit saveDate with null when remove button is clicked', () => {
- findRemoveButton().trigger('click');
-
- expect(wrapper.emitted('saveDate')).toEqual([[null]]);
- });
- });
-
- describe('showToggleSidebar', () => {
- beforeEach(() => {
- createComponent({ showToggleSidebar: true });
- });
-
- it('should render toggle-sidebar when showToggleSidebar', () => {
- expect(findSidebarToggle().exists()).toBe(true);
- });
-
- it('should emit toggleCollapse when toggle sidebar is clicked', () => {
- findSidebarToggle().trigger('click');
-
- expect(wrapper.emitted('toggleCollapse')).toEqual([[]]);
- });
- });
-});
diff --git a/spec/lib/gitlab/application_context_spec.rb b/spec/lib/gitlab/application_context_spec.rb
index 5ecec978017..55f5ae7d7dc 100644
--- a/spec/lib/gitlab/application_context_spec.rb
+++ b/spec/lib/gitlab/application_context_spec.rb
@@ -125,6 +125,17 @@ RSpec.describe Gitlab::ApplicationContext do
.to include(project: project.full_path, root_namespace: project.full_path_components.first)
end
+ it 'contains known keys' do
+ context = described_class.new(project: project)
+
+ # Make sure all possible keys would be included
+ allow(context).to receive_message_chain(:set_values, :include?).and_return(true)
+
+ # If a newly added key is added to the context hash, we need to list it in
+ # the known keys constant. This spec ensures that we do.
+ expect(context.to_lazy_hash.keys).to contain_exactly(*described_class.known_keys)
+ end
+
describe 'setting the client' do
let_it_be(:remote_ip) { '127.0.0.1' }
let_it_be(:runner) { create(:ci_runner) }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 31bcb3e2bbf..58f58232d52 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -2612,6 +2612,12 @@ RSpec.describe User do
it 'returns users with a exact matching name shorter than 3 chars regardless of the casing' do
expect(described_class.search(user3.name.upcase)).to eq([user3])
end
+
+ context 'when use_minimum_char_limit is false' do
+ it 'returns users with a partially matching name' do
+ expect(described_class.search('u', use_minimum_char_limit: false)).to eq([user3, user, user2])
+ end
+ end
end
describe 'email matching' do
@@ -2671,204 +2677,20 @@ RSpec.describe User do
it 'returns users with a exact matching username shorter than 3 chars regardless of the casing' do
expect(described_class.search(user3.username.upcase)).to eq([user3])
end
- end
-
- it 'returns no matches for an empty string' do
- expect(described_class.search('')).to be_empty
- end
-
- it 'returns no matches for nil' do
- expect(described_class.search(nil)).to be_empty
- end
- end
-
- describe '.search_without_secondary_emails' do
- let_it_be(:user) { create(:user, name: 'John Doe', username: 'john.doe', email: 'someone.1@example.com' ) }
- let_it_be(:another_user) { create(:user, name: 'Albert Smith', username: 'albert.smith', email: 'another.2@example.com' ) }
- let_it_be(:email) { create(:email, user: another_user, email: 'alias@example.com') }
- it 'returns users with a matching name' do
- expect(described_class.search_without_secondary_emails(user.name)).to eq([user])
- end
-
- it 'returns users with a partially matching name' do
- expect(described_class.search_without_secondary_emails(user.name[0..2])).to eq([user])
- end
-
- it 'returns users with a matching name regardless of the casing' do
- expect(described_class.search_without_secondary_emails(user.name.upcase)).to eq([user])
- end
-
- it 'returns users with a matching email' do
- expect(described_class.search_without_secondary_emails(user.email)).to eq([user])
- end
-
- it 'does not return users with a partially matching email' do
- expect(described_class.search_without_secondary_emails(user.email[1...-1])).to be_empty
- end
-
- it 'returns users with a matching email regardless of the casing' do
- expect(described_class.search_without_secondary_emails(user.email.upcase)).to eq([user])
- end
-
- it 'returns users with a matching username' do
- expect(described_class.search_without_secondary_emails(user.username)).to eq([user])
- end
-
- it 'returns users with a partially matching username' do
- expect(described_class.search_without_secondary_emails(user.username[0..2])).to eq([user])
- end
-
- it 'returns users with a matching username regardless of the casing' do
- expect(described_class.search_without_secondary_emails(user.username.upcase)).to eq([user])
- end
-
- it 'does not return users with a matching whole secondary email' do
- expect(described_class.search_without_secondary_emails(email.email)).not_to include(email.user)
- end
-
- it 'does not return users with a matching part of secondary email' do
- expect(described_class.search_without_secondary_emails(email.email[1...-1])).to be_empty
- end
-
- it 'returns no matches for an empty string' do
- expect(described_class.search_without_secondary_emails('')).to be_empty
- end
-
- it 'returns no matches for nil' do
- expect(described_class.search_without_secondary_emails(nil)).to be_empty
- end
- end
-
- describe '.search_with_public_emails' do
- let_it_be(:user) { create(:user, name: 'John Doe', username: 'john.doe', email: 'someone.1@example.com' ) }
- let_it_be(:another_user) { create(:user, name: 'Albert Smith', username: 'albert.smith', email: 'another.2@example.com' ) }
- let_it_be(:public_email) do
- create(:email, :confirmed, user: another_user, email: 'alias@example.com').tap do |email|
- another_user.update!(public_email: email.email)
+ context 'when use_minimum_char_limit is false' do
+ it 'returns users with a partially matching username' do
+ expect(described_class.search('se', use_minimum_char_limit: false)).to eq([user3, user, user2])
+ end
end
end
- let_it_be(:secondary_email) do
- create(:email, :confirmed, user: another_user, email: 'secondary@example.com')
- end
-
- it 'returns users with a matching name' do
- expect(described_class.search_with_public_emails(user.name)).to match_array([user])
- end
-
- it 'returns users with a partially matching name' do
- expect(described_class.search_with_public_emails(user.name[0..2])).to match_array([user])
- end
-
- it 'returns users with a matching name regardless of the casing' do
- expect(described_class.search_with_public_emails(user.name.upcase)).to match_array([user])
- end
-
- it 'returns users with a matching public email' do
- expect(described_class.search_with_public_emails(another_user.public_email)).to match_array([another_user])
- end
-
- it 'does not return users with a partially matching email' do
- expect(described_class.search_with_public_emails(another_user.public_email[1...-1])).to be_empty
- end
-
- it 'returns users with a matching email regardless of the casing' do
- expect(described_class.search_with_public_emails(another_user.public_email.upcase)).to match_array([another_user])
- end
-
- it 'returns users with a matching username' do
- expect(described_class.search_with_public_emails(user.username)).to match_array([user])
- end
-
- it 'returns users with a partially matching username' do
- expect(described_class.search_with_public_emails(user.username[0..2])).to match_array([user])
- end
-
- it 'returns users with a matching username regardless of the casing' do
- expect(described_class.search_with_public_emails(user.username.upcase)).to match_array([user])
- end
-
- it 'does not return users with a matching whole private email' do
- expect(described_class.search_with_public_emails(user.email)).not_to include(user)
- end
-
- it 'does not return users with a matching whole private email' do
- expect(described_class.search_with_public_emails(secondary_email.email)).to be_empty
- end
-
- it 'does not return users with a matching part of secondary email' do
- expect(described_class.search_with_public_emails(secondary_email.email[1...-1])).to be_empty
- end
-
- it 'does not return users with a matching part of private email' do
- expect(described_class.search_with_public_emails(user.email[1...-1])).to be_empty
- end
-
- it 'returns no matches for an empty string' do
- expect(described_class.search_with_public_emails('')).to be_empty
- end
-
- it 'returns no matches for nil' do
- expect(described_class.search_with_public_emails(nil)).to be_empty
- end
- end
-
- describe '.search_with_secondary_emails' do
- let_it_be(:user) { create(:user, name: 'John Doe', username: 'john.doe', email: 'someone.1@example.com' ) }
- let_it_be(:another_user) { create(:user, name: 'Albert Smith', username: 'albert.smith', email: 'another.2@example.com' ) }
- let_it_be(:email) { create(:email, user: another_user, email: 'alias@example.com') }
-
- it 'returns users with a matching name' do
- expect(described_class.search_with_secondary_emails(user.name)).to eq([user])
- end
-
- it 'returns users with a partially matching name' do
- expect(described_class.search_with_secondary_emails(user.name[0..2])).to eq([user])
- end
-
- it 'returns users with a matching name regardless of the casing' do
- expect(described_class.search_with_secondary_emails(user.name.upcase)).to eq([user])
- end
-
- it 'returns users with a matching email' do
- expect(described_class.search_with_secondary_emails(user.email)).to eq([user])
- end
-
- it 'does not return users with a partially matching email' do
- expect(described_class.search_with_secondary_emails(user.email[1...-1])).to be_empty
- end
-
- it 'returns users with a matching email regardless of the casing' do
- expect(described_class.search_with_secondary_emails(user.email.upcase)).to eq([user])
- end
-
- it 'returns users with a matching username' do
- expect(described_class.search_with_secondary_emails(user.username)).to eq([user])
- end
-
- it 'returns users with a partially matching username' do
- expect(described_class.search_with_secondary_emails(user.username[0..2])).to eq([user])
- end
-
- it 'returns users with a matching username regardless of the casing' do
- expect(described_class.search_with_secondary_emails(user.username.upcase)).to eq([user])
- end
-
- it 'returns users with a matching whole secondary email' do
- expect(described_class.search_with_secondary_emails(email.email)).to eq([email.user])
- end
-
- it 'does not return users with a matching part of secondary email' do
- expect(described_class.search_with_secondary_emails(email.email[1...-1])).to be_empty
- end
-
it 'returns no matches for an empty string' do
- expect(described_class.search_with_secondary_emails('')).to be_empty
+ expect(described_class.search('')).to be_empty
end
it 'returns no matches for nil' do
- expect(described_class.search_with_secondary_emails(nil)).to be_empty
+ expect(described_class.search(nil)).to be_empty
end
end
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index 0f24883bd7b..2421fd56c47 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -60,7 +60,8 @@ RSpec.describe Ci::RetryBuildService do
artifacts_file artifacts_metadata artifacts_size commands
resource resource_group_id processed security_scans author
pipeline_id report_results pending_state pages_deployments
- queuing_entry runtime_metadata trace_metadata].freeze
+ queuing_entry runtime_metadata trace_metadata
+ dast_site_profile dast_scanner_profile].freeze
shared_examples 'build duplication' do
let_it_be(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index 127c94763d9..ecb856bd1a4 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -388,7 +388,7 @@ RSpec.describe MergeRequests::MergeService do
end
it 'logs and saves error if there is an error when squashing' do
- error_message = 'Failed to squash. Should be done manually'
+ error_message = 'Squashing failed: Squash the commits locally, resolve any conflicts, then push the branch.'
allow_any_instance_of(MergeRequests::SquashService).to receive(:squash!).and_return(nil)
merge_request.update!(squash: true)
diff --git a/spec/services/merge_requests/rebase_service_spec.rb b/spec/services/merge_requests/rebase_service_spec.rb
index e671bbf2cd6..a47e626666b 100644
--- a/spec/services/merge_requests/rebase_service_spec.rb
+++ b/spec/services/merge_requests/rebase_service_spec.rb
@@ -82,7 +82,7 @@ RSpec.describe MergeRequests::RebaseService do
context 'with a pre-receive failure' do
let(:pre_receive_error) { "Commit message does not follow the pattern 'ACME'" }
- let(:merge_error) { "Something went wrong during the rebase pre-receive hook: #{pre_receive_error}." }
+ let(:merge_error) { "The rebase pre-receive hook failed: #{pre_receive_error}." }
before do
allow(repository).to receive(:gitaly_operation_client).and_raise(Gitlab::Git::PreReceiveError, "GitLab: #{pre_receive_error}")
diff --git a/spec/services/merge_requests/squash_service_spec.rb b/spec/services/merge_requests/squash_service_spec.rb
index e5bea0e7b14..387be8471b5 100644
--- a/spec/services/merge_requests/squash_service_spec.rb
+++ b/spec/services/merge_requests/squash_service_spec.rb
@@ -168,7 +168,7 @@ RSpec.describe MergeRequests::SquashService do
it 'raises a squash error' do
expect(service.execute).to match(
status: :error,
- message: a_string_including('does not allow squashing commits when merge requests are accepted'))
+ message: a_string_including('allow you to squash commits when merging'))
end
end
@@ -205,7 +205,7 @@ RSpec.describe MergeRequests::SquashService do
end
it 'returns an error' do
- expect(service.execute).to match(status: :error, message: a_string_including('squash'))
+ expect(service.execute).to match(status: :error, message: a_string_including('Squash'))
end
end
end
@@ -232,7 +232,7 @@ RSpec.describe MergeRequests::SquashService do
end
it 'returns an error' do
- expect(service.execute).to match(status: :error, message: a_string_including('squash'))
+ expect(service.execute).to match(status: :error, message: a_string_including('Squash'))
end
it 'cleans up the temporary directory' do
diff --git a/spec/support/matchers/schema_matcher.rb b/spec/support/matchers/schema_matcher.rb
index 5e08e96f4e1..d2f32b60464 100644
--- a/spec/support/matchers/schema_matcher.rb
+++ b/spec/support/matchers/schema_matcher.rb
@@ -36,12 +36,38 @@ end
RSpec::Matchers.define :match_response_schema do |schema, dir: nil, **options|
match do |response|
- schema_path = Pathname.new(SchemaPath.expand(schema, dir))
- validator = SchemaPath.validator(schema_path)
+ @schema_path = Pathname.new(SchemaPath.expand(schema, dir))
+ validator = SchemaPath.validator(@schema_path)
- data = Gitlab::Json.parse(response.body)
+ @data = Gitlab::Json.parse(response.body)
- validator.valid?(data)
+ @schema_errors = validator.validate(@data)
+ @schema_errors.none?
+ end
+
+ failure_message do |actual|
+ message = []
+
+ message << <<~MESSAGE
+ expected JSON response to match schema #{@schema_path.inspect}.
+
+ JSON input: #{Gitlab::Json.pretty_generate(@data).indent(2)}
+
+ Schema errors:
+ MESSAGE
+
+ @schema_errors.each do |error|
+ property_name, actual_value = error.values_at('data_pointer', 'data')
+ property_name = 'root' if property_name.empty?
+
+ message << <<~MESSAGE
+ Property: #{property_name}
+ Actual value: #{Gitlab::Json.pretty_generate(actual_value).indent(2)}
+ Error: #{JSONSchemer::Errors.pretty(error)}
+ MESSAGE
+ end
+
+ message.join("\n")
end
end
diff --git a/spec/tasks/gitlab/info_rake_spec.rb b/spec/tasks/gitlab/info_rake_spec.rb
deleted file mode 100644
index 19ed43723e2..00000000000
--- a/spec/tasks/gitlab/info_rake_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-require 'rake_helper'
-
-RSpec.describe 'gitlab:env:info', :silence_stdout do
- before do
- Rake.application.rake_require 'tasks/gitlab/info'
-
- stub_warn_user_is_not_gitlab
- allow(Gitlab::Popen).to receive(:popen)
- end
-
- describe 'git version' do
- before do
- allow(Gitlab::Popen).to receive(:popen).with([Gitlab.config.git.bin_path, '--version'])
- .and_return(git_version)
- end
-
- context 'when git installed' do
- let(:git_version) { 'git version 2.10.0' }
-
- it 'prints git version' do
- run_rake_task('gitlab:env:info')
-
- expect($stdout.string).to match(/Git Version:(.*)2.10.0/)
- end
- end
-
- context 'when git not installed' do
- let(:git_version) { '' }
-
- it 'prints unknown' do
- run_rake_task('gitlab:env:info')
-
- expect($stdout.string).to match(/Git Version:(.*)unknown/)
- end
- end
- end
-end