From 0a362b6da1865ef32ab52053f157c3a47bfdcf12 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 26 Jun 2025 06:11:26 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../issuable/issuable_bulk_update_actions.js | 7 +++++ .../issuable/issuable_bulk_update_sidebar.js | 18 +++++++++---- .../javascripts/rapid_diffs/diff_file.js | 18 ++++++++++++- .../rapid_diffs/toggle_file/adapter.js | 26 +++++++++++++++---- .../components/status/status_dropdown.vue | 4 +-- .../rapid_diffs/diff_file_component.scss | 4 +++ .../rapid_diffs/diff_file_component.html.haml | 5 ++-- .../issuable/_bulk_update_sidebar.html.haml | 4 ++- doc/administration/gitaly/praefect.md | 2 +- doc/ci/services/mysql.md | 6 ++--- doc/development/packages/dependency_proxy.md | 6 ----- .../policies/pipeline_execution_policies.md | 2 -- doc/user/compliance/audit_event_types.md | 2 +- doc/user/storage_management_automation.md | 4 +-- locale/gitlab.pot | 2 +- spec/components/rapid_diffs/shared.rb | 1 + .../list/user_bulk_edits_issues_spec.rb | 6 ++--- .../merge_requests/user_mass_updates_spec.rb | 4 +-- spec/frontend/rapid_diffs/diff_file_spec.js | 11 +++++++- .../rapid_diffs/toggle_file/adapter_spec.js | 14 +++++++--- .../components/status/status_dropdown_spec.js | 4 +-- 21 files changed, 105 insertions(+), 45 deletions(-) diff --git a/app/assets/javascripts/issuable/issuable_bulk_update_actions.js b/app/assets/javascripts/issuable/issuable_bulk_update_actions.js index 45ea5c4827e..78501398b47 100644 --- a/app/assets/javascripts/issuable/issuable_bulk_update_actions.js +++ b/app/assets/javascripts/issuable/issuable_bulk_update_actions.js @@ -44,6 +44,7 @@ export default { getFormDataAsObject() { const assigneeIds = this.form.find('input[name="update[assignee_ids][]"]').val(); + const statusId = this.form.find('input[name="update[status]"]')?.val(); const formData = { update: { state_event: this.form.find('input[name="update[state_event]"]').val(), @@ -58,6 +59,12 @@ export default { confidential: this.form.find('input[name="update[confidentiality]"]').val(), }, }; + + if (statusId) { + // when the FF is disabled or license check fails the status dropdown is not visible + formData.update.status = statusId; + } + if (assigneeIds) { formData.update.assignee_ids = [assigneeIds]; } diff --git a/app/assets/javascripts/issuable/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable/issuable_bulk_update_sidebar.js index b45271c7fe1..701b8637e69 100644 --- a/app/assets/javascripts/issuable/issuable_bulk_update_sidebar.js +++ b/app/assets/javascripts/issuable/issuable_bulk_update_sidebar.js @@ -73,11 +73,19 @@ export default class IssuableBulkUpdateSidebar { // runtime this block won't execute. if (IS_EE) { import('ee_else_ce/sidebar/mount_sidebar') - .then(({ mountEpicDropdown, mountHealthStatusDropdown, mountIterationDropdown }) => { - mountEpicDropdown(); - mountHealthStatusDropdown(); - mountIterationDropdown(); - }) + .then( + ({ + mountEpicDropdown, + mountHealthStatusDropdown, + mountIterationDropdown, + mountSidebarCustomStatusWidget, + }) => { + mountEpicDropdown(); + mountHealthStatusDropdown(); + mountIterationDropdown(); + mountSidebarCustomStatusWidget(); + }, + ) .catch(() => {}); } } diff --git a/app/assets/javascripts/rapid_diffs/diff_file.js b/app/assets/javascripts/rapid_diffs/diff_file.js index c6ef6feb192..55cba43292b 100644 --- a/app/assets/javascripts/rapid_diffs/diff_file.js +++ b/app/assets/javascripts/rapid_diffs/diff_file.js @@ -18,6 +18,9 @@ export class DiffFile extends HTMLElement { trigger; /** @type {Object} Storage for intermediate state used by adapters */ sink = {}; + _hookTeardowns = { + [events.MOUNTED]: new Set(), + }; static findByFileHash(hash) { return document.querySelector(`diff-file[id="${hash}"]`); @@ -37,11 +40,19 @@ export class DiffFile extends HTMLElement { this.observeVisibility(); // eslint-disable-next-line no-underscore-dangle this.trigger = this._trigger.bind(this); - this.trigger(events.MOUNTED); + this.trigger(events.MOUNTED, this.registerMountedTeardowns.bind(this)); this.dispatchEvent(new CustomEvent(DIFF_FILE_MOUNTED, { bubbles: true })); } disconnectedCallback() { + // eslint-disable-next-line no-underscore-dangle + const mountedTeardowns = this._hookTeardowns[events.MOUNTED]; + mountedTeardowns.forEach((cb) => { + cb(); + }); + mountedTeardowns.clear(); + // eslint-disable-next-line no-underscore-dangle + this._hookTeardowns = undefined; // app might be missing if the file was destroyed before mounting // for example: changing view settings in the middle of the streaming if (this.app) this.unobserveVisibility(); @@ -60,6 +71,11 @@ export class DiffFile extends HTMLElement { this.adapters.forEach((adapter) => adapter[event]?.call?.(this.adapterContext, ...args)); } + registerMountedTeardowns(callback) { + // eslint-disable-next-line no-underscore-dangle + this._hookTeardowns[events.MOUNTED].add(callback); + } + observeVisibility() { if (!this.adapters.some((adapter) => adapter[events.VISIBLE] || adapter[events.INVISIBLE])) return; diff --git a/app/assets/javascripts/rapid_diffs/toggle_file/adapter.js b/app/assets/javascripts/rapid_diffs/toggle_file/adapter.js index 72380aa9e72..25002a4cc5a 100644 --- a/app/assets/javascripts/rapid_diffs/toggle_file/adapter.js +++ b/app/assets/javascripts/rapid_diffs/toggle_file/adapter.js @@ -1,4 +1,8 @@ -import { COLLAPSE_FILE, EXPAND_FILE } from '~/rapid_diffs/events'; +import { COLLAPSE_FILE, EXPAND_FILE, MOUNTED } from '~/rapid_diffs/events'; + +function getDetails(root) { + return root.querySelector('[data-file-body]'); +} function getOppositeToggleButton(clicked) { const isOpened = clicked.dataset.opened; @@ -12,15 +16,13 @@ function getOppositeToggleButton(clicked) { function collapse(root = this.diffElement) { // eslint-disable-next-line no-param-reassign root.dataset.collapsed = true; - // eslint-disable-next-line no-param-reassign - root.querySelector('[data-file-body]').hidden = true; + getDetails(root).removeAttribute('open'); } function expand(root = this.diffElement) { // eslint-disable-next-line no-param-reassign delete root.dataset.collapsed; - // eslint-disable-next-line no-param-reassign - root.querySelector('[data-file-body]').hidden = false; + getDetails(root).open = true; } function stopTransition(element) { @@ -51,4 +53,18 @@ export const ToggleFileAdapter = { [COLLAPSE_FILE]() { collapse.call(this); }, + [MOUNTED](onUnmounted) { + const details = getDetails(this.diffElement); + const handleToggle = () => { + if (details.open) { + expand.call(this); + } else { + collapse.call(this); + } + }; + details.addEventListener('toggle', handleToggle); + onUnmounted(() => { + details.removeEventListener('toggle', handleToggle); + }); + }, }; diff --git a/app/assets/javascripts/sidebar/components/status/status_dropdown.vue b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue index 69ec4214712..faa62ce0814 100644 --- a/app/assets/javascripts/sidebar/components/status/status_dropdown.vue +++ b/app/assets/javascripts/sidebar/components/status/status_dropdown.vue @@ -27,8 +27,8 @@ export default { }, }, i18n: { - dropdownTitle: __('Change status'), - defaultDropdownText: __('Select status'), + dropdownTitle: __('Change state'), + defaultDropdownText: __('Select state'), resetText: __('Reset'), }, statusDropdownOptions, diff --git a/app/assets/stylesheets/components/rapid_diffs/diff_file_component.scss b/app/assets/stylesheets/components/rapid_diffs/diff_file_component.scss index 6d2868af9ea..79e730faab4 100644 --- a/app/assets/stylesheets/components/rapid_diffs/diff_file_component.scss +++ b/app/assets/stylesheets/components/rapid_diffs/diff_file_component.scss @@ -14,6 +14,10 @@ --rd-diff-file-border-radius: #{$gl-border-radius-base}; } +.rd-diff-file-details > summary { + display: none; +} + .rd-diff-file[data-virtual]:not([data-collapsed]) { // header top/bottom borders + body border $total-borders: constants.$diff-file-border-width * 3; diff --git a/app/components/rapid_diffs/diff_file_component.html.haml b/app/components/rapid_diffs/diff_file_component.html.haml index ebdd43d7d38..9d515514ffc 100644 --- a/app/components/rapid_diffs/diff_file_component.html.haml +++ b/app/components/rapid_diffs/diff_file_component.html.haml @@ -3,8 +3,9 @@ %diff-file.rd-diff-file-component{ id: id, data: { testid: 'rd-diff-file', file_data: file_data.to_json } } %article.rd-diff-file{ data: { virtual: virtual? }, style: ("--total-rows: #{total_rows}" if virtual?), aria: { labelledby: heading_id } } = header || default_header - -# extra wrapper needed so content-visibility: hidden doesn't require removing border or other styles - %div{ data: { file_body: '' } } + -# extra wrapper needed so hidden state doesn't require removing border or other styles + %details.rd-diff-file-details{ open: true, data: { file_body: '' } } + %summary> .rd-diff-file-body = render viewer_component.new(diff_file: @diff_file) %diff-file-mounted diff --git a/app/views/shared/issuable/_bulk_update_sidebar.html.haml b/app/views/shared/issuable/_bulk_update_sidebar.html.haml index ac051cc84a5..8acfd548618 100644 --- a/app/views/shared/issuable/_bulk_update_sidebar.html.haml +++ b/app/views/shared/issuable/_bulk_update_sidebar.html.haml @@ -11,9 +11,11 @@ = _('Update selected') = render Pajamas::ButtonComponent.new(button_options: { class: 'js-bulk-update-menu-hide gl-float-right' }) do = _('Cancel') + - if is_issue + = render_if_exists 'shared/issuable/status_dropdown', parent: @project.group .block.js-status-dropdown-container .title - = _('Status') + = _('State') .js-status-dropdown .block .title diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md index 0b607f0f055..4bc8b369573 100644 --- a/doc/administration/gitaly/praefect.md +++ b/doc/administration/gitaly/praefect.md @@ -206,7 +206,7 @@ not be replicated. {{< /alert >}} These instructions help set up a single PostgreSQL database, which creates a single point of failure. To avoid this, you can configure your own clustered -PostgreSQL. Support for PostgreSQL replication and failover using the Linux package is proposed in [epic 7814](https://gitlab.com/groups/gitlab-org/-/epics/7814). +PostgreSQL. Clustered database support for other databases (for example, Praefect and Geo databases) is proposed in [issue 7292](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/7292). diff --git a/doc/ci/services/mysql.md b/doc/ci/services/mysql.md index 5e47ff00749..d500e4cd58b 100644 --- a/doc/ci/services/mysql.md +++ b/doc/ci/services/mysql.md @@ -45,12 +45,12 @@ For more information, see [GitLab CI/CD variables](../variables/_index.md). ```yaml variables: # Configure mysql environment variables (https://hub.docker.com/_/mysql/) - MYSQL_DATABASE: $MYSQL_DATABASE - MYSQL_ROOT_PASSWORD: $MYSQL_ROOT_PASSWORD + MYSQL_DATABASE: $MYSQL_DB + MYSQL_ROOT_PASSWORD: $MYSQL_PASS ``` The MySQL container uses `MYSQL_DATABASE` and `MYSQL_ROOT_PASSWORD` to connect to the database. - Pass these values by using variables (`$MYSQL_DB` and `$MYSQL_PASS`), + Pass these values by using [GitLab CI/CD variables](../variables/_index.md) (`$MYSQL_DB` and `$MYSQL_PASS` in the example above), [rather than calling them directly](https://gitlab.com/gitlab-org/gitlab/-/issues/30178). 1. Configure your application to use the database, for example: diff --git a/doc/development/packages/dependency_proxy.md b/doc/development/packages/dependency_proxy.md index 1d1db16086b..096706406bf 100644 --- a/doc/development/packages/dependency_proxy.md +++ b/doc/development/packages/dependency_proxy.md @@ -8,12 +8,6 @@ title: Dependency Proxy The Dependency Proxy is a pull-through-cache for public registry images from DockerHub. This document describes how this feature is constructed in GitLab. -{{< alert type="note" >}} - -Support for private registry images is proposed in [issue 331741](https://gitlab.com/gitlab-org/gitlab/-/issues/331741). - -{{< /alert >}} - ## Container registry The Dependency Proxy for the container registry acts a stand-in for a remote container registry. In our case, diff --git a/doc/user/application_security/policies/pipeline_execution_policies.md b/doc/user/application_security/policies/pipeline_execution_policies.md index d3116cb12f5..a7ca52d8453 100644 --- a/doc/user/application_security/policies/pipeline_execution_policies.md +++ b/doc/user/application_security/policies/pipeline_execution_policies.md @@ -809,8 +809,6 @@ To ensure that both pipeline execution policies and scan execution policies are - Consider using a different strategy for pipeline execution policies, such as `inject_policy`. - If you must use `override_project_ci`, include the scanner templates that you require in your pipeline execution policy to maintain the desired security scans. -Support for improvements in the integration between these policy types is proposed in [issue 504434](https://gitlab.com/gitlab-org/gitlab/-/issues/504434). - ## Examples These examples demonstrate what you can achieve with pipeline execution policies. diff --git a/doc/user/compliance/audit_event_types.md b/doc/user/compliance/audit_event_types.md index 0bf8dfad658..78d9af2078d 100644 --- a/doc/user/compliance/audit_event_types.md +++ b/doc/user/compliance/audit_event_types.md @@ -683,7 +683,7 @@ Audit event types belong to the following product categories. | Type name | Event triggered when | Saved to database | Introduced in | Scope | |:----------|:---------------------|:------------------|:--------------|:------| | [`email_confirmation_sent`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129261) | Users add or change an email address and it must be confirmed | {{< icon name="dotted-circle" >}} No | GitLab [16.3](https://gitlab.com/gitlab-org/gitlab/-/issues/377625) | User | -| [`remove_ssh_key`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65615) | A SSH key is removed | {{< icon name="check-circle" >}} Yes | GitLab [14.1](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) | User | +| [`remove_ssh_key`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65615) | An SSH key is removed from a user's profile. Group scope was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/195390) for enterprise users in GitLab 18.2. | {{< icon name="check-circle" >}} Yes | GitLab [14.1](https://gitlab.com/gitlab-org/gitlab/-/issues/220127) | User, Group | | [`user_admin_status_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65168) | A user is either made an administrator or removed as an administrator | {{< icon name="check-circle" >}} Yes | GitLab [14.1](https://gitlab.com/gitlab-org/gitlab/-/issues/323905) | User | | [`user_auditor_status_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136456) | A user is either made an auditor or removed as an auditor | {{< icon name="check-circle" >}} Yes | GitLab [16.6](https://gitlab.com/gitlab-org/gitlab/-/issues/430235) | User | | [`user_email_address_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/2103) | A user updates their email address | {{< icon name="check-circle" >}} Yes | GitLab [10.1](https://gitlab.com/gitlab-org/gitlab-ee/issues/1370) | User | diff --git a/doc/user/storage_management_automation.md b/doc/user/storage_management_automation.md index 3fde3a157b3..d490f37697f 100644 --- a/doc/user/storage_management_automation.md +++ b/doc/user/storage_management_automation.md @@ -605,7 +605,7 @@ Support for creating a retention policy for job logs is proposed in [issue 37471 ### Delete old pipelines -Pipelines do not add to the overall storage usage, but if required you can automate their deletion. +Pipelines do not add to the overall storage usage, but if required you can [automate their deletion](../ci/pipelines/settings.md#automatic-pipeline-cleanup). To delete pipelines based on a specific date, specify the `created_at` key. You can use the date to calculate the difference between the current date and @@ -802,8 +802,6 @@ the `created_at` attribute to implement a similar algorithm that compares the jo {{< /tabs >}} -Automatic deletion of old pipelines is proposed in [issue 338480](https://gitlab.com/gitlab-org/gitlab/-/issues/338480). - ### List expiry settings for job artifacts To manage artifact storage, you can update or configure when an artifact expires. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 6279832daf8..031f2f5b94e 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12501,7 +12501,7 @@ msgstr "" msgid "Change severity" msgstr "" -msgid "Change status" +msgid "Change state" msgstr "" msgid "Change subscription" diff --git a/spec/components/rapid_diffs/shared.rb b/spec/components/rapid_diffs/shared.rb index 9d64601f899..f4d9fe8fe9a 100644 --- a/spec/components/rapid_diffs/shared.rb +++ b/spec/components/rapid_diffs/shared.rb @@ -24,6 +24,7 @@ RSpec.shared_context "with diff file component tests" do render_component expect(page).to have_selector(web_component_selector) expect(page).to have_selector("#{web_component_selector}-mounted") + expect(page).to have_selector("details[data-file-body]") end it "renders server data" do diff --git a/spec/features/issues/list/user_bulk_edits_issues_spec.rb b/spec/features/issues/list/user_bulk_edits_issues_spec.rb index 718fce806db..a0298098b39 100644 --- a/spec/features/issues/list/user_bulk_edits_issues_spec.rb +++ b/spec/features/issues/list/user_bulk_edits_issues_spec.rb @@ -19,13 +19,13 @@ RSpec.describe 'Multiple issue updating from issues#index', :js, feature_categor sign_in(user) end - context 'status' do + context 'state' do it 'sets to closed', :js do visit project_issues_path(project) click_button 'Bulk edit' check 'Select all' - select_from_listbox('Closed', from: 'Select status') + select_from_listbox('Closed', from: 'Select state') click_update_issues_button expect(page).to have_selector('.issue', count: 0) @@ -37,7 +37,7 @@ RSpec.describe 'Multiple issue updating from issues#index', :js, feature_categor click_button 'Bulk edit' check 'Select all' - select_from_listbox('Open', from: 'Select status') + select_from_listbox('Open', from: 'Select state') click_update_issues_button expect(page).to have_selector('.issue', count: 0) diff --git a/spec/features/merge_requests/user_mass_updates_spec.rb b/spec/features/merge_requests/user_mass_updates_spec.rb index 9f2843235bb..2b938cb68d7 100644 --- a/spec/features/merge_requests/user_mass_updates_spec.rb +++ b/spec/features/merge_requests/user_mass_updates_spec.rb @@ -51,7 +51,7 @@ RSpec.describe 'Merge requests > User mass updates', :js, feature_category: :cod click_button 'Bulk edit' - expect(page).not_to have_button 'Select status' + expect(page).not_to have_button 'Select state' end end @@ -115,7 +115,7 @@ RSpec.describe 'Merge requests > User mass updates', :js, feature_category: :cod def change_status(text) click_button 'Bulk edit' check 'Select all' - select_from_listbox(text, from: 'Select status') + select_from_listbox(text, from: 'Select state') click_update_merge_requests_button end diff --git a/spec/frontend/rapid_diffs/diff_file_spec.js b/spec/frontend/rapid_diffs/diff_file_spec.js index 51eb549d15b..2b033ee06c5 100644 --- a/spec/frontend/rapid_diffs/diff_file_spec.js +++ b/spec/frontend/rapid_diffs/diff_file_spec.js @@ -12,6 +12,7 @@ describe('DiffFile Web Component', () => { `; let app; let adapter; + let adapterMountedCleanup; const getDiffElement = () => document.querySelector('[id=foo]'); const getWebComponentElement = () => document.querySelector('diff-file'); @@ -29,7 +30,10 @@ describe('DiffFile Web Component', () => { }, visible: jest.fn(), invisible: jest.fn(), - mounted: jest.fn(), + mounted: jest.fn((onUnmounted) => { + adapterMountedCleanup = jest.fn(); + onUnmounted(adapterMountedCleanup); + }), }); const initRapidDiffsApp = (currentAdapter = createDefaultAdapter(), appData = {}) => { @@ -77,6 +81,10 @@ describe('DiffFile Web Component', () => { customElements.define('diff-file', DiffFile); }); + beforeEach(() => { + adapterMountedCleanup = undefined; + }); + it('observes diff element', () => { mount(); expect(app.observe).toHaveBeenCalledWith(getWebComponentElement()); @@ -98,6 +106,7 @@ describe('DiffFile Web Component', () => { const element = getWebComponentElement(); document.body.innerHTML = ''; expect(app.unobserve).toHaveBeenCalledWith(element); + expect(adapterMountedCleanup).toHaveBeenCalled(); }); it('can self replace', () => { diff --git a/spec/frontend/rapid_diffs/toggle_file/adapter_spec.js b/spec/frontend/rapid_diffs/toggle_file/adapter_spec.js index ffa4e7c3f29..8dae0ebc9f8 100644 --- a/spec/frontend/rapid_diffs/toggle_file/adapter_spec.js +++ b/spec/frontend/rapid_diffs/toggle_file/adapter_spec.js @@ -37,7 +37,7 @@ describe('Diff File Toggle Behavior', () => { -
+
`; @@ -67,19 +67,19 @@ describe('Diff File Toggle Behavior', () => { delegatedClick(hide); - expect(body.hidden).toEqual(true); + expect(body.open).toEqual(false); expect(document.activeElement).toEqual(show); }); it('collapses file', () => { get('file').trigger(COLLAPSE_FILE); - expect(get('body').hidden).toEqual(true); + expect(get('body').open).toEqual(false); expect(get('file').diffElement.dataset.collapsed).toEqual('true'); }); it('expands file', () => { get('file').trigger(EXPAND_FILE); - expect(get('body').hidden).toEqual(false); + expect(get('body').open).toEqual(true); expect(get('file').diffElement.dataset.collapsed).not.toEqual('true'); }); @@ -99,4 +99,10 @@ describe('Diff File Toggle Behavior', () => { tick(); expect(get('hide').style.transition).toBe(''); }); + + it('removes events listeners from body', () => { + const spy = jest.spyOn(get('body'), 'removeEventListener'); + get('file').remove(); + expect(spy).toHaveBeenCalledWith('toggle', expect.any(Function)); + }); }); diff --git a/spec/frontend/sidebar/components/status/status_dropdown_spec.js b/spec/frontend/sidebar/components/status/status_dropdown_spec.js index 923b171e763..23408798e16 100644 --- a/spec/frontend/sidebar/components/status/status_dropdown_spec.js +++ b/spec/frontend/sidebar/components/status/status_dropdown_spec.js @@ -26,7 +26,7 @@ describe('SubscriptionsDropdown component', () => { }); it('renders default text', () => { - expect(findDropdown().props('toggleText')).toBe('Select status'); + expect(findDropdown().props('toggleText')).toBe('Select state'); }); it('renders dropdown items with `isSelected` prop set to `false`', () => { @@ -74,7 +74,7 @@ describe('SubscriptionsDropdown component', () => { expect(dropdownItems.at(0).props('isSelected')).toBe(false); expect(dropdownItems.at(1).props('isSelected')).toBe(false); - expect(findDropdown().props('toggleText')).toBe('Select status'); + expect(findDropdown().props('toggleText')).toBe('Select state'); }); }); });