Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-06-27 21:23:55 +00:00
parent cbb6a29694
commit 59d0f7a222
180 changed files with 11714 additions and 7455 deletions

View File

@ -748,6 +748,7 @@ lib/gitlab/checks/**
/doc/api/project_aliases.md @msedlakjakubowski
/doc/api/project_badges.md @msedlakjakubowski
/doc/api/project_clusters.md @phillipwells
/doc/api/project_container_registry_protection_rules.md @marcel.amirault
/doc/api/project_import_export.md @eread
/doc/api/project_job_token_scopes.md @marcel.amirault
/doc/api/project_level_variables.md @marcel.amirault
@ -830,8 +831,12 @@ lib/gitlab/checks/**
/doc/ci/testing/code_quality.md @rdickenson
/doc/ci/testing/code_quality_troubleshooting.md @rdickenson @phillipwells
/doc/ci/variables/ @marcel.amirault
/doc/ci/yaml/ci_job_log_timestamps.md @ashrafkhamis
/doc/ci/yaml/signing_examples.md @marcel.amirault
/doc/development/advanced_search.md @gitlab-org/search-team/migration-maintainers
/doc/development/ai_architecture.md @gitlab-org/ai-powered
/doc/development/ai_features/ @gitlab-org/ai-powered
/doc/development/ai_features/embeddings.md @gitlab-org/search-team/migration-maintainers
/doc/development/application_limits.md @gitlab-org/distribution
/doc/development/audit_event_guide/ @gitlab-org/govern/security-policies-frontend @gitlab-org/govern/threat-insights-frontend-team @gitlab-org/govern/threat-insights-backend-team
/doc/development/avoiding_required_stops.md @gitlab-org/distribution
@ -1013,9 +1018,7 @@ lib/gitlab/checks/**
/doc/user/project/remote_development/ @ashrafkhamis
/doc/user/project/repository/code_explain.md @sselhorn @jglassman1 @fneill
/doc/user/project/repository/code_suggestions/ @jglassman1
/doc/user/project/repository/files/geojson.md @msedlakjakubowski
/doc/user/project/repository/files/index.md @ashrafkhamis
/doc/user/project/repository/files/jupyter_notebooks/ @msedlakjakubowski
/doc/user/project/repository/monorepos/ @eread
/doc/user/project/repository/web_editor.md @ashrafkhamis
/doc/user/project/settings/import_export.md @eread

View File

@ -2,7 +2,7 @@
include:
- local: .gitlab/ci/cng/main.gitlab-ci.yml
- project: 'gitlab-org/quality/pipeline-common'
ref: '8.18.0'
ref: '8.18.1'
file: ci/base.gitlab-ci.yml
stages:

View File

@ -2706,7 +2706,6 @@ RSpec/FeatureCategory:
- 'spec/lib/gitlab/email/message/repository_push_spec.rb'
- 'spec/lib/gitlab/email/smime/signer_spec.rb'
- 'spec/lib/gitlab/emoji_spec.rb'
- 'spec/lib/gitlab/empty_search_results_spec.rb'
- 'spec/lib/gitlab/encrypted_configuration_spec.rb'
- 'spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb'
- 'spec/lib/gitlab/error_tracking/error_repository/open_api_strategy_spec.rb'

View File

@ -2042,7 +2042,6 @@ RSpec/NamedSubject:
- 'spec/lib/gitlab/doctor/secrets_spec.rb'
- 'spec/lib/gitlab/email/handler/service_desk_handler_spec.rb'
- 'spec/lib/gitlab/email/html_to_markdown_parser_spec.rb'
- 'spec/lib/gitlab/empty_search_results_spec.rb'
- 'spec/lib/gitlab/encoding_helper_spec.rb'
- 'spec/lib/gitlab/error_tracking/error_repository/open_api_strategy_spec.rb'
- 'spec/lib/gitlab/etag_caching/store_spec.rb'

View File

@ -33,7 +33,6 @@ RSpec/RepeatedExampleGroupBody:
- 'spec/lib/gitlab/ci/pipeline/seed/build_spec.rb'
- 'spec/lib/gitlab/ci/yaml_processor_spec.rb'
- 'spec/lib/gitlab/database/migrations/batched_background_migration_helpers_spec.rb'
- 'spec/lib/gitlab/empty_search_results_spec.rb'
- 'spec/lib/gitlab/import_export/project/sample/relation_factory_spec.rb'
- 'spec/lib/gitlab/lfs/client_spec.rb'
- 'spec/lib/gitlab/sanitizers/exif_spec.rb'

View File

@ -1 +1 @@
117d83886351afe9c05c3a3464cba7e22bc234a3
01ac062b95b09b5d2f6390a7b73799d26ef42de2

View File

@ -30,12 +30,12 @@ export const TARGET_NAMESPACE_FIELD = 'targetNamespace';
export const ROOT_NAMESPACE = { fullPath: '', id: null };
const PLACEHOLDER_STATUS_PENDING_ASSIGNMENT = 'pending_assignment';
const PLACEHOLDER_STATUS_AWAITING_APPROVAL = 'awaiting_approval';
export const PLACEHOLDER_STATUS_AWAITING_APPROVAL = 'awaiting_approval';
const PLACEHOLDER_STATUS_REJECTED = 'rejected';
const PLACEHOLDER_STATUS_REASSIGNING = 'reassigning'; // backend state still pending
export const PLACEHOLDER_STATUS_REASSIGNING = 'reassignment_in_progress';
const PLACEHOLDER_STATUS_FAILED = 'failed';
const PLACEHOLDER_STATUS_SAVED = 'saved'; // backend state still pending
const PLACEHOLDER_STATUS_COMPLETED = 'completed';
export const PLACEHOLDER_STATUS_KEPT_AS_PLACEHOLDER = 'keep_as_placeholder';
export const PLACEHOLDER_STATUS_COMPLETED = 'completed';
export const placeholderUserBadges = {
[PLACEHOLDER_STATUS_PENDING_ASSIGNMENT]: {
@ -63,7 +63,7 @@ export const placeholderUserBadges = {
variant: 'danger',
tooltip: s__('UserMapping|Reassignment failed.'),
},
[PLACEHOLDER_STATUS_SAVED]: {
[PLACEHOLDER_STATUS_KEPT_AS_PLACEHOLDER]: {
text: s__('UserMapping|Kept as placeholder'),
variant: 'success',
tooltip: s__('UserMapping|Placeholder user was made permanent.'),

View File

@ -7,6 +7,10 @@ import { createAlert } from '~/alert';
import searchUsersQuery from '~/graphql_shared/queries/users_search_all_paginated.query.graphql';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import {
PLACEHOLDER_STATUS_AWAITING_APPROVAL,
PLACEHOLDER_STATUS_REASSIGNING,
} from '~/import_entities/import_groups/constants';
const USERS_PER_PAGE = 20;
@ -36,6 +40,7 @@ export default {
return {
isLoadingInitial: true,
isLoadingMore: false,
isValidated: false,
search: '',
selectedUser: null,
};
@ -74,6 +79,10 @@ export default {
return this.$apollo.queries.users.loading && !this.isLoadingMore;
},
userSelectInvalid() {
return this.isValidated && !this.selectedUser;
},
userItems() {
return this.users?.nodes?.map((user) => createUserObject(user));
},
@ -101,9 +110,20 @@ export default {
confirmText() {
return this.dontReassignSelected ? __('Confirm') : s__('UserMapping|Reassign');
},
statusIsAwaitingApproval() {
return this.placeholder.status === PLACEHOLDER_STATUS_AWAITING_APPROVAL;
},
statusIsReassigning() {
return this.placeholder.status === PLACEHOLDER_STATUS_REASSIGNING;
},
},
created() {
if (this.statusIsAwaitingApproval || this.statusIsReassigning) {
this.selectedUser = this.placeholder.reassignToUser;
}
this.debouncedSetSearch = debounce(this.setSearch, DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
},
@ -154,56 +174,87 @@ export default {
this.selectedUser = this.userItems.find((user) => user.value === value);
}
},
onNotify() {
this.$toast.show(s__('UserMapping|Notification email sent.'));
},
onCancel() {
this.$emit('cancel');
},
onConfirm() {
this.isValidated = true;
if (!this.userSelectInvalid) {
this.$emit('confirm', this.selectedUserValue);
}
},
},
};
</script>
<template>
<div class="gl-flex gl-gap-3">
<gl-collapsible-listbox
ref="userSelect"
block
is-check-centered
toggle-class="gl-w-28"
:header-text="s__('UserMapping|Re-assign to')"
:toggle-text="toggleText"
:loading="isLoadingInitial"
:items="userItems"
:selected="selectedUserValue"
searchable
:searching="isLoading"
infinite-scroll
:infinite-scroll-loading="isLoadingMore"
@search="debouncedSetSearch"
@select="onSelect"
@bottom-reached="loadMoreUsers"
>
<template #list-item="{ item }">
<gl-avatar-labeled
shape="circle"
:size="32"
:src="item.avatarUrl"
:label="item.text"
:sub-label="item.username"
/>
</template>
<div class="gl-flex gl-items-start gl-gap-3">
<div>
<gl-collapsible-listbox
ref="userSelect"
block
is-check-centered
toggle-class="gl-w-28"
:class="{ 'is-invalid': userSelectInvalid }"
:header-text="s__('UserMapping|Re-assign to')"
:toggle-text="toggleText"
:disabled="statusIsAwaitingApproval || statusIsReassigning"
:loading="isLoadingInitial"
:items="userItems"
:selected="selectedUserValue"
searchable
:searching="isLoading"
infinite-scroll
:infinite-scroll-loading="isLoadingMore"
@search="debouncedSetSearch"
@select="onSelect"
@bottom-reached="loadMoreUsers"
>
<template #list-item="{ item }">
<gl-avatar-labeled
shape="circle"
:size="32"
:src="item.avatarUrl"
:label="item.text"
:sub-label="item.username"
/>
</template>
<template #footer>
<div
class="gl-border-t-solid gl-border-t-1 gl-border-t-gray-200 gl-flex gl-flex-col !gl-p-2 !gl-pt-0"
>
<gl-button
category="tertiary"
class="!gl-justify-start gl-mt-2"
data-testid="dont-reassign-button"
@click="onSelect('')"
<template #footer>
<div
class="gl-border-t-solid gl-border-t-1 gl-border-t-gray-200 gl-flex gl-flex-col !gl-p-2 !gl-pt-0"
>
{{ s__("UserMapping|Don't reassign") }}
</gl-button>
</div>
</template>
</gl-collapsible-listbox>
<gl-button
category="tertiary"
class="!gl-justify-start gl-mt-2"
data-testid="dont-reassign-button"
@click="onSelect('')"
>
{{ s__("UserMapping|Don't reassign") }}
</gl-button>
</div>
</template>
</gl-collapsible-listbox>
<gl-button variant="confirm" data-testid="confirm-button">{{ confirmText }}</gl-button>
<span v-if="userSelectInvalid" class="invalid-feedback">
{{ __('This field is required.') }}
</span>
</div>
<template v-if="statusIsAwaitingApproval || statusIsReassigning">
<gl-button :disabled="statusIsReassigning" data-testid="notify-button" @click="onNotify">{{
s__('UserMapping|Notify again')
}}</gl-button>
<gl-button :disabled="statusIsReassigning" data-testid="cancel-button" @click="onCancel">{{
__('Cancel')
}}</gl-button>
</template>
<gl-button v-else variant="confirm" data-testid="confirm-button" @click="onConfirm">{{
confirmText
}}</gl-button>
</div>
</template>

View File

@ -2,7 +2,11 @@
import { GlAvatarLabeled, GlBadge, GlTableLite, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import { placeholderUserBadges } from '~/import_entities/import_groups/constants';
import {
PLACEHOLDER_STATUS_KEPT_AS_PLACEHOLDER,
PLACEHOLDER_STATUS_COMPLETED,
placeholderUserBadges,
} from '~/import_entities/import_groups/constants';
import PlaceholderActions from './placeholder_actions.vue';
export default {
@ -59,6 +63,21 @@ export default {
statusBadge(item) {
return placeholderUserBadges[item.status];
},
isReassignedItem(item) {
return (
(item.status === PLACEHOLDER_STATUS_KEPT_AS_PLACEHOLDER ||
item.status === PLACEHOLDER_STATUS_COMPLETED) &&
item.reassignToUser
);
},
onCancel(item) {
this.$emit('cancel', item);
},
onConfirm(item, selectedUserId) {
this.$emit('confirm', item, selectedUserId);
},
},
};
</script>
@ -88,8 +107,20 @@ export default {
>
</template>
<template #cell(actions)>
<placeholder-actions />
<template #cell(actions)="{ item }">
<gl-avatar-labeled
v-if="isReassignedItem(item)"
:size="32"
:src="item.reassignToUser.avatar_url"
:label="item.reassignToUser.name"
:sub-label="item.reassignToUser.username"
/>
<placeholder-actions
v-else
:placeholder="item"
@confirm="onConfirm(item, $event)"
@cancel="onCancel(item)"
/>
</template>
</gl-table-lite>
</template>

View File

@ -1,5 +1,5 @@
<script>
import { GlDrawer, GlBadge, GlSprintf, GlButton, GlIcon, GlAlert } from '@gitlab/ui';
import { GlDrawer, GlSprintf, GlButton, GlIcon, GlAlert } from '@gitlab/ui';
import { DRAWER_Z_INDEX } from '~/lib/utils/constants';
import { getContentWrapperHeight } from '~/lib/utils/dom_utils';
import { helpPagePath } from '~/helpers/help_page_helper';
@ -14,7 +14,7 @@ import {
MEMBERS_TAB_TYPES,
} from '~/members/constants';
import * as Sentry from '~/ci/runner/sentry_utils';
import MemberAvatar from './member_avatar.vue';
import MemberAvatar from '../member_avatar.vue';
import RoleSelector from './role_selector.vue';
// The API to update members uses different property names for the access level, depending on if it's a user or a group.
@ -29,12 +29,12 @@ export default {
MemberAvatar,
MembersTableCell,
GlDrawer,
GlBadge,
GlSprintf,
GlButton,
GlIcon,
GlAlert,
RoleSelector,
RoleBadges: () => import('ee_component/members/components/table/role_badges.vue'),
GuestOverageConfirmation: () =>
import('ee_component/members/components/table/guest_overage_confirmation.vue'),
},
@ -193,19 +193,16 @@ export default {
<dl>
<dt class="gl-mb-3" data-testid="role-header">{{ s__('MemberRole|Role') }}</dt>
<dd>
<dd class="gl-flex gl-flex-wrap gl-gap-x-2 gl-gap-y-3">
<role-selector
v-if="permissions.canUpdate"
v-model="selectedRole"
:roles="roles"
:loading="isSavingRole"
class="gl-mb-3"
class="gl-w-full"
/>
<span v-else class="gl-mr-1" data-testid="role-text">{{ selectedRole.text }}</span>
<gl-badge v-if="selectedRole.memberRoleId">
{{ s__('MemberRole|Custom role') }}
</gl-badge>
<span v-else data-testid="role-text">{{ selectedRole.text }}</span>
<role-badges :member="member" :role="selectedRole" />
</dd>
<template v-if="selectedRole.description">

View File

@ -33,7 +33,7 @@ import MemberSource from './member_source.vue';
import MemberActivity from './member_activity.vue';
import MaxRole from './max_role.vue';
import MembersPagination from './members_pagination.vue';
import RoleDetailsDrawer from './role_details_drawer.vue';
import RoleDetailsDrawer from './drawer/role_details_drawer.vue';
export default {
components: {
@ -58,6 +58,7 @@ export default {
import('ee_component/members/components/modals/ldap_override_confirmation_modal.vue'),
UserLimitReachedAlert: () =>
import('ee_component/members/components/table/user_limit_reached_alert.vue'),
RoleBadges: () => import('ee_component/members/components/table/role_badges.vue'),
},
directives: { GlTooltip: GlTooltipDirective },
mixins: [glFeatureFlagsMixin()],
@ -299,9 +300,7 @@ export default {
>
{{ member.accessLevel.stringValue }}
</gl-button>
<gl-badge v-if="member.accessLevel.memberRoleId" class="gl-mt-3">
{{ s__('MemberRole|Custom role') }}
</gl-badge>
<role-badges :member="member" :role="member.accessLevel" class="gl-mt-3" />
</div>
<max-role v-else :permissions="permissions" :member="member" />
</members-table-cell>

View File

@ -21,11 +21,14 @@ import {
RECENT_SEARCHES_STORAGE_KEY_GROUPS,
RECENT_SEARCHES_STORAGE_KEY_PROJECTS,
} from '~/filtered_search/recent_searches_storage_keys';
import * as Sentry from '~/sentry/sentry_browser_wrapper';
import userPreferencesUpdate from '../graphql/mutations/user_preferences_update.mutation.graphql';
import {
DISPLAY_LISTBOX_ITEMS,
SORT_ITEMS,
FILTERED_SEARCH_TERM_KEY,
FILTERED_SEARCH_NAMESPACE,
SORT_ITEMS_GRAPHQL_ENUMS,
} from '../constants';
export default {
@ -47,11 +50,12 @@ export default {
},
displayListboxItems: DISPLAY_LISTBOX_ITEMS,
sortItems: SORT_ITEMS,
inject: ['userPreferenceSortName', 'userPreferenceSortDirection', 'userPreferenceDisplay'],
computed: {
displayQuery() {
const { display } = this.$route.query;
return display;
return display || this.userPreferenceDisplay;
},
routerView() {
switch (this.displayQuery) {
@ -84,10 +88,10 @@ export default {
);
},
sortName() {
return this.$route.query.sort_name || SORT_ITEM_NAME.value;
return this.$route.query.sort_name || this.userPreferenceSortName;
},
sortDirection() {
return this.$route.query.sort_direction || SORT_DIRECTION_ASC;
return this.$route.query.sort_direction || this.userPreferenceSortDirection;
},
isAscending() {
return this.sortDirection !== SORT_DIRECTION_DESC;
@ -102,10 +106,8 @@ export default {
return this.$route.query[QUERY_PARAM_END_CURSOR] || null;
},
displayListboxSelected() {
const { display } = this.$route.query;
return [RESOURCE_TYPE_GROUPS, RESOURCE_TYPE_PROJECTS].includes(display)
? display
return [RESOURCE_TYPE_GROUPS, RESOURCE_TYPE_PROJECTS].includes(this.displayQuery)
? this.displayQuery
: RESOURCE_TYPE_GROUPS;
},
search() {
@ -133,19 +135,24 @@ export default {
},
onDisplayListboxSelect(display) {
this.pushQuery({ display });
this.userPreferencesUpdateMutate({
organizationGroupsProjectsDisplay: display.toUpperCase(),
});
},
onSortByChange(sortValue) {
if (this.$route.query.sort_name === sortValue) {
onSortByChange(sortName) {
if (this.$route.query.sort_name === sortName) {
return;
}
this.pushQuery({ ...this.routeQueryWithoutPagination, sort_name: sortValue });
this.pushQuery({ ...this.routeQueryWithoutPagination, sort_name: sortName });
this.userPreferencesUpdateSort();
},
onSortDirectionChange(isAscending) {
this.pushQuery({
...this.routeQueryWithoutPagination,
sort_direction: isAscending ? SORT_DIRECTION_ASC : SORT_DIRECTION_DESC,
});
this.userPreferencesUpdateSort();
},
onFilter(filters) {
const { display, sort_name, sort_direction } = this.$route.query;
@ -167,6 +174,32 @@ export default {
onPageChange(pagination) {
this.pushQuery(onPageChange({ ...pagination, routeQuery: this.$route.query }));
},
async userPreferencesUpdateMutate(input) {
try {
await this.$apollo.mutate({
mutation: userPreferencesUpdate,
variables: {
input,
},
});
} catch (error) {
// Silently fail but capture exception in Sentry
Sentry.captureException(error);
}
},
userPreferencesUpdateSort() {
const sortGraphQLEnum = SORT_ITEMS_GRAPHQL_ENUMS[this.sortName];
if (!sortGraphQLEnum) {
return;
}
const direction = this.isAscending ? SORT_DIRECTION_ASC : SORT_DIRECTION_DESC;
this.userPreferencesUpdateMutate({
organizationGroupsProjectsSort: `${sortGraphQLEnum}_${direction.toUpperCase()}`,
});
},
},
};
</script>

View File

@ -1,5 +1,12 @@
import { __ } from '~/locale';
import { SORT_ITEM_NAME, SORT_ITEM_CREATED_AT, SORT_ITEM_UPDATED_AT } from '../shared/constants';
import {
SORT_ITEM_NAME,
SORT_ITEM_CREATED_AT,
SORT_ITEM_UPDATED_AT,
SORT_NAME,
SORT_CREATED_AT,
SORT_UPDATED_AT,
} from '../shared/constants';
export const DISPLAY_QUERY_GROUPS = 'groups';
export const DISPLAY_QUERY_PROJECTS = 'projects';
@ -19,3 +26,9 @@ export const DISPLAY_LISTBOX_ITEMS = [
];
export const SORT_ITEMS = [SORT_ITEM_NAME, SORT_ITEM_CREATED_AT, SORT_ITEM_UPDATED_AT];
export const SORT_ITEMS_GRAPHQL_ENUMS = {
[SORT_NAME]: 'NAME',
[SORT_CREATED_AT]: 'CREATED',
[SORT_UPDATED_AT]: 'UPDATED',
};

View File

@ -0,0 +1,8 @@
mutation userPreferencesUpdate($input: UserPreferencesUpdateInput!) {
userPreferencesUpdate(input: $input) {
userPreferences {
organizationGroupsProjectsSort
organizationGroupsProjectsDisplay
}
}
}

View File

@ -4,6 +4,7 @@ import VueRouter from 'vue-router';
import createDefaultClient from '~/lib/graphql';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { ORGANIZATION_ROOT_ROUTE_NAME } from '~/organizations/shared/constants';
import { userPreferenceSortName, userPreferenceSortDirection } from './utils';
import App from './components/app.vue';
export const createRouter = () => {
@ -35,6 +36,8 @@ export const initOrganizationsGroupsAndProjects = () => {
canCreateGroup,
canCreateProject,
hasGroups,
userPreferenceSort,
userPreferenceDisplay,
} = convertObjectPropsToCamelCase(JSON.parse(appData));
Vue.use(VueRouter);
@ -57,6 +60,9 @@ export const initOrganizationsGroupsAndProjects = () => {
canCreateGroup,
canCreateProject,
hasGroups,
userPreferenceSortName: userPreferenceSortName(userPreferenceSort),
userPreferenceSortDirection: userPreferenceSortDirection(userPreferenceSort),
userPreferenceDisplay,
},
render(createElement) {
return createElement(App);

View File

@ -0,0 +1,29 @@
import {
SORT_DIRECTION_ASC,
SORT_DIRECTION_DESC,
SORT_ITEM_NAME,
} from '~/organizations/shared/constants';
import { SORT_ITEMS_GRAPHQL_ENUMS } from './constants';
export const userPreferenceSortName = (userPreferenceSort) => {
if (!userPreferenceSort) {
return SORT_ITEM_NAME.value;
}
const userPreferenceSortNameGraphQLEnum = userPreferenceSort
.replace(`_${SORT_DIRECTION_ASC}`, '')
.replace(`_${SORT_DIRECTION_DESC}`, '')
.toUpperCase();
return (
Object.entries(SORT_ITEMS_GRAPHQL_ENUMS).find(
([, sortNameGraphQLEnum]) => sortNameGraphQLEnum === userPreferenceSortNameGraphQLEnum,
)?.[0] || SORT_ITEM_NAME.value
);
};
export const userPreferenceSortDirection = (userPreferenceSort) => {
return userPreferenceSort?.endsWith(SORT_DIRECTION_DESC)
? SORT_DIRECTION_DESC
: SORT_DIRECTION_ASC;
};

View File

@ -33,6 +33,7 @@ import {
STATE_MACHINE,
MT_SKIP_TRAIN,
MT_RESTART_TRAIN,
MWCP_MERGE_STRATEGY,
} from '../../constants';
import eventHub from '../../event_hub';
import mergeRequestQueryVariablesMixin from '../../mixins/merge_request_query_variables';
@ -243,6 +244,9 @@ export default {
this.state.availableAutoMergeStrategies,
);
},
isPreferredAutoMergeStrategyMWPC() {
return this.preferredAutoMergeStrategy === MWCP_MERGE_STRATEGY;
},
squashIsSelected() {
return this.isSquashReadOnly ? this.state.squashOnMerge : this.state.squash;
},

View File

@ -1,8 +1,17 @@
import { s__ } from '~/locale';
import { MWCP_MERGE_STRATEGY } from '~/vue_merge_request_widget/constants';
export default {
computed: {
statusText() {
const { autoMergeStrategy } = this.state;
if (autoMergeStrategy === MWCP_MERGE_STRATEGY) {
return s__(
'mrWidget|Set by %{merge_author} to be merged automatically when all merge checks pass',
);
}
return s__(
'mrWidget|Set by %{merge_author} to be merged automatically when the pipeline succeeds',
);

View File

@ -10,6 +10,11 @@ export const PIPELINE_MUST_SUCCEED_CONFLICT_TEXT = __(
);
export const PIPELINE_SKIPPED_STATUS = 'SKIPPED';
// TODO: Add documentation
const MERGE_WHEN_CHECKS_PASS_HELP = helpPagePath(
'/user/project/merge_requests/merge_when_checks_pass.html',
);
export default {
computed: {
isMergeButtonDisabled() {
@ -28,9 +33,23 @@ export default {
return __('Set to auto-merge');
},
autoMergeHelperText() {
if (this.isPreferredAutoMergeStrategyMWPC) {
return __('Merge when all merge checks pass');
}
return __('Merge when pipeline succeeds');
},
autoMergePopoverSettings() {
if (this.isPreferredAutoMergeStrategyMWPC) {
return {
helpLink: MERGE_WHEN_CHECKS_PASS_HELP,
bodyText: __(
'When all the merge checks for this merge request pass, it will %{linkStart}automatically merge%{linkEnd}.',
),
title: __('Merge when checks pass'),
};
}
return {
helpLink: helpPagePath('/user/project/merge_requests/merge_when_pipeline_succeeds.html'),
bodyText: __(

View File

@ -1,4 +1,4 @@
import { DETAILED_MERGE_STATUS } from '../constants';
import { DETAILED_MERGE_STATUS, MWCP_MERGE_STRATEGY } from '../constants';
import { stateKey } from './state_maps';
export default function deviseState() {
@ -22,7 +22,8 @@ export default function deviseState() {
}
if (
this.detailedMergeStatus === DETAILED_MERGE_STATUS.MERGEABLE ||
this.detailedMergeStatus === DETAILED_MERGE_STATUS.CI_STILL_RUNNING
this.detailedMergeStatus === DETAILED_MERGE_STATUS.CI_STILL_RUNNING ||
(!this.autoMergeEnabled && this.preferredAutoMergeStrategy === MWCP_MERGE_STRATEGY)
) {
return stateKey.readyToMerge;
}

View File

@ -394,7 +394,7 @@ export default class MergeRequestStore {
}
get preventMerge() {
return this.isApprovalNeeded;
return this.isApprovalNeeded && this.preferredAutoMergeStrategy !== MWCP_MERGE_STRATEGY;
}
// Because the state machine doesn't yet handle every state and transition,

View File

@ -7,7 +7,9 @@ module Groups
feature_category :package_registry
urgency :low
before_action :set_feature_flag_packages_protected_packages, only: :show
before_action :set_feature_flag_packages_protected_packages, only: [:show, :index]
def index; end
# The show action renders index to allow frontend routing to work on page refresh
def show

View File

@ -528,7 +528,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
AutoMergeService.new(project, current_user, merge_params)
.execute(
merge_request,
params[:auto_merge_strategy] || AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS
params[:auto_merge_strategy] || merge_request.default_auto_merge_strategy
)
end
else

View File

@ -56,7 +56,7 @@ class SearchController < ApplicationController
@group = search_service.group
@search_service_presenter = Gitlab::View::Presenter::Factory.new(search_service, current_user: current_user).fabricate!
return unless search_term_valid?
return unless search_term_valid? && search_type_valid?
return if check_single_commit_result?
@ -73,7 +73,7 @@ class SearchController < ApplicationController
@search_highlight = @search_service_presenter.search_highlight
end
return if @search_results.respond_to?(:failed?) && @search_results.failed?
return if @search_results.respond_to?(:failed?) && @search_results.failed?(@scope)
Gitlab::Metrics::GlobalSearchSlis.record_apdex(
elapsed: @global_search_duration_s,
@ -186,6 +186,17 @@ class SearchController < ApplicationController
true
end
def search_type_valid?
search_type_errors = search_service.search_type_errors
if search_type_errors
flash[:alert] = search_type_errors
return false
end
true
end
def check_single_commit_result?
return false if params[:force_search_results]
return false unless @project.present?

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
module Import
class SourceUsersFinder
def initialize(namespace, current_user, params = {})
@namespace = namespace
@current_user = current_user
@params = params
end
def execute
return Import::SourceUser.none unless authorized?
namespace.import_source_users
end
private
attr_reader :namespace, :current_user, :params
def authorized?
Ability.allowed?(current_user, :admin_namespace, namespace)
end
end
end

View File

@ -4,29 +4,31 @@ module Packages
module Conan
class PackageFinder
MAX_PACKAGES_COUNT = 500
QUERY_SEPARATOR = '/'
def initialize(current_user, params, project: nil)
@current_user = current_user
@query = params[:query]
@name, @version = params[:query].to_s.split(QUERY_SEPARATOR)
@project = project
end
def execute
return ::Packages::Conan::Package.none unless query
return ::Packages::Conan::Package.none unless name.present?
packages
end
private
attr_reader :current_user, :query, :project
attr_reader :current_user, :name, :project, :version
def packages
base
.installable
.preload_conan_metadatum
.with_name_like(query)
.limit_recent(MAX_PACKAGES_COUNT)
matching_packages = base
.installable
.preload_conan_metadatum
.with_name_like(name)
matching_packages = matching_packages.with_version(version) if version
matching_packages.limit_recent(MAX_PACKAGES_COUNT)
end
def base

View File

@ -23,3 +23,5 @@ module BatchLoaders
end
end
end
BatchLoaders::AwardEmojiVotesBatchLoader.prepend_mod

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
module Mutations
module Import
module SourceUsers
class CancelReassignment < BaseMutation
graphql_name 'ImportSourceUserCancelReassignment'
argument :id, Types::GlobalIDType[::Import::SourceUser],
required: true,
description: 'Global ID of the mapping of a user on source instance to a user on destination instance.'
field :import_source_user,
Types::Import::SourceUserType,
null: true,
description: "Mapping of a user on source instance to a user on destination instance after mutation."
authorize :admin_import_source_user
def resolve(args)
if Feature.disabled?(:bulk_import_user_mapping, current_user)
raise_resource_not_available_error! '`bulk_import_user_mapping` feature flag is disabled.'
end
import_source_user = authorized_find!(id: args[:id])
result = ::Import::SourceUsers::CancelReassignmentService.new(import_source_user,
current_user: current_user).execute
{ import_source_user: result.payload, errors: result.errors }
end
end
end
end
end

View File

@ -0,0 +1,34 @@
# frozen_string_literal: true
module Mutations
module Import
module SourceUsers
class KeepAsPlaceholder < BaseMutation
graphql_name 'ImportSourceUserKeepAsPlaceholder'
argument :id, Types::GlobalIDType[::Import::SourceUser],
required: true,
description: 'Global ID of the mapping of a user on source instance to a user on destination instance.'
field :import_source_user,
Types::Import::SourceUserType,
null: true,
description: "Mapping of a user on source instance to a user on destination instance after mutation."
authorize :admin_import_source_user
def resolve(args)
if Feature.disabled?(:bulk_import_user_mapping, current_user)
raise_resource_not_available_error! '`bulk_import_user_mapping` feature flag is disabled.'
end
import_source_user = authorized_find!(id: args[:id])
result = ::Import::SourceUsers::KeepAsPlaceholderService.new(import_source_user,
current_user: current_user).execute
{ import_source_user: result.payload, errors: result.errors }
end
end
end
end
end

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
module Mutations
module Import
module SourceUsers
class Reassign < BaseMutation
graphql_name 'ImportSourceUserReassign'
argument :id, Types::GlobalIDType[::Import::SourceUser],
required: true,
description: 'Global ID of the mapping of a user on source instance to a user on destination instance.'
argument :assignee_user_id, Types::GlobalIDType[::User],
required: true,
loads: Types::UserType,
description: 'Global ID of the assignee user.'
field :import_source_user,
Types::Import::SourceUserType,
null: true,
description: "Mapping of a user on source instance to a user on destination instance after mutation."
authorize :admin_import_source_user
def resolve(args)
if Feature.disabled?(:bulk_import_user_mapping, current_user)
raise_resource_not_available_error! '`bulk_import_user_mapping` feature flag is disabled.'
end
import_source_user = authorized_find!(id: args[:id])
result = ::Import::SourceUsers::ReassignService.new(import_source_user, args[:assignee_user],
current_user: current_user).execute
{ import_source_user: result.payload, errors: result.errors }
end
end
end
end
end

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
module Resolvers
module Import
class SourceUsersResolver < BaseResolver
include ::LooksAhead
include Gitlab::Graphql::Authorize::AuthorizeResource
authorizes_object!
authorize :admin_namespace
type Types::Import::SourceUserType.connection_type, null: true
alias_method :namespace, :object
def resolve_with_lookahead(**args)
return [] if Feature.disabled?(:bulk_import_user_mapping, current_user)
apply_lookahead(::Import::SourceUsersFinder.new(namespace, context[:current_user], args).execute)
end
private
def preloads
{
reassign_to_user: [:reassign_to_user],
placeholder_user: [:placeholder_user],
reassigned_by_user: [:reassigned_by_user]
}
end
end
end
end

View File

@ -21,13 +21,14 @@ module Types
description: 'Inputs for the component.'
def inputs
spec_inputs = object.spec.fetch('inputs', {})
spec_inputs.map do |key, value|
object.spec.fetch('inputs', {}).map do |key, value|
{
name: key,
required: !value&.key?('default'),
default: value&.dig('default')
default: value&.dig('default'),
description: value&.dig('description'),
regex: value&.dig('regex'),
type: value&.dig('type') || 'string'
}
end
end

View File

@ -10,8 +10,16 @@ module Types
graphql_name 'CiCatalogResourceComponentInput'
field :default, GraphQL::Types::String, null: true, description: 'Default value for the input.'
field :description, GraphQL::Types::String, null: true, description: 'Description of the input.'
field :name, GraphQL::Types::String, null: true, description: 'Name of the input.'
field :regex, GraphQL::Types::String, null: true,
description: 'Pattern that the input value must match. Only applicable to string inputs.'
field :required, GraphQL::Types::Boolean, null: true, description: 'Indicates if an input is required.'
field :type, Types::Ci::Catalog::Resources::Components::InputTypeEnum, null: true,
description: 'Type of the input.'
end
# rubocop: enable Graphql/AuthorizeTypes
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
module Types
module Ci
module Catalog
module Resources
module Components
class InputTypeEnum < BaseEnum
graphql_name 'CiCatalogResourceComponentInputType'
description 'Available input types'
::Gitlab::Ci::Config::Interpolation::Inputs.input_types.each do |input_type|
value input_type.upcase, description: "#{input_type.capitalize} input", value: input_type
end
end
end
end
end
end
end

View File

@ -206,6 +206,8 @@ module Types
field :trigger, GraphQL::Types::Boolean, method: :trigger?, null: false, description: "If the pipeline was created by a Trigger request."
field :manual_variables, ManualVariableType.connection_type, null: true, description: 'CI/CD variables added to a manual pipeline.'
def commit
BatchLoader::GraphQL.wrap(object.commit)
end
@ -242,6 +244,13 @@ module Types
pipeline.sha
end
def manual_variables
variables = object.variables
return variables if Ability.allowed?(current_user, :read_pipeline_variable, pipeline)
variables.map { |variable| { key: variable.key, value: nil } }
end
alias_method :pipeline, :object
end
end

View File

@ -11,7 +11,7 @@ module Types
def import_source_description(import_source)
return "Not imported" if import_source == :none
"Imported from #{import_source.to_s.titleize}."
import_source.to_s.titleize
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
module Types
module Import
class SourceUserStatusEnum < BaseEnum
graphql_name 'ImportSourceUserStatus'
::Import::SourceUser.state_machines[:status].states.each do |state|
value state.name.upcase,
description: "An import source user mapping that is #{state.human_name}.",
value: state.value
end
end
end
end

View File

@ -0,0 +1,63 @@
# frozen_string_literal: true
module Types
module Import
class SourceUserType < BaseObject
graphql_name 'ImportSourceUser'
authorize :admin_import_source_user
field :id,
Types::GlobalIDType[::Import::SourceUser],
null: false,
description: 'Global ID of the mapping of a user on source instance to a user on destination instance.'
field :placeholder_user,
Types::UserType,
null: true,
description: 'Placeholder user associated with the import source user.'
field :reassign_to_user,
Types::UserType,
null: true,
description: 'User that contributions are reassigned to.'
field :reassigned_by_user,
Types::UserType,
null: true,
description: 'User that did the reassignment.'
# rubocop:disable GraphQL/ExtractType -- no need to extract allowed types into a separate field
field :source_user_identifier,
GraphQL::Types::String,
null: false,
description: 'ID of the user in the source instance.'
field :source_username,
GraphQL::Types::String,
null: true,
description: 'Username of user in the source instance.'
field :source_name,
GraphQL::Types::String,
null: true,
description: 'Name of user in the source instance.'
field :source_hostname,
GraphQL::Types::String,
null: false,
description: 'Source instance hostname.'
# rubocop:enable GraphQL/ExtractType
field :import_type,
Types::Import::ImportSourceEnum,
null: false,
description: 'Name of the importer.'
field :status,
Types::Import::SourceUserStatusEnum,
null: false,
description: 'Status of the mapping.'
end
end
end

View File

@ -58,6 +58,9 @@ module Types
mount_mutation Mutations::Environments::Delete
mount_mutation Mutations::Environments::Stop
mount_mutation Mutations::Environments::Update
mount_mutation Mutations::Import::SourceUsers::CancelReassignment, alpha: { milestone: '17.2' }
mount_mutation Mutations::Import::SourceUsers::KeepAsPlaceholder, alpha: { milestone: '17.2' }
mount_mutation Mutations::Import::SourceUsers::Reassign, alpha: { milestone: '17.2' }
mount_mutation Mutations::IncidentManagement::TimelineEvent::Create, alpha: { milestone: '15.6' }
mount_mutation Mutations::IncidentManagement::TimelineEvent::PromoteFromNote
mount_mutation Mutations::IncidentManagement::TimelineEvent::Update

View File

@ -90,6 +90,15 @@ module Types
connection: true,
description: "List of the namespaces's Pages Deployments."
field :import_source_users, Import::SourceUserType.connection_type,
null: true,
alpha: { milestone: '17.2' },
resolver: Resolvers::Import::SourceUsersResolver,
description: 'Import source users of the namespace. This field can only be resolved for one namespace in any ' \
'single request.' do
extension(::Gitlab::Graphql::Limit::FieldCallCount, limit: 1)
end
markdown_field :description_html, null: true
def achievements_path

View File

@ -39,3 +39,5 @@ module Types
end
end
end
Types::WorkItems::Widgets::AwardEmojiType.prepend_mod

View File

@ -89,7 +89,7 @@ module MergeRequestsHelper
def merge_params(merge_request)
{
auto_merge_strategy: AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS,
auto_merge_strategy: merge_request.default_auto_merge_strategy,
should_remove_source_branch: true,
sha: merge_request.diff_head_sha,
squash: merge_request.squash_on_merge?

View File

@ -30,7 +30,10 @@ module Organizations
end
def organization_groups_and_projects_app_data(organization)
shared_groups_and_projects_app_data(organization).to_json
{
user_preference_sort: current_user&.user_preference&.organization_groups_projects_sort,
user_preference_display: current_user&.user_preference&.organization_groups_projects_display
}.merge(shared_groups_and_projects_app_data(organization)).to_json
end
def organization_index_app_data

View File

@ -88,3 +88,5 @@ class AwardEmoji < ApplicationRecord
Gitlab::HookData::EmojiBuilder.new(self).build
end
end
AwardEmoji.prepend_mod

View File

@ -46,40 +46,49 @@ module Enums
dismissed: 2
}.with_indifferent_access.freeze
OWASP_TOP_10 = {
"A1:2017-Injection" => 1,
"A2:2017-Broken Authentication" => 2,
"A3:2017-Sensitive Data Exposure" => 3,
"A4:2017-XML External Entities (XXE)" => 4,
"A5:2017-Broken Access Control" => 5,
"A6:2017-Security Misconfiguration" => 6,
"A7:2017-Cross-Site Scripting (XSS)" => 7,
"A8:2017-Insecure Deserialization" => 8,
"A9:2017-Using Components with Known Vulnerabilities" => 9,
"A10:2017-Insufficient Logging & Monitoring" => 10,
OWASP_TOP_10_BY_YEAR = {
'2017' => {
"A1:2017-Injection" => 1,
"A2:2017-Broken Authentication" => 2,
"A3:2017-Sensitive Data Exposure" => 3,
"A4:2017-XML External Entities (XXE)" => 4,
"A5:2017-Broken Access Control" => 5,
"A6:2017-Security Misconfiguration" => 6,
"A7:2017-Cross-Site Scripting (XSS)" => 7,
"A8:2017-Insecure Deserialization" => 8,
"A9:2017-Using Components with Known Vulnerabilities" => 9,
"A10:2017-Insufficient Logging & Monitoring" => 10
},
'2021' => {
# 2021 switched to zero-padded identifiers, previous mapping is temporary
# to be fixed with https://gitlab.com/gitlab-org/gitlab/-/issues/429565
"A1:2021-Broken Access Control" => 11,
"A01:2021-Broken Access Control" => 11,
"A2:2021-Cryptographic Failures" => 12,
"A02:2021-Cryptographic Failures" => 12,
"A3:2021-Injection" => 13,
"A03:2021-Injection" => 13,
"A4:2021-Insecure Design" => 14,
"A04:2021-Insecure Design" => 14,
"A5:2021-Security Misconfiguration" => 15,
"A05:2021-Security Misconfiguration" => 15,
"A6:2021-Vulnerable and Outdated Components" => 16,
"A06:2021-Vulnerable and Outdated Components" => 16,
"A7:2021-Identification and Authentication Failures" => 17,
"A07:2021-Identification and Authentication Failures" => 17,
"A8:2021-Software and Data Integrity Failures" => 18,
"A08:2021-Software and Data Integrity Failures" => 18,
"A9:2021-Security Logging and Monitoring Failures" => 19,
"A09:2021-Security Logging and Monitoring Failures" => 19,
"A10:2021-Server-Side Request Forgery" => 20
}
}.freeze
# 2021 switched to zero-padded identifiers, previous mapping is temporary
# to be fixed with https://gitlab.com/gitlab-org/gitlab/-/issues/429565
"A1:2021-Broken Access Control" => 11,
"A01:2021-Broken Access Control" => 11,
"A2:2021-Cryptographic Failures" => 12,
"A02:2021-Cryptographic Failures" => 12,
"A3:2021-Injection" => 13,
"A03:2021-Injection" => 13,
"A4:2021-Insecure Design" => 14,
"A04:2021-Insecure Design" => 14,
"A5:2021-Security Misconfiguration" => 15,
"A05:2021-Security Misconfiguration" => 15,
"A6:2021-Vulnerable and Outdated Components" => 16,
"A06:2021-Vulnerable and Outdated Components" => 16,
"A7:2021-Identification and Authentication Failures" => 17,
"A07:2021-Identification and Authentication Failures" => 17,
"A8:2021-Software and Data Integrity Failures" => 18,
"A08:2021-Software and Data Integrity Failures" => 18,
"A9:2021-Security Logging and Monitoring Failures" => 19,
"A09:2021-Security Logging and Monitoring Failures" => 19,
"A10:2021-Server-Side Request Forgery" => 20
}.with_indifferent_access.freeze
OWASP_TOP_10 = OWASP_TOP_10_BY_YEAR.values.inject(&:merge).with_indifferent_access.freeze
OWASP_CATEGORIES = OWASP_TOP_10.keys.map { |key| key.split(':').first }.uniq.freeze
OWASP_YEARS = OWASP_TOP_10_BY_YEAR.keys.freeze
def self.confidence_levels
CONFIDENCE_LEVELS
@ -112,6 +121,14 @@ module Enums
def self.owasp_top_10
OWASP_TOP_10
end
def self.owasp_categories
OWASP_CATEGORIES
end
def self.owasp_years
OWASP_YEARS
end
end
end

View File

@ -22,12 +22,16 @@ module Import
state :completed, value: 5
state :keep_as_placeholder, value: 6
event :cancel_assignment do
event :reassign do
transition [:pending_assignment, :rejected] => :awaiting_approval
end
event :cancel_reassignment do
transition [:awaiting_approval, :rejected] => :pending_assignment
end
event :keep_as_placeholder do
transition [:pending_assignment, :awaiting_approval, :rejected] => :keep_as_placeholder
transition [:pending_assignment, :rejected] => :keep_as_placeholder
end
event :accept do
@ -61,5 +65,13 @@ module Import
import_type: import_type
)
end
def reassignable_status?
pending_assignment? || rejected?
end
def cancelable_status?
awaiting_approval? || rejected?
end
end
end

View File

@ -1361,7 +1361,15 @@ class MergeRequest < ApplicationRecord
def auto_merge_strategy
return unless auto_merge_enabled?
merge_params['auto_merge_strategy'] || AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS
merge_params['auto_merge_strategy'] || default_auto_merge_strategy
end
def default_auto_merge_strategy
if Feature.enabled?(:merge_when_checks_pass, project)
AutoMergeService::STRATEGY_MERGE_WHEN_CHECKS_PASS
else
AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS
end
end
def auto_merge_strategy=(strategy)

View File

@ -110,6 +110,8 @@ class Namespace < ApplicationRecord
has_many :jira_connect_subscriptions, class_name: 'JiraConnectSubscription', foreign_key: :namespace_id, inverse_of: :namespace
has_many :import_source_users, class_name: 'Import::SourceUser', foreign_key: :namespace_id, inverse_of: :namespace
validates :owner, presence: true, if: ->(n) { n.owner_required? }
validates :name,
presence: true,

View File

@ -3354,6 +3354,11 @@ class Project < ApplicationRecord
false
end
# Overridden in EE
def merge_trains_enabled?
false
end
private
# overridden in EE

View File

@ -0,0 +1,12 @@
# frozen_string_literal: true
module Import
class SourceUserPolicy < ::BasePolicy
condition(:admin_source_user_namespace) { can?(:admin_namespace, @subject.namespace) }
desc "User can administrate namespace"
rule { admin_source_user_namespace }.policy do
enable :admin_import_source_user
end
end
end

View File

@ -0,0 +1,76 @@
# frozen_string_literal: true
# rubocop:disable Gitlab/BoundedContexts -- Existing module
module AutoMerge
class MergeWhenChecksPassService < AutoMerge::BaseService
extend Gitlab::Utils::Override
override :execute
def execute(merge_request)
super do
add_system_note(merge_request)
end
end
override :process
def process(merge_request)
logger.info("Processing Automerge - MWCP")
return if merge_request.has_ci_enabled? && !merge_request.diff_head_pipeline_success?
logger.info("Pipeline Success - MWCP")
return unless merge_request.mergeable?
logger.info("Merge request mergeable - MWCP")
merge_request.merge_async(merge_request.merge_user_id, merge_request.merge_params)
end
override :cancel
def cancel(merge_request)
super do
SystemNoteService.cancel_auto_merge(merge_request, project, current_user)
end
end
override :abort
def abort(merge_request, reason)
super do
SystemNoteService.abort_auto_merge(merge_request, project, current_user, reason)
end
end
override :available_for
def available_for?(merge_request)
super do
next false if Feature.disabled?(:merge_when_checks_pass, merge_request.project)
next false if merge_request.project.merge_trains_enabled?
next false if merge_request.mergeable? && !merge_request.diff_head_pipeline_considered_in_progress?
next true
end
end
private
def add_system_note(merge_request)
return unless merge_request.saved_change_to_auto_merge_enabled?
SystemNoteService.merge_when_checks_pass(
merge_request,
project,
current_user,
merge_request.merge_params.symbolize_keys[:sha]
)
end
def notify(merge_request)
return unless merge_request.saved_change_to_auto_merge_enabled?
notification_service.async.merge_when_pipeline_succeeds(merge_request,
current_user)
end
end
end
# rubocop:enable Gitlab/BoundedContexts

View File

@ -34,6 +34,8 @@ module AutoMerge
def available_for?(merge_request)
super do
next false if Feature.enabled?(:merge_when_checks_pass, merge_request.project)
merge_request.diff_head_pipeline_considered_in_progress?
end
end

View File

@ -4,9 +4,9 @@ class AutoMergeService < BaseService
include Gitlab::Utils::StrongMemoize
STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS = 'merge_when_pipeline_succeeds'
# Currently only EE but will be moved to CE in (https://gitlab.com/gitlab-org/gitlab/-/merge_requests/146730)
# `merge_when_checks_pass` enables auto-merge to be set before all merge checks are passing
STRATEGY_MERGE_WHEN_CHECKS_PASS = 'merge_when_checks_pass'
STRATEGIES = [STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS].freeze
STRATEGIES = [STRATEGY_MERGE_WHEN_CHECKS_PASS, STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS].freeze
class << self
def all_strategies_ordered_by_preference

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
module Import
module SourceUsers
class BaseService
private
def error_invalid_permissions
ServiceResponse.error(
message: s_('Import|You have insufficient permissions to update the import source user'),
reason: :forbidden
)
end
def error_invalid_status
ServiceResponse.error(
message: s_('Import|Import source user has an invalid status for this operation'),
reason: :invalid_status,
payload: import_source_user
)
end
end
end
end

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
module Import
module SourceUsers
class CancelReassignmentService < BaseService
def initialize(import_source_user, current_user:)
@import_source_user = import_source_user
@current_user = current_user
end
def execute
return error_invalid_permissions unless current_user.can?(:admin_import_source_user, import_source_user)
return error_invalid_status unless import_source_user.cancelable_status?
if cancel_reassignment
ServiceResponse.success(payload: import_source_user)
else
ServiceResponse.error(payload: import_source_user, message: import_source_user.errors.full_messages)
end
end
private
attr_reader :import_source_user, :current_user, :params
def cancel_reassignment
import_source_user.reassign_to_user = nil
import_source_user.reassigned_by_user = nil
import_source_user.cancel_reassignment
end
end
end
end

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
module Import
module SourceUsers
class KeepAsPlaceholderService < BaseService
def initialize(import_source_user, current_user:)
@import_source_user = import_source_user
@current_user = current_user
end
def execute
return error_invalid_permissions unless current_user.can?(:admin_import_source_user, import_source_user)
return error_invalid_status unless import_source_user.reassignable_status?
if keep_as_placeholder
ServiceResponse.success(payload: import_source_user)
else
ServiceResponse.error(payload: import_source_user, message: import_source_user.errors.full_messages)
end
end
private
attr_reader :import_source_user, :current_user, :params
def keep_as_placeholder
import_source_user.reassigned_by_user = current_user
import_source_user.keep_as_placeholder
end
end
end
end

View File

@ -0,0 +1,47 @@
# frozen_string_literal: true
module Import
module SourceUsers
class ReassignService < BaseService
def initialize(import_source_user, assignee_user, current_user:)
@import_source_user = import_source_user
@current_user = current_user
@assignee_user = assignee_user
end
def execute
return error_invalid_permissions unless current_user.can?(:admin_import_source_user, import_source_user)
return error_invalid_status unless import_source_user.reassignable_status?
return error_invalid_assignee unless valid_assignee?(assignee_user)
if reassign_user
ServiceResponse.success(payload: import_source_user)
else
ServiceResponse.error(payload: import_source_user, message: import_source_user.errors.full_messages)
end
end
private
attr_reader :import_source_user, :current_user, :assignee_user
def reassign_user
import_source_user.reassign_to_user = assignee_user
import_source_user.reassigned_by_user = current_user
import_source_user.reassign
end
def error_invalid_assignee
ServiceResponse.error(
message: s_('Import|Only active regular, auditor, or administrator users can be assigned'),
reason: :invalid_assignee,
payload: import_source_user
)
end
def valid_assignee?(user)
user.present? && user.human? && user.active?
end
end
end
end

View File

@ -40,6 +40,10 @@ class SearchService
# overridden in EE
end
def search_type_errors
# overridden in EE
end
def global_search?
project.blank? && group.blank?
end
@ -58,10 +62,9 @@ class SearchService
delegate :valid_terms_count?, :valid_query_length?, to: :params
def search_results
strong_memoize(:search_results) do
abuse_detected? ? Gitlab::EmptySearchResults.new : search_service.execute
end
abuse_detected? ? ::Search::EmptySearchResults.new : search_service.execute
end
strong_memoize_attr :search_results
def search_objects(preload_method = nil)
@search_objects ||= redact_unauthorized_results(

View File

@ -1,7 +1,7 @@
- page_title _('DevOps Reports')
- add_page_specific_style 'page_bundles/dev_ops_reports'
.gl-mt-3
.gl-mt-3{ data: { event_tracking_load: 'true', event_tracking: 'view_admin_dev_ops_reports_pageload' } }
- if show_adoption?
= render_if_exists 'admin/dev_ops_report/devops_tabs'
- else

View File

@ -3,7 +3,7 @@
- if @timeout
= render partial: "search/results/timeout"
- elsif @search_results.respond_to?(:failed?) && @search_results.failed?
- elsif @search_results.respond_to?(:failed?) && @search_results.failed?(@scope)
= render partial: "search/results/error"
- elsif @search_objects.blank?
= render partial: "search/results/empty"

View File

@ -9,7 +9,7 @@
- group_attributes = @group&.attributes&.slice('id', 'name')&.merge(full_name: @group&.full_name)
- project_attributes = @project&.attributes&.slice('id', 'namespace_id', 'name')&.merge(name_with_namespace: @project&.name_with_namespace)
- if @search_results && !(@search_results.respond_to?(:failed?) && @search_results.failed?)
- if @search_results && !(@search_results.respond_to?(:failed?) && @search_results.failed?(@scope))
- if @search_service_presenter.without_count?
- page_description(_("%{scope} results for term '%{term}'") % { scope: @scope, term: @search_term })
- else

View File

@ -0,0 +1,16 @@
---
description: Tracks pageviews for the admin audit events / logs page
internal_events: true
action: view_admin_audit_logs_pageload
identifiers:
- user
product_group: personal_productivity
milestone: '17.2'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156830
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,16 @@
---
description: Tracks pageviews for the admin devops reports page
internal_events: true
action: view_admin_dev_ops_reports_pageload
identifiers:
- user
product_group: personal_productivity
milestone: '17.2'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156816
distributions:
- ce
- ee
tiers:
- free
- premium
- ultimate

View File

@ -0,0 +1,9 @@
---
name: ci_expand_variables_in_compare_to
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/369916
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/157147
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/466374
milestone: '17.2'
group: group::pipeline security
type: gitlab_com_derisk
default_enabled: false

View File

@ -193,10 +193,6 @@ dast_scanner_profiles_builds:
- table: p_ci_builds
column: ci_build_id
on_delete: async_delete
dast_scanner_profiles_tags:
- table: tags
column: tag_id
on_delete: async_delete
dast_site_profiles_builds:
- table: ci_builds
column: ci_build_id
@ -204,10 +200,6 @@ dast_site_profiles_builds:
- table: p_ci_builds
column: ci_build_id
on_delete: async_delete
dast_site_profiles_pipelines:
- table: ci_pipelines
column: ci_pipeline_id
on_delete: async_delete
deployment_clusters:
- table: clusters
column: cluster_id

View File

@ -0,0 +1,22 @@
---
key_path: redis_hll_counters.count_distinct_user_id_from_view_admin_audit_logs_pageload_monthly
description: Monthly count of unique users who visited the admin audit events / audit logs page
product_group: personal_productivity
performance_indicator_type: []
value_type: number
status: active
milestone: '17.2'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156830
time_frame: 28d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: view_admin_audit_logs_pageload
unique: user.id

View File

@ -0,0 +1,22 @@
---
key_path: redis_hll_counters.count_distinct_user_id_from_view_admin_dev_ops_reports_pageload_monthly
description: Monthly count of unique users who visit the devops reports page
product_group: personal_productivity
performance_indicator_type: []
value_type: number
status: active
milestone: '17.2'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156816
time_frame: 28d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: view_admin_dev_ops_reports_pageload
unique: user.id

View File

@ -0,0 +1,21 @@
---
key_path: counts.count_total_view_admin_audit_logs_pageload_monthly
description: Monthly count of total users who visited the admin audit events / audit logs page
product_group: personal_productivity
performance_indicator_type: []
value_type: number
status: active
milestone: '17.2'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156830
time_frame: 28d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: view_admin_audit_logs_pageload

View File

@ -0,0 +1,21 @@
---
key_path: counts.count_total_view_admin_dev_ops_reports_pageload_monthly
description: Monthly count of total users who visit the admin devops reports page
product_group: personal_productivity
performance_indicator_type: []
value_type: number
status: active
milestone: '17.2'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156816
time_frame: 28d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: view_admin_dev_ops_reports_pageload

View File

@ -0,0 +1,22 @@
---
key_path: redis_hll_counters.count_distinct_user_id_from_view_admin_audit_logs_pageload_weekly
description: Weekly count of unique users who visited the admin audit events / audit logs page
product_group: personal_productivity
performance_indicator_type: []
value_type: number
status: active
milestone: '17.2'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156830
time_frame: 7d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: view_admin_audit_logs_pageload
unique: user.id

View File

@ -0,0 +1,22 @@
---
key_path: redis_hll_counters.count_distinct_user_id_from_view_admin_dev_ops_reports_pageload_weekly
description: Weekly count of unique users who visit the devops reports page
product_group: personal_productivity
performance_indicator_type: []
value_type: number
status: active
milestone: '17.2'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156816
time_frame: 7d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: view_admin_dev_ops_reports_pageload
unique: user.id

View File

@ -0,0 +1,21 @@
---
key_path: counts.count_total_view_admin_audit_logs_pageload_weekly
description: Weekly count of total users who visited the admin audit events / audit logs page
product_group: personal_productivity
performance_indicator_type: []
value_type: number
status: active
milestone: '17.2'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156830
time_frame: 7d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: view_admin_audit_logs_pageload

View File

@ -0,0 +1,21 @@
---
key_path: counts.count_total_view_admin_dev_ops_reports_pageload_weekly
description: Weekly count of total users who visit the admin devops reports page
product_group: personal_productivity
performance_indicator_type: []
value_type: number
status: active
milestone: '17.2'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156816
time_frame: 7d
data_source: internal_events
data_category: optional
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
events:
- name: view_admin_dev_ops_reports_pageload

View File

@ -9,6 +9,7 @@ lock_gitlab_schemas:
- gitlab_main_clusterwide
- gitlab_main_cell
- gitlab_pm
- gitlab_sec
klass: Ci::ApplicationRecord
# if CI database is not configured, use this database
fallback_database: main

View File

@ -8,6 +8,7 @@ gitlab_schemas:
- gitlab_pm
lock_gitlab_schemas:
- gitlab_ci
- gitlab_sec
# Note that we use ActiveRecord::Base here and not ApplicationRecord.
# This is deliberate, as:
# - the load balancer must be enabled for _all_ models

View File

@ -0,0 +1,16 @@
name: sec
description: Cell-local GitLab database holding Security feature related tables.
gitlab_schemas:
- gitlab_internal
- gitlab_shared
- gitlab_sec
lock_gitlab_schemas:
- gitlab_main
- gitlab_main_clusterwide
- gitlab_main_cell
- gitlab_ci
- gitlab_pm
klass: Gitlab::Database::SecApplicationRecord
# if Sec database is not configured, use this database
fallback_database: main
uses_load_balancing: true

View File

@ -5,5 +5,7 @@ feature_categories:
- dynamic_application_security_testing
description: Join Table for Runner tags and DAST Scanner Profiles
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104909
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/153443
milestone: '15.7'
removed_in_milestone: '17.2'
gitlab_schema: gitlab_main

View File

@ -5,5 +5,7 @@ feature_categories:
- dynamic_application_security_testing
description: Join table between DAST Site Profiles and CI Pipelines
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60090
removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/153443
milestone: '13.12'
removed_in_milestone: '17.2'
gitlab_schema: gitlab_main

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
class DropTableDastScannerProfilesTags < Gitlab::Database::Migration[2.2]
milestone '17.2'
disable_ddl_transaction!
def up
drop_table :dast_scanner_profiles_tags if table_exists? :dast_scanner_profiles_tags
end
def down
unless table_exists?(:dast_scanner_profiles_tags)
create_table :dast_scanner_profiles_tags do |t|
t.bigint :dast_scanner_profile_id, null: false
t.bigint :tag_id, null: false
t.index :dast_scanner_profile_id, name: :i_dast_scanner_profiles_tags_on_scanner_profiles_id
t.index :tag_id, name: :index_dast_scanner_profiles_tags_on_tag_id
end
end
return if foreign_key_exists?(:dast_scanner_profiles_tags, :dast_scanner_profiles, name: :fk_rails_deb79b7f19)
add_concurrent_foreign_key(
:dast_scanner_profiles_tags,
:dast_scanner_profiles,
column: :dast_scanner_profile_id,
name: :fk_rails_deb79b7f19)
end
end

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
class DropTableDastSiteProfilesPipelines < Gitlab::Database::Migration[2.2]
milestone '17.2'
disable_ddl_transaction!
def up
drop_table :dast_site_profiles_pipelines if table_exists? :dast_site_profiles_pipelines
end
def down
unless table_exists?(:dast_site_profiles_pipelines)
create_table :dast_site_profiles_pipelines, id: false do |t|
t.bigint :dast_site_profile_id, null: false
t.bigint :ci_pipeline_id, null: false
t.index [:dast_site_profile_id, :ci_pipeline_id], name: :dast_site_profiles_pipelines_pkey, unique: true
t.index :ci_pipeline_id, name: :index_dast_site_profiles_pipelines_on_ci_pipeline_id, unique: true
end
end
add_primary_key_using_index(
:dast_site_profiles_pipelines,
:dast_site_profiles_pipelines_pkey,
:dast_site_profiles_pipelines_pkey
)
return if foreign_key_exists?(:dast_site_profiles_pipelines, :dast_site_profiles, name: :fk_cf05cf8fe1)
add_concurrent_foreign_key(
:dast_site_profiles_pipelines,
:dast_site_profiles,
column: :dast_site_profile_id,
name: :fk_cf05cf8fe1)
execute(<<~SQL)
COMMENT ON TABLE dast_site_profiles_pipelines IS '{"owner":"group::dynamic analysis","description":"Join table between DAST Site Profiles and CI Pipelines"}';
SQL
end
end

View File

@ -0,0 +1 @@
ca71b00a277e322207a71d3916a3db50c13a9bef9b85653236b017de6821a45f

View File

@ -0,0 +1 @@
e197e135ef13ee40965df703ffbc23c830b39887c3914f3c541a128ddac0ba01

1
db/sec_structure.sql Normal file
View File

@ -0,0 +1 @@
db/structure.sql

File diff suppressed because it is too large Load Diff

View File

@ -744,8 +744,8 @@ You can solve this error in two ways.
### Rename references to the LDAP server
This solution is suitable when the LDAP servers are replicas of each other, and the affected users should be able to sign in using a configured LDAP server. For example, if a
load balancer is now used to manage LDAP high availability and a separate secondary sign-in option is no longer needed.
This solution is suitable when the LDAP servers are replicas of each other, and the affected users should be able to sign in using a configured LDAP server.
For example, if a load balancer is now used to manage LDAP high availability and a separate secondary sign-in option is no longer needed.
NOTE:
If the LDAP servers aren't replicas of each other, this solution stops affected users from being able to sign in.
@ -758,8 +758,14 @@ sudo gitlab-rake gitlab:ldap:rename_provider[ldapsecondary,ldapmain]
### Remove the `identity` records that relate to the removed LDAP server
With this solution, affected users can sign in with the configured LDAP servers and a new `identity` record is created by GitLab. In a
[Rails console](../../operations/rails_console.md), delete the `ldapsecondary` identities:
Prerequisites:
- Ensure that `auto_link_ldap_user` is enabled.
With this solution, after the identity is deleted, affected users can sign in with the
configured LDAP servers and a new `identity` record is created by GitLab.
Because the LDAP server that was removed was `ldapsecondary`, in a [Rails console](../../operations/rails_console.md), delete all the `ldapsecondary` identities:
```ruby
ldap_identities = Identity.where(provider: "ldapsecondary")

View File

@ -359,7 +359,6 @@ Prerequisites:
1. Expand **Visibility and access controls**.
1. In **Globally-allowed IP ranges**, provide a list of IP address ranges. This list:
- Has no limit on the number of IP address ranges.
- Has a size limit of 1 GB.
- Applies to both SSH or HTTP authorized IP address ranges. You cannot split
this list by authorization type.
1. Select **Save changes**.

View File

@ -5385,6 +5385,76 @@ Input type: `HttpIntegrationUpdateInput`
| <a id="mutationhttpintegrationupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationhttpintegrationupdateintegration"></a>`integration` | [`AlertManagementHttpIntegration`](#alertmanagementhttpintegration) | HTTP integration. |
### `Mutation.importSourceUserCancelReassignment`
DETAILS:
**Introduced** in GitLab 17.2.
**Status**: Experiment.
Input type: `ImportSourceUserCancelReassignmentInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationimportsourceusercancelreassignmentclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationimportsourceusercancelreassignmentid"></a>`id` | [`ImportSourceUserID!`](#importsourceuserid) | Global ID of the mapping of a user on source instance to a user on destination instance. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationimportsourceusercancelreassignmentclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationimportsourceusercancelreassignmenterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationimportsourceusercancelreassignmentimportsourceuser"></a>`importSourceUser` | [`ImportSourceUser`](#importsourceuser) | Mapping of a user on source instance to a user on destination instance after mutation. |
### `Mutation.importSourceUserKeepAsPlaceholder`
DETAILS:
**Introduced** in GitLab 17.2.
**Status**: Experiment.
Input type: `ImportSourceUserKeepAsPlaceholderInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationimportsourceuserkeepasplaceholderclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationimportsourceuserkeepasplaceholderid"></a>`id` | [`ImportSourceUserID!`](#importsourceuserid) | Global ID of the mapping of a user on source instance to a user on destination instance. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationimportsourceuserkeepasplaceholderclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationimportsourceuserkeepasplaceholdererrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationimportsourceuserkeepasplaceholderimportsourceuser"></a>`importSourceUser` | [`ImportSourceUser`](#importsourceuser) | Mapping of a user on source instance to a user on destination instance after mutation. |
### `Mutation.importSourceUserReassign`
DETAILS:
**Introduced** in GitLab 17.2.
**Status**: Experiment.
Input type: `ImportSourceUserReassignInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationimportsourceuserreassignassigneeuserid"></a>`assigneeUserId` | [`UserID!`](#userid) | Global ID of the assignee user. |
| <a id="mutationimportsourceuserreassignclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationimportsourceuserreassignid"></a>`id` | [`ImportSourceUserID!`](#importsourceuserid) | Global ID of the mapping of a user on source instance to a user on destination instance. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationimportsourceuserreassignclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationimportsourceuserreassignerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationimportsourceuserreassignimportsourceuser"></a>`importSourceUser` | [`ImportSourceUser`](#importsourceuser) | Mapping of a user on source instance to a user on destination instance after mutation. |
### `Mutation.instanceAuditEventStreamingDestinationsCreate`
DETAILS:
@ -7539,7 +7609,7 @@ Input type: `ProjectSavedReplyUpdateInput`
### `Mutation.projectSetComplianceFramework`
Assign (or unset) a compliance framework to a project.
Assign (or unset) a compliance framework to a project. This mutation raises an error if the project has more than one compliance framework associated with it.
Input type: `ProjectSetComplianceFrameworkInput`
@ -12750,6 +12820,29 @@ The edge type for [`GroupWikiRepositoryRegistry`](#groupwikirepositoryregistry).
| <a id="groupwikirepositoryregistryedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="groupwikirepositoryregistryedgenode"></a>`node` | [`GroupWikiRepositoryRegistry`](#groupwikirepositoryregistry) | The item at the end of the edge. |
#### `ImportSourceUserConnection`
The connection type for [`ImportSourceUser`](#importsourceuser).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="importsourceuserconnectionedges"></a>`edges` | [`[ImportSourceUserEdge]`](#importsourceuseredge) | A list of edges. |
| <a id="importsourceuserconnectionnodes"></a>`nodes` | [`[ImportSourceUser]`](#importsourceuser) | A list of nodes. |
| <a id="importsourceuserconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `ImportSourceUserEdge`
The edge type for [`ImportSourceUser`](#importsourceuser).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="importsourceuseredgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="importsourceuseredgenode"></a>`node` | [`ImportSourceUser`](#importsourceuser) | The item at the end of the edge. |
#### `IncidentManagementOncallRotationConnection`
The connection type for [`IncidentManagementOncallRotation`](#incidentmanagementoncallrotation).
@ -17674,8 +17767,11 @@ four standard [pagination arguments](#pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="cicatalogresourcecomponentinputdefault"></a>`default` | [`String`](#string) | Default value for the input. |
| <a id="cicatalogresourcecomponentinputdescription"></a>`description` | [`String`](#string) | Description of the input. |
| <a id="cicatalogresourcecomponentinputname"></a>`name` | [`String`](#string) | Name of the input. |
| <a id="cicatalogresourcecomponentinputregex"></a>`regex` | [`String`](#string) | Pattern that the input value must match. Only applicable to string inputs. |
| <a id="cicatalogresourcecomponentinputrequired"></a>`required` | [`Boolean`](#boolean) | Indicates if an input is required. |
| <a id="cicatalogresourcecomponentinputtype"></a>`type` | [`CiCatalogResourceComponentInputType`](#cicatalogresourcecomponentinputtype) | Type of the input. |
### `CiCatalogResourceVersion`
@ -21682,6 +21778,7 @@ GPG signature for a signed commit.
| <a id="groupgooglecloudloggingconfigurations"></a>`googleCloudLoggingConfigurations` | [`GoogleCloudLoggingConfigurationTypeConnection`](#googlecloudloggingconfigurationtypeconnection) | Google Cloud logging configurations that receive audit events belonging to the group. (see [Connections](#connections)) |
| <a id="groupgroupmemberscount"></a>`groupMembersCount` | [`Int!`](#int) | Count of direct members of this group. |
| <a id="groupid"></a>`id` | [`ID!`](#id) | ID of the namespace. |
| <a id="groupimportsourceusers"></a>`importSourceUsers` **{warning-solid}** | [`ImportSourceUserConnection`](#importsourceuserconnection) | **Introduced** in GitLab 17.2. **Status**: Experiment. Import source users of the namespace. This field can only be resolved for one namespace in any single request. |
| <a id="groupisadjourneddeletionenabled"></a>`isAdjournedDeletionEnabled` **{warning-solid}** | [`Boolean!`](#boolean) | **Introduced** in GitLab 16.11. **Status**: Experiment. Indicates if delayed group deletion is enabled. |
| <a id="grouplfsenabled"></a>`lfsEnabled` | [`Boolean`](#boolean) | Indicates if Large File Storage (LFS) is enabled for namespace. |
| <a id="grouplockduofeaturesenabled"></a>`lockDuoFeaturesEnabled` **{warning-solid}** | [`Boolean`](#boolean) | **Introduced** in GitLab 16.10. **Status**: Experiment. Indicates if the GitLab Duo features enabled setting is enforced for all subgroups. |
@ -23325,6 +23422,23 @@ IDE settings and feature flags.
| ---- | ---- | ----------- |
| <a id="idecodesuggestionsenabled"></a>`codeSuggestionsEnabled` | [`Boolean!`](#boolean) | Indicates whether AI assisted code suggestions are enabled. |
### `ImportSourceUser`
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="importsourceuserid"></a>`id` | [`ImportSourceUserID!`](#importsourceuserid) | Global ID of the mapping of a user on source instance to a user on destination instance. |
| <a id="importsourceuserimporttype"></a>`importType` | [`ImportSource!`](#importsource) | Name of the importer. |
| <a id="importsourceuserplaceholderuser"></a>`placeholderUser` | [`UserCore`](#usercore) | Placeholder user associated with the import source user. |
| <a id="importsourceuserreassigntouser"></a>`reassignToUser` | [`UserCore`](#usercore) | User that contributions are reassigned to. |
| <a id="importsourceuserreassignedbyuser"></a>`reassignedByUser` | [`UserCore`](#usercore) | User that did the reassignment. |
| <a id="importsourceusersourcehostname"></a>`sourceHostname` | [`String!`](#string) | Source instance hostname. |
| <a id="importsourceusersourcename"></a>`sourceName` | [`String`](#string) | Name of user in the source instance. |
| <a id="importsourceusersourceuseridentifier"></a>`sourceUserIdentifier` | [`String!`](#string) | ID of the user in the source instance. |
| <a id="importsourceusersourceusername"></a>`sourceUsername` | [`String`](#string) | Username of user in the source instance. |
| <a id="importsourceuserstatus"></a>`status` | [`ImportSourceUserStatus!`](#importsourceuserstatus) | Status of the mapping. |
### `IncidentManagementOncallRotation`
Describes an incident management on-call rotation.
@ -26098,6 +26212,7 @@ Product analytics events for a specific month and year.
| <a id="namespacefullname"></a>`fullName` | [`String!`](#string) | Full name of the namespace. |
| <a id="namespacefullpath"></a>`fullPath` | [`ID!`](#id) | Full path of the namespace. |
| <a id="namespaceid"></a>`id` | [`ID!`](#id) | ID of the namespace. |
| <a id="namespaceimportsourceusers"></a>`importSourceUsers` **{warning-solid}** | [`ImportSourceUserConnection`](#importsourceuserconnection) | **Introduced** in GitLab 17.2. **Status**: Experiment. Import source users of the namespace. This field can only be resolved for one namespace in any single request. |
| <a id="namespacelfsenabled"></a>`lfsEnabled` | [`Boolean`](#boolean) | Indicates if Large File Storage (LFS) is enabled for namespace. |
| <a id="namespacename"></a>`name` | [`String!`](#string) | Name of the namespace. |
| <a id="namespacepackagesettings"></a>`packageSettings` | [`PackageSettings`](#packagesettings) | Package settings for the namespace. |
@ -27041,6 +27156,7 @@ Returns [`UserMergeRequestInteraction`](#usermergerequestinteraction).
| <a id="pipelineiid"></a>`iid` | [`String!`](#string) | Internal ID of the pipeline. |
| <a id="pipelinejobartifacts"></a>`jobArtifacts` | [`[CiJobArtifact!]`](#cijobartifact) | Job artifacts of the pipeline. |
| <a id="pipelinelatest"></a>`latest` | [`Boolean!`](#boolean) | If the pipeline is the latest one or not. |
| <a id="pipelinemanualvariables"></a>`manualVariables` | [`CiManualVariableConnection`](#cimanualvariableconnection) | CI/CD variables added to a manual pipeline. (see [Connections](#connections)) |
| <a id="pipelinemergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | The MR which the Pipeline is attached to. |
| <a id="pipelinemergerequesteventtype"></a>`mergeRequestEventType` | [`PipelineMergeRequestEventType`](#pipelinemergerequesteventtype) | Event type of the pipeline associated with a merge request. |
| <a id="pipelinename"></a>`name` | [`String`](#string) | Name of the pipeline. |
@ -33348,6 +33464,17 @@ Status of a merge train's car.
| <a id="carstatusskip_merged"></a>`SKIP_MERGED` | Car's status: skip_merged. |
| <a id="carstatusstale"></a>`STALE` | Car's status: stale. |
### `CiCatalogResourceComponentInputType`
Available input types.
| Value | Description |
| ----- | ----------- |
| <a id="cicatalogresourcecomponentinputtypearray"></a>`ARRAY` | Array input. |
| <a id="cicatalogresourcecomponentinputtypeboolean"></a>`BOOLEAN` | Boolean input. |
| <a id="cicatalogresourcecomponentinputtypenumber"></a>`NUMBER` | Number input. |
| <a id="cicatalogresourcecomponentinputtypestring"></a>`STRING` | String input. |
### `CiCatalogResourceScope`
Values for scoping catalog resources.
@ -34401,19 +34528,31 @@ Import source.
| Value | Description |
| ----- | ----------- |
| <a id="importsourcebitbucket"></a>`BITBUCKET` | Imported from Bitbucket. |
| <a id="importsourcebitbucket_server"></a>`BITBUCKET_SERVER` | Imported from Bitbucket Server. |
| <a id="importsourcecustom_template"></a>`CUSTOM_TEMPLATE` | Imported from Custom Template. |
| <a id="importsourcefogbugz"></a>`FOGBUGZ` | Imported from Fogbugz. |
| <a id="importsourcegit"></a>`GIT` | Imported from Git. |
| <a id="importsourcegitea"></a>`GITEA` | Imported from Gitea. |
| <a id="importsourcegithub"></a>`GITHUB` | Imported from Github. |
| <a id="importsourcegitlab_group"></a>`GITLAB_GROUP` | Imported from Gitlab Group. |
| <a id="importsourcegitlab_migration"></a>`GITLAB_MIGRATION` | Imported from Gitlab Migration. |
| <a id="importsourcegitlab_project"></a>`GITLAB_PROJECT` | Imported from Gitlab Project. |
| <a id="importsourcemanifest"></a>`MANIFEST` | Imported from Manifest. |
| <a id="importsourcebitbucket"></a>`BITBUCKET` | Bitbucket. |
| <a id="importsourcebitbucket_server"></a>`BITBUCKET_SERVER` | Bitbucket Server. |
| <a id="importsourcecustom_template"></a>`CUSTOM_TEMPLATE` | Custom Template. |
| <a id="importsourcefogbugz"></a>`FOGBUGZ` | Fogbugz. |
| <a id="importsourcegit"></a>`GIT` | Git. |
| <a id="importsourcegitea"></a>`GITEA` | Gitea. |
| <a id="importsourcegithub"></a>`GITHUB` | Github. |
| <a id="importsourcegitlab_group"></a>`GITLAB_GROUP` | Gitlab Group. |
| <a id="importsourcegitlab_migration"></a>`GITLAB_MIGRATION` | Gitlab Migration. |
| <a id="importsourcegitlab_project"></a>`GITLAB_PROJECT` | Gitlab Project. |
| <a id="importsourcemanifest"></a>`MANIFEST` | Manifest. |
| <a id="importsourcenone"></a>`NONE` | Not imported. |
### `ImportSourceUserStatus`
| Value | Description |
| ----- | ----------- |
| <a id="importsourceuserstatusawaiting_approval"></a>`AWAITING_APPROVAL` | An import source user mapping that is awaiting approval. |
| <a id="importsourceuserstatuscompleted"></a>`COMPLETED` | An import source user mapping that is completed. |
| <a id="importsourceuserstatusfailed"></a>`FAILED` | An import source user mapping that is failed. |
| <a id="importsourceuserstatuskeep_as_placeholder"></a>`KEEP_AS_PLACEHOLDER` | An import source user mapping that is keep as placeholder. |
| <a id="importsourceuserstatuspending_assignment"></a>`PENDING_ASSIGNMENT` | An import source user mapping that is pending assignment. |
| <a id="importsourceuserstatusreassignment_in_progress"></a>`REASSIGNMENT_IN_PROGRESS` | An import source user mapping that is reassignment in progress. |
| <a id="importsourceuserstatusrejected"></a>`REJECTED` | An import source user mapping that is rejected. |
### `IntegrationType`
Integration Names.
@ -36760,6 +36899,12 @@ An ISO 8601-encoded date.
An ISO 8601-encoded datetime.
### `ImportSourceUserID`
A `ImportSourceUserID` is a global ID. It is encoded as a string.
An example `ImportSourceUserID` is: `"gid://gitlab/Import::SourceUser/1"`.
### `IncidentManagementEscalationPolicyID`
A `IncidentManagementEscalationPolicyID` is a global ID. It is encoded as a string.

View File

@ -0,0 +1,73 @@
---
stage: Package
group: Container Registry
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments"
description: "Documentation for the REST API for container registry protection rules in GitLab."
---
# Container registry protection rules API
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** Self-managed
**Status:** Experiment
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/155798) in GitLab 17.2 [with a flag](../administration/feature_flags.md) named `container_registry_protected_containers`. Disabled by default.
FLAG:
The availability of this feature is controlled by a feature flag.
For more information, see the history.
This feature is available for testing, but not ready for production use.
This API endpoint manages the protection rules for container registries in a project. This feature is an experiment.
## List container registry protection rules
Gets a list of container registry protection rules from a project.
```plaintext
GET /api/v4/projects/:id/registry/protection/rules
```
Supported attributes:
| Attribute | Type | Required | Description |
|-------------------------------|-----------------|----------|--------------------------------|
| `id` | integer/string | Yes | ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding) owned by the authenticated user. |
If successful, returns [`200`](rest/index.md#status-codes) and a list of container registry protection rules.
Can return the following status codes:
- `200 OK`: A list of container registry protection rules.
- `401 Unauthorized`: The access token is invalid.
- `403 Forbidden`: The user does not have permission to list container registry protection rules for this project.
- `404 Not Found`: The project was not found.
Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" \
--url "https://gitlab.example.com/api/v4/projects/7/registry/protection/rules"
```
Example response:
```json
[
{
"id": 1,
"project_id": 7,
"repository_path_pattern": "flightjs/flight0",
"minimum_access_level_for_push": "maintainer",
"minimum_access_level_for_delete": "maintainer"
},
{
"id": 2,
"project_id": 7,
"repository_path_pattern": "flightjs/flight1",
"minimum_access_level_for_push": "maintainer",
"minimum_access_level_for_delete": "maintainer"
},
]
```

View File

@ -1,7 +1,7 @@
---
status: proposed
status: ongoing
creation-date: "2024-02-20"
authors: [ "@bvenker", "@mikolaj_wawrzyniak" ]
authors: [ "@maddievn", "@mikolaj_wawrzyniak", "@dgruzd" ]
coach: [ "@stanhu" ]
approvers: [ "@pwietchner", "@oregand", "@shinya.meda", "@mikolaj_wawrzyniak" ]
owning-stage: "~devops::data stores"
@ -50,7 +50,7 @@ LLMs:
enhance performance but may also increase computational costs due to longer
processing times.
- **Duplicate Contents:** Repetitive content can reduce the diversity of search
results. For instance, if a semantic search yields ten results indicating
results. For instance, if a semantic search yields ten results indicating
"Tom is a president" but the eleventh reveals "Tom lives in the United States,"
solely using the top ten would omit critical information. Filtering out
duplicate content, for example, through Maximal Marginal Relevance (MMR), can
@ -101,7 +101,7 @@ execution of multiple tools and LLM inferences.
Choosing the appropriate search method is pivotal for feature design and UX optimization. Here are common search techniques:
### Semantic Search
### Semantic Search using embeddings
Semantic search shines when handling complex queries that demand an
understanding of the context or intent behind the words, not just the words
@ -114,7 +114,7 @@ related information.
In the realm of semantic search, the K-Nearest Neighbors (KNN) method is
commonly employed to identify data segments that are semantically closer to the
user's input. To measure the semantic proximity, various methods are used:
user's input by using embeddings. To measure the semantic proximity, various methods are used:
- **Cosine Similarity:** Focuses solely on the direction of vectors.
- **L2 Distance (Euclidean Distance):** Takes into account both the direction
@ -137,6 +137,13 @@ approximate nearest neighbors (ANN) search, is a popular strategy for this
purpose. For insights into HNSW's effectiveness, consider reviewing
[benchmarks on its performance in large-scale applications](https://supabase.com/blog/increase-performance-pgvector-hnsw).
Due to the existing framework and scalability of Elasticsearch, embeddings will
be stored on Elasticsearch for large datasets such as
[issues](https://gitlab.com/gitlab-org/gitlab/-/issues/451431), merge requests,
etc. This will be used to perform [Hybrid Search](https://gitlab.com/gitlab-org/gitlab/-/issues/440424)
but will also be useful for other features such as finding duplicates, similar results or
categorizing documents.
### Keyword Search
Keyword search is the go-to method for straightforward, specific queries where
@ -151,6 +158,10 @@ that have a high frequency of the query terms. Its efficiency and directness
make it particularly useful for situations where users expect quick and precise
results based on specific keywords or phrases.
Elasicsearch uses a BM25 algorigthm to perform keyword search.
If one of the existing [indexed document types](../../../integration/advanced_search/elasticsearch.md#advanced-search-index-scopes)
is not covered, a [new document type](../../../development/advanced_search.md#add-a-new-document-type-to-elasticsearch) can be added.
### Hybrid Search
Hybrid search combines the depth of semantic search with the precision of
@ -169,13 +180,22 @@ contrasted with the relative efficiency of [BM25](https://pub.aimind.so/understa
keyword searches, making hybrid search a strategic choice for optimizing
performance across diverse datasets.
The first hybrid search scope is for issues which combines keyword search with kNN matches using embeddings.
### Code Search
Like the other data types above, a source code search task can utilize different
search types, each more suited to address different queries. Currently,
search types, each more suited to address different queries.
Two code searches are available currrently: `Elasticsearch` and `Zoekt`.
Elasticsearch provides blob search which supports [Advanced Search Syntax](../../../drawers/advanced_search_syntax.md).
[Zoekt](../code_search_with_zoekt/index.md) is employed on GitLab.com to provide
exact match keyword search and regular expression search capabilities for source
code. Semantic search and hybrid search functionalities are yet to be
code.
Semantic search and hybrid search functionalities are yet to be
implemented for code.
### ID Search
@ -283,17 +303,3 @@ To read more about the [GitLab Duo Chat PoCs](../gitlab_duo_rag/index.md) conduc
- [PGVector PoC](../gitlab_duo_rag/postgresql.md)
- [Elasticsearch PoC](../gitlab_duo_rag/elasticsearch.md)
- [Google Vertex PoC](../gitlab_duo_rag/vertex_ai_search.md)
## Proposed solution
_Disclaimer: This blueprint is in the first iteration and the chosen solutions could change._
Due to the existing framework and scalability of Elasticsearch, embeddings will
be stored on Elasticsearch for large datasets such as
[issues](https://gitlab.com/gitlab-org/gitlab/-/issues/451431), merge requests,
etc. This will be used to perform [Hybrid Search](https://gitlab.com/gitlab-org/gitlab/-/issues/440424)
but will also be useful for other features such as finding duplicates, similar results or
categorizing documents.
[Vertext AI Search](../gitlab_duo_rag/vertex_ai_search.md) is going to be
implemented to serve GitLab DUO documentation for self-managed instances.

View File

@ -344,6 +344,15 @@ These [predefined variables](../variables/predefined_variables.md)
ensure that your component also works when used on another instance, for example when using
[a GitLab.com component in a self-managed instance](#use-a-gitlabcom-component-in-a-self-managed-instance).
### Do not assume API resources are always public
Ensure that the component and its testing pipeline work also [in a self-managed instance](#use-a-gitlabcom-component-in-a-self-managed-instance).
While some API resources of public projects on GitLab.com could be accessed via unauthenticated requests
on self-managed a component project could be mirrored as private or internal project.
It's important that an access token can optionally be provided via inputs or variables to
authenticate requests on self-managed instances.
### Avoid using global keywords
Avoid using [global keywords](../yaml/index.md#global-keywords) in a component.

View File

@ -45,6 +45,7 @@ There are two places defined variables can be used. On the:
| [`only:variables`](../yaml/index.md#onlyvariables--exceptvariables) (Deprecated) | no | Not applicable | The variable must be in the form of `$variable`. Not supported are the following:<br/><br/>- `CI_ENVIRONMENT_SLUG` variable.<br/>- [Persisted variables](#persisted-variables). |
| [`resource_group`](../yaml/index.md#resource_group) | yes | GitLab | Similar to `environment:url`, but the variables expansion doesn't support the following:<br/>- `CI_ENVIRONMENT_URL`<br/>- [Persisted variables](#persisted-variables). |
| [`rules:changes`](../yaml/index.md#ruleschanges) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. |
| [`rules:changes:compare_to`](../yaml/index.md#ruleschangescompare_to) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. |
| [`rules:exists`](../yaml/index.md#rulesexists) | yes | GitLab | The variable expansion is made by the [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism) in GitLab. |
| [`rules:if`](../yaml/index.md#rulesif) | no | Not applicable | The variable must be in the form of `$variable`. Not supported are the following:<br/><br/>- `CI_ENVIRONMENT_SLUG` variable.<br/>- [Persisted variables](#persisted-variables). |
| [`script`](../yaml/index.md#script) | yes | Script execution shell | The variable expansion is made by the [execution shell environment](#execution-shell-environment). |

View File

@ -1043,8 +1043,6 @@ Use `after_script` to define an array of commands to run last, after a job's `be
- Long commands [split over multiple lines](script.md#split-long-commands).
- [YAML anchors](yaml_optimization.md#yaml-anchors-for-scripts).
CI/CD variables [are supported](../variables/where_variables_can_be_used.md#gitlab-ciyml-file).
**Example of `after_script`**:
```yaml
@ -4201,6 +4199,7 @@ In this example, both jobs have the same behavior.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/293645) in GitLab 15.3 [with a flag](../../administration/feature_flags.md) named `ci_rules_changes_compare`. Enabled by default.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/366412) in GitLab 15.5. Feature flag `ci_rules_changes_compare` removed.
> - Support for CI/CD variables [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/369916) in GitLab 17.2 [with a flag](../../administration/feature_flags.md) named `ci_expand_variables_in_compare_to`. Disabled by default.
Use `rules:changes:compare_to` to specify which ref to compare against for changes to the files
listed under [`rules:changes:paths`](#ruleschangespaths).
@ -4213,6 +4212,8 @@ listed under [`rules:changes:paths`](#ruleschangespaths).
- A tag name, like `tag1` or `refs/tags/tag1`.
- A commit SHA, like `2fg31ga14b`.
CI/CD variables [are supported](../variables/where_variables_can_be_used.md#gitlab-ciyml-file).
**Example of `rules:changes:compare_to`**:
```yaml

View File

@ -8,8 +8,8 @@ info: Any user with at least the Maintainer role can merge updates to this conte
To allow GitLab to scale further we
[decomposed the GitLab application database into multiple databases](https://gitlab.com/groups/gitlab-org/-/epics/6168).
The two databases are `main` and `ci`. GitLab supports being run with either one database or two databases.
On GitLab.com we are using two separate databases.
The main databases are `main`, `ci`, and (optionally) `sec`. GitLab supports being run with one, two, or three databases.
On GitLab.com we are using separate `main` and `ci` databases.
For the purpose of building the [Cells](../../architecture/blueprints/cells/index.md) architecture, we are decomposing
the databases further, to introduce another database `gitlab_main_clusterwide`.
@ -47,6 +47,7 @@ The usage of schema enforces the base class to be used:
- `Geo::TrackingBase` for `gitlab_geo`
- `Gitlab::Database::SharedModel` for `gitlab_shared`
- `PackageMetadata::ApplicationRecord` for `gitlab_pm`
- `Gitlab::Database::SecApplicationRecord` for `gitlab_sec`
### Choose either the `gitlab_main_cell` or `gitlab_main_clusterwide` schema

View File

@ -1156,17 +1156,19 @@ Instead of:
## level
If you can, avoid using `level` in the context of an instance or group.
If you can, avoid using `level` in the context of an instance, project, or group.
Use:
- This setting is turned on for the instance.
- This setting is turned on for the group and its subgroups.
- This setting is turned on for projects.
Instead of:
- This setting is turned on at the instance level.
- This setting is turned on at the group level.
- This is a project-level setting.
## list

View File

@ -456,24 +456,6 @@ end
This script finds tokens that do not have an expiry date, that is, `expires_at` is set to `NULL`. For users who have not yet upgraded to GitLab version 16.0 or later, the token `expires_at` value will be `NULL` and can be used to identify tokens that will be set with an expiration date.
```ruby
# This script finds tokens which do not have an expires_at value set.
# Check for expiring personal access tokens
PersonalAccessToken.owner_is_human.where(expires_at: nil).find_each do |token|
puts "Expires_at is nil for Personal Access Token ID: #{token.id}, User Email: #{token.user.email}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end
# Check for expiring project and group access tokens
PersonalAccessToken.project_access_token.where(expires_at: nil).find_each do |token|
token.user.members.each do |member|
type = member.is_a?(GroupMember) ? 'Group' : 'Project'
puts "Expires_at is nil for #{type} access token in #{type} ID #{member.source_id}, Token ID: #{token.id}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end
end
```
You can use this script in either the [Rails console](../administration/operations/rails_console.md) or the [Rails Runner](../administration/operations/rails_console.md#using-the-rails-runner):
::Tabs
@ -485,6 +467,24 @@ You can use this script in either the [Rails console](../administration/operatio
1. Paste in the entire script.
1. Press <kbd>Enter</kbd>.
```ruby
# This script finds tokens which do not have an expires_at value set.
# Check for expiring personal access tokens
PersonalAccessToken.owner_is_human.where(expires_at: nil).find_each do |token|
puts "Expires_at is nil for Personal Access Token ID: #{token.id}, User Email: #{token.user.email}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end
# Check for expiring project and group access tokens
PersonalAccessToken.project_access_token.where(expires_at: nil).find_each do |token|
token.user.members.each do |member|
type = member.is_a?(GroupMember) ? 'Group' : 'Project'
puts "Expires_at is nil for #{type} access token in #{type} ID #{member.source_id}, Token ID: #{token.id}, Name: #{token.name}, Scopes: #{token.scopes}, Last used: #{token.last_used_at}"
end
end
```
:::TabTitle Rails Runner
1. In your terminal window, connect to your instance.

View File

@ -80,7 +80,10 @@ To enable the Advanced SAST analyzer:
when: never
- if: $SAST_EXCLUDED_ANALYZERS =~ /gitlab-advanced-sast/
when: never
- if: $CI_COMMIT_BRANCH
- if: $CI_PIPELINE_SOURCE == "merge_request_event" # Add the job to merge request pipelines if there's an open merge request.
exists:
- '**/*.py'
- if: $CI_COMMIT_BRANCH # If there's no open merge request, add it to a *branch* pipeline instead.
exists:
- '**/*.py'
```

View File

@ -80,7 +80,6 @@ To restrict group access by IP address:
1. In the **Restrict access by IP address** text box, enter a list of IPv4 or IPv6
address ranges in CIDR notation. This list:
- Has no limit on the number of IP address ranges.
- Has a size limit of 1 GB.
- Applies to both SSH or HTTP authorized IP address ranges. You cannot split
this list by type of authorization.
1. Select **Save changes**.

View File

@ -31,8 +31,8 @@ module Keeps
class QuarantineFlakyTests < ::Gitlab::Housekeeper::Keep
MINIMUM_REMAINING_RATE = 25
QUERY_URL_TEMPLATE = "https://gitlab.com/api/v4/projects/278964/issues/?order_by=updated_at&state=opened&labels[]=test&labels[]=failure::flaky-test&labels[]=%<flakiness_label>s&not[labels][]=QA&not[labels][]=quarantine&per_page=20"
# https://rubular.com/r/WnMxnDPvGGjoGE
EXAMPLE_LINE_REGEX = /\bit (?<description_and_metadata>[\w'",: ]*(?:,\n)?[\w\'",: ]+?) do$/m
# https://rubular.com/r/OoeQIEwPkL1m7E
EXAMPLE_LINE_REGEX = /\bit (?<description_and_metadata>[\w'",: \#\{\}]*(?:,\n)?[\w\'",: ]+?) do$/m
FLAKINESS_LABELS = %w[flakiness::1 flakiness::2].freeze
def each_change

View File

@ -302,6 +302,7 @@ module API
mount ::API::ProjectAvatar
mount ::API::ProjectClusters
mount ::API::ProjectContainerRepositories
mount ::API::ProjectContainerRegistryProtectionRules
mount ::API::ProjectDebianDistributions
mount ::API::ProjectEvents
mount ::API::ProjectExport

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