Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-10-29 09:21:55 +00:00
parent 9ff690d328
commit 9f31f06ca0
101 changed files with 1621 additions and 288 deletions

View File

@ -442,7 +442,6 @@ Lint/RedundantCopDisableDirective:
- 'spec/scripts/duo_chat/reporter_spec.rb'
- 'spec/services/alert_management/metric_images/upload_service_spec.rb'
- 'spec/services/boards/lists/list_service_spec.rb'
- 'spec/services/pages_domains/create_acme_order_service_spec.rb'
- 'spec/support/finder_collection.rb'
- 'spec/support/forgery_protection.rb'
- 'spec/support/helpers/database/multiple_databases_helpers.rb'

View File

@ -1442,7 +1442,6 @@ RSpec/BeEq:
- 'spec/services/packages/maven/metadata/create_plugins_xml_service_spec.rb'
- 'spec/services/packages/npm/create_package_service_spec.rb'
- 'spec/services/pages/delete_service_spec.rb'
- 'spec/services/pages_domains/update_service_spec.rb'
- 'spec/services/projects/auto_devops/disable_service_spec.rb'
- 'spec/services/projects/autocomplete_service_spec.rb'
- 'spec/services/projects/batch_open_issues_count_service_spec.rb'

View File

@ -1267,7 +1267,6 @@ RSpec/BeforeAllRoleAssignment:
- 'spec/services/packages/mark_packages_for_destruction_service_spec.rb'
- 'spec/services/packages/maven/metadata/sync_service_spec.rb'
- 'spec/services/packages/rubygems/dependency_resolver_service_spec.rb'
- 'spec/services/pages_domains/create_service_spec.rb'
- 'spec/services/post_receive_service_spec.rb'
- 'spec/services/projects/autocomplete_service_spec.rb'
- 'spec/services/projects/container_repository/destroy_service_spec.rb'

View File

@ -2518,7 +2518,6 @@ RSpec/ContextWording:
- 'spec/services/packages/rubygems/dependency_resolver_service_spec.rb'
- 'spec/services/packages/rubygems/process_gem_service_spec.rb'
- 'spec/services/packages/terraform_module/create_package_service_spec.rb'
- 'spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb'
- 'spec/services/personal_access_tokens/create_service_spec.rb'
- 'spec/services/personal_access_tokens/revoke_service_spec.rb'
- 'spec/services/post_receive_service_spec.rb'

View File

@ -360,7 +360,6 @@ RSpec/ExpectInHook:
- 'spec/services/packages/maven/metadata/sync_service_spec.rb'
- 'spec/services/packages/rubygems/process_gem_service_spec.rb'
- 'spec/services/packages/update_package_file_service_spec.rb'
- 'spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb'
- 'spec/services/projects/branches_by_mode_service_spec.rb'
- 'spec/services/projects/container_repository/delete_tags_service_spec.rb'
- 'spec/services/projects/container_repository/gitlab/delete_tags_service_spec.rb'

View File

@ -222,7 +222,6 @@ RSpec/VerifiedDoubleReference:
- 'spec/services/issues/close_service_spec.rb'
- 'spec/services/merge_requests/after_create_service_spec.rb'
- 'spec/services/merge_requests/mergeability/check_lfs_file_locks_service_spec.rb'
- 'spec/services/pages_domains/create_acme_order_service_spec.rb'
- 'spec/services/projects/destroy_service_spec.rb'
- 'spec/services/projects/fork_service_spec.rb'
- 'spec/services/repositories/replicate_service_spec.rb'

View File

@ -44,17 +44,6 @@ Style/IfUnlessModifier:
- 'app/models/ci/pending_build.rb'
- 'app/models/ci/pipeline.rb'
- 'app/models/ci/runner.rb'
- 'app/models/ci/running_build.rb'
- 'app/models/clusters/cluster.rb'
- 'app/models/clusters/clusters_hierarchy.rb'
- 'app/models/clusters/platforms/kubernetes.rb'
- 'app/models/commit.rb'
- 'app/models/concerns/atomic_internal_id.rb'
- 'app/models/concerns/avatarable.rb'
- 'app/models/concerns/bulk_insert_safe.rb'
- 'app/models/concerns/bulk_insertable_associations.rb'
- 'app/models/concerns/bulk_users_by_email_load.rb'
- 'app/models/concerns/cache_markdown_field.rb'
- 'app/models/concerns/ci/artifactable.rb'
- 'app/models/concerns/deprecated_assignee.rb'
- 'app/models/concerns/group_descendant.rb'

View File

