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
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-10-23 21:11:07 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-10-23 21:11:07 +0300
commitc5da163db1c10676b1a01a898b7b3a4506e65b89 (patch)
tree0110bf1518c8049cddab8d62e77d4f7dad2478a7 /app
parent750a63ac4cc7acbecff3b8d22232cb1c15af34fb (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue6
-rw-r--r--app/assets/javascripts/ci/constants.js1
-rw-r--r--app/assets/javascripts/ci/pipeline_editor/components/file_nav/branch_switcher.vue97
-rw-r--r--app/assets/javascripts/ci/pipelines_page/pipelines.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue36
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue106
-rw-r--r--app/assets/javascripts/work_items/components/work_item_labels.vue7
-rw-r--r--app/controllers/dashboard_controller.rb1
-rw-r--r--app/controllers/groups_controller.rb1
-rw-r--r--app/controllers/projects/issues_controller.rb1
-rw-r--r--app/graphql/types/merge_request_type.rb3
-rw-r--r--app/models/merge_request.rb12
-rw-r--r--app/models/users/phone_number_validation.rb1
-rw-r--r--app/services/merge_requests/mergeability/check_base_service.rb5
-rw-r--r--app/services/merge_requests/mergeability/check_ci_status_service.rb2
-rw-r--r--app/services/merge_requests/mergeability/check_discussions_status_service.rb2
-rw-r--r--app/services/merge_requests/mergeability/check_rebase_status_service.rb2
-rw-r--r--app/services/merge_requests/mergeability/detailed_merge_status_service.rb6
-rw-r--r--app/services/merge_requests/mergeability/run_checks_service.rb2
-rw-r--r--app/views/projects/readme_templates/default.md.tt5
21 files changed, 215 insertions, 92 deletions
diff --git a/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue b/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue
index c0feb52c185..026a30988fd 100644
--- a/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue
+++ b/app/assets/javascripts/ci/catalog/components/details/ci_resource_details.vue
@@ -30,12 +30,12 @@ export default {
<template>
<gl-tabs>
- <gl-tab v-if="glFeatures.ciCatalogComponentsTab" :title="$options.i18n.tabs.components" lazy>
- <ci-resource-components :resource-id="resourceId"
- /></gl-tab>
<gl-tab :title="$options.i18n.tabs.readme" lazy>
<ci-resource-readme :resource-id="resourceId" />
</gl-tab>
+ <gl-tab v-if="glFeatures.ciCatalogComponentsTab" :title="$options.i18n.tabs.components" lazy>
+ <ci-resource-components :resource-id="resourceId"
+ /></gl-tab>
</gl-tabs>
</template>
<style></style>
diff --git a/app/assets/javascripts/ci/constants.js b/app/assets/javascripts/ci/constants.js
index 5b60528f521..138a44a8dd0 100644
--- a/app/assets/javascripts/ci/constants.js
+++ b/app/assets/javascripts/ci/constants.js
@@ -37,4 +37,5 @@ export const TRACKING_CATEGORIES = {
search: 'pipelines_filtered_search',
failed: 'pipeline_failed_jobs_tab',
tests: 'pipeline_tests_tab',
+ listbox: 'pipeline_id_iid_listbox',
};
diff --git a/app/assets/javascripts/ci/pipeline_editor/components/file_nav/branch_switcher.vue b/app/assets/javascripts/ci/pipeline_editor/components/file_nav/branch_switcher.vue
index 221a45d4d9a..21e21d54758 100644
--- a/app/assets/javascripts/ci/pipeline_editor/components/file_nav/branch_switcher.vue
+++ b/app/assets/javascripts/ci/pipeline_editor/components/file_nav/branch_switcher.vue
@@ -1,13 +1,5 @@
<script>
-import {
- GlDropdown,
- GlDropdownItem,
- GlDropdownSectionHeader,
- GlInfiniteScroll,
- GlLoadingIcon,
- GlSearchBoxByType,
- GlTooltipDirective,
-} from '@gitlab/ui';
+import { GlCollapsibleListbox, GlTooltipDirective } from '@gitlab/ui';
import { produce } from 'immer';
import { historyPushState } from '~/lib/utils/common_utils';
import { setUrlParams } from '~/lib/utils/url_utility';
@@ -25,17 +17,11 @@ import getLastCommitBranch from '~/ci/pipeline_editor/graphql/queries/client/las
export default {
i18n: {
dropdownHeader: __('Switch branch'),
- title: __('Branches'),
fetchError: __('Unable to fetch branch list for this project.'),
},
inputDebounce: BRANCH_SEARCH_DEBOUNCE,
components: {
- GlDropdown,
- GlDropdownItem,
- GlDropdownSectionHeader,
- GlInfiniteScroll,
- GlLoadingIcon,
- GlSearchBoxByType,
+ GlCollapsibleListbox,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -66,6 +52,7 @@ export default {
pageCounter: 0,
searchTerm: '',
lastCommitBranch: '',
+ infiniteScrollLoading: false,
};
},
apollo: {
@@ -112,6 +99,18 @@ export default {
},
},
computed: {
+ infiniteScrollEnabled() {
+ return this.availableBranches.length > 0;
+ },
+ branchesData() {
+ return this.availableBranches.map((branch) => ({
+ text: branch,
+ extraAttrs: {
+ 'data-qa-selector': 'branch_menu_item_button',
+ },
+ value: branch,
+ }));
+ },
availableBranchesVariables() {
if (this.searchTerm.length > 0) {
return {
@@ -128,7 +127,7 @@ export default {
enableBranchSwitcher() {
return this.availableBranches.length > 0 || this.searchTerm.length > 0;
},
- isBranchesLoading() {
+ areBranchesLoading() {
return this.$apollo.queries.availableBranches.loading;
},
},
@@ -143,7 +142,7 @@ export default {
// if there is no searchPattern, paginate by {paginationLimit} branches
fetchNextBranches() {
if (
- this.isBranchesLoading ||
+ this.areBranchesLoading ||
this.searchTerm.length > 0 ||
this.availableBranches.length >= this.totalBranches
) {
@@ -178,16 +177,14 @@ export default {
this.$emit('refetchContent');
},
selectBranch(newBranch) {
- if (newBranch !== this.currentBranch) {
- // If there are unsaved changes, we want to show the user
- // a modal to confirm what to do with these before changing
- // branches.
- if (this.hasUnsavedChanges) {
- this.branchSelected = newBranch;
- this.$emit('select-branch', newBranch);
- } else {
- this.changeBranch(newBranch);
- }
+ // If there are unsaved changes, we want to show the user
+ // a modal to confirm what to do with these before changing
+ // branches.
+ if (this.hasUnsavedChanges) {
+ this.branchSelected = newBranch;
+ this.$emit('select-branch', newBranch);
+ } else {
+ this.changeBranch(newBranch);
}
},
async setSearchTerm(newSearchTerm) {
@@ -211,41 +208,23 @@ export default {
</script>
<template>
- <gl-dropdown
+ <gl-collapsible-listbox
+ v-model="currentBranch"
v-gl-tooltip.hover
+ data-qa-selector="branch_selector_button"
+ searchable
+ :items="branchesData"
:title="$options.i18n.dropdownHeader"
:header-text="$options.i18n.dropdownHeader"
- :text="currentBranch"
+ :toggle-text="currentBranch"
:disabled="!enableBranchSwitcher"
icon="branch"
data-testid="branch-selector"
- >
- <gl-search-box-by-type :debounce="$options.inputDebounce" @input="setSearchTerm" />
- <gl-dropdown-section-header>
- {{ $options.i18n.title }}
- </gl-dropdown-section-header>
-
- <gl-infinite-scroll
- :fetched-items="availableBranches.length"
- :max-list-height="250"
- @bottomReached="fetchNextBranches"
- >
- <template #items>
- <gl-dropdown-item
- v-for="branch in availableBranches"
- :key="branch"
- :is-checked="currentBranch === branch"
- is-check-item
- @click="selectBranch(branch)"
- >
- {{ branch }}
- </gl-dropdown-item>
- </template>
- <template #default>
- <gl-dropdown-item v-if="isBranchesLoading" key="loading">
- <gl-loading-icon size="lg" />
- </gl-dropdown-item>
- </template>
- </gl-infinite-scroll>
- </gl-dropdown>
+ :no-results-text="$options.i18n.fetchError"
+ :infinite-scroll-loading="areBranchesLoading"
+ :infinite-scroll="infiniteScrollEnabled"
+ @select="selectBranch"
+ @search="setSearchTerm"
+ @bottom-reached="fetchNextBranches"
+ />
</template>
diff --git a/app/assets/javascripts/ci/pipelines_page/pipelines.vue b/app/assets/javascripts/ci/pipelines_page/pipelines.vue
index c4db0ecd05a..98e005a162f 100644
--- a/app/assets/javascripts/ci/pipelines_page/pipelines.vue
+++ b/app/assets/javascripts/ci/pipelines_page/pipelines.vue
@@ -306,6 +306,12 @@ export default {
},
changeVisibilityPipelineIDType(idType) {
this.visibilityPipelineIdType = idType;
+ if (idType === PIPELINE_IID_KEY) {
+ this.track('pipelines_display_options', {
+ label: TRACKING_CATEGORIES.listbox,
+ property: idType,
+ });
+ }
if (isLoggedIn()) {
this.saveVisibilityPipelineIDType(idType);
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue
index 346384e3023..d39e4d2ee42 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue
@@ -292,7 +292,9 @@ export default {
this.recentSearchesService.save(resultantSearches);
this.recentSearches = [];
},
- handleFilterSubmit() {
+ async handleFilterSubmit() {
+ this.blurSearchInput();
+ await this.$nextTick();
const filterTokens = uniqueTokens(this.filterValue);
this.filterValue = filterTokens;
@@ -309,7 +311,6 @@ export default {
// https://gitlab.com/gitlab-org/gitlab-foss/issues/30821
});
}
- this.blurSearchInput();
this.$emit('onFilter', this.removeQuotesEnclosure(filterTokens));
},
historyTokenOptionTitle(historyToken) {
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
index 23de8dd5596..3857dd9c55d 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
@@ -7,9 +7,10 @@ import {
GlDropdownText,
GlLoadingIcon,
} from '@gitlab/ui';
-import { debounce } from 'lodash';
+import { debounce, last } from 'lodash';
import { stripQuotes } from '~/lib/utils/text_utility';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { DEBOUNCE_DELAY, FILTERS_NONE_ANY, OPERATOR_NOT, OPERATOR_OR } from '../constants';
import { getRecentlyUsedSuggestions, setTokenValueToRecentlyUsed } from '../filtered_search_utils';
@@ -22,6 +23,7 @@ export default {
GlDropdownText,
GlLoadingIcon,
},
+ mixins: [glFeatureFlagMixin()],
props: {
config: {
type: Object,
@@ -70,6 +72,11 @@ export default {
required: false,
default: undefined,
},
+ multiSelectValues: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
data() {
return {
@@ -94,7 +101,11 @@ export default {
return this.preloadedSuggestions.map((tokenValue) => tokenValue[this.valueIdentifier]);
},
activeTokenValue() {
- return this.getActiveTokenValue(this.suggestions, this.value.data);
+ const data =
+ this.glFeatures.groupMultiSelectTokens && Array.isArray(this.value.data)
+ ? last(this.value.data)
+ : this.value.data;
+ return this.getActiveTokenValue(this.suggestions, data);
},
availableDefaultSuggestions() {
if ([OPERATOR_NOT, OPERATOR_OR].includes(this.value.operator)) {
@@ -146,10 +157,14 @@ export default {
watch: {
active: {
immediate: true,
- handler(newValue) {
- if (!newValue && !this.suggestions.length) {
- const search = this.searchTerm ? this.searchTerm : this.value.data;
- this.$emit('fetch-suggestions', search);
+ handler(active) {
+ if (!active && !this.suggestions.length) {
+ // data could be a string or an array of strings
+ const selectedItems = [this.value.data].flat();
+ selectedItems.forEach((item) => {
+ const search = this.searchTerm ? this.searchTerm : item;
+ this.$emit('fetch-suggestions', search);
+ });
}
},
},
@@ -163,6 +178,9 @@ export default {
},
methods: {
handleInput: debounce(function debouncedSearch({ data, operator }) {
+ // in multiSelect mode, data could be an array
+ if (Array.isArray(data)) return;
+
// Prevent fetching suggestions when data or operator is not present
if (data || operator) {
this.searchKey = data;
@@ -181,8 +199,11 @@ export default {
}
}, DEBOUNCE_DELAY),
handleTokenValueSelected(selectedValue) {
- const activeTokenValue = this.getActiveTokenValue(this.suggestions, selectedValue);
+ if (this.glFeatures.groupMultiSelectTokens) {
+ this.$emit('token-selected', selectedValue);
+ }
+ const activeTokenValue = this.getActiveTokenValue(this.suggestions, selectedValue);
// Make sure that;
// 1. Recently used values feature is enabled
// 2. User has actually selected a value
@@ -210,6 +231,7 @@ export default {
:config="config"
:value="value"
:active="active"
+ :multi-select-values="multiSelectValues"
v-bind="$attrs"
v-on="$listeners"
@input="handleInput"
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue
index 4601287b417..c5326ead60d 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue
@@ -1,11 +1,12 @@
<script>
-import { GlAvatar, GlFilteredSearchSuggestion } from '@gitlab/ui';
+import { GlAvatar, GlIcon, GlIntersperse, GlFilteredSearchSuggestion } from '@gitlab/ui';
import { compact } from 'lodash';
import { createAlert } from '~/alert';
import { __ } from '~/locale';
import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import usersAutocompleteQuery from '~/graphql_shared/queries/users_autocomplete.query.graphql';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { OPTIONS_NONE_ANY } from '../constants';
import BaseToken from './base_token.vue';
@@ -14,8 +15,11 @@ export default {
components: {
BaseToken,
GlAvatar,
+ GlIcon,
+ GlIntersperse,
GlFilteredSearchSuggestion,
},
+ mixins: [glFeatureFlagMixin()],
props: {
config: {
type: Object,
@@ -32,8 +36,11 @@ export default {
},
data() {
return {
+ // current users visible in list
users: this.config.initialUsers || [],
+ allUsers: this.config.initialUsers || [],
loading: false,
+ selectedUsernames: [],
};
},
computed: {
@@ -49,13 +56,69 @@ export default {
fetchUsersQuery() {
return this.config.fetchUsers ? this.config.fetchUsers : this.fetchUsersBySearchTerm;
},
+ multiSelectEnabled() {
+ return this.config.multiSelect && this.glFeatures.groupMultiSelectTokens;
+ },
+ },
+ watch: {
+ value: {
+ deep: true,
+ immediate: true,
+ handler(newValue) {
+ const { data } = newValue;
+
+ if (!this.multiSelectEnabled) {
+ return;
+ }
+
+ // don't add empty values to selectedUsernames
+ if (!data) {
+ return;
+ }
+
+ if (Array.isArray(data)) {
+ this.selectedUsernames = data;
+ // !active so we don't add strings while searching, e.g. r, ro, roo
+ // !includes so we don't add the same usernames (if @input is emitted twice)
+ } else if (!this.active && !this.selectedUsernames.includes(data)) {
+ this.selectedUsernames = this.selectedUsernames.concat(data);
+ }
+ },
+ },
},
methods: {
getActiveUser(users, data) {
return users.find((user) => user.username.toLowerCase() === data.toLowerCase());
},
getAvatarUrl(user) {
- return user.avatarUrl || user.avatar_url;
+ return user?.avatarUrl || user?.avatar_url;
+ },
+ displayNameFor(username) {
+ return this.getActiveUser(this.allUsers, username)?.name || `@${username}`;
+ },
+ avatarFor(username) {
+ const user = this.getActiveUser(this.allUsers, username);
+ return this.getAvatarUrl(user);
+ },
+ addCheckIcon(username) {
+ return this.multiSelectEnabled && this.selectedUsernames.includes(username);
+ },
+ addPadding(username) {
+ return this.multiSelectEnabled && !this.selectedUsernames.includes(username);
+ },
+ handleSelected(username) {
+ if (!this.multiSelectEnabled) {
+ return;
+ }
+
+ const index = this.selectedUsernames.indexOf(username);
+ if (index > -1) {
+ this.selectedUsernames.splice(index, 1);
+ } else {
+ this.selectedUsernames.push(username);
+ }
+
+ this.$emit('input', { ...this.value, data: '' });
},
fetchUsersBySearchTerm(search) {
return this.$apollo
@@ -79,6 +142,7 @@ export default {
// TODO: rm when completed https://gitlab.com/gitlab-org/gitlab/-/issues/345756
this.users = Array.isArray(res) ? compact(res) : compact(res.data);
+ this.allUsers = this.allUsers.concat(this.users);
})
.catch(() =>
createAlert({
@@ -103,18 +167,32 @@ export default {
:get-active-token-value="getActiveUser"
:default-suggestions="defaultUsers"
:preloaded-suggestions="preloadedUsers"
+ :multi-select-values="selectedUsernames"
v-bind="$attrs"
@fetch-suggestions="fetchUsers"
+ @token-selected="handleSelected"
v-on="$listeners"
>
<template #view="{ viewTokenProps: { inputValue, activeTokenValue } }">
- <gl-avatar
- v-if="activeTokenValue"
- :size="16"
- :src="getAvatarUrl(activeTokenValue)"
- class="gl-mr-2"
- />
- {{ activeTokenValue ? activeTokenValue.name : inputValue }}
+ <gl-intersperse v-if="multiSelectEnabled" separator=",">
+ <span
+ v-for="(username, index) in selectedUsernames"
+ :key="username"
+ :class="{ 'gl-ml-2': index > 0 }"
+ ><gl-avatar :size="16" :src="avatarFor(username)" class="gl-mr-1" />{{
+ displayNameFor(username)
+ }}</span
+ >
+ </gl-intersperse>
+ <template v-else>
+ <gl-avatar
+ v-if="activeTokenValue"
+ :size="16"
+ :src="getAvatarUrl(activeTokenValue)"
+ class="gl-mr-2"
+ />
+ {{ activeTokenValue ? activeTokenValue.name : inputValue }}
+ </template>
</template>
<template #suggestions-list="{ suggestions }">
<gl-filtered-search-suggestion
@@ -122,7 +200,15 @@ export default {
:key="user.username"
:value="user.username"
>
- <div class="gl-display-flex">
+ <div
+ class="gl-display-flex gl-align-items-center"
+ :class="{ 'gl-pl-6': addPadding(user.username) }"
+ >
+ <gl-icon
+ v-if="addCheckIcon(user.username)"
+ name="check"
+ class="gl-mr-3 gl-text-secondary gl-flex-shrink-0"
+ />
<gl-avatar :size="32" :src="getAvatarUrl(user)" />
<div>
<div>{{ user.name }}</div>
diff --git a/app/assets/javascripts/work_items/components/work_item_labels.vue b/app/assets/javascripts/work_items/components/work_item_labels.vue
index 3cdbf816421..7a5d3b1155f 100644
--- a/app/assets/javascripts/work_items/components/work_item_labels.vue
+++ b/app/assets/javascripts/work_items/components/work_item_labels.vue
@@ -3,7 +3,8 @@ import { GlTokenSelector, GlLabel, GlSkeletonLoader } from '@gitlab/ui';
import { debounce, uniqueId, without } from 'lodash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import Tracking from '~/tracking';
-import labelSearchQuery from '~/sidebar/components/labels/labels_select_widget/graphql/project_labels.query.graphql';
+import groupLabelsQuery from '~/sidebar/components/labels/labels_select_widget/graphql/group_labels.query.graphql';
+import projectLabelsQuery from '~/sidebar/components/labels/labels_select_widget/graphql/project_labels.query.graphql';
import LabelItem from '~/sidebar/components/labels/labels_select_widget/label_item.vue';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { isScopedLabel } from '~/lib/utils/common_utils';
@@ -90,7 +91,9 @@ export default {
},
},
searchLabels: {
- query: labelSearchQuery,
+ query() {
+ return this.isGroup ? groupLabelsQuery : projectLabelsQuery;
+ },
variables() {
return {
fullPath: this.fullPath,
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 188a8540a58..a0997484c58 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -14,6 +14,7 @@ class DashboardController < Dashboard::ApplicationController
before_action only: :issues do
push_frontend_feature_flag(:frontend_caching)
+ push_frontend_feature_flag(:group_multi_select_tokens)
end
before_action only: :merge_requests do
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index edc590e1370..83c4efa5a42 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -37,6 +37,7 @@ class GroupsController < Groups::ApplicationController
push_frontend_feature_flag(:frontend_caching, group)
push_force_frontend_feature_flag(:work_items, group.work_items_feature_flag_enabled?)
push_frontend_feature_flag(:issues_grid_view)
+ push_frontend_feature_flag(:group_multi_select_tokens, group)
end
before_action only: :merge_requests do
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index ed1712ac714..40e1b846268 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -60,6 +60,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action only: [:index, :service_desk] do
push_frontend_feature_flag(:or_issuable_queries, project)
push_frontend_feature_flag(:frontend_caching, project&.group)
+ push_frontend_feature_flag(:group_multi_select_tokens, project)
end
before_action only: :show do
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index e6625e44508..9dca82f1750 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -106,7 +106,8 @@ module Types
null: false,
description: 'Status of all mergeability checks of the merge request.',
method: :all_mergeability_checks_results,
- alpha: { milestone: '16.5' }
+ alpha: { milestone: '16.5' },
+ calls_gitaly: true
field :mergeable_discussions_state, GraphQL::Types::Boolean, null: true,
calls_gitaly: true,
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index d9726e76c4b..e22739f2dc8 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -1386,7 +1386,7 @@ class MergeRequest < ApplicationRecord
end
def mergeable_discussions_state?
- return true unless project.only_allow_merge_if_all_discussions_are_resolved?(inherit_group_setting: true)
+ return true unless only_allow_merge_if_all_discussions_are_resolved?
unresolved_notes.none?(&:to_be_resolved?)
end
@@ -1566,8 +1566,16 @@ class MergeRequest < ApplicationRecord
access.can_push_to_branch?(target_branch)
end
+ def only_allow_merge_if_pipeline_succeeds?
+ project.only_allow_merge_if_pipeline_succeeds?(inherit_group_setting: true)
+ end
+
+ def only_allow_merge_if_all_discussions_are_resolved?
+ project.only_allow_merge_if_all_discussions_are_resolved?(inherit_group_setting: true)
+ end
+
def mergeable_ci_state?
- return true unless project.only_allow_merge_if_pipeline_succeeds?(inherit_group_setting: true)
+ return true unless only_allow_merge_if_pipeline_succeeds?
return false unless actual_head_pipeline
return true if project.allow_merge_on_skipped_pipeline?(inherit_group_setting: true) && actual_head_pipeline.skipped?
diff --git a/app/models/users/phone_number_validation.rb b/app/models/users/phone_number_validation.rb
index e033445d76b..27f13845102 100644
--- a/app/models/users/phone_number_validation.rb
+++ b/app/models/users/phone_number_validation.rb
@@ -33,6 +33,7 @@ module Users
validates :telesign_reference_xid, length: { maximum: 255 }
scope :for_user, -> (user_id) { where(user_id: user_id) }
+ scope :by_reference_id, ->(reference_id) { find_by(telesign_reference_xid: reference_id) }
def self.related_to_banned_user?(international_dial_code, phone_number)
joins(:banned_user).where(
diff --git a/app/services/merge_requests/mergeability/check_base_service.rb b/app/services/merge_requests/mergeability/check_base_service.rb
index e1c4d751296..b8a275b6c32 100644
--- a/app/services/merge_requests/mergeability/check_base_service.rb
+++ b/app/services/merge_requests/mergeability/check_base_service.rb
@@ -42,6 +42,11 @@ module MergeRequests
.failed(payload: default_payload(args))
end
+ def inactive(**args)
+ Gitlab::MergeRequests::Mergeability::CheckResult
+ .inactive(payload: default_payload(args))
+ end
+
def default_payload(args)
args.merge(identifier: self.class.identifier)
end
diff --git a/app/services/merge_requests/mergeability/check_ci_status_service.rb b/app/services/merge_requests/mergeability/check_ci_status_service.rb
index f7fa3259d97..b4e60e964b7 100644
--- a/app/services/merge_requests/mergeability/check_ci_status_service.rb
+++ b/app/services/merge_requests/mergeability/check_ci_status_service.rb
@@ -7,6 +7,8 @@ module MergeRequests
end
def execute
+ return inactive unless merge_request.only_allow_merge_if_pipeline_succeeds?
+
if merge_request.mergeable_ci_state?
success
else
diff --git a/app/services/merge_requests/mergeability/check_discussions_status_service.rb b/app/services/merge_requests/mergeability/check_discussions_status_service.rb
index 34db5f8a944..f9cff5d1e5f 100644
--- a/app/services/merge_requests/mergeability/check_discussions_status_service.rb
+++ b/app/services/merge_requests/mergeability/check_discussions_status_service.rb
@@ -7,6 +7,8 @@ module MergeRequests
end
def execute
+ return inactive unless merge_request.only_allow_merge_if_all_discussions_are_resolved?
+
if merge_request.mergeable_discussions_state?
success
else
diff --git a/app/services/merge_requests/mergeability/check_rebase_status_service.rb b/app/services/merge_requests/mergeability/check_rebase_status_service.rb
index 2163fec8bd6..02cd0587be0 100644
--- a/app/services/merge_requests/mergeability/check_rebase_status_service.rb
+++ b/app/services/merge_requests/mergeability/check_rebase_status_service.rb
@@ -8,6 +8,8 @@ module MergeRequests
end
def execute
+ return inactive unless merge_request.project.ff_merge_must_be_possible?
+
if merge_request.should_be_rebased?
failure(reason: failure_reason)
else
diff --git a/app/services/merge_requests/mergeability/detailed_merge_status_service.rb b/app/services/merge_requests/mergeability/detailed_merge_status_service.rb
index 86c8122604c..92f0fb0429c 100644
--- a/app/services/merge_requests/mergeability/detailed_merge_status_service.rb
+++ b/app/services/merge_requests/mergeability/detailed_merge_status_service.rb
@@ -18,10 +18,10 @@ module MergeRequests
# If everything else is mergeable, but CI is not, the frontend expects two potential states to be returned
# See discussion: gitlab.com/gitlab-org/gitlab/-/merge_requests/96778#note_1093063523
- if check_ci_results.success?
- :mergeable
- else
+ if check_ci_results.failed?
ci_check_failure_reason
+ else
+ :mergeable
end
else
check_results.payload[:failure_reason]
diff --git a/app/services/merge_requests/mergeability/run_checks_service.rb b/app/services/merge_requests/mergeability/run_checks_service.rb
index 5150c03d0a3..92f3e5e951a 100644
--- a/app/services/merge_requests/mergeability/run_checks_service.rb
+++ b/app/services/merge_requests/mergeability/run_checks_service.rb
@@ -65,7 +65,7 @@ module MergeRequests
end
def all_results_success?
- results.all?(&:success?)
+ results.none?(&:failed?)
end
def failure_reason
diff --git a/app/views/projects/readme_templates/default.md.tt b/app/views/projects/readme_templates/default.md.tt
index 779b87336ea..7432918be21 100644
--- a/app/views/projects/readme_templates/default.md.tt
+++ b/app/views/projects/readme_templates/default.md.tt
@@ -38,7 +38,7 @@ git push -uf origin <%= params[:default_branch] %>
Use the built-in continuous integration in GitLab.
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
+- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
@@ -47,9 +47,10 @@ Use the built-in continuous integration in GitLab.
# Editing this README
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
+When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
## Suggestions for a good README
+
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
## Name