Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-12-30 12:26:31 +00:00
parent 3d62377d24
commit fc32942743
25 changed files with 131 additions and 147 deletions

View File

@ -2,13 +2,12 @@
import {
GlBadge,
GlDisclosureDropdown,
GlDisclosureDropdownItem,
GlTooltipDirective,
GlResizeObserverDirective,
} from '@gitlab/ui';
import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
import { sprintf } from '~/locale';
import { JOB_DROPDOWN, SINGLE_JOB } from '../constants';
import PipelineMiniGraphJobItem from '~/ci/pipeline_mini_graph/job_item.vue';
import { JOB_DROPDOWN } from '../constants';
import JobItem from './job_item.vue';
/**
@ -22,7 +21,7 @@ export default {
JobItem,
GlBadge,
GlDisclosureDropdown,
GlDisclosureDropdownItem,
PipelineMiniGraphJobItem,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -51,7 +50,6 @@ export default {
},
jobItemTypes: {
jobDropdown: JOB_DROPDOWN,
singleJob: SINGLE_JOB,
},
data() {
return {
@ -63,8 +61,8 @@ export default {
computedJobId() {
return this.pipelineId > -1 ? `${this.group.name}-${this.pipelineId}` : '';
},
jobStatusText() {
return this.jobItemTooltip(this.group);
dropdownTooltip() {
return !this.showTooltip ? this.group?.status?.tooltip || this.group?.status?.text : '';
},
placement() {
// MR !49053:
@ -74,32 +72,8 @@ export default {
// https://gitlab.com/gitlab-org/gitlab-ui/-/issues/2615
return this.isMobile ? 'bottom-start' : 'right-start';
},
moreActionsTooltip() {
return !this.showTooltip ? this.jobStatusText : '';
},
},
methods: {
pipelineActionRequestComplete() {
this.$emit('pipelineActionRequestComplete');
},
jobItem(job) {
return {
text: job.name,
href: job.status?.detailsPath,
};
},
jobItemTooltip(job) {
const { tooltip: statusTooltip } = job.status;
const { text: statusText } = job.status;
if (statusTooltip) {
if (this.isDelayedJob) {
return sprintf(statusTooltip, { remainingTime: job.remainingTime });
}
return statusTooltip;
}
return statusText;
},
handleResize() {
this.isMobile = GlBreakpointInstance.getBreakpointSize() === 'xs';
},
@ -117,8 +91,7 @@ export default {
:id="computedJobId"
v-gl-resize-observer="handleResize"
v-gl-tooltip.viewport.left="{ customClass: 'ci-job-component-tooltip' }"
:title="moreActionsTooltip"
class="ci-job-group-dropdown"
:title="dropdownTooltip"
block
fluid-width
:placement="placement"
@ -141,26 +114,13 @@ export default {
</div>
</button>
</template>
<gl-disclosure-dropdown-item
v-for="job in group.jobs"
:key="job.id"
v-gl-tooltip.viewport.left="{
title: jobItemTooltip(job),
customClass: 'ci-job-component-tooltip',
}"
:item="jobItem(job)"
>
<template #list-item>
<job-item
:is-link="false"
:job="job"
:type="$options.jobItemTypes.singleJob"
css-class-job-name="gl-p-3"
hide-tooltip
@pipelineActionRequestComplete="pipelineActionRequestComplete"
/>
</template>
</gl-disclosure-dropdown-item>
<ul class="gl-m-0 gl-w-34 gl-overflow-y-auto gl-p-0" @click.stop>
<pipeline-mini-graph-job-item
v-for="job in group.jobs"
:key="job.id"
:job="job"
@jobActionExecuted="$emit('pipelineActionRequestComplete')"
/>
</ul>
</gl-disclosure-dropdown>
</template>

View File

@ -35,7 +35,7 @@ export default {
};
},
status() {
return this.job.detailedStatus || {};
return this.job.detailedStatus || this.job.status;
},
tooltipText() {
const statusTooltip = capitalizeFirstCharacter(this.status?.tooltip);
@ -49,7 +49,7 @@ export default {
};
</script>
<template>
<gl-disclosure-dropdown-item :item="item" class="ci-job-component" data-testid="job-item">
<gl-disclosure-dropdown-item :item="item" class="ci-job-component" data-testid="ci-job-item">
<template #list-item>
<div class="-gl-my-2 gl-flex gl-items-center gl-justify-between">
<job-name-component

View File

@ -950,6 +950,7 @@ export default {
@deleteWorkItemError="issuesError = __('An error occurred while deleting an issuable.')"
@workItemDeleted="deleteIssuable"
@promotedToObjective="promoteToObjective"
@workItemTypeChanged="updateIssuablesCache($event)"
/>
<issuable-list
v-if="hasAnyIssues"

View File

@ -516,6 +516,7 @@ export function mapWorkItemWidgetsToIssuableFields({
activeItem.title = workItem.title;
activeItem.confidential = workItem.confidential;
activeItem.type = workItem?.workItemType?.name?.toUpperCase();
});
}

View File

@ -175,11 +175,6 @@ export default {
required: false,
default: false,
},
isDrawer: {
type: Boolean,
required: false,
default: false,
},
hideSubscribe: {
type: Boolean,
required: false,
@ -313,7 +308,7 @@ export default {
: this.$options.i18n.confidentialityEnabled;
},
showChangeType() {
return !(this.isEpic || this.isDrawer) && this.glFeatures.workItemsBeta;
return !this.isEpic && this.glFeatures.workItemsBeta;
},
},
methods: {

View File

@ -666,6 +666,7 @@ export default {
},
workItemTypeChanged() {
this.$apollo.queries.workItem.refetch();
this.$emit('workItemTypeChanged', this.workItem);
},
},
WORK_ITEM_TYPE_VALUE_OBJECTIVE,
@ -784,7 +785,6 @@ export default {
:work-item-reference="workItem.reference"
:work-item-create-note-email="workItem.createNoteEmail"
:is-modal="isModal"
:is-drawer="isDrawer"
:work-item-state="workItem.state"
:has-children="hasChildren"
:has-parent="shouldShowAncestors"

View File

@ -260,6 +260,7 @@ export default {
is-drawer
class="work-item-drawer !gl-pt-0 xl:!gl-px-6"
@deleteWorkItem="deleteWorkItem"
@workItemTypeChanged="$emit('workItemTypeChanged', $event)"
v-on="$listeners"
/>
</template>

View File

@ -294,21 +294,13 @@
}
}
.stage-column .ci-job-group-dropdown {
.stage-column {
// stylelint-disable-next-line gitlab/no-gl-class
&,
.gl-new-dropdown-custom-toggle {
width: 100%;
}
// Reset padding, as inner element will
// define padding
// stylelint-disable-next-line gitlab/no-gl-class
.gl-new-dropdown-item-content,
.gl-new-dropdown-item-text-wrapper {
padding: 0;
}
// Set artificial focus on the menu-item to keep
// it consistent with the original dropdown items
// stylelint-disable-next-line gitlab/no-gl-class

View File

@ -50,6 +50,7 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag(:issues_list_drawer, project)
push_frontend_feature_flag(:notifications_todos_buttons, current_user)
push_force_frontend_feature_flag(:glql_integration, project&.glql_integration_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_beta, project&.work_items_beta_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_alpha, project&.work_items_alpha_feature_flag_enabled?)
end
@ -63,7 +64,6 @@ class Projects::IssuesController < Projects::ApplicationController
before_action only: :show do
push_frontend_feature_flag(:work_items_beta, project&.group)
push_force_frontend_feature_flag(:work_items_beta, project&.work_items_beta_feature_flag_enabled?)
push_frontend_feature_flag(:epic_widget_edit_confirmation, project)
push_frontend_feature_flag(:namespace_level_work_items, project&.group)
push_frontend_feature_flag(:work_items_view_preference, current_user)

View File

@ -8,7 +8,8 @@ module Types
abilities :read_issue, :admin_issue, :update_issue, :reopen_issue,
:read_design, :create_design, :destroy_design,
:create_note, :update_design, :admin_issue_relation
:create_note, :update_design, :move_design,
:admin_issue_relation
end
end
end

View File

@ -18,7 +18,7 @@ module Types
:admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki,
:create_pages, :destroy_pages, :read_pages_content, :admin_operations,
:read_merge_request, :read_design, :create_design, :update_design, :destroy_design,
:read_environment, :view_edit_page
:move_design, :read_environment, :view_edit_page
permission_field :create_snippet

View File

@ -36,12 +36,19 @@ module AccessTokensHelper
end
def expires_at_field_data
return {} unless Gitlab::CurrentSettings.require_personal_access_token_expiry?
{
min_date: 1.day.from_now.iso8601
min_date: 1.day.from_now.iso8601,
max_date: max_date_allowed
}
end
private
def max_date_allowed
return unless Gitlab::CurrentSettings.require_personal_access_token_expiry?
::PersonalAccessToken.max_expiration_lifetime_in_days.days.from_now.iso8601
end
end
AccessTokensHelper.prepend_mod

View File

@ -66,14 +66,17 @@ swap:
pop-up: "dialog"
popup: "dialog"
repo: "repository"
root group: "top-level group"
signed in user: "authenticated user"
signed-in user: "authenticated user"
since: "because' or 'after"
source (?:install|installation): self-compiled installation
source (?:installs|installations): self-compiled installations
sub group: "subgroup"
sub-group: "subgroup"
sub-groups: "subgroups"
timezone: "time zone"
top level group: "top-level group"
utiliz(?:es?|ing): "use"
VSCode: "VS Code"
we recommend: "you should"

View File

@ -27147,6 +27147,7 @@ Check permissions for the current user on a issue.
| <a id="issuepermissionscreatedesign"></a>`createDesign` | [`Boolean!`](#boolean) | If `true`, the user can perform `create_design` on this resource. |
| <a id="issuepermissionscreatenote"></a>`createNote` | [`Boolean!`](#boolean) | If `true`, the user can perform `create_note` on this resource. |
| <a id="issuepermissionsdestroydesign"></a>`destroyDesign` | [`Boolean!`](#boolean) | If `true`, the user can perform `destroy_design` on this resource. |
| <a id="issuepermissionsmovedesign"></a>`moveDesign` | [`Boolean!`](#boolean) | If `true`, the user can perform `move_design` on this resource. |
| <a id="issuepermissionsreaddesign"></a>`readDesign` | [`Boolean!`](#boolean) | If `true`, the user can perform `read_design` on this resource. |
| <a id="issuepermissionsreadissue"></a>`readIssue` | [`Boolean!`](#boolean) | If `true`, the user can perform `read_issue` on this resource. |
| <a id="issuepermissionsreopenissue"></a>`reopenIssue` | [`Boolean!`](#boolean) | If `true`, the user can perform `reopen_issue` on this resource. |
@ -33719,6 +33720,7 @@ Returns [`UserMergeRequestInteraction`](#usermergerequestinteraction).
| <a id="projectpermissionsdownloadcode"></a>`downloadCode` | [`Boolean!`](#boolean) | If `true`, the user can perform `download_code` on this resource. |
| <a id="projectpermissionsdownloadwikicode"></a>`downloadWikiCode` | [`Boolean!`](#boolean) | If `true`, the user can perform `download_wiki_code` on this resource. |
| <a id="projectpermissionsforkproject"></a>`forkProject` | [`Boolean!`](#boolean) | If `true`, the user can perform `fork_project` on this resource. |
| <a id="projectpermissionsmovedesign"></a>`moveDesign` | [`Boolean!`](#boolean) | If `true`, the user can perform `move_design` on this resource. |
| <a id="projectpermissionspushcode"></a>`pushCode` | [`Boolean!`](#boolean) | If `true`, the user can perform `push_code` on this resource. |
| <a id="projectpermissionspushtodeleteprotectedbranch"></a>`pushToDeleteProtectedBranch` | [`Boolean!`](#boolean) | If `true`, the user can perform `push_to_delete_protected_branch` on this resource. |
| <a id="projectpermissionsreadcommitstatus"></a>`readCommitStatus` | [`Boolean!`](#boolean) | If `true`, the user can perform `read_commit_status` on this resource. |

View File

@ -1378,6 +1378,13 @@ Do not use:
- GitLab Runner Kubernetes executor, because this can infringe on the Kubernetes trademark.
## language model, large language model
When referring to language models, be precise. Not all language models are large,
and not all models are language models. When in doubt, ask a developer or PM for confirmation.
You can use LLM to refer to a large language model if you spell it out on first use.
## later
Use **later** when talking about version numbers.
@ -1615,6 +1622,10 @@ Use:
- The GitLab model registry supports A, B, and C.
- You can publish a model to your project's model registry.
## models
For usage, see [language models](#language-model-large-language-model).
## n/a, N/A, not applicable
When possible, use **not applicable**. Spelling out the phrase helps non-English speaking users and avoids

View File

@ -708,6 +708,28 @@ Google Workspace Administrator also provides the IdP metadata, Entity ID, and SH
fingerprint. However, GitLab does not need this information to connect to the
Google Workspace SAML application.
### Set up Microsoft Entra ID
1. Sign in to the [Microsoft Entra admin center](https://entra.microsoft.com/).
1. [Create a non-gallery application](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/overview-application-gallery#create-your-own-application).
1. [Configure SSO for that application](https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/add-application-portal-setup-sso).
The following settings in your `gitlab.rb` file correspond to the Microsoft Entra ID fields:
| `gitlab.rb` setting | Microsoft Entra ID field |
| ------------------------------------| ---------------------------------------------- |
| `issuer` | **Identifier (Entity ID)** |
| `assertion_consumer_service_url` | **Reply URL (Assertion Consumer Service URL)** |
| `idp_sso_target_url` | **Login URL** |
| `idp_cert_fingerprint` | **Thumbprint** |
1. Set the following attributes:
- **Unique User Identifier (Name ID)** to `user.objectID`.
- **Name identifier format** to `persistent`. For more information, see how to [manage user SAML identity](../user/group/saml_sso/index.md#manage-user-saml-identity).
- **Additional claims** to [supported attributes](#configure-assertions).
For more information, see an [example configuration page](../user/group/saml_sso/example_saml_config.md#azure-active-directory).
### Set up other IdPs
Some IdPs have documentation on how to use them as the IdP in SAML configurations.
@ -3222,6 +3244,12 @@ such as the following:
For example configurations, see the [notes on specific providers](#set-up-identity-providers).
## Configure SAML with Geo
To configure Geo with SAML, see [Configuring instance-wide SAML](../administration/geo/replication/single_sign_on.md#configuring-instance-wide-saml).
For more information, see [Geo with Single Sign On (SSO)](../administration/geo/replication/single_sign_on.md).
## Glossary
| Term | Description |

View File

@ -120,17 +120,13 @@ module QA
def has_skipped_job_in_group?
within_element('disclosure-content') do
all_elements('ci-job-item', minimum: 1).all? do
has_selector?('.ci-status-icon-skipped')
end
has_selector?('[aria-label="Status: Skipped"]')
end
end
def has_no_skipped_job_in_group?
within_element('disclosure-content') do
all_elements('ci-job-item', minimum: 1).all? do
has_no_selector?('.ci-status-icon-skipped')
end
has_no_selector?('[aria-label="Status: Skipped"]')
end
end

View File

@ -132,7 +132,6 @@ spec/frontend/ci/job_details/components/log/line_header_spec.js
spec/frontend/ci/job_details/components/log/line_spec.js
spec/frontend/ci/job_details/components/log/log_spec.js
spec/frontend/ci/job_details/components/sidebar/job_sidebar_retry_button_spec.js
spec/frontend/ci/pipeline_details/graph/components/job_group_dropdown_spec.js
spec/frontend/ci/pipeline_details/header/components/header_badges_spec.js
spec/frontend/ci/pipeline_editor/components/header/validation_segment_spec.js
spec/frontend/ci/pipeline_editor/components/job_assistant_drawer/accordion_items/rules_item_spec.js

View File

@ -617,7 +617,7 @@ RSpec.describe 'Pipelines', :js, feature_category: :continuous_integration do
wait_for_requests
within first('[data-testid="job-item"]') do
within first('[data-testid="ci-job-item"]') do
expect(find_by_testid('play-icon')).to be_visible
end

View File

@ -1,9 +1,9 @@
import { shallowMount, mount } from '@vue/test-utils';
import { GlDisclosureDropdown, GlDisclosureDropdownItem } from '@gitlab/ui';
import { GlDisclosureDropdown } from '@gitlab/ui';
import PipelineMiniGraphJobItem from '~/ci/pipeline_mini_graph/job_item.vue';
import JobGroupDropdown from '~/ci/pipeline_details/graph/components/job_group_dropdown.vue';
import JobItem from '~/ci/pipeline_details/graph/components/job_item.vue';
import { SINGLE_JOB } from '~/ci/pipeline_details/graph/constants';
describe('job group dropdown component', () => {
const group = {
@ -70,7 +70,7 @@ describe('job group dropdown component', () => {
const findJobItem = () => wrapper.findComponent(JobItem);
const findTriggerButton = () => wrapper.find('button');
const findDisclosureDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
const findDisclosureDropdownItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem);
const findPipelineMiniGraphJobItems = () => wrapper.findAllComponents(PipelineMiniGraphJobItem);
const createComponent = ({ props, mountFn = shallowMount } = {}) => {
wrapper = mountFn(JobGroupDropdown, {
@ -81,14 +81,6 @@ describe('job group dropdown component', () => {
});
};
it('renders dropdown with jobs', () => {
createComponent({ mountFn: mount });
expect(wrapper.findAll('[data-testid="disclosure-content"] > li').length).toBe(
group.jobs.length,
);
});
it('renders dropdown', () => {
createComponent();
@ -125,31 +117,12 @@ describe('job group dropdown component', () => {
it('renders parallel jobs in group', () => {
createComponent({ mountFn: mount });
const [item1, item2] = findDisclosureDropdownItems().wrappers;
const [item1, item2] = findPipelineMiniGraphJobItems().wrappers;
expect(findDisclosureDropdownItems()).toHaveLength(2);
expect(findPipelineMiniGraphJobItems()).toHaveLength(2);
expect(item1.props('item')).toEqual({
text: group.jobs[0].name,
href: group.jobs[0].status.detailsPath,
});
expect(item1.findComponent(JobItem).props()).toMatchObject({
isLink: false,
job: group.jobs[0],
type: SINGLE_JOB,
cssClassJobName: 'gl-p-3',
});
expect(item2.props('item')).toEqual({
text: group.jobs[1].name,
href: group.jobs[1].status.detailsPath,
});
expect(item2.findComponent(JobItem).props()).toMatchObject({
isLink: false,
job: group.jobs[1],
type: SINGLE_JOB,
cssClassJobName: 'gl-p-3',
});
expect(item1.props('job')).toEqual(group.jobs[0]);
expect(item2.props('job')).toEqual(group.jobs[1]);
});
describe('tooltip', () => {

View File

@ -6,17 +6,25 @@ import JobNameComponent from '~/ci/common/private/job_name_component.vue';
import { mockPipelineJob } from './mock_data';
const { detailedStatus, ...mockJobInfo } = mockPipelineJob;
const mockJobDetailedStatus = {
...mockJobInfo,
detailedStatus,
};
const mockJobStatus = {
...mockJobInfo,
status: detailedStatus,
};
describe('JobItem', () => {
let wrapper;
const defaultProps = {
job: mockPipelineJob,
};
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMount(JobItem, {
propsData: {
...defaultProps,
job: mockPipelineJob,
...props,
},
});
@ -25,9 +33,12 @@ describe('JobItem', () => {
const findJobNameComponent = () => wrapper.findComponent(JobNameComponent);
const findJobActionButton = () => wrapper.findComponent(JobActionButton);
describe('when mounted', () => {
describe.each([
['has detailedStatus', mockJobDetailedStatus],
['has status', mockJobStatus],
])('when job contains "%s"', (_, job) => {
beforeEach(() => {
createComponent();
createComponent({ props: { job } });
});
describe('job name', () => {
@ -37,13 +48,13 @@ describe('JobItem', () => {
it('sends the necessary props to the job name component', () => {
expect(findJobNameComponent().props()).toMatchObject({
name: mockPipelineJob.name,
status: mockPipelineJob.detailedStatus,
name: mockJobInfo.name,
status: detailedStatus,
});
});
it('sets the correct tooltip for the job item', () => {
const tooltip = capitalizeFirstCharacter(mockPipelineJob.detailedStatus.tooltip);
const tooltip = capitalizeFirstCharacter(detailedStatus.tooltip);
expect(findJobNameComponent().attributes('title')).toBe(tooltip);
});
@ -57,9 +68,9 @@ describe('JobItem', () => {
it('sends the necessary props to the job action button', () => {
expect(findJobActionButton().props()).toMatchObject({
jobId: mockPipelineJob.id,
jobAction: mockPipelineJob.detailedStatus.action,
jobName: mockPipelineJob.name,
jobId: mockJobInfo.id,
jobAction: detailedStatus.action,
jobName: mockJobInfo.name,
});
});

View File

@ -129,7 +129,6 @@ describe('WorkItemActions component', () => {
hasChildren = false,
canCreateRelatedItem = true,
workItemsBeta = true,
isDrawer = false,
} = {}) => {
wrapper = shallowMountExtended(WorkItemActions, {
isLoggedIn: isLoggedIn(),
@ -160,7 +159,6 @@ describe('WorkItemActions component', () => {
hideSubscribe,
hasChildren,
canCreateRelatedItem,
isDrawer,
},
mocks: {
$toast,
@ -623,11 +621,5 @@ describe('WorkItemActions component', () => {
expect(findChangeTypeButton().exists()).toBe(false);
});
it('hides the action in case of drawer', () => {
createComponent({ isDrawer: true });
expect(findChangeTypeButton().exists()).toBe(false);
});
});
});

View File

@ -7,7 +7,8 @@ RSpec.describe Types::PermissionTypes::Issue do
expected_permissions = [
:read_issue, :admin_issue, :update_issue, :reopen_issue,
:read_design, :create_design, :destroy_design,
:create_note, :update_design, :admin_issue_relation
:create_note, :update_design, :move_design,
:admin_issue_relation
]
expected_permissions.each do |permission|

View File

@ -13,8 +13,8 @@ RSpec.describe Types::PermissionTypes::Project do
:create_merge_request_from, :create_wiki, :push_code, :create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages, :admin_remote_mirror, :create_label,
:update_wiki, :destroy_wiki, :create_pages, :destroy_pages, :read_pages_content,
:read_merge_request, :read_design, :create_design, :update_design, :destroy_design, :read_environment,
:view_edit_page
:read_merge_request, :read_design, :create_design, :update_design, :destroy_design, :move_design,
:read_environment, :view_edit_page
]
expected_permissions.each do |permission|

View File

@ -67,19 +67,29 @@ RSpec.describe AccessTokensHelper, feature_category: :system_access do
end
describe '#expires_at_field_data', :freeze_time do
before do
# Test the CE version of `expires_at_field_data` by satisfying the condition in the EE
# that calls the `super` method.
allow(helper).to receive(:personal_access_token_expiration_policy_enabled?).and_return(false)
end
it 'returns expected hash' do
expect(helper.expires_at_field_data).to eq({
min_date: 1.day.from_now.iso8601
min_date: 1.day.from_now.iso8601,
max_date: 400.days.from_now.iso8601
})
end
context 'when require_personal_access_token_expiry is true' do
context 'when require_personal_access_token_expiry is false' do
before do
stub_application_setting(require_personal_access_token_expiry: false)
end
it 'returns an empty hash' do
expect(helper.expires_at_field_data).to eq({})
expect(helper.expires_at_field_data).to eq({
min_date: 1.day.from_now.iso8601,
max_date: nil
})
end
end
end