Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-06-06 15:08:03 +00:00
parent 6e562b7010
commit 22d47e2001
100 changed files with 431 additions and 368 deletions

View File

@ -286,7 +286,6 @@ export default {
'ee/app/assets/javascripts/iterations/components/iteration_form.vue',
'ee/app/assets/javascripts/iterations/components/iteration_report.vue',
'ee/app/assets/javascripts/merge_requests/components/reviewers/approval_summary.vue',
'ee/app/assets/javascripts/notes/components/ai_summary.vue',
'ee/app/assets/javascripts/oncall_schedules/components/add_edit_schedule_form.vue',
'ee/app/assets/javascripts/oncall_schedules/components/oncall_schedule.vue',
'ee/app/assets/javascripts/oncall_schedules/components/rotations/components/rotation_assignee.vue',

View File

@ -26,7 +26,6 @@ Gitlab/FeatureAvailableUsage:
- 'ee/app/helpers/ee/form_helper.rb'
- 'ee/app/helpers/ee/graph_helper.rb'
- 'ee/app/helpers/ee/issues_helper.rb'
- 'ee/app/helpers/ee/lock_helper.rb'
- 'ee/app/helpers/ee/operations_helper.rb'
- 'ee/app/helpers/ee/projects/incidents_helper.rb'
- 'ee/app/helpers/ee/projects_helper.rb'

View File

@ -521,7 +521,6 @@ Layout/LineLength:
- 'ee/app/helpers/ee/integrations_helper.rb'
- 'ee/app/helpers/ee/issues_helper.rb'
- 'ee/app/helpers/ee/labels_helper.rb'
- 'ee/app/helpers/ee/lock_helper.rb'
- 'ee/app/helpers/ee/merge_requests_helper.rb'
- 'ee/app/helpers/ee/mirror_helper.rb'
- 'ee/app/helpers/ee/notes_helper.rb'
@ -4127,7 +4126,6 @@ Layout/LineLength:
- 'spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb'
- 'spec/views/projects/settings/operations/show.html.haml_spec.rb'
- 'spec/views/projects/tags/index.html.haml_spec.rb'
- 'spec/views/projects/tree/show.html.haml_spec.rb'
- 'spec/views/shared/milestones/_issuable.html.haml_spec.rb'
- 'spec/views/shared/projects/_project.html.haml_spec.rb'
- 'spec/views/shared/snippets/_snippet.html.haml_spec.rb'

View File

@ -198,7 +198,6 @@ Lint/UnusedMethodArgument:
- 'ee/app/graphql/types/security_orchestration/security_policy_source_type.rb'
- 'ee/app/graphql/types/vulnerability_detail_type.rb'
- 'ee/app/graphql/types/vulnerability_location_type.rb'
- 'ee/app/helpers/ee/lock_helper.rb'
- 'ee/app/models/boards/epic_board.rb'
- 'ee/app/models/burndown.rb'
- 'ee/app/models/concerns/elastic/application_versioned_search.rb'

View File

@ -58,7 +58,6 @@ Rails/HelperInstanceVariable:
- 'ee/app/helpers/ee/integrations_helper.rb'
- 'ee/app/helpers/ee/kerberos_helper.rb'
- 'ee/app/helpers/ee/labels_helper.rb'
- 'ee/app/helpers/ee/lock_helper.rb'
- 'ee/app/helpers/ee/mirror_helper.rb'
- 'ee/app/helpers/ee/notes_helper.rb'
- 'ee/app/helpers/ee/operations_helper.rb'

View File

@ -241,7 +241,6 @@ RSpec/ContextWording:
- 'ee/spec/helpers/ee/groups/group_members_helper_spec.rb'
- 'ee/spec/helpers/ee/issuables_helper_spec.rb'
- 'ee/spec/helpers/ee/issues_helper_spec.rb'
- 'ee/spec/helpers/ee/lock_helper_spec.rb'
- 'ee/spec/helpers/ee/operations_helper_spec.rb'
- 'ee/spec/helpers/ee/personal_access_tokens_helper_spec.rb'
- 'ee/spec/helpers/ee/projects/security/api_fuzzing_configuration_helper_spec.rb'

View File

@ -31,7 +31,6 @@ RSpec/FactoryBot/AvoidCreate:
- 'ee/spec/helpers/ee/issuables_helper_spec.rb'
- 'ee/spec/helpers/ee/issues_helper_spec.rb'
- 'ee/spec/helpers/ee/labels_helper_spec.rb'
- 'ee/spec/helpers/ee/lock_helper_spec.rb'
- 'ee/spec/helpers/ee/namespaces_helper_spec.rb'
- 'ee/spec/helpers/ee/operations_helper_spec.rb'
- 'ee/spec/helpers/ee/projects/pipeline_helper_spec.rb'
@ -579,7 +578,6 @@ RSpec/FactoryBot/AvoidCreate:
- 'spec/views/projects/settings/merge_requests/show.html.haml_spec.rb'
- 'spec/views/projects/settings/operations/show.html.haml_spec.rb'
- 'spec/views/projects/tags/index.html.haml_spec.rb'
- 'spec/views/projects/tree/show.html.haml_spec.rb'
- 'spec/views/search/_results.html.haml_spec.rb'
- 'spec/views/shared/_label_row.html.haml_spec.rb'
- 'spec/views/shared/issuable/_sidebar.html.haml_spec.rb'

View File

@ -350,7 +350,6 @@ RSpec/FeatureCategory:
- 'ee/spec/helpers/ee/groups/settings_helper_spec.rb'
- 'ee/spec/helpers/ee/hooks_helper_spec.rb'
- 'ee/spec/helpers/ee/labels_helper_spec.rb'
- 'ee/spec/helpers/ee/lock_helper_spec.rb'
- 'ee/spec/helpers/ee/operations_helper_spec.rb'
- 'ee/spec/helpers/ee/profiles_helper_spec.rb'
- 'ee/spec/helpers/ee/projects/incidents_helper_spec.rb'
@ -3847,7 +3846,6 @@ RSpec/FeatureCategory:
- 'spec/views/projects/settings/integrations/edit.html.haml_spec.rb'
- 'spec/views/projects/settings/operations/show.html.haml_spec.rb'
- 'spec/views/projects/tags/index.html.haml_spec.rb'
- 'spec/views/projects/tree/show.html.haml_spec.rb'
- 'spec/views/shared/_label_row.html.haml_spec.rb'
- 'spec/views/shared/_milestones_sort_dropdown.html.haml_spec.rb'
- 'spec/views/shared/gitlab_version/_security_patch_upgrade_alert.html.haml_spec.rb'

View File

@ -180,7 +180,6 @@ RSpec/NamedSubject:
- 'ee/spec/helpers/ee/gitlab_routing_helper_spec.rb'
- 'ee/spec/helpers/ee/groups/group_members_helper_spec.rb'
- 'ee/spec/helpers/ee/groups_helper_spec.rb'
- 'ee/spec/helpers/ee/lock_helper_spec.rb'
- 'ee/spec/helpers/ee/merge_requests_helper_spec.rb'
- 'ee/spec/helpers/ee/operations_helper_spec.rb'
- 'ee/spec/helpers/ee/projects/incidents_helper_spec.rb'

View File

@ -23,7 +23,6 @@ RSpec/ReceiveMessages:
- 'ee/spec/helpers/ee/ide_helper_spec.rb'
- 'ee/spec/helpers/ee/issuables_helper_spec.rb'
- 'ee/spec/helpers/ee/issues_helper_spec.rb'
- 'ee/spec/helpers/ee/lock_helper_spec.rb'
- 'ee/spec/helpers/ee/registrations_helper_spec.rb'
- 'ee/spec/helpers/ee/subscribable_banner_helper_spec.rb'
- 'ee/spec/helpers/ee/users/callouts_helper_spec.rb'
@ -551,7 +550,6 @@ RSpec/ReceiveMessages:
- 'spec/views/projects/settings/operations/show.html.haml_spec.rb'
- 'spec/views/projects/settings/repository/_protected_branches.html.haml_spec.rb'
- 'spec/views/projects/tags/index.html.haml_spec.rb'
- 'spec/views/projects/tree/show.html.haml_spec.rb'
- 'spec/views/shared/_broadcast_message.html.haml_spec.rb'
- 'spec/views/shared/projects/_list.html.haml_spec.rb'
- 'spec/views/shared/projects/_project_card.html.haml_spec.rb'

View File

@ -2,7 +2,6 @@
RSpec/SubjectDeclaration:
Exclude:
- 'ee/spec/finders/app_sec/fuzzing/coverage/corpuses_finder_spec.rb'
- 'ee/spec/helpers/ee/lock_helper_spec.rb'
- 'ee/spec/helpers/nav/new_dropdown_helper_spec.rb'
- 'ee/spec/lib/ee/api/helpers/notes_helpers_spec.rb'
- 'ee/spec/lib/gitlab/expiring_subscription_message_spec.rb'

View File

@ -1 +1 @@
20f07b8be43ca7682d11bc1bafbc814a93d6befd
73881b727a2a17e48dc365784b7f0370f9c62376

View File

@ -1 +1 @@
1ae70272d95c4c8128d8e8eb7c3dd0569f9980e4
9f4dcab5a68db7c3fb30f29fa315b80011449cf0

View File

@ -279,7 +279,7 @@ export default {
:header-height="getDrawerHeaderHeight"
:z-index="$options.DRAWER_Z_INDEX"
:open="drawerOpened"
class="merge-request-review-drawer"
class="merge-request-review-drawer !gl-w-screen !gl-max-w-2xl"
data-testid="review-drawer-toggle"
@close="setDrawerOpened(false)"
>

View File

@ -28,6 +28,11 @@ export default {
required: false,
default: false,
},
preselectAllInputs: {
type: Boolean,
required: false,
default: false,
},
queryRef: {
type: String,
required: true,
@ -78,7 +83,7 @@ export default {
...input,
savedValue,
value: hasSavedValue ? savedValue : input.default,
isSelected: hasSavedValue,
isSelected: hasSavedValue || this.preselectAllInputs,
};
});

View File

@ -1,5 +1,6 @@
<script>
import { GlButton } from '@gitlab/ui';
import { GlButton, GlLink } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { __, s__ } from '~/locale';
import Tracking from '~/tracking';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@ -15,9 +16,11 @@ export default {
browseCatalog: __('CI/CD Catalog'),
help: __('Help'),
jobAssistant: s__('JobAssistant|Job assistant'),
editorA11y: s__('PipelineEditor|Editor accessibility guide'),
},
components: {
GlButton,
GlLink,
},
mixins: [glFeatureFlagMixin(), Tracking.mixin()],
inject: ['ciCatalogPath'],
@ -31,6 +34,13 @@ export default {
required: true,
},
},
computed: {
editorA11yHelpPagePath() {
return helpPagePath('ci/pipeline_editor/_index.md', {
anchor: 'editor-accessibility-options',
});
},
},
methods: {
toggleHelpDrawer() {
if (this.showHelpDrawer) {
@ -61,7 +71,7 @@ export default {
<template>
<div
class="gl-flex gl-flex-col gl-gap-3 gl-border-1 gl-border-solid gl-border-default gl-p-3 md:gl-flex-row"
class="gl-flex gl-flex-col gl-gap-3 gl-border-1 gl-border-solid gl-border-default gl-p-3 md:gl-flex-row md:gl-items-center"
>
<slot></slot>
<gl-button
@ -90,5 +100,12 @@ export default {
>
{{ $options.i18n.jobAssistant }}
</gl-button>
<gl-link
class="gl-p-2 gl-text-center"
:href="editorA11yHelpPagePath"
data-testid="editor-accessibility-link"
>
{{ $options.i18n.editorA11y }}
</gl-link>
</div>
</template>

View File

@ -256,6 +256,7 @@ export default {
</gl-form-group>
<pipeline-inputs-form
emit-modified-only
preselect-all-inputs
:project-path="projectPath"
:query-ref="refQueryParam"
:empty-selection-text="s__('Pipeline|Select inputs to create a new pipeline.')"

View File

@ -237,7 +237,7 @@ export default {
</gl-sprintf>
</div>
</div>
<div class="gl-flex gl-w-full">
<div class="gl-flex gl-w-full gl-justify-between">
<ul
class="merge-request-tabs nav-tabs nav nav-links gl-m-0 gl-flex gl-flex-nowrap gl-border-b-0 gl-p-0"
>
@ -259,9 +259,9 @@ export default {
</gl-link>
</li>
</ul>
<div class="gl-ml-auto gl-hidden gl-items-center lg:gl-flex">
<div class="gl-hidden gl-items-center gl-gap-3 lg:gl-flex">
<discussion-counter :blocks-merge="blocksMerge" hide-options />
<div v-if="isSignedIn" :class="{ 'gl-flex gl-gap-3': isNotificationsTodosButtons }">
<template v-if="isSignedIn">
<todo-widget
:issuable-id="issuableId"
:issuable-iid="issuableIid"
@ -274,8 +274,8 @@ export default {
:full-path="projectPath"
issuable-type="merge_request"
/>
</div>
<submit-review-button v-if="glFeatures.improvedReviewExperience" class="gl-ml-3" />
</template>
<submit-review-button v-if="glFeatures.improvedReviewExperience" />
</div>
</div>
</div>

View File

@ -5,7 +5,6 @@ import { mapGetters as mapVuexGetters } from 'vuex';
import { mapActions, mapState } from 'pinia';
import { throttle } from 'lodash';
import { __ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import {
keysFor,
MR_NEXT_UNRESOLVED_DISCUSSION,
@ -25,7 +24,7 @@ export default {
GlButton,
GlButtonGroup,
},
mixins: [glFeatureFlagsMixin(), discussionNavigation],
mixins: [discussionNavigation],
props: {
blocksMerge: {
type: Boolean,
@ -106,9 +105,6 @@ export default {
return options;
},
isNotificationsTodosButtons() {
return this.glFeatures.notificationsTodosButtons;
},
},
methods: {
...mapActions(useMrNotes, ['toggleAllVisibleDiscussions']),
@ -128,8 +124,6 @@ export default {
:class="{
'gl-bg-feedback-warning': blocksMerge && !allResolved,
'gl-bg-strong': !blocksMerge || allResolved,
'gl-mr-3': !isNotificationsTodosButtons,
'gl-mr-5': isNotificationsTodosButtons,
}"
data-testid="discussions-counter-text"
>

View File

@ -87,10 +87,9 @@ const initCodeDropdown = () => {
const { gitpodEnabled, showWebIdeButton, showGitpodButton, webIdeUrl, gitpodUrl } =
convertObjectPropsToCamelCase(ideData ? JSON.parse(ideData) : {});
const CodeDropdownComponent =
gon.features.directoryCodeDropdownUpdates && gon.features.blobRepositoryVueHeaderApp
? CompactCodeDropdown
: CodeDropdown;
const CodeDropdownComponent = gon.features.directoryCodeDropdownUpdates
? CompactCodeDropdown
: CodeDropdown;
return new Vue({
el: codeDropdownEl,

View File

@ -299,7 +299,7 @@ export default {
:upload-path="uploadPath"
:new-dir-path="newDirPath"
/>
<!-- EE: = render_if_exists 'projects/tree/lock_link' -->
<!-- EE lock directory -->
<lock-directory-button
v-if="!isRoot"
:project-path="projectPath"

View File

@ -197,10 +197,9 @@ export default function setupVueRepositoryList() {
projectId,
} = codeDropdownEl.dataset;
const CodeDropdownComponent =
gon.features.directoryCodeDropdownUpdates && gon.features.blobRepositoryVueHeaderApp
? CompactCodeDropdown
: CodeDropdown;
const CodeDropdownComponent = gon.features.directoryCodeDropdownUpdates
? CompactCodeDropdown
: CodeDropdown;
return new Vue({
el: codeDropdownEl,

View File

@ -58,7 +58,7 @@ function mountSubmitReviewButton(pinia) {
el,
pinia,
render(h) {
return h(SubmitReviewButton, { attrs: { class: 'gl-ml-3' } });
return h(SubmitReviewButton);
},
});
}

View File

@ -1,7 +1,6 @@
<script>
import {
GlLoadingIcon,
GlButton,
GlIcon,
GlDisclosureDropdown,
GlDisclosureDropdownItem,
@ -42,7 +41,6 @@ export default {
},
components: {
GlLoadingIcon,
GlButton,
GlIcon,
GlDisclosureDropdown,
GlDisclosureDropdownItem,
@ -224,7 +222,7 @@ export default {
</script>
<template>
<div class="gl-flex gl-w-full gl-justify-end" data-testid="merge-request-actions">
<div class="gl-self-start" data-testid="merge-request-actions">
<gl-disclosure-dropdown
id="new-actions-header-dropdown"
ref="mrMoreActionsDropdown"
@ -235,31 +233,15 @@ export default {
block
class="gl-w-full"
:auto-close="false"
icon="ellipsis_v"
category="tertiary"
text-sr-only
no-caret
:toggle-text="$options.i18n.mergeRequestActions"
toggle-class="gl-flex"
@shown="showDropdown"
@hidden="hideDropdown"
>
<template #toggle>
<div class="gl-mb-2 gl-min-h-7 sm:!gl-mb-0">
<gl-button
class="gl-new-dropdown-toggle gl-w-full sm:!gl-hidden"
button-text-classes="gl-flex gl-justify-between gl-w-full"
category="secondary"
tabindex="0"
:aria-label="$options.i18n.mergeRequestActions"
>
<span class="">{{ $options.i18n.mergeRequestActions }}</span>
<gl-icon class="dropdown-chevron" name="chevron-down" />
</gl-button>
<gl-button
class="gl-new-dropdown-toggle gl-new-dropdown-icon-only gl-new-dropdown-toggle-no-caret gl-hidden sm:!gl-flex"
category="tertiary"
icon="ellipsis_v"
tabindex="0"
:aria-label="$options.i18n.mergeRequestActions"
:title="$options.i18n.mergeRequestActions"
/>
</div>
</template>
<gl-disclosure-dropdown-group v-if="isLoggedIn && !isNotificationsTodosButtons">
<sidebar-subscriptions-widget
:iid="String(mr.iid)"

View File

@ -840,10 +840,10 @@ export default {
numberOfDiscussionsResolved: this.numberOfDiscussionsResolved,
});
const autosaveKey = getNewWorkItemAutoSaveKey(
this.selectedProjectFullPath,
this.selectedWorkItemTypeName,
);
const autosaveKey = getNewWorkItemAutoSaveKey({
fullPath: this.selectedProjectFullPath,
workItemType: this.selectedWorkItemTypeName,
});
clearDraft(autosaveKey);
} catch {
this.error = this.createErrorText;
@ -863,10 +863,10 @@ export default {
}
},
handleDiscardDraft() {
const autosaveKey = getNewWorkItemAutoSaveKey(
this.selectedProjectFullPath,
this.selectedWorkItemTypeName,
);
const autosaveKey = getNewWorkItemAutoSaveKey({
fullPath: this.selectedProjectFullPath,
workItemType: this.selectedWorkItemTypeName,
});
clearDraft(autosaveKey);
const selectedWorkItemWidgets = this.selectedWorkItemType?.widgetDefinitions || [];

View File

@ -357,7 +357,7 @@ export const setNewWorkItemCache = async ({
const widgets = [];
const autosaveKey = getNewWorkItemAutoSaveKey(fullPath, workItemType);
const autosaveKey = getNewWorkItemAutoSaveKey({ fullPath, workItemType });
const getStorageDraftString = getDraft(autosaveKey);
const draftData = JSON.parse(getDraft(autosaveKey));

View File

@ -196,7 +196,7 @@ export const updateNewWorkItemCache = (input, cache) => {
const newData = cache.readQuery({ query, variables });
const autosaveKey = getNewWorkItemAutoSaveKey(fullPath, workItemType);
const autosaveKey = getNewWorkItemAutoSaveKey({ fullPath, workItemType });
const isQueryDataValid = !isEmpty(newData) && newData?.workspace?.workItem;

View File

@ -297,6 +297,11 @@ export default {
}
return this.workItemsFull;
},
shouldShowList() {
return (
this.hasAnyIssues || this.error || this.initialLoadWasFiltered || this.workItems.length > 0
);
},
detailLoading() {
return this.$apollo.queries.workItemsFull.loading;
},
@ -945,7 +950,7 @@ export default {
<template>
<gl-loading-icon v-if="!isInitialLoadComplete && !error" class="gl-mt-5" size="lg" />
<div v-else-if="hasAnyIssues || error || initialLoadWasFiltered">
<div v-else-if="shouldShowList">
<div v-if="showLocalBoard">
<local-board :work-item-list-data="workItems" @back="showLocalBoard = false" />
</div>

View File

@ -399,10 +399,20 @@ export const makeDrawerUrlParam = (activeItem, fullPath, issuableType = TYPE_ISS
);
};
export const getNewWorkItemAutoSaveKey = (fullPath, workItemType) => {
export const getNewWorkItemAutoSaveKey = ({ fullPath, workItemType }) => {
if (!workItemType || !fullPath) return '';
const queryParamString = new URLSearchParams(window.location.search).toString();
const allowedKeysInQueryParamString = ['vulnerability_id', 'discussion_to_resolve'];
const queryParams = new URLSearchParams(window.location.search);
// Remove extra params from queryParams
const allKeys = Array.from(queryParams.keys());
for (const key of allKeys) {
if (!allowedKeysInQueryParamString.includes(key)) {
queryParams.delete(key);
}
}
const queryParamString = queryParams.toString();
if (queryParamString) {
return `new-${fullPath}-${workItemType.toLowerCase()}-${queryParamString}-draft`;
}

View File

@ -287,8 +287,3 @@ $comparison-empty-state-height: 62px;
width: 12px;
height: 12px;
}
.merge-request-review-drawer {
max-width: 680px;
width: 100%;
}

View File

@ -958,8 +958,6 @@ $diff-file-header-top: 11px;
}
.review-preview-item-content {
width: 100%;
p {
display: block;
width: 100%;

View File

@ -22,7 +22,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
respond_to do |format|
format.html do
render
render 'dashboard/projects/index'
end
format.atom do
load_events

View File

@ -9,7 +9,6 @@ module Groups
push_force_frontend_feature_flag(:work_items, !!group&.work_items_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_beta, !!group&.work_items_beta_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_alpha, !!group&.work_items_alpha_feature_flag_enabled?)
push_force_frontend_feature_flag(:namespace_level_work_items, namespace_work_items_enabled?)
push_force_frontend_feature_flag(:create_group_level_work_items,
!!group&.create_group_level_work_items_feature_flag_enabled?)
push_force_frontend_feature_flag(:glql_integration, !!group&.glql_integration_feature_flag_enabled?)
@ -22,11 +21,11 @@ module Groups
before_action :handle_new_work_item_path, only: [:show]
def index
not_found unless namespace_work_items_enabled?
not_found unless ::Feature.enabled?(:work_item_planning_view, group)
end
def show
not_found unless !!group&.supports_group_work_items?
not_found unless group.supports_group_work_items?
@work_item = ::WorkItems::WorkItemsFinder.new(current_user, group_id: group.id)
.execute.with_work_item_type.find_by_iid(show_params[:iid])
@ -34,15 +33,11 @@ module Groups
private
def namespace_work_items_enabled?
!!group&.namespace_work_items_enabled?
end
# The work_items/:iid route renders a Vue app that takes care of the show and new pages.
def handle_new_work_item_path
return unless show_params[:iid] == 'new'
if namespace_work_items_enabled?
if group.supports_group_work_items?
render :show
else
not_found

View File

@ -40,7 +40,6 @@ class GroupsController < Groups::ApplicationController
push_frontend_feature_flag(:issues_grid_view)
push_frontend_feature_flag(:issues_list_drawer, group)
push_frontend_feature_flag(:work_item_status_feature_flag, group&.root_ancestor)
push_force_frontend_feature_flag(:namespace_level_work_items, group.namespace_work_items_enabled?)
end
before_action only: :merge_requests do

View File

@ -48,7 +48,6 @@ class Projects::BlobController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:inline_blame, @project)
push_frontend_feature_flag(:filter_blob_path, current_user)
push_frontend_feature_flag(:blob_repository_vue_header_app, @project)
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
push_frontend_feature_flag(:directory_code_dropdown_updates, current_user)
push_frontend_feature_flag(:repository_file_tree_browser, @project)

View File

@ -19,7 +19,6 @@ class Projects::TreeController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:inline_blame, @project)
push_frontend_feature_flag(:blob_repository_vue_header_app, @project)
push_frontend_feature_flag(:filter_blob_path, current_user)
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
push_frontend_feature_flag(:directory_code_dropdown_updates, current_user)

View File

@ -16,7 +16,6 @@ class Projects::WorkItemsController < Projects::ApplicationController
push_force_frontend_feature_flag(:glql_load_on_click, !!project&.glql_load_on_click_feature_flag_enabled?)
push_force_frontend_feature_flag(:continue_indented_text, !!project&.continue_indented_text_feature_flag_enabled?)
push_frontend_feature_flag(:work_item_status_feature_flag, project&.root_ancestor)
push_frontend_feature_flag(:namespace_level_work_items, project&.group)
push_frontend_feature_flag(:work_item_planning_view, project&.group)
end

View File

@ -42,7 +42,6 @@ class ProjectsController < Projects::ApplicationController
push_frontend_feature_flag(:edit_branch_rules, @project)
# TODO: We need to remove the FF eventually when we rollout page_specific_styles
push_frontend_feature_flag(:page_specific_styles, current_user)
push_frontend_feature_flag(:blob_repository_vue_header_app, @project)
push_frontend_feature_flag(:filter_blob_path, current_user)
push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks)
push_frontend_feature_flag(:directory_code_dropdown_updates, current_user)

View File

@ -16,7 +16,9 @@ class RootController < Dashboard::ProjectsController
CACHE_CONTROL_HEADER = 'no-store'
def index # rubocop:disable Lint/UselessMethodDefinition -- we need to explicitly define this action for the `skip_before_action`
def index
render('root/index') && return if Feature.enabled?(:personal_homepage, current_user)
super
end
@ -36,6 +38,8 @@ class RootController < Dashboard::ProjectsController
def redirect_logged_user
case current_user.dashboard
when 'projects'
redirect_to(dashboard_projects_path) if Feature.enabled?(:personal_homepage, current_user)
when 'stars'
flash.keep
redirect_to(starred_dashboard_projects_path)

View File

@ -319,15 +319,13 @@ module Types
field :work_item, Types::WorkItemType,
resolver: Resolvers::Namespaces::WorkItemResolver,
experiment: { milestone: '16.4' },
description: 'Find a work item by IID directly associated with the group. Returns `null` if the ' \
'`namespace_level_work_items` feature flag is disabled.'
description: 'Find a work item by IID directly associated with the group.'
field :work_item_state_counts,
Types::WorkItemStateCountsType,
null: true,
experiment: { milestone: '16.7' },
description: 'Counts of work items by state for the namespace. Returns `null` if the ' \
'`namespace_level_work_items` feature flag is disabled.',
description: 'Counts of work items by state for the namespace.',
resolver: Resolvers::Namespaces::WorkItemStateCountsResolver
field :autocomplete_users,

View File

@ -97,8 +97,7 @@ module Types
null: true,
resolver: Resolvers::Namespaces::WorkItemResolver,
experiment: { milestone: '16.10' },
description: 'Find a work item by IID directly associated with the namespace(project or group). Returns ' \
'`null` for group level work items if the `namespace_level_work_items` feature flag is disabled.'
description: 'Find a work item by IID directly associated with the namespace(project or group)'
field :work_item_types, Types::WorkItems::TypeType.connection_type,
resolver: Resolvers::WorkItems::TypesResolver,

View File

@ -20,6 +20,8 @@ module PreferencesHelper
validate_dashboard_choices!(dashboards)
dashboards -= excluded_dashboard_choices
dashboards -= ['homepage'] unless Feature.enabled?(:personal_homepage, current_user)
dashboards.map do |key|
{
# Use `fetch` so `KeyError` gets raised when a key is missing
@ -43,7 +45,8 @@ module PreferencesHelper
todos: _("Your To-Do List"),
issues: _("Assigned issues"),
merge_requests: _("Assigned merge requests"),
operations: _("Operations Dashboard")
operations: _("Operations Dashboard"),
homepage: _("Personal homepage")
}.compact.with_indifferent_access.freeze
end

View File

@ -1115,11 +1115,6 @@ class Group < Namespace
feature_flag_enabled_for_self_or_ancestor?(:glql_load_on_click)
end
# Note: this method is overridden in EE to check the work_item_epics feature flag which also enables this feature
def namespace_work_items_enabled?
::Feature.enabled?(:namespace_level_work_items, self, type: :development)
end
def work_item_epics_list_enabled?
::Feature.enabled?(:work_item_epics_list, root_ancestor, type: :wip)
end

View File

@ -8,7 +8,7 @@ class GroupLabel < Label
validates :group, presence: true
alias_attribute :subject, :group
alias_method :subject, :group
def subject_foreign_key
'group_id'

View File

@ -618,7 +618,8 @@ class MergeRequest < ApplicationRecord
.pick(MergeRequest::Metrics.time_to_merge_expression)
end
alias_attribute :project, :target_project
alias_method :project, :target_project
alias_method :project=, :target_project=
alias_attribute :project_id, :target_project_id
# Currently, `merge_when_pipeline_succeeds` column is used as a flag

View File

@ -8,7 +8,8 @@ class OauthAccessToken < Doorkeeper::AccessToken
validates :expires_in, presence: true
alias_attribute :user, :resource_owner
alias_method :user, :resource_owner
alias_method :user=, :resource_owner=
scope :latest_per_application, -> { select('distinct on(application_id) *').order(application_id: :desc, created_at: :desc) }
scope :preload_application, -> { preload(:application) }

View File

@ -15,7 +15,7 @@ class ProjectLabel < Label
delegate :group, to: :project, allow_nil: true
alias_attribute :subject, :project
alias_method :subject, :project
def subject_foreign_key
'project_id'

View File

@ -411,7 +411,8 @@ class User < ApplicationRecord
issues: 6,
merge_requests: 7,
operations: 8,
followed_user_activity: 9
followed_user_activity: 9,
homepage: 12
}
# User's Project preference

View File

@ -11,17 +11,9 @@ module Groups
finder_params = { group_id: group.id, state: 'opened' }
finder_params[:confidential] = true if confidential_only.present?
finder_params[:issue_types] = issue_types if issue_types.present?
finder_params[:include_descendants] = true
finder_class =
if group.namespace_work_items_enabled?
finder_params[:include_descendants] = true
WorkItems::WorkItemsFinder
else
finder_params[:include_subgroups] = true
IssuesFinder
end
relation = finder_class.new(current_user, finder_params).execute
relation = WorkItems::WorkItemsFinder.new(current_user, finder_params).execute
relation = relation.gfm_autocomplete_search(params[:search]).limit(SEARCH_LIMIT) if params[:search]

View File

@ -76,7 +76,7 @@ module Milestones
# rubocop: disable CodeReuse/ActiveRecord
def update_merge_requests_milestone(old_milestone_id, new_milestone_id)
MergeRequest.where(project: project, milestone_id: old_milestone_id)
MergeRequest.where(project_id: project.id, milestone_id: old_milestone_id)
.update_all(milestone_id: new_milestone_id)
end
# rubocop: enable CodeReuse/ActiveRecord

View File

@ -29,7 +29,7 @@
= f.gitlab_ui_radio_component :session_expire_from_init, true, s_('Settings|Expire from time of session creation')
.form-group
= f.label :remember_me_enabled, _('Remember me'), class: 'label-light'
- remember_me_help_link = help_page_path('user/profile/_index.md', anchor: 'stay-signed-in-for-two-weeks')
- remember_me_help_link = help_page_path('user/profile/_index.md', anchor: 'stay-signed-in-for-one-week')
- remember_me_help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: remember_me_help_link }
= f.gitlab_ui_checkbox_component :remember_me_enabled, _('Allow users to extend their session'), help_text: _("Users can select 'Remember me' on sign-in to keep their session active beyond the session duration. %{link_start}Learn more%{link_end}.").html_safe % { link_start: remember_me_help_link_start, link_end: '</a>'.html_safe }

View File

@ -8,24 +8,19 @@
- if (readme = @repository.readme) && readme.rich_viewer
.tree-holder.gl-mt-5
- if Feature.enabled?(:blob_repository_vue_header_app, project)
#js-repository-blob-header-app{ data: {
project_id: @project.id,
ref: ref,
ref_type: @ref_type.to_s,
breadcrumbs: breadcrumb_data_attributes,
project_root_path: project_path(@project),
project_path: project.full_path,
compare_path: compare_path,
web_ide_button_options: web_ide_button_data.merge(fork_options).to_json,
web_ide_button_default_branch: @project.default_branch_or_main,
escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref),
#js-repository-blob-header-app{ data: {
project_id: @project.id,
ref: ref,
ref_type: @ref_type.to_s,
breadcrumbs: breadcrumb_data_attributes,
project_root_path: project_path(@project),
project_path: project.full_path,
compare_path: compare_path,
web_ide_button_options: web_ide_button_data.merge(fork_options).to_json,
web_ide_button_default_branch: @project.default_branch_or_main,
escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref),
download_links: []
}.merge(vue_readme_header_additional_data) }
- else
.nav-block.mt-0
= render 'projects/tree/tree_header', tree: @tree
}.merge(vue_readme_header_additional_data) }
%article.file-holder.readme-holder{ id: 'readme', class: ("limited-width-container" unless fluid_layout) }
.js-file-title.file-title-flex-parent

View File

@ -1,8 +1,4 @@
- if Feature.enabled?(:blob_repository_vue_header_app, project)
#js-repository-blob-header-app{ data: vue_tree_header_app_data(project, repository, ref, pipeline) }
- else
.nav-block.gl-flex.gl-flex-col.sm:gl-flex-row.gl-items-stretch
= render 'projects/tree/tree_header', tree: tree
#js-repository-blob-header-app{ data: vue_tree_header_app_data(project, repository, ref, pipeline) }
- if project.forked?
#js-fork-info{ data: vue_fork_divergence_data(project, ref) }

View File

@ -6,10 +6,7 @@
- if blob.rich_viewer && blob.extension != 'geojson'
- add_page_startup_api_call local_assigns.fetch(:viewer_url) { url_for(safe_params.merge(viewer: blob.rich_viewer.type, format: :json)) }
- if Feature.enabled?(:blob_repository_vue_header_app, project)
#js-repository-blob-header-app{ data: vue_blob_header_app_data(project, blob, ref) }
- else
= render "projects/blob/breadcrumb", blob: blob
#js-repository-blob-header-app{ data: vue_blob_header_app_data(project, blob, ref) }
- if project.forked?
#js-fork-info{ data: vue_fork_divergence_data(project, ref) }

View File

@ -1,8 +0,0 @@
- project = local_assigns.fetch(:project)
- ref = local_assigns.fetch(:ref, nil)
- root_ref = local_assigns.fetch(:root_ref, nil)
- unless ref.blank? || root_ref == ref
- compare_path = project_compare_index_path(project, from: root_ref, to: ref)
= link_button_to compare_path, class: 'shortcuts-compare', rel: 'nofollow' do
= _('Compare')

View File

@ -1,15 +0,0 @@
.js-mr-more-dropdown{ data: {
merge_request: @merge_request.to_json,
project_path: @project.full_path,
url: merge_request_url(@merge_request),
edit_url: edit_project_merge_request_path(@project, @merge_request),
is_current_user: issuable_author_is_current_user(@merge_request),
is_logged_in: current_user,
can_update_merge_request: can?(current_user, :update_merge_request, @merge_request),
open: @merge_request.open?,
merged: @merge_request.merged?,
source_project_missing: @merge_request.source_project_missing?,
clipboard_text: @merge_request.to_reference(full: true),
report_abuse_path: add_category_abuse_reports_path,
reported_user_id: @merge_request.author.id,
} }

View File

@ -1,4 +1,4 @@
.sm:gl-ml-3.dropdown.gl-dropdown
.dropdown.gl-dropdown.gl-flex-1
#js-check-out-modal{ data: how_merge_modal_data(@merge_request) }
= render Pajamas::ButtonComponent.new(category: :primary, variant: :confirm, button_text_classes: 'gl-inline-flex gl-justify-between gl-w-full', button_options: { class: 'gl-flex gl-self-start gl-w-full sm:gl-w-auto !gl-pr-3', data: { toggle: 'dropdown', testid: 'mr-code-dropdown' } }) do
= _('Code')

View File

@ -12,26 +12,39 @@
- c.with_body do
= _('The source project of this merge request has been removed.')
.detail-page-header.border-bottom-0.gl-block.gl-pt-5.gl-gap-4{ class: "sm:!gl-flex #{'is-merge-request' if !fluid_layout}" }
.detail-page-header-body
%h1.title.gl-heading-1.gl-block.gl-grow.gl-break-anywhere{ class: '!gl-m-0', data: { testid: 'title-content' } }
= markdown_field(@merge_request, :title)
.detail-page-header.gl-flex.gl-pt-5.gl-gap-3.gl-flex-wrap.sm:gl-flex-nowrap.gl-mb-3.sm:gl-mb-0
%h1.title.gl-heading-1.gl-self-center.gl-mb-0.gl-flex-1{ data: { testid: 'title-content' } }
= markdown_field(@merge_request, :title)
- unless hide_gutter_toggle
%div
= render Pajamas::ButtonComponent.new(icon: "chevron-double-lg-left", button_options: { class: "btn-icon gl-float-right gl-block gutter-toggle issuable-gutter-toggle js-sidebar-toggle sm:!gl-hidden" })
.detail-page-header-actions.gl-self-start.is-merge-request.js-issuable-actions.gl-flex.gl-mt-1
.gl-flex.gl-flex-row.gl-gap-3.gl-w-full.sm:gl-w-auto.js-issuable-actions
- if can_update_merge_request
- edit_action_description = _('Edit merge request')
- edit_action_shortcut = 'e'
- edit_button_title = "#{edit_action_description} <kbd class='flat ml-1' aria-hidden=true>#{edit_action_shortcut}</kbd>"
= render Pajamas::ButtonComponent.new(href: edit_project_merge_request_path(@project, @merge_request), button_options: { aria: {label: edit_action_description, keyshortcuts: edit_action_shortcut}, class: "gl-hidden sm:gl-inline-flex gl-self-start has-tooltip js-issuable-edit", data: { html: "true", testid: "edit-title-button" }, title: edit_button_title }) do
= _('Edit')
- if @merge_request.source_project
= render 'projects/merge_requests/code_dropdown'
.gl-flex.gl-flex-col.sm:gl-flex-row.gl-gap-3.gl-w-full.sm:gl-w-auto.gl-mt-2.sm:gl-mt-0
- if @merge_request.source_project
= render 'projects/merge_requests/code_dropdown'
- if current_user
- merge_request_data = { iid: @merge_request.iid, draft: @merge_request.draft, target_project_id: @merge_request.target_project_id }
- if current_user
= render 'projects/merge_requests/close_reopen_draft_report_toggle'
.js-mr-more-dropdown{ data: {
merge_request: merge_request_data.to_json,
project_path: @project.full_path,
url: merge_request_url(@merge_request),
edit_url: edit_project_merge_request_path(@project, @merge_request),
is_current_user: issuable_author_is_current_user(@merge_request),
is_logged_in: current_user,
can_update_merge_request: can?(current_user, :update_merge_request, @merge_request),
open: @merge_request.open?,
merged: @merge_request.merged?,
source_project_missing: @merge_request.source_project_missing?,
clipboard_text: @merge_request.to_reference(full: true),
report_abuse_path: add_category_abuse_reports_path,
reported_user_id: @merge_request.author.id,
} }
- unless hide_gutter_toggle
= render Pajamas::ButtonComponent.new(icon: "chevron-double-lg-left", button_options: { class: "btn-icon gl-h-fit gutter-toggle js-sidebar-toggle lg:!gl-hidden" }) do
%span.sr-only= _('Expand sidebar')

View File

@ -49,19 +49,13 @@
= tab_link_for @merge_request, :diffs do
= _("Changes")
= gl_badge_tag tab_count_display(@merge_request, @diffs_count), { class: 'js-changes-tab-count', data: { gid: @merge_request.to_gid.to_s } }
.gl-flex.gl-flex-wrap.gl-items-center.justify-content-lg-end
.gl-flex.gl-flex-wrap.gl-items-center.gl-gap-3
#js-vue-discussion-counter{ data: { blocks_merge: @project.only_allow_merge_if_all_discussions_are_resolved?.to_s } }
- if !!@issuable_sidebar.dig(:current_user, :id)
.gl-flex.gl-gap-3
.js-sidebar-todo-widget-root{ data: { project_path: @issuable_sidebar[:project_full_path], iid: @issuable_sidebar[:iid], id: @issuable_sidebar[:id] } }
- if notifications_todos_buttons_enabled?
.js-sidebar-subscriptions-widget-root{ data: { full_path: @issuable_sidebar[:project_full_path], iid: @issuable_sidebar[:iid] } }
.js-sidebar-todo-widget-root{ data: { project_path: @issuable_sidebar[:project_full_path], iid: @issuable_sidebar[:iid], id: @issuable_sidebar[:id] } }
- if notifications_todos_buttons_enabled?
.js-sidebar-subscriptions-widget-root{ data: { full_path: @issuable_sidebar[:project_full_path], iid: @issuable_sidebar[:iid] } }
#js-submit-review-button
.gl-ml-auto.gl-items-center.gl-hidden.sm:gl-flex.lg:gl-hidden.gl-ml-3.js-expand-sidebar.gl-absolute.gl-right-5
= render Pajamas::ButtonComponent.new(icon: 'chevron-double-lg-left',
button_options: { class: 'js-sidebar-toggle' }) do
= _('Expand')
.tab-content#diff-notes-app
#js-diff-file-finder
#js-code-navigation

View File

@ -1,21 +0,0 @@
.tree-ref-container.gl-flex.gl-flex-wrap.gl-gap-2.mb-2.mb-md-0
.tree-ref-holder.gl-max-w-26{ data: { testid: 'ref-dropdown-container' } }
#js-tree-ref-switcher{ data: { project_id: @project.id, ref_type: @ref_type.to_s, project_root_path: project_path(@project) } }
#js-repo-breadcrumb{ data: breadcrumb_data_attributes }
#js-blob-controls
.tree-controls
.gl-flex.gl-flex-wrap.gl-gap-3.gl-mb-3.sm:gl-mb-0
= render_if_exists 'projects/tree/lock_link'
= render 'projects/buttons/compare', project: @project, ref: @ref, root_ref: @repository&.root_ref
= render 'projects/find_file_link'
= render 'shared/web_ide_button', blob: nil, css_classes: 'gl-w-full sm:gl-w-auto'
.project-code-holder.gl-hidden.sm:gl-inline-block
= render "projects/buttons/code", dropdown_class: 'dropdown-menu-right', ref: @ref
.project-code-holder.gl-flex.gl-gap-3{ class: 'sm:!gl-hidden' }
= render 'projects/buttons/download', project: @project, ref: @ref
= render "shared/mobile_clone_panel", ref: @ref

View File

@ -0,0 +1 @@
- page_title _("Home")

View File

@ -17,7 +17,7 @@ module BulkImports
project = tracker.entity.project
klass.constantize.where(id: object_ids, project: project).find_each do |object|
klass.constantize.where(id: object_ids, project_id: project.id).find_each do |object|
transform_and_save(object)
end
end

View File

@ -1,9 +0,0 @@
---
name: blob_repository_vue_header_app
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/509327
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/176182
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/513582
milestone: '17.9'
group: group::source code
type: beta
default_enabled: true

View File

@ -1,8 +0,0 @@
---
name: namespace_level_work_items
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127124
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/419186
milestone: '16.3'
type: development
group: group::project management
default_enabled: false

View File

@ -0,0 +1,10 @@
---
name: personal_homepage
description:
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/546151
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/192774
rollout_issue_url:
milestone: '18.1'
group: group::personal productivity
type: wip
default_enabled: false

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class AddIndexToLabelsForTypeProjectIdId < Gitlab::Database::Migration[2.3]
milestone '18.1'
disable_ddl_transaction!
TABLE_NAME = :labels
NEW_INDEX_COLUMNS = [:type, :project_id, :id]
NEW_INDEX_NAME = 'index_labels_on_type_project_id_and_id'
def up
add_concurrent_index TABLE_NAME, NEW_INDEX_COLUMNS, name: NEW_INDEX_NAME
end
def down
remove_concurrent_index_by_name TABLE_NAME, name: NEW_INDEX_NAME
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class RemovePreviousLabelProjectIndex < Gitlab::Database::Migration[2.3]
milestone '18.1'
disable_ddl_transaction!
TABLE_NAME = :labels
OLD_INDEX_COLUMNS = [:type, :project_id]
OLD_INDEX_NAME = 'index_labels_on_type_and_project_id'
def up
remove_concurrent_index_by_name TABLE_NAME, name: OLD_INDEX_NAME
end
def down
add_concurrent_index TABLE_NAME, OLD_INDEX_COLUMNS, name: OLD_INDEX_NAME
end
end

View File

@ -0,0 +1 @@
3c7c1f276df9e9842dff4856929417281ba089c9c3c63ad3e68f20bab86f0d79

View File

@ -0,0 +1 @@
8ab5eadcb7c18477641f32fee83aa34f786b72d8845c58b1e251e314069fa2bc

View File

@ -35738,7 +35738,7 @@ CREATE INDEX index_labels_on_template ON labels USING btree (template) WHERE tem
CREATE INDEX index_labels_on_title_varchar ON labels USING btree (title varchar_pattern_ops);
CREATE INDEX index_labels_on_type_and_project_id ON labels USING btree (type, project_id);
CREATE INDEX index_labels_on_type_project_id_and_id ON labels USING btree (type, project_id, id);
CREATE INDEX index_last_usages_on_last_used_date ON catalog_resource_component_last_usages USING btree (last_used_date);

View File

@ -29703,7 +29703,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
##### `Group.workItem`
Find a work item by IID directly associated with the group. Returns `null` if the `namespace_level_work_items` feature flag is disabled.
Find a work item by IID directly associated with the group.
{{< details >}}
**Introduced** in GitLab 16.4.
@ -29742,7 +29742,7 @@ four standard [pagination arguments](#pagination-arguments):
##### `Group.workItemStateCounts`
Counts of work items by state for the namespace. Returns `null` if the `namespace_level_work_items` feature flag is disabled.
Counts of work items by state for the namespace.
{{< details >}}
**Introduced** in GitLab 16.7.
@ -34037,7 +34037,7 @@ four standard [pagination arguments](#pagination-arguments):
##### `Namespace.workItem`
Find a work item by IID directly associated with the namespace(project or group). Returns `null` for group level work items if the `namespace_level_work_items` feature flag is disabled.
Find a work item by IID directly associated with the namespace(project or group).
{{< details >}}
**Introduced** in GitLab 16.10.
@ -34457,7 +34457,7 @@ four standard [pagination arguments](#pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="organizationworkspacesclusteragentsfilter"></a>`filter` | [`NamespaceClusterAgentFilter!`](#namespaceclusteragentfilter) | Filter the types of cluster agents to return. |
| <a id="organizationworkspacesclusteragentsfilter"></a>`filter` | [`OrganizationClusterAgentFilter!`](#organizationclusteragentfilter) | Filter the types of cluster agents to return. |
### `OrganizationStateCounts`
@ -45663,6 +45663,7 @@ Possible filter types for remote development cluster agents in a namespace.
| Value | Description |
| ----- | ----------- |
| <a id="namespaceclusteragentfilterall"></a>`ALL` | All cluster agents in the namespace that can be used for hosting worksapces. |
| <a id="namespaceclusteragentfilteravailable"></a>`AVAILABLE` | Cluster agents in the namespace that can be used for hosting workspaces. |
| <a id="namespaceclusteragentfilterdirectly_mapped"></a>`DIRECTLY_MAPPED` | Cluster agents that are directly mapped to the given namespace. |
| <a id="namespaceclusteragentfilterunmapped"></a>`UNMAPPED` | Cluster agents within a namespace that are not directly mapped to it. |
@ -45745,6 +45746,15 @@ Enum defining the type of OpenTelemetry metric.
| <a id="opentelemetrymetrictypehistogram_type"></a>`HISTOGRAM_TYPE` | Histogram Type type. |
| <a id="opentelemetrymetrictypesum_type"></a>`SUM_TYPE` | Sum Type type. |
### `OrganizationClusterAgentFilter`
Possible filter types for remote development cluster agents in an organization.
| Value | Description |
| ----- | ----------- |
| <a id="organizationclusteragentfilterall"></a>`ALL` | All cluster agents in the organization that can be used for hosting workspaces. |
| <a id="organizationclusteragentfilterdirectly_mapped"></a>`DIRECTLY_MAPPED` | Cluster agents that are directly mapped to the given organization. |
### `OrganizationGroupProjectDisplay`
Default list view for organization groups and projects.

View File

@ -199,6 +199,17 @@ checkbox appears. Select it to start a new merge request after you commit the ch
![The commit form with a new branch](img/pipeline_editor_commit_v13_8.png)
## Editor accessibility options
The pipeline editor is based on the [Monaco Editor](https://github.com/microsoft/monaco-editor)
which has several [accessibility features](https://github.com/microsoft/monaco-editor/wiki/Monaco-Editor-Accessibility-Guide),
including:
| Feature | Shortcut on Windows or Linux | Shortcut on macOS | Details |
|----------------------------------|-----------------------------------|------------------------------------------------------|---------|
| Keyboard navigation command list | <kbd>f1</kbd> | <kbd>f1</kbd> | A [list of commands](https://github.com/microsoft/monaco-editor/wiki/Monaco-Editor-Accessibility-Guide#keyboard-navigation) that make the editor easier to use without a mouse. |
| Tab trapping | <kbd>Control</kbd> + <kbd>m</kbd> | <kbd>Control</kbd> + <kbd>Shift</kbd> + <kbd>m</kbd> | Enable [tab trapping](https://github.com/microsoft/monaco-editor/wiki/Monaco-Editor-Accessibility-Guide#tab-trapping) to go to the next focusable element on the page instead of inserting a tab character. |
## Troubleshooting
### `Configuration validation currently not available` message

View File

@ -73,3 +73,13 @@ Experimental settings that allow Duo Workflow graph to be swapped. Includes:
Allow users to get access to tools that require approval such as running terminal commands.
`"gitlab.duo.workflow.toolApproval": true`
## Evaluate Workflow
### Running evals
To evaluate your local setup, please refer to [Duo Workflow Tests](https://gitlab.com/gitlab-org/duo-workflow/testing/duo-workflow-tests) repo.
### Comparing results
Once you finish a evaluation and have a experiment ID from LangSmith, compare results using [this notebook](https://gitlab.com/gitlab-org/duo-workflow/testing/notebooks/-/blob/main/notebooks/compare-swe-bench-evals.ipynb?ref_type=heads) from the [Duo Workflow Notebooks](https://gitlab.com/gitlab-org/duo-workflow/testing/notebooks) repo.

View File

@ -244,7 +244,7 @@ The **GitLab Duo (AI)** filter is available when:
- Non-OWASP category in OWASP top 10 grouping [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/442526) in GitLab 17.1 [with a flag](../../../administration/feature_flags.md) named `owasp_top_10_null_filtering`. Disabled by default.
- Non-OWASP category in OWASP top 10 grouping [enabled on GitLab Self-Managed, and GitLab Dedicated](https://gitlab.com/gitlab-org/gitlab/-/issues/463783) in GitLab 17.5.
- Non-OWASP category in OWASP top 10 grouping [generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/463783) in GitLab 17.6. Feature flag `owasp_top_10_null_filtering` removed.
- OWASP 2021 top 10 grouping [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/466034) on GitLab.com and GitLab Dedicated in GitLab 18.1 [with a flag](../../../administration/feature_flags.md) named `vulnerability_report_owasp_2021`. Disabled by default. Feature also requires [advanced vulnerability management](#advanced-vulnerability-management).
- OWASP 2021 top 10 grouping [added](https://gitlab.com/gitlab-org/gitlab/-/issues/466034) on GitLab.com and GitLab Dedicated in GitLab 18.1.
{{< /history >}}

View File

@ -1,6 +1,6 @@
---
stage: AI-powered
group: AI Model Validation
group: AI Framework
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: AI-native features and functionality.
title: GitLab Duo data usage

View File

@ -1,6 +1,6 @@
---
stage: AI-powered
group: AI Model Validation
group: AI Framework
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
title: AI gateway
---

View File

@ -1,6 +1,6 @@
---
stage: AI-powered
group: AI Model Validation
group: AI Framework
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: AI-native features and functionality.
title: GitLab Duo prompt guardrails

View File

@ -478,7 +478,7 @@ To disconnect a sign-in service used for signing in to GitLab:
## Session duration
### Stay signed in for two weeks
### Stay signed in for one week
By default, you are signed out of GitLab after seven days (10080 minutes) of inactivity or until you close your browser
window, whichever comes first.

View File

@ -9,7 +9,7 @@ module Gitlab
attr_accessor :issues, :external_url, :project
alias_attribute :gitlab_project, :project
alias_method :gitlab_project, :project
def initialize(project:, external_url: nil, issues: [])
@project = project

View File

@ -214,7 +214,7 @@ Usage: rake "gitlab:cleanup:list_orphan_job_artifact_final_objects[provider]")
# rubocop: disable Layout/LineLength
MergeRequest
.merged
.where(project: project)
.where(project_id: project.id)
.each_batch(of: batch_size) do |mrs|
matching_mrs = mrs.where(
"merge_params LIKE '%force_remove_source_branch: ''1''%' OR merge_params LIKE '%should_remove_source_branch: ''1''%'"
@ -227,7 +227,8 @@ Usage: rake "gitlab:cleanup:list_orphan_job_artifact_final_objects[provider]")
next unless mr.source_branch_exists? && mr.can_remove_source_branch?(user)
# Ensuring that only this MR exists for the source branch
if MergeRequest.where(project: project).where.not(id: mr.id).where(source_branch: mr.source_branch).exists?
if MergeRequest.where(project_id: project.id).where.not(id: mr.id)
.where(source_branch: mr.source_branch).exists?
next
end

View File

@ -4487,6 +4487,9 @@ msgstr ""
msgid "AdminSelfHostedModels|To remove %{boldStart}%{modelName}%{boldEnd}, you must first remove it from the following AI Feature(s):"
msgstr ""
msgid "AdminSelfHostedModels|View AI-native features"
msgstr ""
msgid "AdminSelfHostedModels|You are about to delete the %{boldStart}%{modelName}%{boldEnd} self-hosted model. This action cannot be undone."
msgstr ""
@ -31216,6 +31219,9 @@ msgstr ""
msgid "History of authentications"
msgstr ""
msgid "Home"
msgstr ""
msgid "Home page URL"
msgstr ""
@ -44700,6 +44706,9 @@ msgstr ""
msgid "Personal access token is required."
msgstr ""
msgid "Personal homepage"
msgstr ""
msgid "Personal namespaces"
msgstr ""
@ -44946,6 +44955,9 @@ msgstr ""
msgid "PipelineEditor|Current content in the Edit tab will be used for the simulation."
msgstr ""
msgid "PipelineEditor|Editor accessibility guide"
msgstr ""
msgid "PipelineEditor|Git push event to the default branch"
msgstr ""
@ -55561,6 +55573,9 @@ msgstr ""
msgid "SecurityOrchestration|Choose exception branches"
msgstr ""
msgid "SecurityOrchestration|Choose optimization preset %{linkStart}Help me choose%{linkEnd}"
msgstr ""
msgid "SecurityOrchestration|Choose specific role"
msgstr ""
@ -56100,6 +56115,9 @@ msgstr ""
msgid "SecurityOrchestration|Scan execution policy"
msgstr ""
msgid "SecurityOrchestration|Scan execution strategy"
msgstr ""
msgid "SecurityOrchestration|Scan will automatically choose a runner to run on because there are no tags exist on runners. You can %{linkStart}create a new tag in settings%{linkEnd}."
msgstr ""
@ -67739,6 +67757,9 @@ msgstr ""
msgid "Violation"
msgstr ""
msgid "Violation Details"
msgstr ""
msgid "VirtualRegistries|API endpoints rate limit"
msgstr ""

View File

@ -534,7 +534,7 @@ tests = [
changed_file: 'ee/app/graphql/resolvers/remote_development/organization/cluster_agents_resolver.rb',
# rubocop:disable Layout/LineLength -- fix CI failures - not sure why other lines in this file don't get errors
expected: %w[
ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_available_filter_arg_spec.rb
ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_all_filter_arg_spec.rb
ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_directly_mapped_filter_arg_spec.rb
ee/spec/requests/api/graphql/remote_development/organization/workspaces_cluster_agents/with_unmapped_filter_arg_spec.rb
]

View File

@ -182,13 +182,59 @@ RSpec.describe RootController, feature_category: :shared do
end
end
context 'who has customized their dashboard setting for personal homepage' do
before do
user.dashboard = 'homepage'
end
context 'with `personal_homepage` feature flag disabled (default)' do
before do
stub_feature_flags(personal_homepage: false)
end
it 'renders the default dashboard' do
get :index
expect(response).to render_template 'dashboard/projects/index'
end
end
context 'with `personal_homepage` feature flag enabled' do
before do
stub_feature_flags(personal_homepage: true)
end
it 'renders the new homepage' do
get :index
expect(response).to render_template 'root/index'
end
end
end
context 'who uses the default dashboard setting', :aggregate_failures do
render_views
context 'with `personal_homepage` feature flag disabled (default)' do
before do
stub_feature_flags(personal_homepage: false)
end
it 'renders the default dashboard' do
get :index
it 'renders the default dashboard' do
get :index
expect(response).to render_template 'dashboard/projects/index'
expect(response).to render_template 'dashboard/projects/index'
end
end
context 'with `personal_homepage` feature flag enabled' do
before do
stub_feature_flags(personal_homepage: true)
end
it 'redirects to the default dashboard' do
get :index
expect(response).to redirect_to dashboard_projects_path
end
end
end
end

View File

@ -8,7 +8,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle', feature_category: :code_r
let(:user) { create(:user) }
context 'on a merge request' do
let(:container) { find('.detail-page-header-actions') }
let(:container) { find('.detail-page-header') }
let(:project) { create(:project, :repository) }
let(:issuable) { create(:merge_request, source_project: project) }
@ -20,6 +20,8 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle', feature_category: :code_r
context 'when user has permission to update', :js do
before do
visit project_merge_request_path(project, issuable)
wait_for_requests
end
context 'close/reopen/report toggle' do

View File

@ -23,7 +23,7 @@ RSpec.describe 'Merge request > User marks merge request as draft', :js, feature
find('#new-actions-header-dropdown button').click
page.within('.detail-page-header-actions') do
page.within('.detail-page-header') do
click_button 'Mark as ready'
end

View File

@ -110,7 +110,7 @@ RSpec.describe 'User views an open merge request', feature_category: :code_revie
it 'renders edit button in preferred language' do
visit(merge_request_path(merge_request))
page.within('.detail-page-header-actions') do
page.within('.detail-page-header') do
expect(page).to have_link('Edit')
end
@ -118,7 +118,7 @@ RSpec.describe 'User views an open merge request', feature_category: :code_revie
visit(merge_request_path(merge_request))
page.within('.detail-page-header-actions') do
page.within('.detail-page-header') do
expect(page).to have_link('Bearbeiten')
end
end

View File

@ -38,8 +38,7 @@ RSpec.describe 'Projects > Files > Open MRs dropdown', :js, feature_category: :s
context 'when feature flags are enabled' do
before do
stub_feature_flags(
filter_blob_path: true,
blob_repository_vue_header_app: true
filter_blob_path: true
)
end
@ -66,8 +65,7 @@ RSpec.describe 'Projects > Files > Open MRs dropdown', :js, feature_category: :s
context 'when feature flags are disabled' do
before do
stub_feature_flags(
filter_blob_path: false,
blob_repository_vue_header_app: false
filter_blob_path: false
)
end

View File

@ -326,6 +326,22 @@ describe('PipelineInputsForm', () => {
});
});
describe('when preselectAllInputs is true', () => {
beforeEach(async () => {
pipelineInputsHandler = jest.fn().mockResolvedValue(mockPipelineInputsResponse);
await createComponent({ props: { preselectAllInputs: true } });
});
it('preselects all inputs', () => {
const updatedSelection = [
{ ...expectedInputs[0], isSelected: true },
{ ...expectedInputs[1], isSelected: true },
{ ...expectedInputs[2], isSelected: true },
];
expect(findInputsTable().props('inputs')).toEqual(updatedSelection);
});
});
describe('savedInputs prop', () => {
beforeEach(async () => {
pipelineInputsHandler = jest.fn().mockResolvedValue(mockPipelineInputsResponse);

View File

@ -38,6 +38,7 @@ describe('CI Editor Header', () => {
const findHelpBtn = () => wrapper.findByTestId('drawer-toggle');
const findCatalogRepoLinkButton = () => wrapper.findByTestId('catalog-repo-link');
const findEditorA11yDocsLinkButton = () => wrapper.findByTestId('editor-accessibility-link');
afterEach(() => {
unmockTracking();
@ -122,4 +123,14 @@ describe('CI Editor Header', () => {
});
});
});
describe('editor accessibility link button', () => {
beforeEach(() => {
createComponent();
});
it('finds the a11y docs button', () => {
expect(findEditorA11yDocsLinkButton().exists()).toBe(true);
});
});
});

View File

@ -127,6 +127,7 @@ describe('Pipeline New Form', () => {
expect(findPipelineInputsForm().props()).toMatchObject({
queryRef: `refs/heads/${defaultProps.refParam}`,
emitModifiedOnly: true,
preselectAllInputs: true,
emptySelectionText: 'Select inputs to create a new pipeline.',
});
});

View File

@ -90,8 +90,15 @@ describe('HeaderArea', () => {
expect(wrapper.exists()).toBe(true);
});
it('renders RefSelector', () => {
expect(findRefSelector().exists()).toBe(true);
describe('Ref selector', () => {
it('renders correctly', () => {
expect(findRefSelector().exists()).toBe(true);
});
it('renders correctly when branch names ending with .json', () => {
createComponent({ props: { refSelectorValue: 'ends-with.json' } });
expect(findRefSelector().exists()).toBe(true);
});
});
it('renders Breadcrumbs component', () => {

View File

@ -268,24 +268,32 @@ describe('work items graphql cache utils', () => {
);
});
it('does not restore cache when localStorage key represents a different route', async () => {
window.location.search = '?foo=bar';
await setNewWorkItemCache(mockNewWorkItemCache);
await waitForPromises();
it.each`
description | locationSearchString | expectedTitle | expectedWidgets
${'restores cache with empty form'} | ${'?vulnerability_id=1'} | ${''} | ${restoredDraftDataWidgetsEmpty}
${'restores cache with empty form'} | ${'?discussion_to_resolve=1'} | ${''} | ${restoredDraftDataWidgetsEmpty}
${'restores cache with draft'} | ${'?type=ISSUE'} | ${mockCreateWorkItemDraftData.workspace.workItem.title} | ${restoredDraftDataWidgets}
`(
'$description when URL params include $locationSearchString',
async ({ locationSearchString, expectedTitle, expectedWidgets }) => {
window.location.search = locationSearchString;
await setNewWorkItemCache(mockNewWorkItemCache);
await waitForPromises();
expect(mockWriteQuery).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
workspace: expect.objectContaining({
workItem: expect.objectContaining({
title: '',
widgets: expect.arrayContaining(restoredDraftDataWidgetsEmpty),
expect(mockWriteQuery).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
workspace: expect.objectContaining({
workItem: expect.objectContaining({
title: expectedTitle,
widgets: expect.arrayContaining(expectedWidgets),
}),
}),
}),
}),
}),
);
});
);
},
);
});
describe('updateCacheAfterCreatingNote', () => {

View File

@ -417,15 +417,35 @@ describe('getNewWorkItemAutoSaveKey', () => {
window.location = originalWindowLocation;
});
it('returns the correct key for a new work item', () => {
const autosaveKey = getNewWorkItemAutoSaveKey('gitlab-org/gitlab', 'issue');
it('returns autosave key for a new work item', () => {
const autosaveKey = getNewWorkItemAutoSaveKey({
fullPath: 'gitlab-org/gitlab',
workItemType: 'issue',
});
expect(autosaveKey).toEqual('new-gitlab-org/gitlab-issue-draft');
});
it('returns the correct key for a new work item when URL params present', () => {
window.location.search = '?foo=bar';
const autosaveKey = getNewWorkItemAutoSaveKey('gitlab-org/gitlab', 'issue');
expect(autosaveKey).toEqual('new-gitlab-org/gitlab-issue-foo=bar-draft');
it('returns autosave key with only allowed params', () => {
window.location.search = '?vulnerability_id=1';
let autosaveKey = getNewWorkItemAutoSaveKey({
fullPath: 'gitlab-org/gitlab',
workItemType: 'issue',
});
expect(autosaveKey).toEqual('new-gitlab-org/gitlab-issue-vulnerability_id=1-draft');
window.location.search = '?discussion_to_resolve=2';
autosaveKey = getNewWorkItemAutoSaveKey({
fullPath: 'gitlab-org/gitlab',
workItemType: 'issue',
});
expect(autosaveKey).toEqual('new-gitlab-org/gitlab-issue-discussion_to_resolve=2-draft');
window.location.search = '?discussion_to_resolve=2&state=opened';
autosaveKey = getNewWorkItemAutoSaveKey({
fullPath: 'gitlab-org/gitlab',
workItemType: 'issue',
});
expect(autosaveKey).toEqual('new-gitlab-org/gitlab-issue-discussion_to_resolve=2-draft');
});
});

View File

@ -88,7 +88,6 @@ RSpec.describe Resolvers::WorkItems::UserWorkItemsResolver, feature_category: :t
context "with group access" do
before do
stub_feature_flags(namespace_level_work_items: true, work_item_epics: true)
stub_licensed_features(epics: true)
end

View File

@ -49,6 +49,16 @@ RSpec.describe PreferencesHelper, feature_category: :shared do
{ text: "Assigned merge requests", value: 'merge_requests' }
]
end
context 'with `personal_homepage` feature flag enabled' do
before do
stub_feature_flags(personal_homepage: true)
end
it 'has an additional option' do
expect(helper.dashboard_choices).to include({ text: "Personal homepage", value: 'homepage' })
end
end
end
describe '#first_day_of_week_choices' do

View File

@ -540,14 +540,6 @@ RSpec.describe 'Create a work item', feature_category: :team_planning do
let(:mutation) { graphql_mutation(:workItemCreate, input.merge('namespacePath' => project.full_path), fields) }
it_behaves_like 'creates work item'
context 'when the namespace_level_work_items feature flag is disabled' do
before do
stub_feature_flags(namespace_level_work_items: false)
end
it_behaves_like 'creates work item'
end
end
end

View File

@ -20,14 +20,15 @@ RSpec.describe 'Group Level Work Items', feature_category: :team_planning do
get work_items_path
expect(response).to have_gitlab_http_status(:ok)
expect(response.body).to have_pushed_frontend_feature_flags(workItemPlanningView: true)
end
context 'when the namespace_level_work_items feature flag is disabled' do
context 'when work_item_planning_view is disabled' do
before do
stub_feature_flags(namespace_level_work_items: false)
stub_feature_flags(work_item_planning_view: false)
end
it 'returns not found' do
it 'renders not found' do
get work_items_path
expect(response).to have_gitlab_http_status(:not_found)

View File

@ -351,6 +351,9 @@ RSpec.configure do |config|
# New approval rules cause tests to fail
# Default false while we make them compatible
stub_feature_flags(v2_approval_rules: false)
# New personal homepage is still a WIP and not functional.
stub_feature_flags(personal_homepage: false)
else
unstub_all_feature_flags
end

View File

@ -654,7 +654,6 @@
- './ee/spec/helpers/ee/issuables_helper_spec.rb'
- './ee/spec/helpers/ee/issues_helper_spec.rb'
- './ee/spec/helpers/ee/labels_helper_spec.rb'
- './ee/spec/helpers/ee/lock_helper_spec.rb'
- './ee/spec/helpers/ee/namespaces_helper_spec.rb'
- './ee/spec/helpers/ee/operations_helper_spec.rb'
- './ee/spec/helpers/ee/personal_access_tokens_helper_spec.rb'

View File

@ -1,62 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'projects/tree/show' do
include Devise::Test::ControllerHelpers
let_it_be(:project) { create(:project, :repository, create_branch: 'bar') }
let(:repository) { project.repository }
let(:ref) { 'master' }
let(:commit) { repository.commit(ref) }
let(:path) { '' }
let(:tree) { repository.tree(commit.id, path) }
before do
stub_feature_flags(blob_repository_vue_header_app: false)
assign(:project, project)
assign(:repository, repository)
allow(view).to receive(:can?).and_return(true)
allow(view).to receive(:can_collaborate_with_project?).and_return(true)
allow(view).to receive_message_chain('user_access.can_push_to_branch?').and_return(true)
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
allow(view).to receive(:current_user).and_return(project.creator)
assign(:id, File.join(ref, path))
assign(:ref, ref)
assign(:path, path)
assign(:last_commit, commit)
assign(:tree, tree)
end
context 'for branch names ending on .json' do
let(:ref) { 'ends-with.json' }
it 'displays correctly' do
render
expect(rendered).to have_css('#js-tree-ref-switcher')
end
end
context 'when on root ref' do
let(:ref) { repository.root_ref }
it 'hides compare button' do
render
expect(rendered).not_to include('Compare')
end
end
context 'when not on root ref' do
let(:ref) { 'bar' }
it 'shows a compare button' do
render
expect(rendered).to include('Compare')
end
end
end