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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-12-21 21:16:10 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-12-21 21:16:10 +0300
commitd2f2219fd58e572c10d77183e2f65de8fcc8df96 (patch)
tree2cde75cd3d994b7febe1349ab61b7a94a95c78cf
parent6323146895db2be6f04846b3c98060b7349207b9 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/environments/components/new_environment_item.vue1
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_bundle.js13
-rw-r--r--app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue76
-rw-r--r--app/assets/javascripts/lib/utils/constants.js5
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js45
-rw-r--r--app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue8
-rw-r--r--app/assets/javascripts/search/store/actions.js51
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue4
-rw-r--r--app/assets/stylesheets/page_bundles/login.scss3
-rw-r--r--app/controllers/projects/environments_controller.rb2
-rw-r--r--app/graphql/mutations/issues/set_assignees.rb2
-rw-r--r--app/graphql/types/merge_request_type.rb12
-rw-r--r--app/graphql/types/project_type.rb12
-rw-r--r--app/helpers/environments_helper.rb11
-rw-r--r--app/models/merge_request.rb6
-rw-r--r--app/models/project.rb12
-rw-r--r--app/models/project_statistics.rb5
-rw-r--r--app/services/issuable_base_service.rb3
-rw-r--r--app/views/layouts/devise.html.haml2
-rw-r--r--app/views/layouts/devise_empty.html.haml2
-rw-r--r--app/views/layouts/signup_onboarding.html.haml2
-rw-r--r--app/views/projects/environments/folder.html.haml2
-rw-r--r--doc/administration/dedicated/index.md37
-rw-r--r--doc/api/graphql/reference/index.md4
-rw-r--r--doc/api/integrations.md8
-rw-r--r--doc/ci/mobile_devops.md28
-rw-r--r--doc/ci/runners/img/runner_fleet_dashboard.png (renamed from doc/development/img/runner_fleet_dashboard.png)bin38440 -> 38440 bytes
-rw-r--r--doc/ci/runners/runner_fleet_dashboard.md76
-rw-r--r--doc/development/runner_fleet_dashboard.md72
-rw-r--r--doc/integration/clickhouse.md17
-rw-r--r--doc/subscriptions/gitlab_dedicated/index.md4
-rw-r--r--doc/user/project/integrations/apple_app_store.md14
-rw-r--r--doc/user/project/pages/index.md2
-rw-r--r--doc/user/project/repository/code_suggestions/index.md12
-rw-r--r--doc/user/project/service_desk/configure.md2
-rw-r--r--doc/user/project/service_desk/index.md1
-rw-r--r--locale/gitlab.pot35
-rw-r--r--package.json2
-rw-r--r--spec/frontend/jira_connect/branches/components/project_dropdown_spec.js78
-rw-r--r--spec/frontend/jira_connect/branches/mock_data.js30
-rw-r--r--spec/frontend/lib/utils/number_utils_spec.js (renamed from spec/frontend/lib/utils/number_utility_spec.js)22
-rw-r--r--spec/frontend/search/store/actions_spec.js25
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js16
-rw-r--r--spec/graphql/types/merge_request_type_spec.rb1
-rw-r--r--spec/graphql/types/project_type_spec.rb1
-rw-r--r--spec/helpers/environments_helper_spec.rb24
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb33
-rw-r--r--spec/models/merge_request_spec.rb28
-rw-r--r--spec/models/project_spec.rb16
-rw-r--r--spec/models/project_statistics_spec.rb18
-rw-r--r--spec/services/issues/update_service_spec.rb12
-rw-r--r--yarn.lock8
52 files changed, 661 insertions, 244 deletions
diff --git a/app/assets/javascripts/environments/components/new_environment_item.vue b/app/assets/javascripts/environments/components/new_environment_item.vue
index aacb460a817..8bd55e697fa 100644
--- a/app/assets/javascripts/environments/components/new_environment_item.vue
+++ b/app/assets/javascripts/environments/components/new_environment_item.vue
@@ -368,6 +368,7 @@ export default {
</div>
<div v-if="clusterAgent" :class="$options.kubernetesOverviewClasses">
<kubernetes-overview
+ :class="{ 'gl-ml-7': inFolder }"
:cluster-agent="clusterAgent"
:namespace="kubernetesNamespace"
:flux-resource-path="fluxResourcePath"
diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
index 0201fb53f77..05a2eba6af3 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js
+++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
@@ -3,6 +3,7 @@ import VueApollo from 'vue-apollo';
import VueRouter from 'vue-router';
import createDefaultClient from '~/lib/graphql';
import Translate from '~/vue_shared/translate';
+import { removeLastSlashInUrlPath } from '~/lib/utils/url_utility';
import { apolloProvider } from '../graphql/client';
import EnvironmentsFolderView from './environments_folder_view.vue';
import EnvironmentsFolderApp from './environments_folder_app.vue';
@@ -20,10 +21,9 @@ export default () => {
if (gon.features.environmentsFolderNewLook) {
Vue.use(VueRouter);
- const folderName = environmentsData.environmentsDataFolderName;
- const folderPath = environmentsData.environmentsDataEndpoint.replace('.json', '');
- const projectPath = environmentsData.environmentsDataProjectPath;
- const helpPagePath = environmentsData.environmentsDataHelpPagePath;
+ const folderPath = environmentsData.endpoint.replace('.json', '');
+ const kasTunnelUrl = removeLastSlashInUrlPath(environmentsData.kasTunnelUrl);
+ const { projectPath, folderName, helpPagePath } = environmentsData;
const router = new VueRouter({
mode: 'history',
@@ -54,6 +54,7 @@ export default () => {
provide: {
projectPath,
helpPagePath,
+ kasTunnelUrl,
},
apolloProvider,
router,
@@ -74,8 +75,8 @@ export default () => {
},
data() {
return {
- endpoint: environmentsData.environmentsDataEndpoint,
- folderName: environmentsData.environmentsDataFolderName,
+ endpoint: environmentsData.endpoint,
+ folderName: environmentsData.folderName,
cssContainerClass: environmentsData.cssClass,
};
},
diff --git a/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue b/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue
index 52a12cc7771..e3fa0ce8073 100644
--- a/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue
+++ b/app/assets/javascripts/jira_connect/branches/components/project_dropdown.vue
@@ -1,20 +1,20 @@
<script>
import { GlAvatarLabeled, GlCollapsibleListbox } from '@gitlab/ui';
import { debounce } from 'lodash';
+import produce from 'immer';
import { __ } from '~/locale';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import { PROJECTS_PER_PAGE } from '../constants';
import getProjectsQuery from '../graphql/queries/get_projects.query.graphql';
export default {
- PROJECTS_PER_PAGE,
- projectQueryPageInfo: {
- endCursor: '',
- },
+ name: 'ProjectDropdown',
+
components: {
GlAvatarLabeled,
GlCollapsibleListbox,
},
+
props: {
selectedProject: {
type: Object,
@@ -22,27 +22,29 @@ export default {
default: null,
},
},
+
data() {
return {
initialProjectsLoading: true,
+ isLoadingMore: false,
projectSearchQuery: '',
selectedProjectId: this.selectedProject?.id,
};
},
+
apollo: {
projects: {
query: getProjectsQuery,
variables() {
return {
- search: this.projectSearchQuery,
- first: this.$options.PROJECTS_PER_PAGE,
- after: this.$options.projectQueryPageInfo.endCursor,
- searchNamespaces: true,
- sort: 'similarity',
+ ...this.queryVariables,
};
},
update(data) {
- return data?.projects?.nodes.filter((project) => !project.repository?.empty) ?? [];
+ return {
+ nodes: data?.projects?.nodes.filter((project) => !project.repository?.empty) ?? [],
+ pageInfo: data?.projects?.pageInfo,
+ };
},
result() {
this.initialProjectsLoading = false;
@@ -52,24 +54,37 @@ export default {
},
},
},
+
computed: {
- projectsLoading() {
- return Boolean(this.$apollo.queries.projects.loading);
+ queryVariables() {
+ return {
+ search: this.projectSearchQuery,
+ first: PROJECTS_PER_PAGE,
+ searchNamespaces: true,
+ sort: 'similarity',
+ };
+ },
+ isLoading() {
+ return this.$apollo.queries.projects.loading && !this.isLoadingMore;
},
projectDropdownText() {
return this.selectedProject?.nameWithNamespace || this.$options.i18n.selectProjectText;
},
projectList() {
- return (this.projects || []).map((project) => ({
+ return (this.projects?.nodes || []).map((project) => ({
...project,
text: project.nameWithNamespace,
value: String(project.id),
}));
},
+ hasNextPage() {
+ return this.projects?.pageInfo?.hasNextPage;
+ },
},
+
methods: {
findProjectById(id) {
- return this.projects.find((project) => id === project.id);
+ return this.projects?.nodes?.find((project) => id === project.id);
},
onProjectSelect(projectId) {
this.$emit('change', this.findProjectById(projectId));
@@ -77,13 +92,41 @@ export default {
onError({ message } = {}) {
this.$emit('error', { message });
},
+ async onBottomReached() {
+ if (!this.hasNextPage) return;
+
+ this.isLoadingMore = true;
+
+ try {
+ await this.$apollo.queries.projects.fetchMore({
+ variables: {
+ ...this.queryVariables,
+ after: this.projects.pageInfo?.endCursor,
+ },
+ updateQuery: (previousResult, { fetchMoreResult }) => {
+ return produce(fetchMoreResult, (draftData) => {
+ draftData.projects.nodes = [
+ ...previousResult.projects.nodes,
+ ...draftData.projects.nodes,
+ ];
+ });
+ },
+ });
+ } catch (error) {
+ this.onError({ message: __('Failed to load projects') });
+ } finally {
+ this.isLoadingMore = false;
+ }
+ },
onSearch: debounce(function debouncedSearch(query) {
this.projectSearchQuery = query;
}, 250),
},
+
i18n: {
selectProjectText: __('Select a project'),
},
+
AVATAR_SHAPE_OPTION_RECT,
};
</script>
@@ -97,8 +140,11 @@ export default {
:header-text="$options.i18n.selectProjectText"
:loading="initialProjectsLoading"
:searchable="true"
- :searching="projectsLoading"
+ :searching="isLoading"
fluid-width
+ infinite-scroll
+ :infinite-scroll-loading="isLoadingMore"
+ @bottom-reached="onBottomReached"
@search="onSearch"
@select="onProjectSelect"
>
diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js
index d9ac0abf7b3..77986539403 100644
--- a/app/assets/javascripts/lib/utils/constants.js
+++ b/app/assets/javascripts/lib/utils/constants.js
@@ -19,10 +19,5 @@ export const DRAWER_Z_INDEX = 252;
export const MIN_USERNAME_LENGTH = 2;
-export const BYTES_FORMAT_BYTES = 'B';
-export const BYTES_FORMAT_KIB = 'KiB';
-export const BYTES_FORMAT_MIB = 'MiB';
-export const BYTES_FORMAT_GIB = 'GiB';
-
export const DEFAULT_CI_CONFIG_PATH = '.gitlab-ci.yml';
export const CI_CONFIG_PATH_EXTENSION = /(\.gitlab-ci\.yml)/;
diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js
index d17719c0bc0..01c5bc1f1fc 100644
--- a/app/assets/javascripts/lib/utils/number_utils.js
+++ b/app/assets/javascripts/lib/utils/number_utils.js
@@ -1,12 +1,5 @@
import { sprintf, __ } from '~/locale';
-import {
- BYTES_IN_KIB,
- THOUSAND,
- BYTES_FORMAT_BYTES,
- BYTES_FORMAT_KIB,
- BYTES_FORMAT_MIB,
- BYTES_FORMAT_GIB,
-} from './constants';
+import { BYTES_IN_KIB, THOUSAND } from './constants';
/**
* Function that allows a number with an X amount of decimals
@@ -73,47 +66,47 @@ export function bytesToGiB(number) {
/**
* Formats the bytes in number into a more understandable
* representation. Returns an array with the first value being the human size
- * and the second value being the format (e.g., [1.5, 'KiB']).
+ * and the second value being the label (e.g., [1.5, 'KiB']).
*
- * @param {Number} size
- * @param {Number} digits - The number of digits to appear after the decimal point
- * @returns {String}
+ * @param {number} size
+ * @param {number} [digits=2] - The number of digits to appear after the decimal point
+ * @returns {string[]}
*/
export function numberToHumanSizeSplit(size, digits = 2) {
const abs = Math.abs(size);
if (abs < BYTES_IN_KIB) {
- return [size.toString(), BYTES_FORMAT_BYTES];
+ return [size.toString(), __('B')];
}
if (abs < BYTES_IN_KIB ** 2) {
- return [bytesToKiB(size).toFixed(digits), BYTES_FORMAT_KIB];
+ return [bytesToKiB(size).toFixed(digits), __('KiB')];
}
if (abs < BYTES_IN_KIB ** 3) {
- return [bytesToMiB(size).toFixed(digits), BYTES_FORMAT_MIB];
+ return [bytesToMiB(size).toFixed(digits), __('MiB')];
}
- return [bytesToGiB(size).toFixed(digits), BYTES_FORMAT_GIB];
+ return [bytesToGiB(size).toFixed(digits), __('GiB')];
}
/**
* Port of rails number_to_human_size
* Formats the bytes in number into a more understandable
- * representation (e.g., giving it 1500 yields 1.5 KB).
+ * representation (e.g., giving it 1536 yields 1.5 KiB).
*
- * @param {Number} size
- * @param {Number} digits - The number of digits to appear after the decimal point
- * @returns {String}
+ * @param {number} size
+ * @param {number} [digits=2] - The number of digits to appear after the decimal point
+ * @returns {string}
*/
export function numberToHumanSize(size, digits = 2) {
- const [humanSize, format] = numberToHumanSizeSplit(size, digits);
+ const [humanSize, label] = numberToHumanSizeSplit(size, digits);
- switch (format) {
- case BYTES_FORMAT_BYTES:
+ switch (label) {
+ case __('B'):
return sprintf(__('%{size} B'), { size: humanSize });
- case BYTES_FORMAT_KIB:
+ case __('KiB'):
return sprintf(__('%{size} KiB'), { size: humanSize });
- case BYTES_FORMAT_MIB:
+ case __('MiB'):
return sprintf(__('%{size} MiB'), { size: humanSize });
- case BYTES_FORMAT_GIB:
+ case __('GiB'):
return sprintf(__('%{size} GiB'), { size: humanSize });
default:
return '';
diff --git a/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue
index 874803a720d..eb56113a4dd 100644
--- a/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue
+++ b/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue
@@ -1,6 +1,6 @@
<script>
// eslint-disable-next-line no-restricted-imports
-import { mapActions, mapState, mapGetters } from 'vuex';
+import { mapActions, mapGetters } from 'vuex';
import { s__ } from '~/locale';
import eventHub from '~/super_sidebar/event_hub';
import NavItem from '~/super_sidebar/components/nav_item.vue';
@@ -15,15 +15,11 @@ export default {
NavItem,
},
computed: {
- ...mapState(['navigation', 'urlQuery']),
...mapGetters(['navigationItems']),
},
created() {
eventHub.$emit('toggle-menu-header', false);
-
- if (this.urlQuery?.search) {
- this.fetchSidebarCount();
- }
+ this.fetchSidebarCount();
},
methods: {
...mapActions(['fetchSidebarCount']),
diff --git a/app/assets/javascripts/search/store/actions.js b/app/assets/javascripts/search/store/actions.js
index 211bbaf82cd..d5e275b8a19 100644
--- a/app/assets/javascripts/search/store/actions.js
+++ b/app/assets/javascripts/search/store/actions.js
@@ -1,7 +1,13 @@
import Api from '~/api';
import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
-import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
+import {
+ visitUrl,
+ setUrlParams,
+ getBaseURL,
+ queryToObject,
+ objectToQuery,
+} from '~/lib/utils/url_utility';
import { logError } from '~/lib/logger';
import { __ } from '~/locale';
import { labelFilterData } from '~/search/sidebar/components/label_filter/data';
@@ -135,19 +141,38 @@ export const setLabelFilterSearch = ({ commit }, { value }) => {
commit(types.SET_LABEL_SEARCH_STRING, value);
};
+const injectWildCardSearch = (state, link) => {
+ const urlObject = new URL(`${getBaseURL()}${link}`);
+ if (!state.urlQuery.search) {
+ const queryObject = queryToObject(urlObject.search);
+ urlObject.search = objectToQuery({ ...queryObject, search: '*' });
+ }
+
+ return urlObject.href;
+};
+
export const fetchSidebarCount = ({ commit, state }) => {
- const promises = Object.values(state.navigation).map((navItem) => {
- // active nav item has count already so we skip it
- if (!navItem.active && navItem.count_link) {
- return axios
- .get(navItem.count_link)
- .then(({ data: { count } }) => {
- commit(types.RECEIVE_NAVIGATION_COUNT, { key: navItem.scope, count });
- })
- .catch((e) => logError(e));
- }
- return Promise.resolve();
- });
+ const items = Object.values(state.navigation)
+ .filter((navigationItem) => !navigationItem.active && navigationItem.count_link)
+ .map((navItem) => {
+ const navigationItem = { ...navItem };
+
+ if (navigationItem.count_link) {
+ navigationItem.count_link = injectWildCardSearch(state, navigationItem.count_link);
+ }
+
+ return navigationItem;
+ });
+
+ const promises = items.map((navigationItem) =>
+ axios
+ .get(navigationItem.count_link)
+ .then(({ data: { count } }) => {
+ commit(types.RECEIVE_NAVIGATION_COUNT, { key: navigationItem.scope, count });
+ })
+ .catch((e) => logError(e)),
+ );
+
return Promise.all(promises);
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
index a4afdee4d49..1aed3362c42 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
@@ -48,7 +48,7 @@ export default {
variables() {
return this.mergeRequestQueryVariables;
},
- update: (data) => data.project.mergeRequest,
+ update: (data) => data.project?.mergeRequest || {},
},
},
components: {
@@ -90,7 +90,7 @@ export default {
return this.state.rebaseInProgress;
},
canPushToSourceBranch() {
- return this.state.userPermissions.pushToSourceBranch;
+ return this.state.userPermissions?.pushToSourceBranch || false;
},
targetBranch() {
return this.state.targetBranch;
diff --git a/app/assets/stylesheets/page_bundles/login.scss b/app/assets/stylesheets/page_bundles/login.scss
index b63f199f7b9..4cc44b01e60 100644
--- a/app/assets/stylesheets/page_bundles/login.scss
+++ b/app/assets/stylesheets/page_bundles/login.scss
@@ -230,11 +230,8 @@
height: 100%;
body {
- padding-top: 48px; // Remove this line when the restyle_login_page feature flag is deleted. Instead, add self-align `center` to container, and maybe a top margin.
-
&.with-system-header {
padding-top: $system-header-height;
- padding-top: calc(#{$system-header-height} + 48px); // Remove this line when the restyle_login_page feature flag is deleted
}
&.with-system-footer {
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 8cdd6efa7c5..65cbe5a78ce 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -26,7 +26,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :cancel_auto_stop]
before_action :verify_api_request!, only: :terminal_websocket_authorize
before_action :expire_etag_cache, only: [:index], unless: -> { request.format.json? }
- before_action :set_kas_cookie, only: [:index, :edit, :new], if: -> { current_user && request.format.html? }
+ before_action :set_kas_cookie, only: [:index, :folder, :edit, :new], if: -> { current_user && request.format.html? }
after_action :expire_etag_cache, only: [:cancel_auto_stop]
track_event :index, :folder, :show, :new, :edit, :create, :update, :stop, :cancel_auto_stop, :terminal,
diff --git a/app/graphql/mutations/issues/set_assignees.rb b/app/graphql/mutations/issues/set_assignees.rb
index 8413c89b010..1e55cdee0a8 100644
--- a/app/graphql/mutations/issues/set_assignees.rb
+++ b/app/graphql/mutations/issues/set_assignees.rb
@@ -8,7 +8,7 @@ module Mutations
include Assignable
def assign!(issue, users, mode)
- permitted, forbidden = users.partition { |u| u.can?(:read_issue, issue) }
+ permitted, forbidden = users.partition { |u| u.can?(:read_issue, issue.resource_parent) }
super(issue, permitted, mode)
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index d7c3b313f84..3572cfd346b 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -248,6 +248,18 @@ module Types
'if `sast_reports_in_inline_diff` feature flag is disabled.',
resolver: ::Resolvers::CodequalityReportsComparerResolver
+ field :allows_multiple_assignees,
+ GraphQL::Types::Boolean,
+ method: :allows_multiple_assignees?,
+ description: 'Allows assigning multiple users to a merge request.',
+ null: false
+
+ field :allows_multiple_reviewers,
+ GraphQL::Types::Boolean,
+ method: :allows_multiple_reviewers?,
+ description: 'Allows assigning multiple reviewers to a merge request.',
+ null: false
+
markdown_field :title_html, null: true
markdown_field :description_html, null: true
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 68a55687419..7f49c717c78 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -669,6 +669,18 @@ module Types
description: 'Finds machine learning models',
resolver: Resolvers::Ml::FindModelsResolver
+ field :allows_multiple_merge_request_assignees,
+ GraphQL::Types::Boolean,
+ method: :allows_multiple_merge_request_assignees?,
+ description: 'Project allows assigning multiple users to a merge request.',
+ null: false
+
+ field :allows_multiple_merge_request_reviewers,
+ GraphQL::Types::Boolean,
+ method: :allows_multiple_merge_request_reviewers?,
+ description: 'Project allows assigning multiple reviewers to a merge request.',
+ null: false
+
def timelog_categories
object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories)
end
diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb
index 6b1e3075968..ac34f429508 100644
--- a/app/helpers/environments_helper.rb
+++ b/app/helpers/environments_helper.rb
@@ -9,13 +9,14 @@ module EnvironmentsHelper
}
end
- def environments_folder_list_view_data
+ def environments_folder_list_view_data(project, folder)
{
- "endpoint" => folder_project_environments_path(@project, @folder, format: :json),
- "folder_name" => @folder,
- "project_path" => project_path(@project),
+ "endpoint" => folder_project_environments_path(project, folder, format: :json),
+ "folder_name" => folder,
+ "project_path" => project.full_path,
"help_page_path" => help_page_path("ci/environments/index"),
- "can_read_environment" => can?(current_user, :read_environment, @project).to_s
+ "can_read_environment" => can?(current_user, :read_environment, @project).to_s,
+ "kas_tunnel_url" => ::Gitlab::Kas.tunnel_url
}
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 2471c0bdb29..f863c1e5093 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -2098,8 +2098,12 @@ class MergeRequest < ApplicationRecord
true
end
+ def allows_multiple_assignees?
+ project.allows_multiple_merge_request_assignees?
+ end
+
def allows_multiple_reviewers?
- false
+ project.allows_multiple_merge_request_reviewers?
end
def supports_assignee?
diff --git a/app/models/project.rb b/app/models/project.rb
index 738b9b1ef72..3ebdc0a72fc 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -3206,6 +3206,16 @@ class Project < ApplicationRecord
end
strong_memoize_attr :code_suggestions_enabled?
+ # Overridden in EE
+ def allows_multiple_merge_request_assignees?
+ false
+ end
+
+ # Overridden in EE
+ def allows_multiple_merge_request_reviewers?
+ false
+ end
+
private
# overridden in EE
@@ -3439,7 +3449,7 @@ class Project < ApplicationRecord
def check_project_export_limit!
return if Gitlab::CurrentSettings.current_application_settings.max_export_size == 0
- if self.statistics.storage_size > Gitlab::CurrentSettings.current_application_settings.max_export_size.megabytes
+ if self.statistics.export_size > Gitlab::CurrentSettings.current_application_settings.max_export_size.megabytes
raise ExportLimitExceeded, _('The project size exceeds the export limit.')
end
end
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index 942f20f6e5e..f89894b77a8 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -145,6 +145,11 @@ class ProjectStatistics < ApplicationRecord
bulk_increment_counter(key, increments)
end
+ # Build artifacts & packages are not included in the project export
+ def export_size
+ storage_size - build_artifacts_size - packages_size
+ end
+
private
def incrementable_attribute?(key)
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 27cfaef2db2..7683b0868ab 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -118,9 +118,8 @@ class IssuableBaseService < ::BaseContainerService
return false unless user
ability_name = :"read_#{issuable.to_ability_name}"
- resource = issuable.persisted? ? issuable : project
- can?(user, ability_name, resource)
+ can?(user, ability_name, issuable.resource_parent)
end
def filter_labels
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 03ffef8bc70..2905ba924ca 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -8,7 +8,7 @@
= render "layouts/init_client_detection_flags"
= yield :sessions_broadcast
.gl-h-full.borderless.gl-display-flex.gl-flex-wrap
- .container
+ .container.gl-align-self-center
.content
= render "layouts/flash"
- if custom_text.present?
diff --git a/app/views/layouts/devise_empty.html.haml b/app/views/layouts/devise_empty.html.haml
index 6816a64ac8f..faf45ae78ef 100644
--- a/app/views/layouts/devise_empty.html.haml
+++ b/app/views/layouts/devise_empty.html.haml
@@ -7,7 +7,7 @@
= render "layouts/init_client_detection_flags"
= render "layouts/header/empty"
.gl-h-full.gl-display-flex.gl-flex-wrap
- .container
+ .container.gl-align-self-center
.content
= render "layouts/flash"
= yield
diff --git a/app/views/layouts/signup_onboarding.html.haml b/app/views/layouts/signup_onboarding.html.haml
index c8e15896b97..e3e071c2226 100644
--- a/app/views/layouts/signup_onboarding.html.haml
+++ b/app/views/layouts/signup_onboarding.html.haml
@@ -7,7 +7,7 @@
= header_message
= render "layouts/init_client_detection_flags"
= render "layouts/header/logo_with_title"
- .container
+ .container.gl-align-self-center
.content
= yield
diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml
index 2b4d19a0e1d..54855999431 100644
--- a/app/views/projects/environments/folder.html.haml
+++ b/app/views/projects/environments/folder.html.haml
@@ -3,4 +3,4 @@
- page_title _("Environments in %{name}") % { name: @folder }
- add_page_specific_style 'page_bundles/environments'
-#environments-folder-list-view{ data: { environments_data: environments_folder_list_view_data, project_path: @project.full_path } }
+#environments-folder-list-view{ data: environments_folder_list_view_data(@project, @folder) }
diff --git a/doc/administration/dedicated/index.md b/doc/administration/dedicated/index.md
index ef9e53c8fb7..05a241b65dc 100644
--- a/doc/administration/dedicated/index.md
+++ b/doc/administration/dedicated/index.md
@@ -20,6 +20,9 @@ Examples of SaaS environment settings include `gitlab.rb` configurations and acc
These environment settings cannot be changed by tenants.
GitLab Dedicated Engineers also don't have direct access to tenant environments, except for [break glass situations](../../subscriptions/gitlab_dedicated/index.md#access-controls).
+NOTE:
+An instance refers to a GitLab Dedicated deployment, whereas a tenant refers to a customer.
+
## Onboarding to GitLab Dedicated using Switchboard
To create a new GitLab Dedicated environment for your organization, provide the following information to your account team:
@@ -33,27 +36,27 @@ If you've been granted access to Switchboard, you receive an email invitation wi
NOTE:
The credentials for Switchboard are separate from any other GitLab credentials you may already have to sign in to a GitLab self-managed or GitLab.com instance.
-After you first sign in to Switchboard, you must update your password and set up MFA before you can complete your onboarding to create a tenant.
+After you first sign in to Switchboard, you must update your password and set up MFA before you can complete your onboarding to create a new instance.
-The following stages guide you through a series of four steps to provide the information required to create your GitLab Dedicated tenant.
+The following stages guide you through a series of four steps to provide the information required to create your GitLab Dedicated instance.
1. Confirm account details: Confirm key attributes of your GitLab Dedicated account:
- Reference architecture: Corresponds with the number of users you provided to your account team when beginning the onboarding process. For more information, see [reference architectures](../../subscriptions/gitlab_dedicated/index.md#availability-and-scalability).
- Total repository storage size: Corresponds with the storage size you provided to your account team when beginning the onboarding process.
- If you need to make changes to these attributes, [submit a support ticket](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=4414917877650).
-1. Tenant configuration: Provides the minimum required information needed to create your GitLab Dedicated tenant:
+1. Tenant configuration: Provides the minimum required information needed to create your GitLab Dedicated instance:
- Desired instance subdomain: The main domain for GitLab Dedicated instances is `gitlab-dedicated.com`. You choose the subdomain name where your instance is accessible from. For example, `customer_name.gitlab-dedicated.com`.
- Desired primary region: Primary AWS region in which your data is stored. Note the [available AWS regions](../../subscriptions/gitlab_dedicated/index.md#available-aws-regions).
- Desired secondary region: Secondary AWS region in which your data is stored. This region is used to recover your GitLab Dedicated instance in case of a disaster.
- Desired backup region: An AWS region where the primary backups of your data are replicated. This can be the same as the primary or secondary region, or different.
- Desired maintenance window: A weekly four-hour time slot that GitLab uses to perform routine maintenance and upgrade operations on all tenant instances. For more information, see [maintenance windows](#maintenance-window).
1. Security: You can provide your own [KMS keys](https://docs.aws.amazon.com/kms/latest/developerguide/overview.html) for encrypted AWS services. If you choose not to provide KMS keys, encryption keys are generated for your instance when it is created. For more information, see [encrypting your data at rest](#encrypted-data-at-rest-byok).
-1. Summary: You confirm that the information you've provided in the previous steps is accurate before initiating the creation of your tenant.
+1. Summary: You confirm that the information you've provided in the previous steps is accurate before initiating the creation of your instance.
NOTE:
-Some configuration settings (like the option to bring your own keys and your tenant name) are permanent and cannot be changed once your tenant has been created.
+Some configuration settings (like the option to bring your own keys and your tenant name) are permanent and cannot be changed once your instance has been created.
-It can take up to 3 hours to create the GitLab Dedicated tenant. When the setup is complete, you will receive a confirmation email with further instructions on how to access your tenant.
+It can take up to 3 hours to create the GitLab Dedicated instance. When the setup is complete, you will receive a confirmation email with further instructions on how to access your instance.
### Maintenance window
@@ -218,7 +221,7 @@ Make sure the AWS KMS keys are replicated to your desired primary, secondary and
Configuration changes requested with a [support ticket](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=4414917877650) are batched up and applied during your environment's weekly four-hour maintenance window.
-This policy does not apply to configuration changes made by a GitLab Dedicated tenant admin [using Switchboard](#configuration-changes-in-switchboard).
+This policy does not apply to configuration changes made by a GitLab Dedicated instance admin [using Switchboard](#configuration-changes-in-switchboard).
To have a change considered for an upcoming weekly maintenance window, all required information
must be submitted in full two business days before the start of the window.
@@ -232,7 +235,7 @@ Changes requested with a support ticket cannot be applied outside of a weekly ma
### Configuration changes in Switchboard
-Switchboard empowers the user to make limited configuration changes to their Dedicated Tenant Instance. As Switchboard matures further configuration changes will be made available.
+Switchboard empowers the user to make limited configuration changes to their GitLab Dedicated instance. As Switchboard matures further configuration changes will be made available.
To change or update the configuration of your GitLab Dedicated instance, use Switchboard following the instructions in the relevant section or open a [support ticket](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=4414917877650) with your request.
@@ -271,12 +274,14 @@ Consider the following when using Outbound Private Links:
and repositories, or use any other GitLab functionality to access private services.
- You can only establish Private Links between VPCs in the same region. Therefore, you can only establish a connection in the regions you selected for
your Dedicated instance.
-- The Network Load Balancer (NLB) that backs the Endpoint Service at your end must be enabled in at least one of the Availability Zones to which your Dedicated tenant was
+- The Network Load Balancer (NLB) that backs the Endpoint Service at your end must be enabled in at least one of the Availability Zones to which your Dedicated instance was
deployed. This is not the user-facing name such as `us-east-1a`, but the underlying [Availability Zone ID](https://docs.aws.amazon.com/ram/latest/userguide/working-with-az-ids.html).
If you did not specify these during onboarding to Dedicated, you must either:
- Ask for the Availability Zone IDs in the ticket you raise to enable the link and ensure the NLB is enabled in those AZs, or
- Ensure the NLB has is enabled in every Availability Zone in the region.
+You can view the `Reverse Private Link IAM Principal` attribute in the **Tenant Details** section of Switchboard.
+
To enable an Outbound Private Link:
1. [Create the Endpoint service](https://docs.aws.amazon.com/vpc/latest/privatelink/create-endpoint-service.html) through which your internal service
@@ -300,7 +305,7 @@ To enable an Outbound Private Link:
provide a list of DNS names that should resolve to the Private Link Endpoint. This list can be updated as needed in future.
GitLab then configures the tenant instance to create the necessary Endpoint Interfaces based on the service names you provided. Any matching outbound
-connections made from the tenant GitLab instance are directed through the PrivateLink into your VPC.
+connections made from the tenant instance are directed through the PrivateLink into your VPC.
### Custom certificates
@@ -348,7 +353,7 @@ Specify a comma separated list of IP addresses that can access your GitLab Dedic
### SAML
NOTE:
-GitLab Dedicated supports a limited number of SAML parameters. Parameters not shown in the configuration below are unavailable for GitLab Dedicated tenant instances.
+GitLab Dedicated supports a limited number of SAML parameters. Parameters not shown in the configuration below are unavailable for GitLab Dedicated instances.
Prerequisites:
@@ -415,7 +420,7 @@ To activate SAML for your GitLab Dedicated instance:
#### Request signing
-If [SAML request signing](../../integration/saml.md#sign-saml-authentication-requests-optional) is desired, a certificate must be obtained. This certificate can be self-signed which has the advantage of not having to prove ownership of an arbitrary Common Name (CN) to a public Certificate Authority (CA)).
+If [SAML request signing](../../integration/saml.md#sign-saml-authentication-requests-optional) is desired, a certificate must be obtained. This certificate can be self-signed which has the advantage of not having to prove ownership of an arbitrary Common Name (CN) to a public Certificate Authority (CA).
If you choose to enable SAML request signing, the manual steps below will need to be completed before you are able to use SAML, since it requires certificate signing to happen.
To enable SAML request signing, indicate on your SAML [support ticket](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=4414917877650) that you want request signing enabled. GitLab works with you on sending the Certificate Signing Request (CSR) for you to sign. Alternatively, the CSR can be signed with a public CA. After the certificate is signed, GitLab adds the certificate and its associated private key to the `security` section of the SAML configuration. Authentication requests from GitLab to your identity provider can then be signed.
@@ -434,12 +439,12 @@ To enable group sync:
1. Add the [required elements](../../user/group/saml_sso/group_sync.md#configure-saml-group-sync) to the SAML configuration block you provide in your [support ticket](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=4414917877650).
1. Configure the [Group Links](../../user/group/saml_sso/group_sync.md#configure-saml-group-links).
-### Add users to a tenant instance
+### Add users to an instance
-Tenant administrators can add Switchboard users to their tenant instance. There are two types of users:
+Administrators can add Switchboard users to their GitLab Dedicated instance. There are two types of users:
-- **Read only**: Users can only view tenant data.
-- **Admin**: Users can edit the tenant configuration and manage users.
+- **Read only**: Users can only view instance data.
+- **Admin**: Users can edit the instance configuration and manage users.
To add a new user to your GitLab Dedicated instance:
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 20cf9ceb7c8..dc8f93d4e7b 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -21431,6 +21431,8 @@ Defines which user roles, users, or groups can merge into a protected branch.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mergerequestallowcollaboration"></a>`allowCollaboration` | [`Boolean`](#boolean) | Indicates if members of the target project can push to the fork. |
+| <a id="mergerequestallowsmultipleassignees"></a>`allowsMultipleAssignees` | [`Boolean!`](#boolean) | Allows assigning multiple users to a merge request. |
+| <a id="mergerequestallowsmultiplereviewers"></a>`allowsMultipleReviewers` | [`Boolean!`](#boolean) | Allows assigning multiple reviewers to a merge request. |
| <a id="mergerequestapprovalstate"></a>`approvalState` | [`MergeRequestApprovalState!`](#mergerequestapprovalstate) | Information relating to rules that must be satisfied to merge this merge request. |
| <a id="mergerequestapprovalsleft"></a>`approvalsLeft` | [`Int`](#int) | Number of approvals left. |
| <a id="mergerequestapprovalsrequired"></a>`approvalsRequired` | [`Int`](#int) | Number of approvals required. |
@@ -24164,6 +24166,8 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="projectactualrepositorysizelimit"></a>`actualRepositorySizeLimit` | [`Float`](#float) | Size limit for the repository in bytes. |
| <a id="projectagentconfigurations"></a>`agentConfigurations` | [`AgentConfigurationConnection`](#agentconfigurationconnection) | Agent configurations defined by the project. (see [Connections](#connections)) |
| <a id="projectallowmergeonskippedpipeline"></a>`allowMergeOnSkippedPipeline` | [`Boolean`](#boolean) | If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge requests of the project can also be merged with skipped jobs. |
+| <a id="projectallowsmultiplemergerequestassignees"></a>`allowsMultipleMergeRequestAssignees` | [`Boolean!`](#boolean) | Project allows assigning multiple users to a merge request. |
+| <a id="projectallowsmultiplemergerequestreviewers"></a>`allowsMultipleMergeRequestReviewers` | [`Boolean!`](#boolean) | Project allows assigning multiple reviewers to a merge request. |
| <a id="projectapifuzzingciconfiguration"></a>`apiFuzzingCiConfiguration` | [`ApiFuzzingCiConfiguration`](#apifuzzingciconfiguration) | API fuzzing configuration for the project. |
| <a id="projectarchived"></a>`archived` | [`Boolean`](#boolean) | Indicates the archived status of the project. |
| <a id="projectautoclosereferencedissues"></a>`autocloseReferencedIssues` | [`Boolean`](#boolean) | Indicates if issues referenced by merge requests and commits within the default branch are closed automatically. |
diff --git a/doc/api/integrations.md b/doc/api/integrations.md
index bb3f6579c58..2017b9cbec9 100644
--- a/doc/api/integrations.md
+++ b/doc/api/integrations.md
@@ -98,17 +98,17 @@ Parameters:
| `app_store_private_key` | string | true | Apple App Store Connect private key. |
| `app_store_protected_refs` | boolean | false | Set variables on protected branches and tags only. |
-### Disable Apple App Store
+### Disable Apple App Store Connect
-Disable the Apple App Store integration for a project. Integration settings are reset.
+Disable the Apple App Store Connect integration for a project. Integration settings are reset.
```plaintext
DELETE /projects/:id/integrations/apple_app_store
```
-### Get Apple App Store settings
+### Get Apple App Store Connect settings
-Get the Apple App Store integration settings for a project.
+Get the Apple App Store Connect integration settings for a project.
```plaintext
GET /projects/:id/integrations/apple_app_store
diff --git a/doc/ci/mobile_devops.md b/doc/ci/mobile_devops.md
index e871a95d29f..4639967fb1d 100644
--- a/doc/ci/mobile_devops.md
+++ b/doc/ci/mobile_devops.md
@@ -338,26 +338,34 @@ To create an iOS distribution with the Apple Store integration and fastlane, you
1. Generate an API Key for App Store Connect API. In the Apple App Store Connect portal,
[generate a new private key for your project](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api).
-1. [Enable the Apple App Store integration](#enable-apple-app-store-integration).
+1. [Enable the Apple App Store Connect integration](#enable-the-apple-app-store-connect-integration).
1. Add the release step to your pipeline and fastlane configuration.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
-For an overview, see [Apple App Store integration demo](https://youtu.be/CwzAWVgJeK8).
+For an overview, see [Apple App Store Connect integration demo](https://youtu.be/CwzAWVgJeK8).
+<!-- Video published on 2023-03-17 -->
-#### Enable Apple App Store Integration
+#### Enable the Apple App Store Connect integration
-Use the [Apple App Store integration](../user/project/integrations/apple_app_store.md)
-to configure your CI/CD pipelines to connect to [App Store Connect](https://appstoreconnect.apple.com/)
-to build and release apps for iOS, iPadOS, macOS, tvOS, and watchOS. To enable the integration:
+Prerequisites:
+
+- You must have an Apple ID enrolled in the [Apple Developer Program](https://developer.apple.com/programs/enroll/).
+- You must [generate a new private key](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) for your project in the Apple App Store Connect portal.
+
+Use the Apple App Store Connect integration to configure your CI/CD pipelines to connect to [App Store Connect](https://appstoreconnect.apple.com).
+With this integration, you can build and release apps for iOS, iPadOS, macOS, tvOS, and watchOS.
+
+To enable the Apple App Store Connect integration in GitLab:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Integrations**.
-1. Select **Apple App Store**.
+1. Select **Apple App Store Connect**.
1. Under **Enable integration**, select the **Active** checkbox.
1. Provide the Apple App Store Connect configuration information:
- - **Issuer ID**: You can find the Apple App Store Connect Issuer ID in the **Keys** section under **Users and Access** in the Apple App Store Connect portal.
- - **Key ID**: The key ID of the new private key that was just generated.
- - **Private Key**: The private key that was just generated. You can only download this key one time.
+ - **Issuer ID**: The Apple App Store Connect issuer ID.
+ - **Key ID**: The key ID of the generated private key.
+ - **Private key**: The generated private key. You can download this key only once.
+ - **Protected branches and tags only**: Enable to set variables on protected branches and tags only.
1. Select **Save changes**.
With the integration enabled, you can use fastlane to distribute a build to TestFlight
diff --git a/doc/development/img/runner_fleet_dashboard.png b/doc/ci/runners/img/runner_fleet_dashboard.png
index 242ebf4aea9..242ebf4aea9 100644
--- a/doc/development/img/runner_fleet_dashboard.png
+++ b/doc/ci/runners/img/runner_fleet_dashboard.png
Binary files differ
diff --git a/doc/ci/runners/runner_fleet_dashboard.md b/doc/ci/runners/runner_fleet_dashboard.md
new file mode 100644
index 00000000000..08579f6ff92
--- /dev/null
+++ b/doc/ci/runners/runner_fleet_dashboard.md
@@ -0,0 +1,76 @@
+---
+stage: Verify
+group: Runner
+info: >-
+ To determine the technical writer assigned to the Stage/Group associated with
+ this page, see
+ https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+# Runner Fleet Dashboard **(ULTIMATE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/424495) in GitLab 16.6
+
+GitLab administrators can use the Runner Fleet Dashboard to assess the health of your instance runners.
+The Runner Fleet Dashboard shows:
+
+- Recent CI errors related caused by runner infrastructure.
+- Number of concurrent jobs executed on most busy runners.
+- Histogram of job queue times [(available only with ClickHouse)](#enable-more-ci-analytics-features-with-clickhouse).
+
+![Runner Fleet Dashboard](img/runner_fleet_dashboard.png)
+
+## View the Runner Fleet Dashboard
+
+Prerequisites:
+
+- You must be an administrator.
+
+To view the runner fleet dashboard:
+
+1. On the left sidebar, at the bottom, select **Admin Area**.
+1. Select **Runners**.
+1. Click **Fleet dashboard**.
+
+Most of the dashboard works without any additional actions, with the
+exception of **Wait time to pick a job** chart and [proposed features](#whats-next).
+These features require [setting up an additional infrastructure](#enable-more-ci-analytics-features-with-clickhouse).
+
+## Enable more CI analytics features with ClickHouse **(ULTIMATE EXPERIMENT)**
+
+> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/11180) in GitLab 16.7 behind several [feature flags](#enable-clickhouse-integration-and-features).
+
+This feature is an [Experiment](../../policy/experiment-beta-support.md).
+To test it, we have launched an early adopters program.
+To join the list of users testing this feature, contact us in
+[epic 11180](https://gitlab.com/groups/gitlab-org/-/epics/11180).
+
+### Enable ClickHouse integration and features
+
+To enable additional CI analytics features:
+
+1. [Configure ClickHouse integration](../../integration/clickhouse.md)
+1. [Enable](../../administration/feature_flags.md#how-to-enable-and-disable-features-behind-flags) the following feature flags:
+
+ | Feature flag name | Purpose |
+ |------------------------------------|---------------------------------------------------------------------------|
+ | `ci_data_ingestion_to_click_house` | Enables synchronization of new finished CI builds to Clickhouse database. |
+ | `clickhouse_ci_analytics` | Enables the **Wait time to pick a job** chart. |
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For a video walkthrough, see [Setting up Runner Fleet Dashboard with ClickHouse](https://www.youtube.com/watch?v=YpGV95Ctbpk).
+
+### What's next
+
+Support for usage and cost analysis are proposed in
+[epic 11183](https://gitlab.com/groups/gitlab-org/-/epics/11183).
+
+## Feedback
+
+To help us improve the Runner Fleet Dashboard, you can provide feedback in
+[issue 421737](https://gitlab.com/gitlab-org/gitlab/-/issues/421737).
+In particular:
+
+- How easy or difficult it was to setup GitLab to make the dashboard work.
+- How useful you found the dashboard.
+- What other information you would like to see on that dashboard.
+- Any other related thoughts and ideas.
diff --git a/doc/development/runner_fleet_dashboard.md b/doc/development/runner_fleet_dashboard.md
index f2683785e59..77c89f2adb5 100644
--- a/doc/development/runner_fleet_dashboard.md
+++ b/doc/development/runner_fleet_dashboard.md
@@ -1,69 +1,11 @@
---
-stage: Verify
-group: Runner
-info: >-
- To determine the technical writer assigned to the Stage/Group associated with
- this page, see
- https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
+redirect_to: '../ci/runners/runner_fleet_dashboard.md'
+remove_date: '2024-03-01'
---
-# Runner Fleet Dashboard **(ULTIMATE EXPERIMENT)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/424495) in GitLab 16.6 behind several [feature flags](../integration/clickhouse.md#enable-feature-flags).
+This document was moved to [another location](../ci/runners/runner_fleet_dashboard.md).
-This feature is an [Experiment](../policy/experiment-beta-support.md).
-To join the list of users testing this feature, contact us in
-[epic 11180](https://gitlab.com/groups/gitlab-org/-/epics/11180).
-
-GitLab administrators can use the Runner Fleet Dashboard to assess the health of your instance runners.
-The Runner Fleet Dashboard shows:
-
-- Recent CI errors related caused by runner infrastructure.
-- Number of concurrent jobs executed on most busy runners.
-- Histogram of job queue times (available only with ClickHouse).
-
-There is a proposal to introduce [more features](#whats-next) to the Runner Fleet Dashboard.
-
-![Runner Fleet Dashboard](img/runner_fleet_dashboard.png)
-
-## View the Runner Fleet Dashboard
-
-Prerequisites:
-
-- You must be an administrator.
-
-To view the runner fleet dashboard:
-
-1. On the left sidebar, at the bottom, select **Admin Area**.
-1. Select **Runners**.
-1. Click **Fleet dashboard**.
-
-Most of the dashboard works without any additional actions, with the
-exception of **Wait time to pick a job** chart and [proposed features](#whats-next).
-These features require setting up an additional infrastructure, described in this page.
-
-To test the Runner Fleet Dashboard and gather feedback, we have launched an early adopters program
-for some customers to try this feature.
-
-## Requirements
-
-To test the Runner Fleet Dashboard as part of the early adopters program, you must:
-
-- Run GitLab 16.7 or above.
-- Have an [Ultimate license](https://about.gitlab.com/pricing/).
-- Be able to run [ClickHouse database](../integration/clickhouse.md). We recommend using [ClickHouse Cloud](https://clickhouse.cloud/).
-
-## What's next
-
-Support for usage and cost analysis are proposed in
-[epic 11183](https://gitlab.com/groups/gitlab-org/-/epics/11183).
-
-## Feedback
-
-To help us improve the Runner Fleet Dashboard, you can provide feedback in
-[issue 421737](https://gitlab.com/gitlab-org/gitlab/-/issues/421737).
-In particular:
-
-- How easy or difficult it was to setup GitLab to make the dashboard work.
-- How useful you found the dashboard.
-- What other information you would like to see on that dashboard.
-- Any other related thoughts and ideas.
+<!-- This redirect file can be deleted after <2024-03-01>. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/integration/clickhouse.md b/doc/integration/clickhouse.md
index 6932df1c47e..1382488ad57 100644
--- a/doc/integration/clickhouse.md
+++ b/doc/integration/clickhouse.md
@@ -4,7 +4,9 @@ group: unassigned
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
-# ClickHouse integration guidelines
+# ClickHouse integration guidelines **(EXPERIMENT)**
+
+This feature is an [Experiment](../policy/experiment-beta-support.md).
Instructions about how to setup integration between GitLab and ClickHouse database.
@@ -15,7 +17,6 @@ To setup ClickHouse as the GitLab data storage:
1. [Run ClickHouse Cluster and configure database](#run-and-configure-clickhouse).
1. [Configure GitLab connection to Clickhouse](#configure-the-gitlab-connection-to-clickhouse).
1. [Run ClickHouse migrations](#run-clickhouse-migrations).
-1. [Enable the feature flags](#enable-feature-flags).
### Run and configure ClickHouse
@@ -123,15 +124,3 @@ To create the required database objects execute:
```shell
sudo gitlab-rake gitlab:clickhouse:migrate
```
-
-### Enable feature flags
-
-Features that use ClickHouse are currently under development and are disabled by feature flags.
-
-To enable these features, [enable](../administration/feature_flags.md#how-to-enable-and-disable-features-behind-flags)
-the following feature flags:
-
-| Feature flag name | Purpose |
-|------------------------------------|---------------------------------------------------------------------------|
-| `ci_data_ingestion_to_click_house` | Enables synchronization of new finished CI builds to Clickhouse database. |
-| `clickhouse_ci_analytics` | Enables the **Wait time to pick a job** chart. |
diff --git a/doc/subscriptions/gitlab_dedicated/index.md b/doc/subscriptions/gitlab_dedicated/index.md
index ae4a7c2fe3f..2259f2f1aa1 100644
--- a/doc/subscriptions/gitlab_dedicated/index.md
+++ b/doc/subscriptions/gitlab_dedicated/index.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# GitLab Dedicated
-GitLab Dedicated is a fully isolated, single-tenant SaaS service that is:
+GitLab Dedicated is a fully isolated, single-tenant SaaS solution that is:
- Hosted and managed by GitLab, Inc.
- Deployed on AWS in a cloud region of your choice. See [available AWS regions](#available-aws-regions).
@@ -69,7 +69,7 @@ GitLab Dedicated offers the following [compliance certifications](https://about.
#### Isolation
-As a single-tenant SaaS service, GitLab Dedicated provides infrastructure-level isolation of your GitLab environment. Your environment is placed into a separate AWS account from other tenants. This AWS account contains all of the underlying infrastructure necessary to host the GitLab application and your data stays within the account boundary. You administer the application while GitLab manages the underlying infrastructure. Tenant environments are also completely isolated from GitLab.com.
+As a single-tenant SaaS solution, GitLab Dedicated provides infrastructure-level isolation of your GitLab environment. Your environment is placed into a separate AWS account from other tenants. This AWS account contains all of the underlying infrastructure necessary to host the GitLab application and your data stays within the account boundary. You administer the application while GitLab manages the underlying infrastructure. Tenant environments are also completely isolated from GitLab.com.
#### Access controls
diff --git a/doc/user/project/integrations/apple_app_store.md b/doc/user/project/integrations/apple_app_store.md
index 3031ae42e4d..18022fbaeb8 100644
--- a/doc/user/project/integrations/apple_app_store.md
+++ b/doc/user/project/integrations/apple_app_store.md
@@ -16,19 +16,20 @@ The feature is still in development, but you can:
- [Report a bug](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=report_bug).
- [Share feedback](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=general_feedback).
-With the Apple App Store Connect integration, you can configure your CI/CD pipelines to connect to [App Store Connect](https://appstoreconnect.apple.com) to build and release apps for iOS, iPadOS, macOS, tvOS, and watchOS.
+Use the Apple App Store Connect integration to configure your CI/CD pipelines to connect to [App Store Connect](https://appstoreconnect.apple.com).
+With this integration, you can build and release apps for iOS, iPadOS, macOS, tvOS, and watchOS.
The Apple App Store Connect integration works out of the box with [fastlane](https://fastlane.tools/). You can also use this integration with other build tools.
-## Prerequisites
+## Enable the integration in GitLab
-An Apple ID enrolled in the [Apple Developer Program](https://developer.apple.com/programs/enroll/) is required to enable this integration.
+Prerequisites:
-## Configure GitLab
+- You must have an Apple ID enrolled in the [Apple Developer Program](https://developer.apple.com/programs/enroll/).
+- You must [generate a new private key](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) for your project in the Apple App Store Connect portal.
-GitLab supports enabling the Apple App Store Connect integration at the project level. Complete these steps in GitLab:
+To enable the Apple App Store Connect integration in GitLab:
-1. In the Apple App Store Connect portal, generate a new private key for your project by following [these instructions](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api).
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Integrations**.
1. Select **Apple App Store Connect**.
@@ -38,7 +39,6 @@ GitLab supports enabling the Apple App Store Connect integration at the project
- **Key ID**: The key ID of the generated private key.
- **Private key**: The generated private key. You can download this key only once.
- **Protected branches and tags only**: Enable to set variables on protected branches and tags only.
-
1. Select **Save changes**.
After you enable the integration:
diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md
index d658a09e760..00dfe0c66d9 100644
--- a/doc/user/project/pages/index.md
+++ b/doc/user/project/pages/index.md
@@ -226,7 +226,7 @@ Some other examples of mixing [variables](../../../ci/variables/index.md) with s
### Use multiple deployments to create pages environments
-You can use multiple GitLap Pages deployments to create a new [environment](../../../ci/environments/index.md).
+You can use multiple GitLab Pages deployments to create a new [environment](../../../ci/environments/index.md).
For example:
```yaml
diff --git a/doc/user/project/repository/code_suggestions/index.md b/doc/user/project/repository/code_suggestions/index.md
index 875b72354e4..3be54ad39f1 100644
--- a/doc/user/project/repository/code_suggestions/index.md
+++ b/doc/user/project/repository/code_suggestions/index.md
@@ -19,7 +19,9 @@ Write code more efficiently by using generative AI to suggest code while you're
With Code Suggestions, you get:
- Code Completion, which suggests completions to the current line you are typing. These suggestions are usually low latency.
-- Code Generation, which generates code based on a natural language code comment block.
+- Code Generation, which generates code based on a natural language code
+ comment block. Write a comment like `# Type more here` to generate the
+ appropriate code, based on the context of your comment and the rest of your code.
- Algorithms or large code blocks may take more than 10 seconds to generate.
- Streaming of code generation responses is supported in VS Code, leading to faster average response times. Other supported IDEs offer slower response times and will return the generated code in a single block.
@@ -53,7 +55,11 @@ For languages not listed in the following table, Code Suggestions might not func
### Supported languages in IDEs
-Editor support for languages is documented in the following table.
+Code Suggestions is aware of common popular programming concepts and
+infrastructure-as-code interfaces, like Kubernetes Resource Model (KRM),
+Google Cloud CLI, and Terraform.
+
+The editor supports these languages:
| Language | VS Code | JetBrains IDEs | Visual Studio | Neovim |
|------------------|------------------------|------------------------|------------------------|--------|
@@ -71,8 +77,6 @@ Editor support for languages is documented in the following table.
| Scala | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes |
| Swift | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes |
| TypeScript | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes |
-| Google Cloud | **{dotted-circle}** No | **{dotted-circle}** No | **{dotted-circle}** No | **{dotted-circle}** No |
-| Kubernetes Resource Model (KRM) | **{dotted-circle}** No | **{dotted-circle}** No | **{dotted-circle}** No | **{dotted-circle}** No |
| Terraform | **{check-circle}** Yes (Requires third-party extension providing Terraform support) | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes (Requires third-party extension providing the `terraform` file type) |
NOTE:
diff --git a/doc/user/project/service_desk/configure.md b/doc/user/project/service_desk/configure.md
index 91dbe7a38dd..87cdf795dd1 100644
--- a/doc/user/project/service_desk/configure.md
+++ b/doc/user/project/service_desk/configure.md
@@ -165,6 +165,7 @@ the assignees of the issue and creates to-do items for them.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a walkthrough, see [a short showcase video](https://youtu.be/163wDM1e43o).
+<!-- Video published on 2023-12-12 -->
Prerequisites:
@@ -191,6 +192,7 @@ Maintain brand identity and instill confidence among support requesters with a d
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview, see [a short showcase video](https://youtu.be/_moD5U3xcQs).
+<!-- Video published on 2023-09-12 -->
This feature is in [Beta](../../../policy/experiment-beta-support.md#beta).
A Beta feature is not production-ready, but is unlikely to change drastically
diff --git a/doc/user/project/service_desk/index.md b/doc/user/project/service_desk/index.md
index 0a76915b3fa..6a15b9798f7 100644
--- a/doc/user/project/service_desk/index.md
+++ b/doc/user/project/service_desk/index.md
@@ -16,6 +16,7 @@ through email.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a video overview, see [Introducing GitLab Service Desk (GitLab 16.7)](https://www.youtube.com/watch?v=LDVQXv3I5rI).
+<!-- Video published on 2023-12-19 -->
## Service Desk workflow
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 32f29c1502a..cfc8e330250 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -7413,6 +7413,9 @@ msgstr ""
msgid "AwardEmoji|No emoji found."
msgstr ""
+msgid "B"
+msgstr ""
+
msgid "Back"
msgstr ""
@@ -21999,6 +22002,9 @@ msgstr ""
msgid "Get started!"
msgstr ""
+msgid "GiB"
+msgstr ""
+
msgid "Git"
msgstr ""
@@ -27915,6 +27921,9 @@ msgstr ""
msgid "Ki"
msgstr ""
+msgid "KiB"
+msgstr ""
+
msgid "Kroki"
msgstr ""
@@ -30599,6 +30608,9 @@ msgstr ""
msgid "Mi"
msgstr ""
+msgid "MiB"
+msgstr ""
+
msgid "Microsoft|Client ID"
msgstr ""
@@ -43995,6 +44007,18 @@ msgstr ""
msgid "SecurityOrchestration|You don't have any security policies yet"
msgstr ""
+msgid "SecurityOrchestration|a license scanner found license violations"
+msgstr ""
+
+msgid "SecurityOrchestration|a merge request has been opened against a protected branch"
+msgstr ""
+
+msgid "SecurityOrchestration|a security policy has been violated"
+msgstr ""
+
+msgid "SecurityOrchestration|a security scanner found vulnerabilities matching the criteria"
+msgstr ""
+
msgid "SecurityOrchestration|all namespaces"
msgstr ""
@@ -46558,9 +46582,6 @@ msgstr ""
msgid "SortOptions|Priority"
msgstr ""
-msgid "SortOptions|Project"
-msgstr ""
-
msgid "SortOptions|Recent last activity"
msgstr ""
@@ -46594,9 +46615,6 @@ msgstr ""
msgid "SortOptions|Title"
msgstr ""
-msgid "SortOptions|Type"
-msgstr ""
-
msgid "SortOptions|Version"
msgstr ""
@@ -50067,6 +50085,11 @@ msgstr ""
msgid "This pipeline was triggered using the api"
msgstr ""
+msgid "This policy needs %{approvals} approval because %{rules}"
+msgid_plural "This policy needs %{approvals} approvals because %{rules}"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "This process deletes the project repository and all related resources."
msgstr ""
diff --git a/package.json b/package.json
index 57cce543a6d..5c613d20904 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.3.0",
"@gitlab/svgs": "3.74.0",
- "@gitlab/ui": "^72.1.0",
+ "@gitlab/ui": "^72.2.0",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "^0.0.1-dev-20231211152737",
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
diff --git a/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js b/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js
index f4f4936a134..b81bdc6ac74 100644
--- a/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js
+++ b/spec/frontend/jira_connect/branches/components/project_dropdown_spec.js
@@ -8,13 +8,15 @@ import ProjectDropdown from '~/jira_connect/branches/components/project_dropdown
import { PROJECTS_PER_PAGE } from '~/jira_connect/branches/constants';
import getProjectsQuery from '~/jira_connect/branches/graphql/queries/get_projects.query.graphql';
-import { mockProjects } from '../mock_data';
+import { mockProjects, mockProjects2 } from '../mock_data';
const mockProjectsQueryResponse = {
data: {
projects: {
+ __typename: 'ProjectsConnection',
nodes: mockProjects,
pageInfo: {
+ __typename: 'PageInfo',
hasNextPage: false,
hasPreviousPage: false,
startCursor: '',
@@ -121,6 +123,80 @@ describe('ProjectDropdown', () => {
});
});
+ describe('when projects query succeeds and has pagination', () => {
+ const mockProjectsWithPaginationQueryResponse = {
+ data: {
+ projects: {
+ __typename: 'ProjectsConnection',
+ nodes: mockProjects2,
+ pageInfo: {
+ __typename: 'PageInfo',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ startCursor: '',
+ endCursor: 'end123',
+ },
+ },
+ },
+ };
+ const mockGetProjectsQuery = jest.fn();
+
+ beforeEach(async () => {
+ mockGetProjectsQuery
+ .mockResolvedValueOnce(mockProjectsWithPaginationQueryResponse)
+ .mockResolvedValueOnce(mockProjectsQueryResponse);
+
+ createComponent({
+ mockApollo: createMockApolloProvider({
+ mockGetProjectsQuery,
+ }),
+ });
+ await waitForPromises();
+ });
+
+ afterEach(() => {
+ mockGetProjectsQuery.mockReset();
+ });
+
+ it('uses infinite-scroll', () => {
+ expect(findDropdown().props()).toMatchObject({
+ infiniteScroll: true,
+ infiniteScrollLoading: false,
+ });
+ });
+
+ describe('when "bottom-reached" event is emitted', () => {
+ beforeEach(() => {
+ findDropdown().vm.$emit('bottom-reached');
+ });
+
+ it('sets infinite-scroll-loading to true', () => {
+ expect(findDropdown().props('infiniteScrollLoading')).toBe(true);
+ });
+
+ it('calls fetchMore to get next page', () => {
+ expect(mockGetProjectsQuery).toHaveBeenCalledTimes(2);
+ expect(mockGetProjectsQuery).toHaveBeenCalledWith(
+ expect.objectContaining({
+ after: 'end123',
+ }),
+ );
+ });
+
+ it('appends query results to "items"', async () => {
+ const allProjects = [...mockProjects2, ...mockProjects];
+
+ await waitForPromises();
+
+ expect(findDropdown().props('infiniteScrollLoading')).toBe(false);
+
+ const dropdownItems = findDropdown().props('items');
+ expect(dropdownItems).toHaveLength(allProjects.length);
+ expect(dropdownItems).toMatchObject(allProjects);
+ });
+ });
+ });
+
describe('when projects query fails', () => {
beforeEach(async () => {
createComponent({
diff --git a/spec/frontend/jira_connect/branches/mock_data.js b/spec/frontend/jira_connect/branches/mock_data.js
index 1720e0118c8..a9e7182cb86 100644
--- a/spec/frontend/jira_connect/branches/mock_data.js
+++ b/spec/frontend/jira_connect/branches/mock_data.js
@@ -31,6 +31,36 @@ export const mockProjects = [
},
},
];
+export const mockProjects2 = [
+ {
+ id: 'gitlab-test',
+ name: 'gitlab-test',
+ nameWithNamespace: 'gitlab-test',
+ avatarUrl: 'https://gitlab.com',
+ path: 'gitlab-test-path',
+ fullPath: 'gitlab-test-path',
+ repository: {
+ empty: false,
+ },
+ userPermissions: {
+ pushCode: true,
+ },
+ },
+ {
+ id: 'gitlab-shell',
+ name: 'GitLab Shell',
+ nameWithNamespace: 'gitlab-org/gitlab-shell',
+ avatarUrl: 'https://gitlab.com',
+ path: 'gitlab-shell',
+ fullPath: 'gitlab-org/gitlab-shell',
+ repository: {
+ empty: false,
+ },
+ userPermissions: {
+ pushCode: true,
+ },
+ },
+];
export const mockProjectQueryResponse = (branchNames = mockBranchNames) => ({
data: {
diff --git a/spec/frontend/lib/utils/number_utility_spec.js b/spec/frontend/lib/utils/number_utils_spec.js
index 07e3e2f0422..831a2f975b6 100644
--- a/spec/frontend/lib/utils/number_utility_spec.js
+++ b/spec/frontend/lib/utils/number_utils_spec.js
@@ -14,12 +14,6 @@ import {
isNumeric,
isPositiveInteger,
} from '~/lib/utils/number_utils';
-import {
- BYTES_FORMAT_BYTES,
- BYTES_FORMAT_KIB,
- BYTES_FORMAT_MIB,
- BYTES_FORMAT_GIB,
-} from '~/lib/utils/constants';
describe('Number Utils', () => {
describe('formatRelevantDigits', () => {
@@ -87,23 +81,23 @@ describe('Number Utils', () => {
describe('numberToHumanSizeSplit', () => {
it('should return bytes', () => {
- expect(numberToHumanSizeSplit(654)).toEqual(['654', BYTES_FORMAT_BYTES]);
- expect(numberToHumanSizeSplit(-654)).toEqual(['-654', BYTES_FORMAT_BYTES]);
+ expect(numberToHumanSizeSplit(654)).toEqual(['654', 'B']);
+ expect(numberToHumanSizeSplit(-654)).toEqual(['-654', 'B']);
});
it('should return KiB', () => {
- expect(numberToHumanSizeSplit(1079)).toEqual(['1.05', BYTES_FORMAT_KIB]);
- expect(numberToHumanSizeSplit(-1079)).toEqual(['-1.05', BYTES_FORMAT_KIB]);
+ expect(numberToHumanSizeSplit(1079)).toEqual(['1.05', 'KiB']);
+ expect(numberToHumanSizeSplit(-1079)).toEqual(['-1.05', 'KiB']);
});
it('should return MiB', () => {
- expect(numberToHumanSizeSplit(10485764)).toEqual(['10.00', BYTES_FORMAT_MIB]);
- expect(numberToHumanSizeSplit(-10485764)).toEqual(['-10.00', BYTES_FORMAT_MIB]);
+ expect(numberToHumanSizeSplit(10485764)).toEqual(['10.00', 'MiB']);
+ expect(numberToHumanSizeSplit(-10485764)).toEqual(['-10.00', 'MiB']);
});
it('should return GiB', () => {
- expect(numberToHumanSizeSplit(10737418240)).toEqual(['10.00', BYTES_FORMAT_GIB]);
- expect(numberToHumanSizeSplit(-10737418240)).toEqual(['-10.00', BYTES_FORMAT_GIB]);
+ expect(numberToHumanSizeSplit(10737418240)).toEqual(['10.00', 'GiB']);
+ expect(numberToHumanSizeSplit(-10737418240)).toEqual(['-10.00', 'GiB']);
});
});
diff --git a/spec/frontend/search/store/actions_spec.js b/spec/frontend/search/store/actions_spec.js
index 889260fc478..1c70bfcf0ef 100644
--- a/spec/frontend/search/store/actions_spec.js
+++ b/spec/frontend/search/store/actions_spec.js
@@ -40,7 +40,15 @@ jest.mock('~/lib/utils/url_utility', () => ({
setUrlParams: jest.fn(),
joinPaths: jest.fn().mockReturnValue(''),
visitUrl: jest.fn(),
+ queryToObject: jest.fn().mockReturnValue({ scope: 'projects', search: '' }),
+ objectToQuery: jest.fn((params) =>
+ Object.keys(params)
+ .map((k) => `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`)
+ .join('&'),
+ ),
+ getBaseURL: jest.fn().mockReturnValue('http://gdk.test:3000'),
}));
+
jest.mock('~/lib/logger', () => ({
logError: jest.fn(),
}));
@@ -328,6 +336,23 @@ describe('Global Search Store Actions', () => {
});
});
+ describe('fetchSidebarCount uses wild card seach', () => {
+ beforeEach(() => {
+ state.navigation = mapValues(MOCK_NAVIGATION_DATA, (navItem) => ({
+ ...navItem,
+ count_link: '/search/count?scope=projects&search=',
+ }));
+ state.urlQuery.search = '';
+ });
+
+ it('should use wild card', async () => {
+ await testAction({ action: actions.fetchSidebarCount, state, expectedMutations: [] });
+ expect(mock.history.get[0].url).toBe(
+ 'http://gdk.test:3000/search/count?scope=projects&search=*',
+ );
+ });
+ });
+
describe.each`
action | axiosMock | type | expectedMutations | errorLogs
${actions.fetchAllAggregation} | ${{ method: 'onGet', code: HTTP_STATUS_OK }} | ${'success'} | ${MOCK_RECEIVE_AGGREGATIONS_SUCCESS_MUTATION} | ${0}
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js
index 9bd46267daa..88ee9375180 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js
@@ -398,4 +398,20 @@ describe('Merge request widget rebase component', () => {
expect(toast).toHaveBeenCalledWith('Rebase completed');
});
});
+
+ // This may happen when the session of a user is expired.
+ // see https://gitlab.com/gitlab-org/gitlab/-/issues/413627
+ describe('with empty project', () => {
+ it('does not throw any error', async () => {
+ const fn = async () => {
+ createWrapper({
+ handler: jest.fn().mockResolvedValue({ data: { project: null } }),
+ });
+
+ await waitForPromises();
+ };
+
+ await expect(fn()).resolves.not.toThrow();
+ });
+ });
});
diff --git a/spec/graphql/types/merge_request_type_spec.rb b/spec/graphql/types/merge_request_type_spec.rb
index be25c5177f6..4d1d651f0ca 100644
--- a/spec/graphql/types/merge_request_type_spec.rb
+++ b/spec/graphql/types/merge_request_type_spec.rb
@@ -38,6 +38,7 @@ RSpec.describe GitlabSchema.types['MergeRequest'], feature_category: :code_revie
has_ci mergeable commits committers commits_without_merge_commits squash security_auto_fix default_squash_commit_message
auto_merge_strategy merge_user award_emoji prepared_at codequality_reports_comparer supports_lock_on_merge
mergeability_checks
+ allows_multiple_assignees allows_multiple_reviewers
]
expect(described_class).to have_graphql_fields(*expected_fields).at_least
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index f97ec4514f9..36d72140006 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -42,6 +42,7 @@ RSpec.describe GitlabSchema.types['Project'], feature_category: :groups_and_proj
timelog_categories fork_targets branch_rules ci_config_variables pipeline_schedules languages
incident_management_timeline_event_tags visible_forks inherited_ci_variables autocomplete_users
ci_cd_settings detailed_import_status value_streams ml_models
+ allows_multiple_merge_request_assignees allows_multiple_merge_request_reviewers
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/helpers/environments_helper_spec.rb b/spec/helpers/environments_helper_spec.rb
index 14f99f144b2..2aae7b61bd1 100644
--- a/spec/helpers/environments_helper_spec.rb
+++ b/spec/helpers/environments_helper_spec.rb
@@ -3,9 +3,12 @@
require 'spec_helper'
RSpec.describe EnvironmentsHelper, feature_category: :environment_management do
+ include ActionView::Helpers::AssetUrlHelper
+
+ folder_name = 'env_folder'
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :repository) }
- let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:environment) { create(:environment, :with_folders, folder: folder_name, project: project) }
describe '#metrics_data', feature_category: :metrics do
before do
@@ -95,4 +98,23 @@ RSpec.describe EnvironmentsHelper, feature_category: :environment_management do
expect(subject).to eq(true)
end
end
+
+ describe '#environments_folder_list_view_data' do
+ subject { helper.environments_folder_list_view_data(project, folder_name) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?).and_return(true)
+ end
+
+ it 'returns folder related data' do
+ expect(subject).to include(
+ 'endpoint' => folder_project_environments_path(project, folder_name, format: :json),
+ 'can_read_environment' => 'true',
+ 'project_path' => project.full_path,
+ 'folder_name' => folder_name,
+ 'help_page_path' => '/help/ci/environments/index'
+ )
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb
index 84c2fb6525e..123428cde0e 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb
@@ -93,6 +93,37 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Helpers, feature_category: :continuo
end
end
+ context 'when drop_reason is nil' do
+ let(:command) { double(project: nil) }
+
+ shared_examples "error function with no drop reason" do
+ it 'drops with out failure reason' do
+ expect(command).to receive(:increment_pipeline_failure_reason_counter)
+
+ call_error
+
+ expect(pipeline.failure_reason).to be_nil
+ expect(pipeline.yaml_errors).to be_nil
+ expect(pipeline.errors[:base]).to include(message)
+ expect(pipeline).to be_failed
+ expect(pipeline).not_to be_persisted
+ end
+ end
+
+ context 'when no drop_reason argument is passed' do
+ let(:call_error) { subject.error(message) }
+
+ it_behaves_like "error function with no drop reason"
+ end
+
+ context 'when drop_reason argument is passed as nil' do
+ let(:drop_reason) { nil }
+ let(:call_error) { subject.error(message, drop_reason: drop_reason) }
+
+ it_behaves_like "error function with no drop reason"
+ end
+ end
+
context 'when config error is false' do
context 'does not set the yaml error or override the drop reason' do
let(:drop_reason) { :size_limit_exceeded }
@@ -107,7 +138,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Helpers, feature_category: :continuo
expect(pipeline).to be_persisted
end
- context ' when the drop reason is not persistable' do
+ context 'when the drop reason is not persistable' do
let(:drop_reason) { :filtered_by_rules }
let(:command) { double(project: nil) }
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 2b5f4165d8c..d9606b50bff 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -6127,4 +6127,32 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev
it { is_expected.to eq(false) }
end
end
+
+ describe '#allows_multiple_assignees?' do
+ let(:merge_request) { build_stubbed(:merge_request) }
+
+ subject(:allows_multiple_assignees?) { merge_request.allows_multiple_assignees? }
+
+ before do
+ allow(merge_request.project)
+ .to receive(:allows_multiple_merge_request_assignees?)
+ .and_return(false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+
+ describe '#allows_multiple_reviewers?' do
+ let(:merge_request) { build_stubbed(:merge_request) }
+
+ subject(:allows_multiple_reviewers?) { merge_request.allows_multiple_reviewers? }
+
+ before do
+ allow(merge_request.project)
+ .to receive(:allows_multiple_merge_request_reviewers?)
+ .and_return(false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index db1754ef991..f7402c273fb 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -8972,6 +8972,22 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
end
end
+ describe '#allows_multiple_merge_request_assignees?' do
+ let(:project) { build_stubbed(:project) }
+
+ subject(:allows_multiple_merge_request_assignees?) { project.allows_multiple_merge_request_assignees? }
+
+ it { is_expected.to eq(false) }
+ end
+
+ describe '#allows_multiple_merge_request_reviewers?' do
+ let(:project) { build_stubbed(:project) }
+
+ subject(:allows_multiple_merge_request_reviewers?) { project.allows_multiple_merge_request_reviewers? }
+
+ it { is_expected.to eq(false) }
+ end
+
private
def finish_job(export_job)
diff --git a/spec/models/project_statistics_spec.rb b/spec/models/project_statistics_spec.rb
index 211ac257c53..d21d29aa469 100644
--- a/spec/models/project_statistics_spec.rb
+++ b/spec/models/project_statistics_spec.rb
@@ -647,4 +647,22 @@ RSpec.describe ProjectStatistics do
end
end
end
+
+ describe '#export_size' do
+ it 'does not include artifacts & packages size' do
+ statistics.update!(
+ repository_size: 3.gigabytes,
+ wiki_size: 3.gigabytes,
+ lfs_objects_size: 3.gigabytes,
+ build_artifacts_size: 3.gigabytes,
+ packages_size: 3.gigabytes,
+ snippets_size: 3.gigabytes,
+ uploads_size: 3.gigabytes
+ )
+
+ statistics.refresh_storage_size!
+
+ expect(statistics.reload.export_size).to eq(15.gigabytes)
+ end
+ end
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 0cb13bfb917..e8bcdc2c44b 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -592,11 +592,19 @@ RSpec.describe Issues::UpdateService, :mailer, feature_category: :team_planning
update_issue(confidential: true)
end
+ it 'allows assignment of guest users' do
+ update_issue(confidential: true)
+
+ update_issue(assignee_ids: [guest.id])
+
+ expect(issue.reload.assignees).to contain_exactly(guest)
+ end
+
it 'does not update assignee_id with unauthorized users' do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
update_issue(confidential: true)
+
non_member = create(:user)
- original_assignees = issue.assignees
+ original_assignees = issue.assignees.to_a
update_issue(assignee_ids: [non_member.id])
diff --git a/yarn.lock b/yarn.lock
index bc886bce3fc..3c8c94d9871 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1274,10 +1274,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.74.0.tgz#b6b41be65b9e70378c0cef0435f96edd5467e759"
integrity sha512-eHoywPSLrYb+I/IYGapei2Tum5vLtgWkFxN0fxmUUAnBnxFSA+67aheI33kQVV3WjANuZGkglfPBX3QAmN8BLA==
-"@gitlab/ui@^72.1.0":
- version "72.1.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-72.1.0.tgz#77279a1acee3ab5d7a9cfe80150ab98eec7a52b4"
- integrity sha512-HKGjnwuqZ9+4Uf/UAgB3xs429ult6oIa07NWGJBe59S6mkCJxcb9co50zejvxQi0Bs7wVjF5pVH8kuZwZ58s6g==
+"@gitlab/ui@^72.2.0":
+ version "72.2.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-72.2.0.tgz#cd62b672559e441347dab6b0f0fb13ac60c4ca7c"
+ integrity sha512-FLse33QYS5yN14m7j4FsIiw9RLLxYLa6sbugVAcoERp+gPHVq7r6MeUFDa99SPhDDuerxItcwSwIGRtQyLfVMw==
dependencies:
"@floating-ui/dom" "1.2.9"
bootstrap-vue "2.23.1"