@ -1,5 +1,5 @@
<script>
import { GlBadge, GlButton, GlTab, GlTabs, GlSprintf, GlIcon, GlLink } from '@gitlab/ui';
import { GlAvatar, GlBadge, GlButton, GlTab, GlTabs, GlSprintf, GlIcon, GlLink } from '@gitlab/ui';
import VueRouter from 'vue-router';
import { n__, s__, sprintf } from '~/locale';
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
@ -46,6 +46,7 @@ export default {
ActionsDropdown,
DeleteModelDisclosureDropdownItem,
TitleArea,
GlAvatar,
GlButton,
GlTabs,
GlTab,
@ -169,6 +170,12 @@ export default {
showCreatedDetail() {
return this.model?.author && this.model?.createdAt;
},
showModelAuthor() {
return this.model?.author;
},
showModelLatestVersion() {
return Boolean(this.model?.latestVersion);
},
},
methods: {
goTo(name) {
@ -197,8 +204,11 @@ export default {
i18n: {
createModelVersionLinkTitle: s__('MlModelRegistry|Create model version'),
editModelButtonLabel: s__('MlModelRegistry|Edit model'),
tabTitle: s__('MlModelRegistry|Model card'),
tabInnerTitle: s__('MlModelRegistry|Versions'),
tabModelCardTitle: s__('MlModelRegistry|Model card'),
tabVersionsTitle: s__('MlModelRegistry|Versions'),
versionCountTitle: s__('MlModelRegistry|Total versions'),
latestVersionTitle: s__('MlModelRegistry|Latest version'),
authorTitle: s__('MlModelRegistry|Publisher'),
},
modelVersionEntity: MODEL_ENTITIES.modelVersion,
ROUTE_DETAILS,
@ -260,23 +270,62 @@ export default {
</template>
</title-area>
<load-or-error-or-show :is-loading="isLoading" :error-message="errorMessage">
<gl-tabs class="gl-mt-4" :value="tabIndex">
<gl-tab
v-if="latestVersion"
:title="s__('MlModelRegistry|Model card')"
@click="goTo($options.ROUTE_DETAILS)"
/>
<gl-tab v-if="latestVersion" @click="goTo($options.ROUTE_VERSIONS)">
<template #title>
{{ s__('MlModelRegistry|Versions') }}
<gl-badge class="gl-tab-counter-badge">{{ versionCount }}</gl-badge>
</template>
</gl-tab>
<div class="gl-grid gl-gap-3 md:gl-grid-cols-4">
<div class="gl-pr-8 md:gl-col-span-3">
<load-or-error-or-show :is-loading="isLoading" :error-message="errorMessage">
<gl-tabs class="gl-mt-4" :value="tabIndex">
<gl-tab
v-if="latestVersion"
:title="$options.i18n.tabModelCardTitle"
@click="goTo($options.ROUTE_DETAILS)"
/>
<gl-tab v-if="latestVersion" @click="goTo($options.ROUTE_VERSIONS)">
<template #title>
{{ $options.i18n.tabVersionsTitle }}
<gl-badge class="gl-tab-counter-badge">{{ versionCount }}</gl-badge>
</template>
</gl-tab>
<router-view :model-id="model.id" :model="model" />
</gl-tabs>
</load-or-error-or-show>
<router-view :model-id="model.id" :model="model" />
</gl-tabs>
</load-or-error-or-show>
</div>
<div class="gl-pt-6 md:gl-col-span-1">
<div>
<div class="gl-text-lg gl-font-bold">{{ $options.i18n.authorTitle }}</div>
<div v-if="showModelAuthor" class="gl-pt-2 gl-text-gray-500">
<gl-link
data-testid="sidebar-author-link"
class="js-user-link gl-font-bold !gl-text-gray-500"
:href="model.author.webUrl"
>
<gl-avatar :label="model.author.name" :src="model.author.avatarUrl" :size="24" />
{{ model.author.name }}
</gl-link>
</div>
</div>
<div class="gl-mt-5">
<div class="gl-text-lg gl-font-bold">{{ $options.i18n.latestVersionTitle }}</div>
<div v-if="showModelLatestVersion" class="gl-pt-2 gl-text-gray-500">
<gl-link
data-testid="sidebar-latest-version-link"
:href="model.latestVersion._links.showPath"
>
{{ model.latestVersion.version }}
</gl-link>
</div>
</div>
<div class="gl-mt-5">
<div class="gl-text-lg gl-font-bold">{{ $options.i18n.versionCountTitle }}</div>
<div v-if="showCreatedDetail" class="gl-pt-2 gl-text-gray-500">
<span data-testid="sidebar-version-count">
{{ versionCount }}
</span>
</div>
</div>
</div>
</div>
</div>
</template>
</delete-model>

View File

@ -4,6 +4,9 @@ import { s__, __ } from '~/locale';
export const RESOURCE_TYPE_GROUPS = 'groups';
export const RESOURCE_TYPE_PROJECTS = 'projects';
export const ACCESS_LEVEL_DEFAULT = 'default';
export const ACCESS_LEVEL_OWNER = 'owner';
export const ORGANIZATION_ROOT_ROUTE_NAME = 'root';
export const FORM_FIELD_NAME = 'name';

View File

@ -2,7 +2,7 @@
import { __, s__ } from '~/locale';
import { createAlert } from '~/alert';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import organizationUsersQuery from '../graphql/organization_users.query.graphql';
import organizationUsersQuery from '../graphql/queries/organization_users.query.graphql';
import { ORGANIZATION_USERS_PER_PAGE } from '../constants';
import UsersView from './users_view.vue';
@ -51,8 +51,16 @@ export default {
const { nodes, pageInfo } = data.organization.organizationUsers;
this.pageInfo = pageInfo;
return nodes.map(({ badges, user }) => {
return { ...user, id: getIdFromGraphQLId(user.id), badges, email: user.publicEmail };
return nodes.map(({ id, badges, accessLevel, userPermissions, user }) => {
return {
...user,
gid: id,
id: getIdFromGraphQLId(user.id),
badges,
accessLevel,
userPermissions,
email: user.publicEmail,
};
});
},
error(error) {

View File

@ -1,15 +1,40 @@
<script>
import { GlLoadingIcon, GlKeysetPagination } from '@gitlab/ui';
import { GlLoadingIcon, GlKeysetPagination, GlCollapsibleListbox } from '@gitlab/ui';
import UsersTable from '~/vue_shared/components/users_table/users_table.vue';
import {
FIELD_NAME,
FIELD_ORGANIZATION_ROLE,
FIELD_CREATED_AT,
FIELD_LAST_ACTIVITY_ON,
} from '~/vue_shared/components/users_table/constants';
import { ACCESS_LEVEL_DEFAULT, ACCESS_LEVEL_OWNER } from '~/organizations/shared/constants';
import { __ } from '~/locale';
export default {
name: 'UsersView',
components: {
GlLoadingIcon,
GlKeysetPagination,
GlCollapsibleListbox,
UsersTable,
},
inject: ['paths'],
roleListboxItems: [
{
text: __('User'),
value: ACCESS_LEVEL_DEFAULT.toUpperCase(),
},
{
text: __('Owner'),
value: ACCESS_LEVEL_OWNER.toUpperCase(),
},
],
usersTable: {
fieldsToRender: [FIELD_NAME, FIELD_ORGANIZATION_ROLE, FIELD_CREATED_AT, FIELD_LAST_ACTIVITY_ON],
columnWidths: {
[FIELD_ORGANIZATION_ROLE]: 'gl-w-20',
},
},
props: {
users: {
type: Array,
@ -26,6 +51,11 @@ export default {
default: false,
},
},
methods: {
roleListboxItemText(accessLevel) {
return this.$options.roleListboxItems.find((item) => item.value === accessLevel).text;
},
},
};
</script>
@ -33,7 +63,23 @@ export default {
<div>
<gl-loading-icon v-if="loading" class="gl-mt-5" size="md" />
<template v-else>
<users-table :users="users" :admin-user-path="paths.adminUser" />
<users-table
:users="users"
:admin-user-path="paths.adminUser"
:fields-to-render="$options.usersTable.fieldsToRender"
:column-widths="$options.usersTable.columnWidths"
>
<template #organization-role="{ user }">
<gl-collapsible-listbox
v-if="user.userPermissions.adminOrganization"
:selected="user.accessLevel.stringValue"
block
toggle-class="gl-form-input-xl"
:items="$options.roleListboxItems"
/>
<span v-else>{{ roleListboxItemText(user.accessLevel.stringValue) }}</span>
</template>
</users-table>
<div class="gl-flex gl-justify-center">
<gl-keyset-pagination v-bind="pageInfo" @prev="$emit('prev')" @next="$emit('next')" />
</div>

View File

@ -25,6 +25,12 @@ query getOrganizationUsers(
createdAt
lastActivityOn
}
accessLevel {
stringValue
}
userPermissions {
adminOrganization
}
}
pageInfo {
...PageInfo

View File

@ -200,7 +200,7 @@ export default {
<div ref="overlay" class="super-sidebar-overlay" @click="collapseSidebar"></div>
<gl-button
v-if="sidebarData.is_logged_in"
class="super-sidebar-skip-to gl-sr-only gl-fixed gl-left-0 gl-m-3 focus:gl-not-sr-only"
class="super-sidebar-skip-to gl-sr-only !gl-fixed gl-left-0 gl-m-3 focus:gl-not-sr-only"
data-testid="super-sidebar-skip-to"
href="#content-body"
variant="confirm"

View File

@ -1,3 +1,11 @@
export const USER_AVATAR_SIZE = 32;
export const LENGTH_OF_USER_NOTE_TOOLTIP = 100;
export const FIELD_NAME = 'name';
export const FIELD_ORGANIZATION_ROLE = 'organizationRole';
export const FIELD_PROJECTS_COUNT = 'projectsCount';
export const FIELD_GROUP_COUNT = 'groupCount';
export const FIELD_CREATED_AT = 'createdAt';
export const FIELD_LAST_ACTIVITY_ON = 'lastActivityOn';
export const FIELD_SETTINGS = 'settings';

View File

@ -1,10 +1,19 @@
<script>
import NO_USERS_SVG from '@gitlab/svgs/dist/illustrations/empty-state/empty-user-settings-md.svg';
import { GlSkeletonLoader, GlTable } from '@gitlab/ui';
import { __ } from '~/locale';
import { __, s__ } from '~/locale';
import EmptyResult from '~/vue_shared/components/empty_result.vue';
import UserDate from '~/vue_shared/components/user_date.vue';
import UserAvatar from './user_avatar.vue';
import {
FIELD_NAME,
FIELD_ORGANIZATION_ROLE,
FIELD_PROJECTS_COUNT,
FIELD_GROUP_COUNT,
FIELD_CREATED_AT,
FIELD_LAST_ACTIVITY_ON,
FIELD_SETTINGS,
} from './constants';
export default {
components: {
@ -33,39 +42,79 @@ export default {
required: false,
default: false,
},
fieldsToRender: {
type: Array,
required: false,
default() {
return [
FIELD_NAME,
FIELD_PROJECTS_COUNT,
FIELD_GROUP_COUNT,
FIELD_CREATED_AT,
FIELD_LAST_ACTIVITY_ON,
FIELD_SETTINGS,
];
},
},
columnWidths: {
type: Object,
required: false,
default() {
return {
[FIELD_NAME]: 'gl-w-8/20',
[FIELD_PROJECTS_COUNT]: 'gl-w-2/20',
[FIELD_GROUP_COUNT]: 'gl-w-2/20',
[FIELD_CREATED_AT]: 'gl-w-3/20',
[FIELD_LAST_ACTIVITY_ON]: 'gl-w-3/20',
[FIELD_SETTINGS]: 'gl-w-2/20',
};
},
},
},
fields: [
{
key: 'name',
label: __('Name'),
thClass: 'gl-w-8/20',
computed: {
availableFields() {
return [
{
key: FIELD_NAME,
label: __('Name'),
thClass: this.columnWidths[FIELD_NAME],
},
{
key: FIELD_ORGANIZATION_ROLE,
label: s__('Organization|Organization role'),
thClass: this.columnWidths[FIELD_ORGANIZATION_ROLE],
},
{
key: FIELD_PROJECTS_COUNT,
label: __('Projects'),
thClass: this.columnWidths[FIELD_PROJECTS_COUNT],
},
{
key: FIELD_GROUP_COUNT,
label: __('Groups'),
thClass: this.columnWidths[FIELD_GROUP_COUNT],
},
{
key: FIELD_CREATED_AT,
label: __('Created on'),
thClass: this.columnWidths[FIELD_CREATED_AT],
},
{
key: FIELD_LAST_ACTIVITY_ON,
label: __('Last activity'),
thClass: this.columnWidths[FIELD_LAST_ACTIVITY_ON],
},
{
key: FIELD_SETTINGS,
label: '',
thClass: this.columnWidths[FIELD_SETTINGS],
},
];
},
{
key: 'projectsCount',
label: __('Projects'),
thClass: 'gl-w-2/20',
fields() {
return this.availableFields.filter((field) => this.fieldsToRender.includes(field.key));
},
{
key: 'groupCount',
label: __('Groups'),
thClass: 'gl-w-2/20',
},
{
key: 'createdAt',
label: __('Created on'),
thClass: 'gl-w-3/20',
},
{
key: 'lastActivityOn',
label: __('Last activity'),
thClass: 'gl-w-3/20',
},
{
key: 'settings',
label: '',
thClass: 'gl-w-2/20',
},
],
},
NO_USERS_SVG,
};
</script>
@ -74,7 +123,7 @@ export default {
<gl-table
v-if="users.length > 0"
:items="users"
:fields="$options.fields"
:fields="fields"
stacked="md"
:tbody-tr-attr="{ 'data-testid': 'user-row-content' }"
>
@ -82,6 +131,10 @@ export default {
<user-avatar :user="user" :admin-user-path="adminUserPath" />
</template>
<template v-if="$scopedSlots['organization-role']" #cell(organizationRole)="{ item: user }">
<slot name="organization-role" :user="user"></slot>
</template>
<template #cell(createdAt)="{ item: { createdAt } }">
<user-date :date="createdAt" />
</template>

View File

@ -1,14 +1,16 @@
<script>
import { GlButton, GlModal, GlDisclosureDropdownItem } from '@gitlab/ui';
import { GlButton, GlModal, GlDisclosureDropdownItem, GlTooltipDirective } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import { setNewWorkItemCache } from '~/work_items/graphql/cache_utils';
import { isWorkItemItemValidEnum } from '~/work_items/utils';
import { isMetaClick } from '~/lib/utils/common_utils';
import { isWorkItemItemValidEnum, newWorkItemPath } from '~/work_items/utils';
import {
I18N_NEW_WORK_ITEM_BUTTON_LABEL,
I18N_WORK_ITEM_CREATED,
sprintfWorkItem,
I18N_WORK_ITEM_ERROR_FETCHING_TYPES,
ROUTES,
} from '../constants';
import namespaceWorkItemTypesQuery from '../graphql/namespace_work_item_types.query.graphql';
import CreateWorkItem from './create_work_item.vue';
@ -20,6 +22,9 @@ export default {
GlModal,
GlDisclosureDropdownItem,
},
directives: {
GlTooltip: GlTooltipDirective,
},
inject: ['fullPath'],
props: {
description: {
@ -117,18 +122,26 @@ export default {
},
},
computed: {
useVueRouter() {
return (
!this.asDropdownItem &&
this.$router &&
this.$router.options.routes.some((route) => route.name === 'workItem')
);
},
newWorkItemPath() {
return newWorkItemPath({
fullPath: this.fullPath,
isGroup: this.isGroup,
workItemTypeName: this.workItemTypeName,
});
},
newWorkItemText() {
return sprintfWorkItem(I18N_NEW_WORK_ITEM_BUTTON_LABEL, this.workItemTypeName);
},
workItemCreatedText() {
return sprintfWorkItem(I18N_WORK_ITEM_CREATED, this.workItemTypeName);
},
dropdownItem() {
return {
text: this.newWorkItemText,
action: this.showModal,
};
},
},
watch: {
visible: {
@ -143,7 +156,15 @@ export default {
this.$emit('hideModal');
this.isVisible = false;
},
showModal() {
showModal(event) {
if (isMetaClick(event)) {
// opening in a new tab
return;
}
// don't follow the link for normal clicks - open in modal
event.preventDefault();
this.isVisible = true;
},
handleCreated(workItem) {
@ -151,11 +172,7 @@ export default {
action: {
text: __('View details'),
onClick: () => {
if (
!this.asDropdownItem &&
this.$router &&
this.$router.options.routes.some((route) => route.name === 'workItem')
) {
if (this.useVueRouter) {
this.$router.push({ name: 'workItem', params: { iid: workItem.iid } });
} else {
visitUrl(workItem.webUrl);
@ -175,6 +192,20 @@ export default {
}
this.hideModal();
},
redirectToNewPage(event) {
if (isMetaClick(event)) {
// opening in a new tab
return;
}
event.preventDefault();
if (this.useVueRouter) {
this.$router.push({ name: ROUTES.new });
} else {
visitUrl(this.newWorkItemPath);
}
},
},
};
</script>
@ -182,12 +213,21 @@ export default {
<template>
<div>
<template v-if="!hideButton">
<gl-disclosure-dropdown-item v-if="asDropdownItem" :item="dropdownItem" />
<!-- overriding default slow because using item.action doesn't pass the click event, so can't prevent href nav -->
<gl-disclosure-dropdown-item v-if="asDropdownItem">
<!-- using an a instead of gl-link to prevent unwanted underline style when active -->
<template #default
><a class="gl-new-dropdown-item-content" :href="newWorkItemPath" @click="showModal"
><span class="gl-new-dropdown-item-text-wrapper">{{ newWorkItemText }}</span></a
></template
>
</gl-disclosure-dropdown-item>
<gl-button
v-else
category="primary"
variant="confirm"
data-testid="new-epic-button"
:href="newWorkItemPath"
@click="showModal"
>{{ newWorkItemText }}
</gl-button>
@ -196,11 +236,27 @@ export default {
modal-id="create-work-item-modal"
modal-class="create-work-item-modal"
:visible="isVisible"
:title="newWorkItemText"
size="lg"
hide-footer
@hide="hideModal"
>
<template #modal-header>
<div class="gl-text gl-flex gl-w-full gl-items-center gl-gap-x-2">
<h2 class="modal-title">{{ newWorkItemText }}</h2>
<gl-button
v-gl-tooltip
data-testid="new-work-item-modal-link"
:href="newWorkItemPath"
:title="__('Open in full page')"
category="tertiary"
class="gl-text-secondary"
icon="maximize"
size="small"
:aria-label="__('Open in full page')"
@click="redirectToNewPage"
/>
</div>
</template>
<create-work-item
:description="description"
hide-form-title

View File

@ -171,6 +171,7 @@ export const WORK_ITEMS_TYPE_MAP = {
icon: `issue-type-issue`,
name: s__('WorkItem|Issue'),
value: WORK_ITEM_TYPE_VALUE_ISSUE,
routeParamName: 'issues',
},
[WORK_ITEM_TYPE_ENUM_TASK]: {
icon: `issue-type-task`,
@ -201,6 +202,7 @@ export const WORK_ITEMS_TYPE_MAP = {
icon: `epic`,
name: s__('WorkItem|Epic'),
value: WORK_ITEM_TYPE_VALUE_EPIC,
routeParamName: 'epics',
},
};

View File

@ -27,6 +27,7 @@ import {
WORK_ITEM_TYPE_ENUM_OBJECTIVE,
WORK_ITEM_TYPE_ENUM_KEY_RESULT,
WORK_ITEM_TYPE_ENUM_REQUIREMENTS,
WORK_ITEM_TYPE_ROUTE_WORK_ITEM,
NEW_WORK_ITEM_GID,
DEFAULT_PAGE_SIZE_CHILD_ITEMS,
} from './constants';
@ -154,6 +155,15 @@ export const markdownPreviewPath = ({ fullPath, iid, isGroup = false }) => {
return `${domain}/${basePath}/-/preview_markdown?target_type=WorkItem&target_id=${iid}`;
};
// the path for creating a new work item of that type, e.g. /groups/gitlab-org/-/epics/new
export const newWorkItemPath = ({ fullPath, isGroup = false, workItemTypeName }) => {
const domain = gon.relative_url_root || '';
const basePath = isGroup ? `groups/${fullPath}` : fullPath;
const type =
WORK_ITEMS_TYPE_MAP[workItemTypeName]?.routeParamName || WORK_ITEM_TYPE_ROUTE_WORK_ITEM;
return `${domain}/${basePath}/-/${type}/new`;
};
export const getDisplayReference = (workItemFullPath, workitemReference) => {
// The reference is replaced by work item fullpath in case the project and group are same.
// e.g., gitlab-org/gitlab-test#45 will be shown as #45

View File

@ -5,7 +5,7 @@ module Types
class OrganizationUser < BasePermissionType
graphql_name 'OrganizationUserPermissions'
abilities :remove_user, :delete_user
abilities :remove_user, :delete_user, :admin_organization
end
end
end

View File

@ -74,7 +74,7 @@ module ApplicationSettingImplementation
disabled_direct_code_suggestions: false,
disabled_oauth_sign_in_sources: [],
disable_password_authentication_for_users_with_sso_identities: false,
dns_rebinding_protection_enabled: true,
dns_rebinding_protection_enabled: Settings.gitlab['dns_rebinding_protection_enabled'],
domain_allowlist: Settings.gitlab['domain_allowlist'],
dsa_key_restriction: default_min_key_size(:dsa),
ecdsa_key_restriction: default_min_key_size(:ecdsa),

View File

@ -17,9 +17,7 @@ module Ci
enum runner_type: ::Ci::Runner.runner_types
def self.upsert_build!(build)
if build.runner.nil?
raise ArgumentError, 'build has not been picked by a runner'
end
raise ArgumentError, 'build has not been picked by a runner' if build.runner.nil?
# Owner namespace of the runner that executed the build
runner_owner_namespace_id = build.runner.owner_runner_namespace.namespace_id if build.runner.group_type?

View File

@ -324,9 +324,7 @@ module Clusters
.where(environment_scope: environment_scope)
.where.not(id: id)
if duplicate_management_clusters.any?
errors.add(:environment_scope, 'cannot add duplicated environment scope')
end
errors.add(:environment_scope, 'cannot add duplicated environment scope') if duplicate_management_clusters.any?
end
def unique_environment_scope
@ -395,15 +393,11 @@ module Clusters
end
def no_groups
if groups.any?
errors.add(:cluster, 'cannot have groups assigned')
end
errors.add(:cluster, 'cannot have groups assigned') if groups.any?
end
def no_projects
if projects.any?
errors.add(:cluster, 'cannot have projects assigned')
end
errors.add(:cluster, 'cannot have projects assigned') if projects.any?
end
end
end

View File

@ -38,9 +38,7 @@ module Clusters
raise ArgumentError, "unknown type for #{clusterable}"
end
if clusterable.is_a?(::Project)
cte << same_namespace_management_clusters_query
end
cte << same_namespace_management_clusters_query if clusterable.is_a?(::Project)
cte << base_query
cte << parent_query(cte)

View File

@ -236,9 +236,7 @@ module Clusters
def build_kube_client!
raise "Incomplete settings" unless api_url
unless (username && password) || token
raise "Either username/password or token is required to access API"
end
raise "Either username/password or token is required to access API" unless (username && password) || token
Gitlab::Kubernetes::KubeClient.new(
api_url,
@ -290,9 +288,7 @@ module Clusters
end
def no_namespace
if namespace
errors.add(:namespace, 'only allowed for project cluster')
end
errors.add(:namespace, 'only allowed for project cluster') if namespace
end
def prevent_modification

View File

@ -147,9 +147,7 @@ class Commit
# Commit in turn expects Time-like instances upon input, so we have to
# manually parse these values.
hash.each do |key, value|
if key.to_s.end_with?(date_suffix) && value.is_a?(String)
hash[key] = Time.zone.parse(value)
end
hash[key] = Time.zone.parse(value) if key.to_s.end_with?(date_suffix) && value.is_a?(String)
end
from_hash(hash, project)
@ -293,9 +291,7 @@ class Commit
}
}
if with_changed_files
data.merge!(repo_changes)
end
data.merge!(repo_changes) if with_changed_files
data
end

View File

@ -135,9 +135,7 @@ module AtomicInternalId
internal_id_scope_usage,
value)
if did_reset
write_attribute(column, nil)
end
write_attribute(column, nil) if did_reset
end
read_attribute(column)

View File

@ -57,13 +57,9 @@ module Avatarable
end
def avatar_path(only_path: true, size: nil)
unless self.try(:id)
return uncached_avatar_path(only_path: only_path, size: size)
end
return uncached_avatar_path(only_path: only_path, size: size) unless self.try(:id)
if self.try(:should_use_security_policy_bot_avatar?)
return self.security_policy_bot_static_avatar_path(size)
end
return self.security_policy_bot_static_avatar_path(size) if self.try(:should_use_security_policy_bot_avatar?)
# Cache this avatar path only within the request because avatars in
# object storage may be generated with time-limited, signed URLs.

View File

@ -157,9 +157,7 @@ module BulkInsertSafe
# Handle insertions for tables with a composite primary key
primary_keys = connection.schema_cache.primary_keys(table_name)
if unique_by.blank? && (composite_primary_key || primary_key != primary_keys)
unique_by = primary_keys
end
unique_by = primary_keys if unique_by.blank? && (composite_primary_key || primary_key != primary_keys)
transaction do
items.each_slice(batch_size).flat_map do |item_batch|

View File

@ -102,9 +102,7 @@ module BulkInsertableAssociations
items.each do |item|
item[reflection.foreign_key] = primary_key_value
if reflection.type
item[reflection.type] = self.class.polymorphic_name
end
item[reflection.type] = self.class.polymorphic_name if reflection.type
end
end

View File

@ -14,7 +14,9 @@ module BulkUsersByEmailLoad
grouped_users_by_email = User.by_any_email(emails, confirmed: true).preload(:emails).group_by(&:all_emails)
grouped_users_by_email.each_with_object({}) do |(found_emails, users), h|
found_emails.each { |e| h[e] = users.first if emails.include?(e) } # don't include all emails for an account, only the ones we want
found_emails.each do |e| # don't include all emails for an account, only the ones we want
h[e] = users.first if emails.include?(e)
end
end
end
end

View File

@ -40,9 +40,7 @@ module CacheMarkdownField
# Banzai is less strict about authors, so don't always have an author key
context[:author] = self.author if self.respond_to?(:author)
if Feature.enabled?(:personal_snippet_reference_filters, context[:author])
context[:user] = self.parent_user
end
context[:user] = self.parent_user if Feature.enabled?(:personal_snippet_reference_filters, context[:author])
context
end

View File

@ -11,12 +11,15 @@ module Namespaces
end
override :self_and_descendant_ids
def self_and_descendant_ids
def self_and_descendant_ids(skope: self.class)
# Cache only works for descendants
# of the same type as the caller.
return super unless skope == self.class
return super unless attempt_to_use_cached_data?
scope_with_cached_ids(
super,
self.class,
skope,
Namespaces::Descendants.arel_table[:self_and_descendant_group_ids]
)
end

View File

@ -120,16 +120,16 @@ module Namespaces
all_projects.select(:id)
end
def self_and_descendants
def self_and_descendants(skope: self.class)
return super unless use_traversal_ids?
lineage(top: self)
lineage(top: self, skope: skope)
end
def self_and_descendant_ids
def self_and_descendant_ids(skope: self.class)
return super unless use_traversal_ids?
self_and_descendants.as_ids
self_and_descendants(skope: skope).as_ids
end
def descendants
@ -265,11 +265,9 @@ module Namespaces
end
# Search this namespace's lineage. Bound inclusively by top node.
def lineage(top: nil, bottom: nil, hierarchy_order: nil)
def lineage(top: nil, bottom: nil, hierarchy_order: nil, skope: self.class)
raise UnboundedSearch, 'Must bound search by either top or bottom' unless top || bottom
skope = self.class
if top
skope = skope.where("traversal_ids @> ('{?}')", top.id)
end

View File

@ -73,13 +73,13 @@ module Namespaces
end
alias_method :recursive_descendants, :descendants
def self_and_descendants
object_hierarchy(self.class.where(id: id)).base_and_descendants
def self_and_descendants(skope: self.class)
object_hierarchy(skope.where(id: id)).base_and_descendants
end
alias_method :recursive_self_and_descendants, :self_and_descendants
def self_and_descendant_ids
object_hierarchy(self.class.where(id: id)).base_and_descendant_ids
def self_and_descendant_ids(skope: self.class)
object_hierarchy(skope.where(id: id)).base_and_descendant_ids
end
alias_method :recursive_self_and_descendant_ids, :self_and_descendant_ids

View File

@ -144,6 +144,10 @@ ci_runners:
- table: users
column: creator_id
on_delete: async_nullify
ci_runners_e59bb2812d:
- table: users
column: creator_id
on_delete: async_nullify
ci_running_builds:
- table: projects
column: project_id
@ -295,6 +299,13 @@ group_security_exclusions:
- table: namespaces
column: group_id
on_delete: async_delete
group_type_ci_runners_e59bb2812d:
- table: users
column: creator_id
on_delete: async_nullify
- table: namespaces
column: sharding_key_id
on_delete: async_delete
groups_visits:
- table: namespaces
column: entity_id
@ -302,6 +313,10 @@ groups_visits:
- table: users
column: user_id
on_delete: async_delete
instance_type_ci_runners_e59bb2812d:
- table: users
column: creator_id
on_delete: async_nullify
member_approvals:
- table: users
column: requested_by_id
@ -460,6 +475,13 @@ project_security_statistics:
- table: projects
column: project_id
on_delete: async_delete
project_type_ci_runners_e59bb2812d:
- table: users
column: creator_id
on_delete: async_nullify
- table: projects
column: sharding_key_id
on_delete: async_delete
projects:
- table: users
column: marked_for_deletion_by_user_id

View File

@ -188,6 +188,7 @@ Settings.gitlab['default_branch_protection_defaults'] ||= ::Gitlab::Access::Bran
# `default_can_create_group` is deprecated since GitLab 15.5 in favour of the `can_create_group` column on `ApplicationSetting`.
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
Settings.gitlab['default_theme'] = Gitlab::Themes::APPLICATION_DEFAULT if Settings.gitlab['default_theme'].nil?
Settings.gitlab['dns_rebinding_protection_enabled'] ||= !Gitlab.http_proxy_env?
Settings.gitlab['custom_html_header_tags'] ||= Settings.gitlab['custom_html_header_tags'] || ''
Settings.gitlab['host'] ||= ENV['GITLAB_HOST'] || 'localhost'
Settings.gitlab['cdn_host'] ||= ENV['GITLAB_CDN_HOST'].presence

View File

@ -16,4 +16,8 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
performance_indicator_type: []

View File

@ -17,4 +17,8 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
performance_indicator_type: []

View File

@ -17,4 +17,8 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
performance_indicator_type: []

View File

@ -16,5 +16,9 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/557
performance_indicator_type: []

View File

@ -16,5 +16,9 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
performance_indicator_type:
- customer_health_score

View File

@ -16,4 +16,8 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
performance_indicator_type: []

View File

@ -16,4 +16,8 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
performance_indicator_type: []

View File

@ -17,4 +17,8 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
performance_indicator_type: []

View File

@ -17,4 +17,8 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
performance_indicator_type: []

View File

@ -16,4 +16,8 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
performance_indicator_type: []

View File

@ -17,4 +17,8 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
performance_indicator_type: []

View File

@ -19,4 +19,8 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate
milestone_removed: "<16.4"

View File

@ -15,3 +15,5 @@ distribution:
- ee
tier:
- ultimate
tiers:
- ultimate

View File

@ -12,9 +12,13 @@ data_category: operational
instrumentation_class: InstallationCreationDateApproximationMetric
performance_indicator_type: []
distribution:
- ce
- ee
- ce
- ee
tier:
- free
- premium
- ultimate
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate

View File

@ -18,3 +18,7 @@ tier:
- free
- premium
- ultimate
tiers:
- free
- premium
- ultimate

View File

@ -16,3 +16,6 @@ distribution:
tier:
- premium
- ultimate
tiers:
- premium
- ultimate

View File

@ -5,5 +5,5 @@ feature_category: mlops
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/165214
milestone: '17.5'
queued_migration_version: 20241003061916
finalize_after: '2024-11-25'
finalized_by:
finalize_after: '2024-10-22'
finalized_by: 20241022155022

View File

@ -0,0 +1,8 @@
---
migration_job_name: RequeueBackfillApprovalProjectRulesProtectedBranchesProjectId
description: Requeue backfill sharding key `approval_project_rules_protected_branches.project_id` from `approval_project_rules` for gitlab.com.
feature_category: source_code_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/170799
milestone: '17.6'
queued_migration_version: 20241028141411
finalized_by: # version of the migration that finalized this BBM

View File

@ -0,0 +1,13 @@
---
table_name: ci_runners_e59bb2812d
classes:
- Ci::Runner
feature_categories:
- runner
- fleet_visibility
- hosted_runners
description: Routing table for CI runners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166308
milestone: '17.6'
gitlab_schema: gitlab_ci
exempt_from_sharding: true

View File

@ -0,0 +1,13 @@
---
table_name: group_type_ci_runners_e59bb2812d
classes:
- Ci::Runner
feature_categories:
- runner
- fleet_visibility
description: Registered CI group runners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166308
milestone: '17.6'
gitlab_schema: gitlab_ci
sharding_key:
sharding_key_id: namespaces

View File

@ -0,0 +1,13 @@
---
table_name: instance_type_ci_runners_e59bb2812d
classes:
- Ci::Runner
feature_categories:
- runner
- fleet_visibility
- hosted_runners
description: Registered CI instance runners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166308
milestone: '17.6'
gitlab_schema: gitlab_ci
exempt_from_sharding: true

View File

@ -0,0 +1,13 @@
---
table_name: project_type_ci_runners_e59bb2812d
classes:
- Ci::Runner
feature_categories:
- runner
- fleet_visibility
description: Registered CI project runners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166308
milestone: '17.6'
gitlab_schema: gitlab_ci
sharding_key:
sharding_key_id: projects

View File

@ -0,0 +1,93 @@
# frozen_string_literal: true
class CreatePartitionedCiRunners < Gitlab::Database::Migration[2.2]
include Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
milestone '17.6'
disable_ddl_transaction!
TABLE_NAME = 'ci_runners'
PARTITIONED_TABLE_PK = %w[id runner_type]
CONSTRAINT_NAME = 'check_sharding_key_id_nullness'
def up
partition_table_by_list(
TABLE_NAME, 'runner_type', primary_key: PARTITIONED_TABLE_PK,
partition_mappings: { instance_type: 1, group_type: 2, project_type: 3 },
partition_name_format: '%{partition_name}_%{table_name}',
create_partitioned_table_fn: ->(name) { create_partitioned_table(name) }
)
Gitlab::Database::PostgresPartitionedTable.each_partition(:ci_runners_e59bb2812d) do |partition|
source = partition.to_s
add_check_constraint(source,
source.start_with?('instance_type') ? 'sharding_key_id IS NULL' : 'sharding_key_id IS NOT NULL',
CONSTRAINT_NAME)
end
end
def down
drop_partitioned_table_for(TABLE_NAME)
end
private
def create_partitioned_table(name)
options = 'PARTITION BY LIST (runner_type)'
# rubocop: disable Migration/EnsureFactoryForTable -- we'll reuse the ci_runners factory once migrated
create_table name, primary_key: PARTITIONED_TABLE_PK, options: options do |t|
t.bigint :id, null: false
t.bigint :creator_id
t.bigint :sharding_key_id, null: true
t.timestamps_with_timezone null: true
t.datetime_with_timezone :contacted_at
t.datetime_with_timezone :token_expires_at
t.float :public_projects_minutes_cost_factor, null: false, default: 1.0
t.float :private_projects_minutes_cost_factor, null: false, default: 1.0
t.integer :access_level, null: false, default: 0
t.integer :maximum_timeout
t.integer :runner_type, null: false, limit: 2
t.integer :registration_type, null: false, limit: 2, default: 0
t.integer :creation_state, null: false, limit: 2, default: 0
t.boolean :active, null: false, default: true
t.boolean :run_untagged, null: false, default: true
t.boolean :locked, null: false, default: false
t.text :name, limit: 256
t.text :token_encrypted, limit: 128
t.text :token, limit: 128
t.text :description, limit: 1024
t.text :maintainer_note, limit: 1024
t.text :allowed_plans, array: true, null: false, default: []
t.bigint :allowed_plan_ids, array: true, null: false, default: []
t.index [:token_encrypted, :runner_type], name: "index_uniq_#{name}_on_token_encrypted_and_type", unique: true
t.index [:token, :runner_type], name: "idx_uniq_#{name}_on_token_and_type_where_not_null", unique: true,
where: "token IS NOT NULL"
t.index :creator_id, name: "index_#{name}_on_creator_id_where_not_null",
where: 'creator_id IS NOT NULL'
t.index :sharding_key_id, name: "index_#{name}_on_sharding_key_id_where_not_null",
where: 'sharding_key_id IS NOT NULL'
t.index %i[active id], name: "index_#{name}_on_active_and_id"
t.index %i[contacted_at id], name: "index_#{name}_on_contacted_at_and_id_desc",
order: { contacted_at: :asc, id: :desc }
t.index %i[contacted_at id], name: "idx_#{name}_on_contacted_at_and_id_where_inactive",
order: { contacted_at: :desc, runner_type: :asc, id: :desc }, where: 'active = false'
t.index %i[contacted_at id], name: "index_#{name}_on_contacted_at_desc_and_id_desc",
order: { contacted_at: :desc, runner_type: :asc, id: :desc }
t.index %i[created_at id], name: "index_#{name}_on_created_at_and_id_desc",
order: { runner_type: :asc, id: :desc }
t.index %i[created_at id], name: "index_#{name}_on_created_at_and_id_where_inactive",
order: { created_at: :desc, runner_type: :asc, id: :desc }, where: 'active = false'
t.index %i[created_at id], name: "index_#{name}_on_created_at_desc_and_id_desc",
order: { created_at: :desc, runner_type: :asc, id: :desc }
t.index :description, name: "index_#{name}_on_description_trigram", using: :gin, opclass: :gin_trgm_ops
t.index :locked, name: "index_#{name}_on_locked"
t.index %i[token_expires_at id], name: "index_#{name}_on_token_expires_at_and_id_desc",
order: { runner_type: :asc, id: :desc }
t.index %i[token_expires_at id], name: "idx_#{name}_on_token_expires_at_desc_and_id_desc",
order: { token_expires_at: :desc, runner_type: :asc, id: :desc }
end
# rubocop: enable Migration/EnsureFactoryForTable
end
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
class FinalizeRecoverDeletedMlModelVersionPackages < Gitlab::Database::Migration[2.2]
milestone '17.6'
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_main
def up
ensure_batched_background_migration_is_finished(
job_class_name: 'RecoverDeletedMlModelVersionPackages',
table_name: :ml_model_versions,
column_name: :id,
job_arguments: []
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,37 @@
# frozen_string_literal: true
class RequeueBackfillApprovalProjectRulesProtectedBranchesProjectId < Gitlab::Database::Migration[2.2]
milestone '17.6'
restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
MIGRATION = "BackfillApprovalProjectRulesProtectedBranchesProjectId"
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 5000
SUB_BATCH_SIZE = 500
TABLE_NAME = :approval_project_rules_protected_branches
BATCH_COLUMN = :approval_project_rule_id
JOB_ARGS = %i[project_id approval_project_rules project_id approval_project_rule_id]
def up
return unless Gitlab.com_except_jh?
delete_batched_background_migration(MIGRATION, TABLE_NAME, BATCH_COLUMN, JOB_ARGS)
queue_batched_background_migration(
MIGRATION,
TABLE_NAME,
BATCH_COLUMN,
*JOB_ARGS,
job_interval: DELAY_INTERVAL,
batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
return unless Gitlab.com_except_jh?
delete_batched_background_migration(MIGRATION, TABLE_NAME, BATCH_COLUMN, JOB_ARGS)
end
end

View File

@ -0,0 +1 @@
86e7ca3910488c0b943d350f84b16bbdf4a4479851e4bc221a435fab22a6ce3f

View File

@ -0,0 +1 @@
4d53b41a006e4a1802d444aa9b769935de5b12905d6cc2a5525ed24bc4e719dc

View File

@ -0,0 +1 @@
f31f9caedeb9665cb122da90be23ed8d0646b105d78f245c00af9ec8950bad21

View File

@ -750,6 +750,95 @@ $$;
COMMENT ON FUNCTION table_sync_function_3f39f64fc3() IS 'Partitioning migration: table sync for merge_request_diff_files table';
CREATE FUNCTION table_sync_function_686d6c7993() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF (TG_OP = 'DELETE') THEN
DELETE FROM ci_runners_e59bb2812d where "id" = OLD."id";
ELSIF (TG_OP = 'UPDATE') THEN
UPDATE ci_runners_e59bb2812d
SET "creator_id" = NEW."creator_id",
"sharding_key_id" = NEW."sharding_key_id",
"created_at" = NEW."created_at",
"updated_at" = NEW."updated_at",
"contacted_at" = NEW."contacted_at",
"token_expires_at" = NEW."token_expires_at",
"public_projects_minutes_cost_factor" = NEW."public_projects_minutes_cost_factor",
"private_projects_minutes_cost_factor" = NEW."private_projects_minutes_cost_factor",
"access_level" = NEW."access_level",
"maximum_timeout" = NEW."maximum_timeout",
"runner_type" = NEW."runner_type",
"registration_type" = NEW."registration_type",
"creation_state" = NEW."creation_state",
"active" = NEW."active",
"run_untagged" = NEW."run_untagged",
"locked" = NEW."locked",
"name" = NEW."name",
"token_encrypted" = NEW."token_encrypted",
"token" = NEW."token",
"description" = NEW."description",
"maintainer_note" = NEW."maintainer_note",
"allowed_plans" = NEW."allowed_plans",
"allowed_plan_ids" = NEW."allowed_plan_ids"
WHERE ci_runners_e59bb2812d."id" = NEW."id";
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO ci_runners_e59bb2812d ("id",
"creator_id",
"sharding_key_id",
"created_at",
"updated_at",
"contacted_at",
"token_expires_at",
"public_projects_minutes_cost_factor",
"private_projects_minutes_cost_factor",
"access_level",
"maximum_timeout",
"runner_type",
"registration_type",
"creation_state",
"active",
"run_untagged",
"locked",
"name",
"token_encrypted",
"token",
"description",
"maintainer_note",
"allowed_plans",
"allowed_plan_ids")
VALUES (NEW."id",
NEW."creator_id",
NEW."sharding_key_id",
NEW."created_at",
NEW."updated_at",
NEW."contacted_at",
NEW."token_expires_at",
NEW."public_projects_minutes_cost_factor",
NEW."private_projects_minutes_cost_factor",
NEW."access_level",
NEW."maximum_timeout",
NEW."runner_type",
NEW."registration_type",
NEW."creation_state",
NEW."active",
NEW."run_untagged",
NEW."locked",
NEW."name",
NEW."token_encrypted",
NEW."token",
NEW."description",
NEW."maintainer_note",
NEW."allowed_plans",
NEW."allowed_plan_ids");
END IF;
RETURN NULL;
END
$$;
COMMENT ON FUNCTION table_sync_function_686d6c7993() IS 'Partitioning migration: table sync for ci_runners table';
CREATE FUNCTION trigger_01b3fc052119() RETURNS trigger
LANGUAGE plpgsql
AS $$
@ -9273,6 +9362,39 @@ CREATE TABLE ci_runners (
CONSTRAINT check_ce275cee06 CHECK ((char_length(maintainer_note) <= 1024))
);
CREATE TABLE ci_runners_e59bb2812d (
id bigint NOT NULL,
creator_id bigint,
sharding_key_id bigint,
created_at timestamp with time zone,
updated_at timestamp with time zone,
contacted_at timestamp with time zone,
token_expires_at timestamp with time zone,
public_projects_minutes_cost_factor double precision DEFAULT 1.0 NOT NULL,
private_projects_minutes_cost_factor double precision DEFAULT 1.0 NOT NULL,
access_level integer DEFAULT 0 NOT NULL,
maximum_timeout integer,
runner_type smallint NOT NULL,
registration_type smallint DEFAULT 0 NOT NULL,
creation_state smallint DEFAULT 0 NOT NULL,
active boolean DEFAULT true NOT NULL,
run_untagged boolean DEFAULT true NOT NULL,
locked boolean DEFAULT false NOT NULL,
name text,
token_encrypted text,
token text,
description text,
maintainer_note text,
allowed_plans text[] DEFAULT '{}'::text[] NOT NULL,
allowed_plan_ids bigint[] DEFAULT '{}'::bigint[] NOT NULL,
CONSTRAINT check_1f8618ab23 CHECK ((char_length(name) <= 256)),
CONSTRAINT check_24b281f5bf CHECK ((char_length(maintainer_note) <= 1024)),
CONSTRAINT check_31c16b2a99 CHECK ((char_length(token_encrypted) <= 128)),
CONSTRAINT check_5db8ae9d30 CHECK ((char_length(description) <= 1024)),
CONSTRAINT check_af25130d5a CHECK ((char_length(token) <= 128))
)
PARTITION BY LIST (runner_type);
CREATE SEQUENCE ci_runners_id_seq
START WITH 1
INCREMENT BY 1
@ -12258,6 +12380,39 @@ CREATE SEQUENCE group_ssh_certificates_id_seq
ALTER SEQUENCE group_ssh_certificates_id_seq OWNED BY group_ssh_certificates.id;
CREATE TABLE group_type_ci_runners_e59bb2812d (
id bigint NOT NULL,
creator_id bigint,
sharding_key_id bigint,
created_at timestamp with time zone,
updated_at timestamp with time zone,
contacted_at timestamp with time zone,
token_expires_at timestamp with time zone,
public_projects_minutes_cost_factor double precision DEFAULT 1.0 NOT NULL,
private_projects_minutes_cost_factor double precision DEFAULT 1.0 NOT NULL,
access_level integer DEFAULT 0 NOT NULL,
maximum_timeout integer,
runner_type smallint NOT NULL,
registration_type smallint DEFAULT 0 NOT NULL,
creation_state smallint DEFAULT 0 NOT NULL,
active boolean DEFAULT true NOT NULL,
run_untagged boolean DEFAULT true NOT NULL,
locked boolean DEFAULT false NOT NULL,
name text,
token_encrypted text,
token text,
description text,
maintainer_note text,
allowed_plans text[] DEFAULT '{}'::text[] NOT NULL,
allowed_plan_ids bigint[] DEFAULT '{}'::bigint[] NOT NULL,
CONSTRAINT check_1f8618ab23 CHECK ((char_length(name) <= 256)),
CONSTRAINT check_24b281f5bf CHECK ((char_length(maintainer_note) <= 1024)),
CONSTRAINT check_31c16b2a99 CHECK ((char_length(token_encrypted) <= 128)),
CONSTRAINT check_5db8ae9d30 CHECK ((char_length(description) <= 1024)),
CONSTRAINT check_af25130d5a CHECK ((char_length(token) <= 128)),
CONSTRAINT check_sharding_key_id_nullness CHECK ((sharding_key_id IS NOT NULL))
);
CREATE TABLE group_wiki_repositories (
shard_id bigint NOT NULL,
group_id bigint NOT NULL,
@ -12783,6 +12938,39 @@ CREATE SEQUENCE instance_integrations_id_seq
ALTER SEQUENCE instance_integrations_id_seq OWNED BY instance_integrations.id;
CREATE TABLE instance_type_ci_runners_e59bb2812d (
id bigint NOT NULL,
creator_id bigint,
sharding_key_id bigint,
created_at timestamp with time zone,
updated_at timestamp with time zone,
contacted_at timestamp with time zone,
token_expires_at timestamp with time zone,
public_projects_minutes_cost_factor double precision DEFAULT 1.0 NOT NULL,
private_projects_minutes_cost_factor double precision DEFAULT 1.0 NOT NULL,
access_level integer DEFAULT 0 NOT NULL,
maximum_timeout integer,
runner_type smallint NOT NULL,
registration_type smallint DEFAULT 0 NOT NULL,
creation_state smallint DEFAULT 0 NOT NULL,
active boolean DEFAULT true NOT NULL,
run_untagged boolean DEFAULT true NOT NULL,
locked boolean DEFAULT false NOT NULL,
name text,
token_encrypted text,
token text,
description text,
maintainer_note text,
allowed_plans text[] DEFAULT '{}'::text[] NOT NULL,
allowed_plan_ids bigint[] DEFAULT '{}'::bigint[] NOT NULL,
CONSTRAINT check_1f8618ab23 CHECK ((char_length(name) <= 256)),
CONSTRAINT check_24b281f5bf CHECK ((char_length(maintainer_note) <= 1024)),
CONSTRAINT check_31c16b2a99 CHECK ((char_length(token_encrypted) <= 128)),
CONSTRAINT check_5db8ae9d30 CHECK ((char_length(description) <= 1024)),
CONSTRAINT check_af25130d5a CHECK ((char_length(token) <= 128)),
CONSTRAINT check_sharding_key_id_nullness CHECK ((sharding_key_id IS NULL))
);
CREATE TABLE integrations (
id bigint NOT NULL,
project_id bigint,
@ -17604,6 +17792,39 @@ CREATE SEQUENCE project_topics_id_seq
ALTER SEQUENCE project_topics_id_seq OWNED BY project_topics.id;
CREATE TABLE project_type_ci_runners_e59bb2812d (
id bigint NOT NULL,
creator_id bigint,
sharding_key_id bigint,
created_at timestamp with time zone,
updated_at timestamp with time zone,
contacted_at timestamp with time zone,
token_expires_at timestamp with time zone,
public_projects_minutes_cost_factor double precision DEFAULT 1.0 NOT NULL,
private_projects_minutes_cost_factor double precision DEFAULT 1.0 NOT NULL,
access_level integer DEFAULT 0 NOT NULL,
maximum_timeout integer,
runner_type smallint NOT NULL,
registration_type smallint DEFAULT 0 NOT NULL,
creation_state smallint DEFAULT 0 NOT NULL,
active boolean DEFAULT true NOT NULL,
run_untagged boolean DEFAULT true NOT NULL,
locked boolean DEFAULT false NOT NULL,
name text,
token_encrypted text,
token text,
description text,
maintainer_note text,
allowed_plans text[] DEFAULT '{}'::text[] NOT NULL,
allowed_plan_ids bigint[] DEFAULT '{}'::bigint[] NOT NULL,
CONSTRAINT check_1f8618ab23 CHECK ((char_length(name) <= 256)),
CONSTRAINT check_24b281f5bf CHECK ((char_length(maintainer_note) <= 1024)),
CONSTRAINT check_31c16b2a99 CHECK ((char_length(token_encrypted) <= 128)),
CONSTRAINT check_5db8ae9d30 CHECK ((char_length(description) <= 1024)),
CONSTRAINT check_af25130d5a CHECK ((char_length(token) <= 128)),
CONSTRAINT check_sharding_key_id_nullness CHECK ((sharding_key_id IS NOT NULL))
);
CREATE TABLE project_wiki_repositories (
id bigint NOT NULL,
project_id bigint NOT NULL,
@ -21996,6 +22217,12 @@ ALTER TABLE ONLY p_ci_pipelines ATTACH PARTITION ci_pipelines FOR VALUES IN ('10
ALTER TABLE ONLY p_ci_stages ATTACH PARTITION ci_stages FOR VALUES IN ('100', '101');
ALTER TABLE ONLY ci_runners_e59bb2812d ATTACH PARTITION group_type_ci_runners_e59bb2812d FOR VALUES IN ('2');
ALTER TABLE ONLY ci_runners_e59bb2812d ATTACH PARTITION instance_type_ci_runners_e59bb2812d FOR VALUES IN ('1');
ALTER TABLE ONLY ci_runners_e59bb2812d ATTACH PARTITION project_type_ci_runners_e59bb2812d FOR VALUES IN ('3');
ALTER TABLE ONLY abuse_events ALTER COLUMN id SET DEFAULT nextval('abuse_events_id_seq'::regclass);
ALTER TABLE ONLY abuse_report_assignees ALTER COLUMN id SET DEFAULT nextval('abuse_report_assignees_id_seq'::regclass);
@ -24321,6 +24548,9 @@ ALTER TABLE ONLY ci_runner_projects
ALTER TABLE ONLY ci_runner_versions
ADD CONSTRAINT ci_runner_versions_pkey PRIMARY KEY (version);
ALTER TABLE ONLY ci_runners_e59bb2812d
ADD CONSTRAINT ci_runners_e59bb2812d_pkey PRIMARY KEY (id, runner_type);
ALTER TABLE ONLY ci_runners
ADD CONSTRAINT ci_runners_pkey PRIMARY KEY (id);
@ -24771,6 +25001,9 @@ ALTER TABLE ONLY group_security_exclusions
ALTER TABLE ONLY group_ssh_certificates
ADD CONSTRAINT group_ssh_certificates_pkey PRIMARY KEY (id);
ALTER TABLE ONLY group_type_ci_runners_e59bb2812d
ADD CONSTRAINT group_type_ci_runners_e59bb2812d_pkey PRIMARY KEY (id, runner_type);
ALTER TABLE ONLY group_wiki_repositories
ADD CONSTRAINT group_wiki_repositories_pkey PRIMARY KEY (group_id);
@ -24855,6 +25088,9 @@ ALTER TABLE ONLY instance_audit_events_streaming_headers
ALTER TABLE ONLY instance_integrations
ADD CONSTRAINT instance_integrations_pkey PRIMARY KEY (id);
ALTER TABLE ONLY instance_type_ci_runners_e59bb2812d
ADD CONSTRAINT instance_type_ci_runners_e59bb2812d_pkey PRIMARY KEY (id, runner_type);
ALTER TABLE ONLY integrations
ADD CONSTRAINT integrations_pkey PRIMARY KEY (id);
@ -25542,6 +25778,9 @@ ALTER TABLE ONLY project_statistics
ALTER TABLE ONLY project_topics
ADD CONSTRAINT project_topics_pkey PRIMARY KEY (id);
ALTER TABLE ONLY project_type_ci_runners_e59bb2812d
ADD CONSTRAINT project_type_ci_runners_e59bb2812d_pkey PRIMARY KEY (id, runner_type);
ALTER TABLE ONLY project_wiki_repositories
ADD CONSTRAINT project_wiki_repositories_pkey PRIMARY KEY (id);
@ -27308,6 +27547,66 @@ CREATE UNIQUE INDEX finding_link_name_url_idx ON vulnerability_finding_links USI
CREATE UNIQUE INDEX finding_link_url_idx ON vulnerability_finding_links USING btree (vulnerability_occurrence_id, url) WHERE (name IS NULL);
CREATE UNIQUE INDEX index_uniq_ci_runners_e59bb2812d_on_token_encrypted_and_type ON ONLY ci_runners_e59bb2812d USING btree (token_encrypted, runner_type);
CREATE UNIQUE INDEX group_type_ci_runners_e59bb2812_token_encrypted_runner_type_idx ON group_type_ci_runners_e59bb2812d USING btree (token_encrypted, runner_type);
CREATE INDEX index_ci_runners_e59bb2812d_on_active_and_id ON ONLY ci_runners_e59bb2812d USING btree (active, id);
CREATE INDEX group_type_ci_runners_e59bb2812d_active_id_idx ON group_type_ci_runners_e59bb2812d USING btree (active, id);
CREATE INDEX index_ci_runners_e59bb2812d_on_contacted_at_and_id_desc ON ONLY ci_runners_e59bb2812d USING btree (contacted_at, id DESC);
CREATE INDEX group_type_ci_runners_e59bb2812d_contacted_at_id_idx ON group_type_ci_runners_e59bb2812d USING btree (contacted_at, id DESC);
CREATE INDEX idx_ci_runners_e59bb2812d_on_contacted_at_and_id_where_inactive ON ONLY ci_runners_e59bb2812d USING btree (contacted_at DESC, id DESC) WHERE (active = false);
CREATE INDEX group_type_ci_runners_e59bb2812d_contacted_at_id_idx1 ON group_type_ci_runners_e59bb2812d USING btree (contacted_at DESC, id DESC) WHERE (active = false);
CREATE INDEX index_ci_runners_e59bb2812d_on_contacted_at_desc_and_id_desc ON ONLY ci_runners_e59bb2812d USING btree (contacted_at DESC, id DESC);
CREATE INDEX group_type_ci_runners_e59bb2812d_contacted_at_id_idx2 ON group_type_ci_runners_e59bb2812d USING btree (contacted_at DESC, id DESC);
CREATE INDEX index_ci_runners_e59bb2812d_on_created_at_and_id_desc ON ONLY ci_runners_e59bb2812d USING btree (created_at, id DESC);
CREATE INDEX group_type_ci_runners_e59bb2812d_created_at_id_idx ON group_type_ci_runners_e59bb2812d USING btree (created_at, id DESC);
CREATE INDEX index_ci_runners_e59bb2812d_on_created_at_and_id_where_inactive ON ONLY ci_runners_e59bb2812d USING btree (created_at DESC, id DESC) WHERE (active = false);
CREATE INDEX group_type_ci_runners_e59bb2812d_created_at_id_idx1 ON group_type_ci_runners_e59bb2812d USING btree (created_at DESC, id DESC) WHERE (active = false);
CREATE INDEX index_ci_runners_e59bb2812d_on_created_at_desc_and_id_desc ON ONLY ci_runners_e59bb2812d USING btree (created_at DESC, id DESC);
CREATE INDEX group_type_ci_runners_e59bb2812d_created_at_id_idx2 ON group_type_ci_runners_e59bb2812d USING btree (created_at DESC, id DESC);
CREATE INDEX index_ci_runners_e59bb2812d_on_creator_id_where_not_null ON ONLY ci_runners_e59bb2812d USING btree (creator_id) WHERE (creator_id IS NOT NULL);
CREATE INDEX group_type_ci_runners_e59bb2812d_creator_id_idx ON group_type_ci_runners_e59bb2812d USING btree (creator_id) WHERE (creator_id IS NOT NULL);
CREATE INDEX index_ci_runners_e59bb2812d_on_description_trigram ON ONLY ci_runners_e59bb2812d USING gin (description gin_trgm_ops);
CREATE INDEX group_type_ci_runners_e59bb2812d_description_idx ON group_type_ci_runners_e59bb2812d USING gin (description gin_trgm_ops);
CREATE INDEX index_ci_runners_e59bb2812d_on_locked ON ONLY ci_runners_e59bb2812d USING btree (locked);
CREATE INDEX group_type_ci_runners_e59bb2812d_locked_idx ON group_type_ci_runners_e59bb2812d USING btree (locked);
CREATE INDEX index_ci_runners_e59bb2812d_on_sharding_key_id_where_not_null ON ONLY ci_runners_e59bb2812d USING btree (sharding_key_id) WHERE (sharding_key_id IS NOT NULL);
CREATE INDEX group_type_ci_runners_e59bb2812d_sharding_key_id_idx ON group_type_ci_runners_e59bb2812d USING btree (sharding_key_id) WHERE (sharding_key_id IS NOT NULL);
CREATE INDEX index_ci_runners_e59bb2812d_on_token_expires_at_and_id_desc ON ONLY ci_runners_e59bb2812d USING btree (token_expires_at, id DESC);
CREATE INDEX group_type_ci_runners_e59bb2812d_token_expires_at_id_idx ON group_type_ci_runners_e59bb2812d USING btree (token_expires_at, id DESC);
CREATE INDEX idx_ci_runners_e59bb2812d_on_token_expires_at_desc_and_id_desc ON ONLY ci_runners_e59bb2812d USING btree (token_expires_at DESC, id DESC);
CREATE INDEX group_type_ci_runners_e59bb2812d_token_expires_at_id_idx1 ON group_type_ci_runners_e59bb2812d USING btree (token_expires_at DESC, id DESC);
CREATE UNIQUE INDEX idx_uniq_ci_runners_e59bb2812d_on_token_and_type_where_not_null ON ONLY ci_runners_e59bb2812d USING btree (token, runner_type) WHERE (token IS NOT NULL);
CREATE UNIQUE INDEX group_type_ci_runners_e59bb2812d_token_runner_type_idx ON group_type_ci_runners_e59bb2812d USING btree (token, runner_type) WHERE (token IS NOT NULL);
CREATE UNIQUE INDEX i_affected_packages_unique_for_upsert ON pm_affected_packages USING btree (pm_advisory_id, purl_type, package_name, distro_version);
CREATE INDEX i_batched_background_migration_job_transition_logs_on_job_id ON ONLY batched_background_migration_job_transition_logs USING btree (batched_background_migration_job_id);
@ -31894,6 +32193,36 @@ CREATE INDEX index_zoom_meetings_on_issue_status ON zoom_meetings USING btree (i
CREATE INDEX index_zoom_meetings_on_project_id ON zoom_meetings USING btree (project_id);
CREATE INDEX instance_type_ci_runners_e59bb2812d_active_id_idx ON instance_type_ci_runners_e59bb2812d USING btree (active, id);
CREATE INDEX instance_type_ci_runners_e59bb2812d_contacted_at_id_idx ON instance_type_ci_runners_e59bb2812d USING btree (contacted_at, id DESC);
CREATE INDEX instance_type_ci_runners_e59bb2812d_contacted_at_id_idx1 ON instance_type_ci_runners_e59bb2812d USING btree (contacted_at DESC, id DESC) WHERE (active = false);
CREATE INDEX instance_type_ci_runners_e59bb2812d_contacted_at_id_idx2 ON instance_type_ci_runners_e59bb2812d USING btree (contacted_at DESC, id DESC);
CREATE INDEX instance_type_ci_runners_e59bb2812d_created_at_id_idx ON instance_type_ci_runners_e59bb2812d USING btree (created_at, id DESC);
CREATE INDEX instance_type_ci_runners_e59bb2812d_created_at_id_idx1 ON instance_type_ci_runners_e59bb2812d USING btree (created_at DESC, id DESC) WHERE (active = false);
CREATE INDEX instance_type_ci_runners_e59bb2812d_created_at_id_idx2 ON instance_type_ci_runners_e59bb2812d USING btree (created_at DESC, id DESC);
CREATE INDEX instance_type_ci_runners_e59bb2812d_creator_id_idx ON instance_type_ci_runners_e59bb2812d USING btree (creator_id) WHERE (creator_id IS NOT NULL);
CREATE INDEX instance_type_ci_runners_e59bb2812d_description_idx ON instance_type_ci_runners_e59bb2812d USING gin (description gin_trgm_ops);
CREATE INDEX instance_type_ci_runners_e59bb2812d_locked_idx ON instance_type_ci_runners_e59bb2812d USING btree (locked);
CREATE INDEX instance_type_ci_runners_e59bb2812d_sharding_key_id_idx ON instance_type_ci_runners_e59bb2812d USING btree (sharding_key_id) WHERE (sharding_key_id IS NOT NULL);
CREATE INDEX instance_type_ci_runners_e59bb2812d_token_expires_at_id_idx ON instance_type_ci_runners_e59bb2812d USING btree (token_expires_at, id DESC);
CREATE INDEX instance_type_ci_runners_e59bb2812d_token_expires_at_id_idx1 ON instance_type_ci_runners_e59bb2812d USING btree (token_expires_at DESC, id DESC);
CREATE UNIQUE INDEX instance_type_ci_runners_e59bb2812d_token_runner_type_idx ON instance_type_ci_runners_e59bb2812d USING btree (token, runner_type) WHERE (token IS NOT NULL);
CREATE UNIQUE INDEX instance_type_ci_runners_e59bb2_token_encrypted_runner_type_idx ON instance_type_ci_runners_e59bb2812d USING btree (token_encrypted, runner_type);
CREATE UNIQUE INDEX issue_user_mentions_on_issue_id_and_note_id_index ON issue_user_mentions USING btree (issue_id, note_id);
CREATE UNIQUE INDEX issue_user_mentions_on_issue_id_index ON issue_user_mentions USING btree (issue_id) WHERE (note_id IS NULL);
@ -31938,6 +32267,36 @@ CREATE INDEX partial_index_user_id_app_id_created_at_token_not_revoked ON oauth_
CREATE UNIQUE INDEX pm_checkpoints_path_components ON pm_checkpoints USING btree (purl_type, data_type, version_format);
CREATE INDEX project_type_ci_runners_e59bb2812d_active_id_idx ON project_type_ci_runners_e59bb2812d USING btree (active, id);
CREATE INDEX project_type_ci_runners_e59bb2812d_contacted_at_id_idx ON project_type_ci_runners_e59bb2812d USING btree (contacted_at, id DESC);
CREATE INDEX project_type_ci_runners_e59bb2812d_contacted_at_id_idx1 ON project_type_ci_runners_e59bb2812d USING btree (contacted_at DESC, id DESC) WHERE (active = false);
CREATE INDEX project_type_ci_runners_e59bb2812d_contacted_at_id_idx2 ON project_type_ci_runners_e59bb2812d USING btree (contacted_at DESC, id DESC);
CREATE INDEX project_type_ci_runners_e59bb2812d_created_at_id_idx ON project_type_ci_runners_e59bb2812d USING btree (created_at, id DESC);
CREATE INDEX project_type_ci_runners_e59bb2812d_created_at_id_idx1 ON project_type_ci_runners_e59bb2812d USING btree (created_at DESC, id DESC) WHERE (active = false);
CREATE INDEX project_type_ci_runners_e59bb2812d_created_at_id_idx2 ON project_type_ci_runners_e59bb2812d USING btree (created_at DESC, id DESC);
CREATE INDEX project_type_ci_runners_e59bb2812d_creator_id_idx ON project_type_ci_runners_e59bb2812d USING btree (creator_id) WHERE (creator_id IS NOT NULL);
CREATE INDEX project_type_ci_runners_e59bb2812d_description_idx ON project_type_ci_runners_e59bb2812d USING gin (description gin_trgm_ops);
CREATE INDEX project_type_ci_runners_e59bb2812d_locked_idx ON project_type_ci_runners_e59bb2812d USING btree (locked);
CREATE INDEX project_type_ci_runners_e59bb2812d_sharding_key_id_idx ON project_type_ci_runners_e59bb2812d USING btree (sharding_key_id) WHERE (sharding_key_id IS NOT NULL);
CREATE INDEX project_type_ci_runners_e59bb2812d_token_expires_at_id_idx ON project_type_ci_runners_e59bb2812d USING btree (token_expires_at, id DESC);
CREATE INDEX project_type_ci_runners_e59bb2812d_token_expires_at_id_idx1 ON project_type_ci_runners_e59bb2812d USING btree (token_expires_at DESC, id DESC);
CREATE UNIQUE INDEX project_type_ci_runners_e59bb2812d_token_runner_type_idx ON project_type_ci_runners_e59bb2812d USING btree (token, runner_type) WHERE (token IS NOT NULL);
CREATE UNIQUE INDEX project_type_ci_runners_e59bb28_token_encrypted_runner_type_idx ON project_type_ci_runners_e59bb2812d USING btree (token_encrypted, runner_type);
CREATE INDEX releases_published_at_index ON releases USING btree (release_published_at);
CREATE INDEX revised_idx_for_owasp_top_10_group_level_reports ON vulnerability_reads USING btree (owasp_top_10, state, report_type, resolved_on_default_branch, severity, traversal_ids, vulnerability_id) WHERE (archived = false);
@ -33578,6 +33937,38 @@ ALTER INDEX p_ci_pipelines_pkey ATTACH PARTITION ci_pipelines_pkey;
ALTER INDEX p_ci_stages_pkey ATTACH PARTITION ci_stages_pkey;
ALTER INDEX index_uniq_ci_runners_e59bb2812d_on_token_encrypted_and_type ATTACH PARTITION group_type_ci_runners_e59bb2812_token_encrypted_runner_type_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_active_and_id ATTACH PARTITION group_type_ci_runners_e59bb2812d_active_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_contacted_at_and_id_desc ATTACH PARTITION group_type_ci_runners_e59bb2812d_contacted_at_id_idx;
ALTER INDEX idx_ci_runners_e59bb2812d_on_contacted_at_and_id_where_inactive ATTACH PARTITION group_type_ci_runners_e59bb2812d_contacted_at_id_idx1;
ALTER INDEX index_ci_runners_e59bb2812d_on_contacted_at_desc_and_id_desc ATTACH PARTITION group_type_ci_runners_e59bb2812d_contacted_at_id_idx2;
ALTER INDEX index_ci_runners_e59bb2812d_on_created_at_and_id_desc ATTACH PARTITION group_type_ci_runners_e59bb2812d_created_at_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_created_at_and_id_where_inactive ATTACH PARTITION group_type_ci_runners_e59bb2812d_created_at_id_idx1;
ALTER INDEX index_ci_runners_e59bb2812d_on_created_at_desc_and_id_desc ATTACH PARTITION group_type_ci_runners_e59bb2812d_created_at_id_idx2;
ALTER INDEX index_ci_runners_e59bb2812d_on_creator_id_where_not_null ATTACH PARTITION group_type_ci_runners_e59bb2812d_creator_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_description_trigram ATTACH PARTITION group_type_ci_runners_e59bb2812d_description_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_locked ATTACH PARTITION group_type_ci_runners_e59bb2812d_locked_idx;
ALTER INDEX ci_runners_e59bb2812d_pkey ATTACH PARTITION group_type_ci_runners_e59bb2812d_pkey;
ALTER INDEX index_ci_runners_e59bb2812d_on_sharding_key_id_where_not_null ATTACH PARTITION group_type_ci_runners_e59bb2812d_sharding_key_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_token_expires_at_and_id_desc ATTACH PARTITION group_type_ci_runners_e59bb2812d_token_expires_at_id_idx;
ALTER INDEX idx_ci_runners_e59bb2812d_on_token_expires_at_desc_and_id_desc ATTACH PARTITION group_type_ci_runners_e59bb2812d_token_expires_at_id_idx1;
ALTER INDEX idx_uniq_ci_runners_e59bb2812d_on_token_and_type_where_not_null ATTACH PARTITION group_type_ci_runners_e59bb2812d_token_runner_type_idx;
ALTER INDEX p_ci_job_artifacts_job_id_file_type_partition_id_idx ATTACH PARTITION idx_ci_job_artifacts_on_job_id_file_type_and_partition_id_uniq;
ALTER INDEX p_ci_pipelines_ci_ref_id_id_idx ATTACH PARTITION idx_ci_pipelines_artifacts_locked;
@ -33710,8 +34101,72 @@ ALTER INDEX p_ci_builds_user_id_name_created_at_idx ATTACH PARTITION index_secur
ALTER INDEX p_ci_builds_name_id_idx ATTACH PARTITION index_security_ci_builds_on_name_and_id_parser_features;
ALTER INDEX index_ci_runners_e59bb2812d_on_active_and_id ATTACH PARTITION instance_type_ci_runners_e59bb2812d_active_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_contacted_at_and_id_desc ATTACH PARTITION instance_type_ci_runners_e59bb2812d_contacted_at_id_idx;
ALTER INDEX idx_ci_runners_e59bb2812d_on_contacted_at_and_id_where_inactive ATTACH PARTITION instance_type_ci_runners_e59bb2812d_contacted_at_id_idx1;
ALTER INDEX index_ci_runners_e59bb2812d_on_contacted_at_desc_and_id_desc ATTACH PARTITION instance_type_ci_runners_e59bb2812d_contacted_at_id_idx2;
ALTER INDEX index_ci_runners_e59bb2812d_on_created_at_and_id_desc ATTACH PARTITION instance_type_ci_runners_e59bb2812d_created_at_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_created_at_and_id_where_inactive ATTACH PARTITION instance_type_ci_runners_e59bb2812d_created_at_id_idx1;
ALTER INDEX index_ci_runners_e59bb2812d_on_created_at_desc_and_id_desc ATTACH PARTITION instance_type_ci_runners_e59bb2812d_created_at_id_idx2;
ALTER INDEX index_ci_runners_e59bb2812d_on_creator_id_where_not_null ATTACH PARTITION instance_type_ci_runners_e59bb2812d_creator_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_description_trigram ATTACH PARTITION instance_type_ci_runners_e59bb2812d_description_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_locked ATTACH PARTITION instance_type_ci_runners_e59bb2812d_locked_idx;
ALTER INDEX ci_runners_e59bb2812d_pkey ATTACH PARTITION instance_type_ci_runners_e59bb2812d_pkey;
ALTER INDEX index_ci_runners_e59bb2812d_on_sharding_key_id_where_not_null ATTACH PARTITION instance_type_ci_runners_e59bb2812d_sharding_key_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_token_expires_at_and_id_desc ATTACH PARTITION instance_type_ci_runners_e59bb2812d_token_expires_at_id_idx;
ALTER INDEX idx_ci_runners_e59bb2812d_on_token_expires_at_desc_and_id_desc ATTACH PARTITION instance_type_ci_runners_e59bb2812d_token_expires_at_id_idx1;
ALTER INDEX idx_uniq_ci_runners_e59bb2812d_on_token_and_type_where_not_null ATTACH PARTITION instance_type_ci_runners_e59bb2812d_token_runner_type_idx;
ALTER INDEX index_uniq_ci_runners_e59bb2812d_on_token_encrypted_and_type ATTACH PARTITION instance_type_ci_runners_e59bb2_token_encrypted_runner_type_idx;
ALTER INDEX p_ci_builds_scheduled_at_idx ATTACH PARTITION partial_index_ci_builds_on_scheduled_at_with_scheduled_jobs;
ALTER INDEX index_ci_runners_e59bb2812d_on_active_and_id ATTACH PARTITION project_type_ci_runners_e59bb2812d_active_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_contacted_at_and_id_desc ATTACH PARTITION project_type_ci_runners_e59bb2812d_contacted_at_id_idx;
ALTER INDEX idx_ci_runners_e59bb2812d_on_contacted_at_and_id_where_inactive ATTACH PARTITION project_type_ci_runners_e59bb2812d_contacted_at_id_idx1;
ALTER INDEX index_ci_runners_e59bb2812d_on_contacted_at_desc_and_id_desc ATTACH PARTITION project_type_ci_runners_e59bb2812d_contacted_at_id_idx2;
ALTER INDEX index_ci_runners_e59bb2812d_on_created_at_and_id_desc ATTACH PARTITION project_type_ci_runners_e59bb2812d_created_at_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_created_at_and_id_where_inactive ATTACH PARTITION project_type_ci_runners_e59bb2812d_created_at_id_idx1;
ALTER INDEX index_ci_runners_e59bb2812d_on_created_at_desc_and_id_desc ATTACH PARTITION project_type_ci_runners_e59bb2812d_created_at_id_idx2;
ALTER INDEX index_ci_runners_e59bb2812d_on_creator_id_where_not_null ATTACH PARTITION project_type_ci_runners_e59bb2812d_creator_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_description_trigram ATTACH PARTITION project_type_ci_runners_e59bb2812d_description_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_locked ATTACH PARTITION project_type_ci_runners_e59bb2812d_locked_idx;
ALTER INDEX ci_runners_e59bb2812d_pkey ATTACH PARTITION project_type_ci_runners_e59bb2812d_pkey;
ALTER INDEX index_ci_runners_e59bb2812d_on_sharding_key_id_where_not_null ATTACH PARTITION project_type_ci_runners_e59bb2812d_sharding_key_id_idx;
ALTER INDEX index_ci_runners_e59bb2812d_on_token_expires_at_and_id_desc ATTACH PARTITION project_type_ci_runners_e59bb2812d_token_expires_at_id_idx;
ALTER INDEX idx_ci_runners_e59bb2812d_on_token_expires_at_desc_and_id_desc ATTACH PARTITION project_type_ci_runners_e59bb2812d_token_expires_at_id_idx1;
ALTER INDEX idx_uniq_ci_runners_e59bb2812d_on_token_and_type_where_not_null ATTACH PARTITION project_type_ci_runners_e59bb2812d_token_runner_type_idx;
ALTER INDEX index_uniq_ci_runners_e59bb2812d_on_token_encrypted_and_type ATTACH PARTITION project_type_ci_runners_e59bb28_token_encrypted_runner_type_idx;
ALTER INDEX p_ci_job_artifacts_expire_at_job_id_idx1 ATTACH PARTITION tmp_index_ci_job_artifacts_on_expire_at_where_locked_unknown;
ALTER INDEX p_ci_builds_token_encrypted_partition_id_idx ATTACH PARTITION unique_ci_builds_token_encrypted_and_partition_id;
@ -33772,6 +34227,8 @@ CREATE TRIGGER push_rules_loose_fk_trigger AFTER DELETE ON push_rules REFERENCIN
CREATE TRIGGER table_sync_trigger_57c8465cd7 AFTER INSERT OR DELETE OR UPDATE ON merge_request_diff_commits FOR EACH ROW EXECUTE FUNCTION table_sync_function_0992e728d3();
CREATE TRIGGER table_sync_trigger_61879721b5 AFTER INSERT OR DELETE OR UPDATE ON ci_runners FOR EACH ROW EXECUTE FUNCTION table_sync_function_686d6c7993();
CREATE TRIGGER table_sync_trigger_cd362c20e2 AFTER INSERT OR DELETE OR UPDATE ON merge_request_diff_files FOR EACH ROW EXECUTE FUNCTION table_sync_function_3f39f64fc3();
CREATE TRIGGER tags_loose_fk_trigger AFTER DELETE ON tags REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();

View File

@ -16,7 +16,7 @@ described, it is possible to adapt these instructions to your needs.
## Architecture overview
![Geo multi-node diagram](img/geo-ha-diagram_v12.png)
![Architecture for running Geo in a multi-node configuration with primary and secondary backend services](img/geo-ha-diagram_v12.png)
_[diagram source - GitLab employees only](https://docs.google.com/drawings/d/1z0VlizKiLNXVVVaERFwgsIOuEgjcUqDTWPdQYsE7Z4c/edit)_

View File

@ -105,6 +105,10 @@ DETAILS:
**Tier:** Premium, Ultimate
**Offering:** Self-managed
NOTE:
The repository size limit includes repository files and LFS, but does not include artifacts, uploads,
wiki, packages, containers, or snippets. The repository size limit applies to both private and public projects.
Repositories in your GitLab instance can grow quickly, especially if you are
using LFS. Their size can grow exponentially, rapidly consuming available storage.
To prevent this from happening, you can set a hard limit for your repositories' size.
@ -150,10 +154,6 @@ The first push of a new project, including LFS objects, is checked for size.
If the sum of their sizes exceeds the maximum allowed repository size, the push
is rejected.
NOTE:
The repository size limit includes repository files and LFS, but does not include artifacts, uploads,
wiki, packages, or snippets. The repository size limit applies to both private and public projects.
For details on manually purging files, see [reducing the repository size using Git](../../user/project/repository/repository_size.md#methods-to-reduce-repository-size).
## Session duration

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -102,7 +102,7 @@ for all authenticated users, and on the **Admin** area pages. The statuses are:
- Red: The version of GitLab you are running is vulnerable. You should install
the latest version with security fixes as soon as possible.
![Version check showing that a non-critical update is available for the GitLab instance](../settings/img/update_available_v10_6.png)
![Version check showing that a non-critical update is available for the GitLab instance](../settings/img/non_critical_update_available_v10_6.png)
### Enable or disable version check

View File

@ -29338,6 +29338,7 @@ An organization user badge.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="organizationuserpermissionsadminorganization"></a>`adminOrganization` | [`Boolean!`](#boolean) | If `true`, the user can perform `admin_organization` on this resource. |
| <a id="organizationuserpermissionsdeleteuser"></a>`deleteUser` | [`Boolean!`](#boolean) | If `true`, the user can perform `delete_user` on this resource. |
| <a id="organizationuserpermissionsremoveuser"></a>`removeUser` | [`Boolean!`](#boolean) | If `true`, the user can perform `remove_user` on this resource. |

View File

@ -352,7 +352,7 @@ GitLab can display the results of one or more reports in the
## `artifacts:reports:repository_xray`
DETAILS:
**Tier:** Ultimate
**Tier:** Premium, Ultimate
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/432235) in GitLab 16.7.

View File

@ -16,6 +16,8 @@ When a project's repository and LFS exceed 10 GiB, the project is set to a read-
You cannot push changes to a read-only project. To increase storage of the project's repository and LFS to more than 10 GiB,
you must [purchase more storage](../subscriptions/gitlab_com/index.md#purchase-more-storage).
Only the project's repository and LFS are included in the storage limit. The container registry, package registry, and build artifacts are not included in the limit.
## View storage
DETAILS:

View File

@ -43,9 +43,12 @@ module Gitlab
argument :key_paths, type: :array, desc: 'Unique JSON key paths for the metrics'
def create_metric_file
say("This generator is DEPRECATED. Use Internal Events tracking framework instead.")
# rubocop: disable Gitlab/DocUrl -- link for developers, not users
say("This generator is DEPRECATED. For event based metrics use Internal Events tracking framework instead.")
# rubocop: disable Gitlab/DocUrl -- links for developers, not users
say("https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/quick_start.html")
say("If you need to implement Database, Prometheus or custom metrics, see")
say("https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_instrumentation.html")
# rubocop: enable Gitlab/DocUrl
desc = ask("Would you like to continue anyway? y/N") || 'n'
return unless desc.casecmp('y') == 0

View File

@ -9,7 +9,8 @@ module Gitlab
include ::Gitlab::Database::MigrationHelpers::LooseForeignKeyHelpers
ALLOWED_TABLES = %w[group_audit_events project_audit_events instance_audit_events user_audit_events
audit_events web_hook_logs merge_request_diff_files merge_request_diff_commits].freeze
audit_events web_hook_logs merge_request_diff_files merge_request_diff_commits
ci_runners ci_runner_machines].freeze
ERROR_SCOPE = 'table partitioning'

View File

@ -35072,6 +35072,9 @@ msgstr ""
msgid "MlModelRegistry|Provide the max allowed file size"
msgstr ""
msgid "MlModelRegistry|Publisher"
msgstr ""
msgid "MlModelRegistry|Save changes"
msgstr ""
@ -35090,6 +35093,9 @@ msgstr ""
msgid "MlModelRegistry|This model has no candidates"
msgstr ""
msgid "MlModelRegistry|Total versions"
msgstr ""
msgid "MlModelRegistry|Triggered by"
msgstr ""
@ -38409,6 +38415,9 @@ msgstr ""
msgid "Organization|Organization overview"
msgstr ""
msgid "Organization|Organization role"
msgstr ""
msgid "Organization|Organization settings"
msgstr ""

View File

@ -2,7 +2,7 @@
source 'https://rubygems.org'
gem 'gitlab-qa', '~> 14', '>= 14.19.0', require: 'gitlab/qa'
gem 'gitlab-qa', '~> 14', '>= 14.19.1', require: 'gitlab/qa'
gem 'gitlab_quality-test_tooling', '~> 2.1.0', require: false
gem 'gitlab-utils', path: '../gems/gitlab-utils'
gem 'activesupport', '~> 7.0.8.4' # This should stay in sync with the root's Gemfile

View File

@ -117,7 +117,7 @@ GEM
gitlab (4.19.0)
httparty (~> 0.20)
terminal-table (>= 1.5.1)
gitlab-qa (14.19.0)
gitlab-qa (14.19.1)
activesupport (>= 6.1, < 7.2)
gitlab (~> 4.19)
http (~> 5.0)
@ -362,7 +362,7 @@ DEPENDENCIES
fog-core (= 2.1.0)
fog-google (~> 1.24, >= 1.24.1)
gitlab-cng!
gitlab-qa (~> 14, >= 14.19.0)
gitlab-qa (~> 14, >= 14.19.1)
gitlab-utils!
gitlab_quality-test_tooling (~> 2.1.0)
googleauth (~> 1.9.0)

View File

@ -517,7 +517,7 @@ module QA
# Uses the API to wait until a pull mirroring update is successful (pull mirroring is treated as an import)
def wait_for_pull_mirroring
mirror_succeeded = Support::Retrier.retry_until(
max_duration: 180,
max_duration: 360,
raise_on_failure: false,
sleep_interval: 1
) do
@ -525,7 +525,10 @@ module QA
api_resource[:import_status] == "finished"
end
raise "Mirroring failed with error: #{api_resource[:import_error]}" unless mirror_succeeded
return if mirror_succeeded
mirror_error = api_resource[:import_error] || 'Did not complete within 360 seconds'
raise "Mirroring was not successful: #{mirror_error}}"
end
def remove_via_api!

View File

@ -110,6 +110,8 @@ RSpec.describe 'Database schema',
ci_runners: %w[sharding_key_id], # This value is meant to populate the partitioned table, no other usage
ci_runner_machines: %w[sharding_key_id], # This value is meant to populate the partitioned table, no other usage
ci_runner_projects: %w[runner_id],
ci_runners_e59bb2812d: %w[sharding_key_id], # This field is only used in the partitions, and has the appropriate FKs
instance_type_ci_runners_e59bb2812d: %w[sharding_key_id], # This field is always NULL in this partition
ci_sources_pipelines: %w[partition_id source_partition_id source_job_id],
ci_sources_projects: %w[partition_id],
ci_stages: %w[partition_id project_id pipeline_id],

View File

@ -169,7 +169,7 @@ RSpec.describe 'Organizations (GraphQL fixtures)', feature_category: :cell do
end
describe 'organization users' do
base_input_path = 'organizations/users/graphql/'
base_input_path = 'organizations/users/graphql/queries/'
base_output_path = 'graphql/organizations/'
query_name = 'organization_users.query.graphql'

View File

@ -1,4 +1,4 @@
import { GlBadge, GlTab, GlTabs, GlIcon, GlSprintf, GlLink } from '@gitlab/ui';
import { GlAvatar, GlBadge, GlTab, GlTabs, GlIcon, GlSprintf, GlLink } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import VueRouter from 'vue-router';
@ -305,4 +305,50 @@ describe('ml/model_registry/apps/show_ml_model', () => {
});
});
});
describe('Sidebar', () => {
beforeEach(() => createWrapper());
const findSidebarAuthorLink = () => wrapper.findByTestId('sidebar-author-link');
const findAvatar = () => wrapper.findComponent(GlAvatar);
const findLatestVersionLink = () => wrapper.findByTestId('sidebar-latest-version-link');
const findVersionCount = () => wrapper.findByTestId('sidebar-version-count');
it('displays sidebar author link', () => {
expect(findSidebarAuthorLink().attributes('href')).toBe('path/to/user');
expect(findSidebarAuthorLink().text()).toBe('Root');
});
it('displays sidebar avatar', () => {
expect(findAvatar().props('src')).toBe('path/to/avatar');
});
it('displays sidebar latest version link', () => {
expect(findLatestVersionLink().attributes('href')).toBe(
'/root/test-project/-/ml/models/1/versions/5000',
);
expect(findLatestVersionLink().text()).toBe('1.0.4999');
});
it('displays sidebar version count', () => {
expect(findVersionCount().text()).toBe('1');
});
describe('when model does not get loaded', () => {
const error = new Error('Failure!');
beforeEach(() => createWrapper({ modelDetailsResolver: jest.fn().mockRejectedValue(error) }));
it('does not display sidebar author link', () => {
expect(findSidebarAuthorLink().exists()).toBe(false);
});
it('does not display sidebar latest version link', () => {
expect(findLatestVersionLink().exists()).toBe(false);
});
it('does not display sidebar version count', () => {
expect(findVersionCount().exists()).toBe(false);
});
});
});
});

View File

@ -5,7 +5,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/alert';
import organizationUsersQuery from '~/organizations/users/graphql/organization_users.query.graphql';
import organizationUsersQuery from '~/organizations/users/graphql/queries/organization_users.query.graphql';
import OrganizationsUsersApp from '~/organizations/users/components/app.vue';
import OrganizationsUsersView from '~/organizations/users/components/users_view.vue';
import { ORGANIZATION_USERS_PER_PAGE } from '~/organizations/users/constants';

View File

@ -1,5 +1,5 @@
import { GlLoadingIcon, GlKeysetPagination } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { GlLoadingIcon, GlKeysetPagination, GlCollapsibleListbox } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import UsersView from '~/organizations/users/components/users_view.vue';
import UsersTable from '~/vue_shared/components/users_table/users_table.vue';
import { pageInfoMultiplePages } from 'jest/organizations/mock_data';
@ -9,7 +9,7 @@ describe('UsersView', () => {
let wrapper;
const createComponent = (props = {}) => {
wrapper = shallowMount(UsersView, {
wrapper = mountExtended(UsersView, {
propsData: {
loading: false,
users: MOCK_USERS_FORMATTED,
@ -66,4 +66,46 @@ describe('UsersView', () => {
expect(wrapper.emitted('prev')).toHaveLength(1);
});
});
describe('Organization role', () => {
describe('when user has permissions to change organization role', () => {
const users = MOCK_USERS_FORMATTED.map((user) => ({
...user,
userPermissions: { ...user.userPermissions, adminOrganization: true },
}));
beforeEach(() => {
createComponent({
loading: false,
users,
});
});
it('renders listbox with role options', () => {
expect(wrapper.findComponent(GlCollapsibleListbox).props()).toMatchObject({
items: [
{
text: 'User',
value: 'DEFAULT',
},
{
text: 'Owner',
value: 'OWNER',
},
],
selected: users[0].accessLevel.stringValue,
});
});
});
describe('when does not have permissions to change organization role', () => {
beforeEach(() => {
createComponent({ loading: false, users: MOCK_USERS_FORMATTED });
});
it('renders role as text', () => {
expect(wrapper.findByRole('cell', { name: 'User' }).exists()).toBe(true);
});
});
});
});

View File

@ -13,6 +13,16 @@ export const MOCK_PATHS = {
adminUser: '/admin/users/:id',
};
export const MOCK_USERS_FORMATTED = users.map(({ badges, user }) => {
return { ...user, id: getIdFromGraphQLId(user.id), badges, email: user.publicEmail };
});
export const MOCK_USERS_FORMATTED = users.map(
({ id, badges, user, accessLevel, userPermissions }) => {
return {
...user,
gid: id,
id: getIdFromGraphQLId(user.id),
badges,
accessLevel,
userPermissions,
email: user.publicEmail,
};
},
);

View File

@ -4,6 +4,7 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
import UsersTable from '~/vue_shared/components/users_table/users_table.vue';
import UserAvatar from '~/vue_shared/components/users_table/user_avatar.vue';
import UserDate from '~/vue_shared/components/user_date.vue';
import { FIELD_NAME, FIELD_ORGANIZATION_ROLE } from '~/vue_shared/components/users_table/constants';
import { MOCK_USERS, MOCK_ADMIN_USER_PATH, MOCK_GROUP_COUNTS } from './mock_data';
describe('UsersTable component', () => {
@ -21,7 +22,7 @@ describe('UsersTable component', () => {
.find(`[data-label="${label}"][role="cell"]`);
};
const initComponent = (props = {}) => {
const initComponent = (props = {}, scopedSlots = {}) => {
wrapper = mountExtended(UsersTable, {
propsData: {
users: MOCK_USERS,
@ -30,6 +31,7 @@ describe('UsersTable component', () => {
groupCountsLoading: false,
...props,
},
scopedSlots,
});
};
@ -93,4 +95,34 @@ describe('UsersTable component', () => {
});
});
});
describe('when fieldsToRender prop is passed', () => {
beforeEach(() => {
initComponent({ fieldsToRender: [FIELD_NAME] });
});
it('only renders specified fields', () => {
expect(getCellByLabel(0, 'Name').exists()).toBe(true);
expect(getCellByLabel(0, 'Created on').exists()).toBe(false);
});
});
describe('when columnWidths prop is passed', () => {
beforeEach(() => {
initComponent({ columnWidths: { [FIELD_NAME]: 'gl-w-5/20' } });
});
it('sets th CSS class', () => {
expect(wrapper.findByRole('columnheader', { name: 'Name' }).classes()).toContain('gl-w-5/20');
});
});
it('renders organization role slot', () => {
initComponent(
{ fieldsToRender: [FIELD_ORGANIZATION_ROLE] },
{ 'organization-role': '<div data-testid="organization-role-slot"></div>' },
);
expect(wrapper.findByTestId('organization-role-slot').exists()).toBe(true);
});
});

View File

@ -8,6 +8,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import CreateWorkItem from '~/work_items/components/create_work_item.vue';
import CreateWorkItemModal from '~/work_items/components/create_work_item_modal.vue';
import { WORK_ITEMS_TYPE_MAP, WORK_ITEM_TYPE_ROUTE_WORK_ITEM } from '~/work_items/constants';
import namespaceWorkItemTypesQuery from '~/work_items/graphql/namespace_work_item_types.query.graphql';
const showToast = jest.fn();
@ -25,6 +26,7 @@ describe('CreateWorkItemModal', () => {
const findDropdownItem = () => wrapper.findComponent(GlDisclosureDropdownItem);
const findModal = () => wrapper.findComponent(GlModal);
const findForm = () => wrapper.findComponent(CreateWorkItem);
const findOpenInFullPageButton = () => wrapper.find('[data-testid="new-work-item-modal-link"]');
const namespaceSingleWorkItemTypeQueryResponse = {
data: {
@ -77,6 +79,9 @@ describe('CreateWorkItemModal', () => {
show: showToast,
},
},
stubs: {
GlModal,
},
});
};
@ -100,16 +105,32 @@ describe('CreateWorkItemModal', () => {
});
describe('default trigger', () => {
it('opens modal on trigger click', async () => {
it('opens modal and prevents following link on click', async () => {
createComponent();
await waitForPromises();
findTrigger().vm.$emit('click');
const mockEvent = { preventDefault: jest.fn() };
findTrigger().vm.$emit('click', mockEvent);
await nextTick();
expect(findModal().props('visible')).toBe(true);
expect(mockEvent.preventDefault).toHaveBeenCalled();
});
it('does not open modal or prevent link default on ctrl+click', async () => {
createComponent();
await waitForPromises();
const mockEvent = { preventDefault: jest.fn(), ctrlKey: true };
findTrigger().vm.$emit('click', mockEvent);
await nextTick();
expect(findModal().props('visible')).toBe(false);
expect(mockEvent.preventDefault).not.toHaveBeenCalled();
});
it('does not render when hideButton=true', () => {
@ -149,6 +170,20 @@ describe('CreateWorkItemModal', () => {
expect(findModal().props('visible')).toBe(false);
});
for (const [workItemTypeName, vals] of Object.entries(WORK_ITEMS_TYPE_MAP)) {
it(`has link to new work item page in modal header for ${workItemTypeName}`, async () => {
createComponent({ workItemTypeName });
const routeParamName = vals.routeParamName || WORK_ITEM_TYPE_ROUTE_WORK_ITEM;
await waitForPromises();
expect(findOpenInFullPageButton().attributes().href).toBe(
`/full-path/-/${routeParamName}/new`,
);
});
}
it('when there are no work item types it does not set the cache', async () => {
createComponent({ namespaceWorkItemTypesQueryHandler: workItemTypesEmptyQueryHandler });

View File

@ -1,7 +1,12 @@
import { NEW_WORK_ITEM_IID } from '~/work_items/constants';
import {
NEW_WORK_ITEM_IID,
WORK_ITEM_TYPE_ENUM_ISSUE,
WORK_ITEM_TYPE_ENUM_EPIC,
} from '~/work_items/constants';
import {
autocompleteDataSources,
markdownPreviewPath,
newWorkItemPath,
isReference,
getWorkItemIcon,
workItemRoadmapPath,
@ -112,6 +117,34 @@ describe('markdownPreviewPath', () => {
});
});
describe('newWorkItemPath', () => {
beforeEach(() => {
gon.relative_url_root = '/foobar';
});
it('returns correct path', () => {
expect(newWorkItemPath({ fullPath: 'group/project' })).toBe(
'/foobar/group/project/-/work_items/new',
);
});
it('returns correct path for workItemType', () => {
expect(
newWorkItemPath({ fullPath: 'group/project', workItemTypeName: WORK_ITEM_TYPE_ENUM_ISSUE }),
).toBe('/foobar/group/project/-/issues/new');
});
it('returns correct data sources with group context', () => {
expect(
newWorkItemPath({
fullPath: 'group',
isGroup: true,
workItemTypeName: WORK_ITEM_TYPE_ENUM_EPIC,
}),
).toBe('/foobar/groups/group/-/epics/new');
});
});
describe('getWorkItemIcon', () => {
it.each(['epic', 'issue-type-epic'])('returns epic icon in case of %s', (icon) => {
expect(getWorkItemIcon(icon)).toBe('epic');

View File

@ -7,6 +7,7 @@ RSpec.describe Types::PermissionTypes::OrganizationUser, feature_category: :cell
expected_permissions = %i[
remove_user
delete_user
admin_organization
]
expect(described_class).to have_graphql_fields(expected_permissions)

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe '1_settings', feature_category: :shared do
include_context 'when loading 1_settings initializer'
it 'settings do not change after reload' do
original_settings = Settings.to_h
load_settings
new_settings = Settings.to_h
# Gitlab::Pages::Settings is a SimpleDelegator, so each time the settings
# are reloaded a new SimpleDelegator wraps the original object. Convert
# the settings to a Hash to ensure the comparison works.
[new_settings, original_settings].each do |settings|
settings['pages'] = settings['pages'].to_h
end
expect(new_settings).to eq(original_settings)
end
describe 'DNS rebinding protection' do
subject(:dns_rebinding_protection_enabled) { Settings.gitlab.dns_rebinding_protection_enabled }
let(:http_proxy) { nil }
before do
# Reset it, because otherwise we might memoize the value across tests.
Settings.gitlab['dns_rebinding_protection_enabled'] = nil
stub_env('http_proxy', http_proxy)
load_settings
end
it { is_expected.to be(true) }
context 'when an HTTP proxy environment variable is set' do
let(:http_proxy) { 'http://myproxy.com:8080' }
it { is_expected.to be(false) }
end
end
end

View File

@ -4,95 +4,107 @@ require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillShardingKeyIdOnCiRunners, schema: 20240923132401,
migration: :gitlab_ci, feature_category: :runner do
let(:runners) { table(:ci_runners) }
let(:runner_projects) { table(:ci_runner_projects) }
let(:runner_namespaces) { table(:ci_runner_namespaces) }
include Database::TableSchemaHelpers
let(:connection) { Ci::ApplicationRecord.connection }
let(:args) do
min, max = runners.pick('MIN(id)', 'MAX(id)')
{
start_id: min,
end_id: max,
batch_table: 'ci_runners',
batch_column: 'id',
sub_batch_size: 100,
pause_ms: 0,
connection: connection
}
end
let(:group_id) { 100 }
let(:other_group_id) { 101 }
let(:project_id) { 1000 }
let(:other_project_id) { 1001 }
let!(:orphaned_project_runner) { create_project_runner(project_ids: []) }
subject(:perform_migration) { described_class.new(**args).perform }
before do
runners.create!(runner_type: 1)
create_project_runner(project_ids: project_id)
create_project_runner(project_ids: other_project_id)
create_group_runner(group_id: group_id)
create_group_runner(group_id: other_group_id)
end
it 'backfills sharding_key_id', :aggregate_failures do
expect { perform_migration }.not_to change { orphaned_project_runner.sharding_key_id }
runners.where(runner_type: 1).find_each do |runner|
expect(runner.sharding_key_id).to be_nil
end
runner_namespaces.find_each do |runner_namespace|
expect(runners.find(runner_namespace.runner_id).sharding_key_id).to eq(runner_namespace.namespace_id)
end
runner_projects.find_each do |runner_project|
expect(runners.find(runner_project.runner_id).sharding_key_id).to eq(runner_project.project_id)
if table_oid('ci_runners_e59bb2812d').present?
connection.execute("ALTER TABLE ci_runners_e59bb2812d DROP CONSTRAINT IF EXISTS check_sharding_key_id_nullness")
end
end
context 'when a project runner is shared with other projects' do
let(:runner_project_ids) { [other_project_id, project_id] }
describe '#perform' do
let(:runners) { table(:ci_runners) }
let(:runner_projects) { table(:ci_runner_projects) }
let(:runner_namespaces) { table(:ci_runner_namespaces) }
it 'backfills sharding_key_id with the id of the owner project' do
shared_project_runner = create_project_runner(project_ids: runner_project_ids)
let(:args) do
min, max = runners.pick('MIN(id)', 'MAX(id)')
expect do
perform_migration
end.to change { shared_project_runner.reload.sharding_key_id }.from(nil).to(other_project_id)
{
start_id: min,
end_id: max,
batch_table: 'ci_runners',
batch_column: 'id',
sub_batch_size: 100,
pause_ms: 0,
connection: connection
}
end
context 'when the project has a different owner project' do
let(:runner_project_ids) { [project_id, other_project_id] }
let(:group_id) { 100 }
let(:other_group_id) { 101 }
let(:project_id) { 1000 }
let(:other_project_id) { 1001 }
let(:orphaned_project_runner) { create_project_runner(project_ids: []) }
subject(:perform_migration) { described_class.new(**args).perform }
before do
runners.create!(runner_type: 1)
orphaned_project_runner
create_project_runner(project_ids: project_id)
create_project_runner(project_ids: other_project_id)
create_group_runner(group_id: group_id)
create_group_runner(group_id: other_group_id)
end
it 'backfills sharding_key_id', :aggregate_failures do
expect { perform_migration }.not_to change { orphaned_project_runner.sharding_key_id }
runners.where(runner_type: 1).find_each do |runner|
expect(runner.sharding_key_id).to be_nil
end
runner_namespaces.find_each do |runner_namespace|
expect(runners.find(runner_namespace.runner_id).sharding_key_id).to eq(runner_namespace.namespace_id)
end
runner_projects.find_each do |runner_project|
expect(runners.find(runner_project.runner_id).sharding_key_id).to eq(runner_project.project_id)
end
end
context 'when a project runner is shared with other projects' do
let(:runner_project_ids) { [other_project_id, project_id] }
it 'backfills sharding_key_id with the id of the owner project' do
shared_project_runner = create_project_runner(project_ids: runner_project_ids)
expect do
perform_migration
end.to change { shared_project_runner.reload.sharding_key_id }.from(nil).to(project_id)
end.to change { shared_project_runner.reload.sharding_key_id }.from(nil).to(other_project_id)
end
context 'when the project has a different owner project' do
let(:runner_project_ids) { [project_id, other_project_id] }
it 'backfills sharding_key_id with the id of the owner project' do
shared_project_runner = create_project_runner(project_ids: runner_project_ids)
expect do
perform_migration
end.to change { shared_project_runner.reload.sharding_key_id }.from(nil).to(project_id)
end
end
end
end
def create_group_runner(group_id:)
runners.create!(runner_type: 2, sharding_key_id: nil).tap do |runner|
runner_namespaces.create!(runner_id: runner.id, namespace_id: group_id)
def create_group_runner(group_id:)
runners.create!(runner_type: 2, sharding_key_id: nil).tap do |runner|
runner_namespaces.create!(runner_id: runner.id, namespace_id: group_id)
end
end
end
def create_project_runner(project_ids:)
project_ids = Array.wrap(project_ids)
def create_project_runner(project_ids:)
project_ids = Array.wrap(project_ids)
runners.create!(runner_type: 3, sharding_key_id: nil).tap do |runner|
project_ids.each do |project_id|
runner_projects.create!(runner_id: runner.id, project_id: project_id)
runners.create!(runner_type: 3, sharding_key_id: nil).tap do |runner|
project_ids.each do |project_id|
runner_projects.create!(runner_id: runner.id, project_id: project_id)
end
end
end
end

View File

@ -136,7 +136,7 @@ RSpec.describe 'new tables missing sharding_key', feature_category: :cell do
"since it now has a valid constraint."
else
expect(not_nullable || has_null_check_constraint).to eq(true),
"Missing a not null constraint for `#{table_name}.#{column_name}` . " \
"Missing a not null constraint for `#{table_name}.#{column_name}`. " \
"All sharding keys must be not nullable or have a NOT NULL check constraint"
end
else

View File

@ -18,48 +18,42 @@ RSpec.describe ::Gitlab::LetsEncrypt::Order, feature_category: :pages do
end
describe '#new_challenge' do
it 'returns challenge' do
expect(order.new_challenge).to be_a(::Gitlab::LetsEncrypt::Challenge)
end
it { expect(order.new_challenge).to be_a ::Gitlab::LetsEncrypt::Challenge }
end
describe '#request_certificate' do
let(:private_key) do
OpenSSL::PKey::RSA.new(4096).to_pem
let(:private_key) { OpenSSL::PKey::RSA.new(4096).to_pem }
before do
allow(acme_order).to receive(:finalize)
end
it 'generates csr and finalizes order' do
expect(acme_order).to receive(:finalize) do |csr:|
expect do
csr.csr # it's being evaluated lazily
end.not_to raise_error
end
order.request_certificate(domain: 'example.com', private_key: private_key)
expect(acme_order).to have_received(:finalize) do |csr:|
# it's being evaluated lazily
expect { csr.csr }.not_to raise_error
end
end
end
describe '#challenge_error' do
it 'returns error if challenge has errors' do
challenge = acme_challenge_double
# error just to give an example
error = {
let(:acme_order) { acme_order_double(authorizations: [acme_authorization_double(challenge)]) }
let(:challenge) { acme_challenge_double(error: expected_challenge_error) }
let(:expected_challenge_error) do
{
"type" => "urn:ietf:params:acme:error:dns",
"detail" => "No valid IP addresses found for test.example.com",
"status" => 400
}
allow(challenge).to receive(:error).and_return(error)
acme_order = acme_order_double(authorizations: [acme_authorization_double(challenge)])
expect(described_class.new(acme_order).challenge_error).to eq(error)
end
context 'when requesting authorizations raises error' do
subject { order.challenge_error }
subject(:challenge_order) { order.challenge_error }
it { is_expected.to eq expected_challenge_error }
context 'when requesting authorizations raises error' do
let(:acme_order) { acme_order_double }
before do

View File

@ -0,0 +1,47 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe RequeueBackfillApprovalProjectRulesProtectedBranchesProjectId, feature_category: :source_code_management do
let!(:batched_migration) { described_class::MIGRATION }
let(:expected_job_args) { %i[project_id approval_project_rules project_id approval_project_rule_id] }
it 'does not schedule a new batched migratio' do
reversible_migration do |migration|
migration.before -> {
expect(batched_migration).not_to have_scheduled_batched_migration
}
migration.after -> {
expect(batched_migration).not_to have_scheduled_batched_migration
}
end
end
context 'when executed on .com' do
before do
allow(Gitlab).to receive(:com_except_jh?).and_return(true)
end
it 'schedules a new batched migration' do
reversible_migration do |migration|
migration.before -> {
expect(batched_migration).not_to have_scheduled_batched_migration
}
migration.after -> {
expect(batched_migration).to have_scheduled_batched_migration(
table_name: described_class::TABLE_NAME,
column_name: described_class::BATCH_COLUMN,
interval: described_class::DELAY_INTERVAL,
batch_size: described_class::BATCH_SIZE,
sub_batch_size: described_class::SUB_BATCH_SIZE,
gitlab_schema: :gitlab_main_cell,
job_arguments: expected_job_args
)
}
end
end
end
end

View File

@ -150,6 +150,16 @@ RSpec.describe Namespaces::Traversal::Cached, feature_category: :database do
expect(ids.sort).to eq([group.id, subgroup.id, subsubgroup.id])
end
end
context 'when the scope is specified' do
it 'returns uncached values that match the scope' do
ids = group.self_and_descendant_ids(skope: Namespace).pluck(:id)
expect(ids).to contain_exactly(
group.id, subgroup.id, subsubgroup.id, project1.project_namespace.id, project2.project_namespace.id
)
end
end
end
describe '#all_project_ids' do

View File

@ -16,7 +16,7 @@ RSpec.describe PagesDomains::CreateAcmeOrderService, feature_category: :pages do
end
let(:lets_encrypt_client) do
instance_double('Gitlab::LetsEncrypt::Client').tap do |client|
instance_double(Gitlab::LetsEncrypt::Client).tap do |client|
allow(client).to receive(:new_order).with(pages_domain.domain)
.and_return(order_double)
end

View File

@ -22,7 +22,7 @@ RSpec.describe ::PagesDomains::CreateService, feature_category: :pages do
end
context 'when the user has the required permissions' do
before do
before_all do
project.add_maintainer(user)
end

View File

@ -95,7 +95,7 @@ RSpec.describe PagesDomains::ObtainLetsEncryptCertificateService, feature_catego
end
%w[pending processing].each do |status|
context "there is an order in '#{status}' status" do
context "when there is an order in '#{status}' status" do
let(:existing_order) do
create(:pages_domain_acme_order, pages_domain: pages_domain)
end
@ -180,7 +180,7 @@ RSpec.describe PagesDomains::ObtainLetsEncryptCertificateService, feature_catego
end
before do
expect(api_order).to receive(:certificate) { certificate }
allow(api_order).to receive(:certificate).and_return(certificate)
end
it 'saves private_key and certificate for domain' do

View File

@ -30,7 +30,7 @@ RSpec.describe PagesDomains::UpdateService, feature_category: :pages do
context 'when it updates the domain successfully' do
it 'updates the domain' do
expect(service.execute(pages_domain)).to eq(true)
expect(service.execute(pages_domain)).to be true
end
it 'publishes a PagesDomainUpdatedEvent' do

View File

@ -38,9 +38,9 @@ module LetsEncryptHelpers
client
end
def acme_challenge_double
def acme_challenge_double(attributes = {})
challenge = instance_double('Acme::Client::Resources::Challenges::HTTP01')
allow(challenge).to receive_messages(ACME_CHALLENGE_METHODS)
allow(challenge).to receive_messages(ACME_CHALLENGE_METHODS.merge(attributes))
challenge
end

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
RSpec.shared_context 'when loading 1_settings initializer' do
def load_settings
# Avoid wrapping Gitlab::Pages::Settings again
Settings.pages = Settings.pages.__getobj__
load Rails.root.join('config/initializers/1_settings.rb')
end
end

Some files were not shown because too many files have changed in this diff Show More