Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-04-30 06:11:52 +00:00
parent 696edc7f23
commit efe8d31390
35 changed files with 566 additions and 179 deletions

View File

@ -1 +1 @@
4586553167d1b1ce3932b76ea5b8721e860dada2
d389b47d972835c6b4f5dbacf07f7965361d0c8f

View File

@ -128,10 +128,11 @@ export default {
<gl-sprintf
:message="
__(
'The subject will be used as the title of the new issue, and the message will be the description. %{quickActionsLinkStart}Quick actions%{quickActionsLinkEnd} and styling with %{markdownLinkStart}Markdown%{markdownLinkEnd} are supported.',
'The subject will be used as the title of the new %{name}, and the message will be the description. %{quickActionsLinkStart}Quick actions%{quickActionsLinkEnd} and styling with %{markdownLinkStart}Markdown%{markdownLinkEnd} are supported.',
)
"
>
<template #name>{{ issuableName }}</template>
<template #quickActionsLink="{ content }">
<gl-link :href="quickActionsHelpPath" target="_blank">{{ content }}</gl-link>
</template>

View File

@ -116,6 +116,7 @@ export async function mountIssuesListApp() {
wiNewCommentTemplatePaths,
hasLinkedItemsEpicsFeature,
timeTrackingLimitToHours,
hasSubepicsFeature,
} = el.dataset;
return new Vue({
@ -200,7 +201,7 @@ export async function mountIssuesListApp() {
issuesListPath: wiIssuesListPath,
labelsManagePath: wiLabelsManagePath,
reportAbusePath: wiReportAbusePath,
hasSubepicsFeature: false,
hasSubepicsFeature: parseBoolean(hasSubepicsFeature),
hasLinkedItemsEpicsFeature: parseBoolean(hasLinkedItemsEpicsFeature),
commentTemplatePaths: JSON.parse(wiNewCommentTemplatePaths),
timeTrackingLimitToHours: parseBoolean(timeTrackingLimitToHours),

View File

@ -261,6 +261,7 @@ export default {
:work-item-iid="childItemIid"
:work-item-web-url="childItemWebUrl"
/>
<slot name="child-contents"></slot>
<span
:id="`statusIcon-${childItem.id}`"
class="gl-cursor-help"

View File

@ -28,6 +28,7 @@ export default {
GlAlert,
},
mixins: [glFeatureFlagMixin()],
inject: ['hasSubepicsFeature'],
actionCancel: {
text: __('Cancel'),
},
@ -352,7 +353,9 @@ export default {
this.warningMessage = '';
this.valueNotPresentWarning = '';
if (this.hasParent) {
const isEpicWithSubepicsFeature =
this.parentWorkItemType === WORK_ITEM_TYPE_NAME_EPIC && this.hasSubepicsFeature;
if (this.hasParent && !isEpicWithSubepicsFeature) {
this.warningMessage = sprintfWorkItem(
s__(
'WorkItem|Parent item type %{parentWorkItemType} is not supported on %{workItemType}. Remove the parent item to change type.',

View File

@ -1,5 +1,6 @@
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import WorkItemLinkChildContents from 'ee_else_ce/work_items/components/shared/work_item_link_child_contents.vue';
import { __, s__ } from '~/locale';
import { createAlert } from '~/alert';
import { WORK_ITEM_TYPE_NAME_TASK } from '../../constants';
@ -7,7 +8,6 @@ import { findHierarchyWidget, getDefaultHierarchyChildrenCount, getItems } from
import toggleHierarchyTreeChildMutation from '../../graphql/client/toggle_hierarchy_tree_child.mutation.graphql';
import isExpandedHierarchyTreeChildQuery from '../../graphql/client/is_expanded_hierarchy_tree_child.query.graphql';
import getWorkItemTreeQuery from '../../graphql/work_item_tree.query.graphql';
import WorkItemLinkChildContents from '../shared/work_item_link_child_contents.vue';
import WorkItemChildrenLoadMore from '../shared/work_item_children_load_more.vue';
export default {
@ -283,13 +283,9 @@ export default {
:child-item="childItem"
:can-update="canUpdate"
:class="childItemClass"
:parent-work-item-id="issuableGid"
:work-item-type="workItemType"
:show-labels="showLabels"
:show-closed="showClosed"
:work-item-full-path="workItemFullPath"
:show-weight="shouldShowWeight"
:is-active="isActive"
@click="$emit('click', $event)"
@removeChild="$emit('removeChild', childItem)"
/>

View File

@ -8,7 +8,7 @@ import { defaultSortableOptions, DRAG_DELAY } from '~/sortable/constants';
import * as Sentry from '~/sentry/sentry_browser_wrapper';
import { sortableStart, sortableEnd } from '~/sortable/utils';
import WorkItemLinkChildContents from '../shared/work_item_link_child_contents.vue';
import WorkItemLinkChildContents from 'ee_else_ce/work_items/components/shared/work_item_link_child_contents.vue';
import removeLinkedItemsMutation from '../../graphql/remove_linked_items.mutation.graphql';
import addLinkedItemsMutation from '../../graphql/add_linked_items.mutation.graphql';

View File

@ -145,10 +145,21 @@ module IssuesHelper
is_signed_in: current_user.present?.to_s,
rss_path: url_for(safe_params.merge(rss_url_options)),
sign_in_path: new_user_session_path,
wi: work_items_data(namespace, current_user)
wi: work_items_data(namespace, current_user),
has_subepics_feature: has_subepics_feature?(namespace).to_s
}
end
def has_subepics_feature?(namespace)
if namespace.is_a?(Group)
return namespace.licensed_feature_available?(:subepics)
elsif namespace.respond_to?(:group) && namespace.group
return namespace.group.licensed_feature_available?(:subepics)
end
false
end
def has_issue_date_filter_feature?(namespace, current_user)
enabled_for_user = Feature.enabled?(:issue_date_filter, current_user)
return true if enabled_for_user

View File

@ -1011,6 +1011,10 @@
- 1
- - vulnerabilities_namespace_statistics_adjustment
- 1
- - vulnerabilities_namespace_statistics_process_group_transfer_events
- 1
- - vulnerabilities_namespace_statistics_process_project_transfer_events
- 1
- - vulnerabilities_process_archived_events
- 1
- - vulnerabilities_process_bulk_dismissed_events

View File

@ -9,14 +9,6 @@ description: The SHA referencing changes to individual designs made using the De
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9801
milestone: '11.10'
gitlab_schema: gitlab_main_cell
desired_sharding_key:
namespace_id:
references: namespaces
backfill_via:
parent:
foreign_key: issue_id
table: issues
sharding_key: namespace_id
belongs_to: issue
desired_sharding_key_migration_job_name: BackfillDesignManagementVersionsNamespaceId
sharding_key:
namespace_id: namespaces
table_size: small

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
class UpdateAiTroubleshootJobEventsProjectFk < Gitlab::Database::Migration[2.3]
include Gitlab::Database::PartitioningMigrationHelpers::ForeignKeyHelpers
disable_ddl_transaction!
milestone '18.0'
def up
with_lock_retries do
remove_foreign_key_if_exists(:ai_troubleshoot_job_events, column: :project_id, reverse_lock_order: true)
end
add_concurrent_partitioned_foreign_key :ai_troubleshoot_job_events, :projects, column: :project_id,
on_delete: :cascade, reverse_lock_order: true
end
def down
with_lock_retries do
remove_foreign_key_if_exists(:ai_troubleshoot_job_events, column: :project_id, reverse_lock_order: true)
end
add_concurrent_partitioned_foreign_key :ai_troubleshoot_job_events, :projects, column: :project_id, on_delete: nil,
reverse_lock_order: true
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddIndexToProjectRequirementStatusNamespaceProjectId < Gitlab::Database::Migration[2.3]
milestone '18.0'
disable_ddl_transaction!
INDEX_NAMESPACE_PROJECT_ID_DESC = 'i_project_requirement_statuses_on_namespace_id_project_id'
def up
add_concurrent_index :project_requirement_compliance_statuses, [:namespace_id, :project_id, :id],
order: { project_id: :asc, id: :asc }, using: :btree, name: INDEX_NAMESPACE_PROJECT_ID_DESC
end
def down
remove_concurrent_index_by_name :project_requirement_compliance_statuses, INDEX_NAMESPACE_PROJECT_ID_DESC
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddIndexToProjectRequirementStatusNamespaceRequirementId < Gitlab::Database::Migration[2.3]
milestone '18.0'
disable_ddl_transaction!
INDEX_NAMESPACE_REQUIREMENT_ID_DESC = 'i_project_requirement_statuses_on_namespace_id_requirement_id'
def up
add_concurrent_index :project_requirement_compliance_statuses, [:namespace_id, :compliance_requirement_id, :id],
order: { compliance_requirement_id: :asc, id: :asc }, using: :btree, name: INDEX_NAMESPACE_REQUIREMENT_ID_DESC
end
def down
remove_concurrent_index_by_name :project_requirement_compliance_statuses, INDEX_NAMESPACE_REQUIREMENT_ID_DESC
end
end

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
class AddIndexToProjectRequirementStatusNamespaceFrameworkId < Gitlab::Database::Migration[2.3]
milestone '18.0'
disable_ddl_transaction!
INDEX_NAMESPACE_FRAMEWORK_ID_DESC = 'i_project_requirement_statuses_on_namespace_id_framework_id'
def up
add_concurrent_index :project_requirement_compliance_statuses, [:namespace_id, :compliance_framework_id, :id],
order: { compliance_framework_id: :asc, id: :asc }, using: :btree, name: INDEX_NAMESPACE_FRAMEWORK_ID_DESC
end
def down
remove_concurrent_index_by_name :project_requirement_compliance_statuses, INDEX_NAMESPACE_FRAMEWORK_ID_DESC
end
end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
class AddDesignManagementVersionsNamespaceIdNotNullConstraint < Gitlab::Database::Migration[2.2]
disable_ddl_transaction!
milestone '18.0'
def up
add_not_null_constraint :design_management_versions, :namespace_id
end
def down
remove_not_null_constraint :design_management_versions, :namespace_id
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class AddBtreeIndexOnTraversalIdsVulNamespaceStatistics < Gitlab::Database::Migration[2.2]
disable_ddl_transaction!
milestone '18.0'
INDEX_NAME = 'index_vuln_namespace_statistics_btree_traversal_ids'
def up
add_concurrent_index :vulnerability_namespace_statistics, :traversal_ids, unique: true, name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :vulnerability_namespace_statistics, name: INDEX_NAME
end
end

View File

@ -0,0 +1 @@
3edbf476db3fbbc6005ce957db22db0f6b6060e38c1d5ac77c1d3d73f1343d0e

View File

@ -0,0 +1 @@
51c23d9955d9a94bc84e9e40e5f7a7b24b35b6e5f02596c29c4da2693e1fc8ff

View File

@ -0,0 +1 @@
03f74b80d288bf570e1e8547bab8e992031c4b100a73e4606652bf9f3fd124e9

View File

@ -0,0 +1 @@
c25d19ea0cc74680d5dd48331e2cd8540492bedf4940a775bd33b43abadfed4d

View File

@ -0,0 +1 @@
32c7f68dee6145388f4deac62338aa9c37d36d0bcfc827e4054491f006f5bb85

View File

@ -0,0 +1 @@
a214e8bff3d1aca417ea6f0b6c6c50114830486286966dea83bfceea1df4eeb3

View File

@ -13753,7 +13753,8 @@ CREATE TABLE design_management_versions (
issue_id bigint,
created_at timestamp with time zone NOT NULL,
author_id bigint,
namespace_id bigint
namespace_id bigint,
CONSTRAINT check_1d0291f47a CHECK ((namespace_id IS NOT NULL))
);
CREATE SEQUENCE design_management_versions_id_seq
@ -32990,6 +32991,12 @@ CREATE UNIQUE INDEX i_pm_package_versions_on_package_id_and_version ON pm_packag
CREATE UNIQUE INDEX i_pm_packages_purl_type_and_name ON pm_packages USING btree (purl_type, name);
CREATE INDEX i_project_requirement_statuses_on_namespace_id_framework_id ON project_requirement_compliance_statuses USING btree (namespace_id, compliance_framework_id, id);
CREATE INDEX i_project_requirement_statuses_on_namespace_id_project_id ON project_requirement_compliance_statuses USING btree (namespace_id, project_id, id);
CREATE INDEX i_project_requirement_statuses_on_namespace_id_requirement_id ON project_requirement_compliance_statuses USING btree (namespace_id, compliance_requirement_id, id);
CREATE INDEX i_project_requirement_statuses_on_namespace_id_updated_at_id ON project_requirement_compliance_statuses USING btree (namespace_id, updated_at DESC, id DESC);
CREATE INDEX i_protected_branch_unprotect_access_levels_protected_branch_nam ON protected_branch_unprotect_access_levels USING btree (protected_branch_namespace_id);
@ -37746,6 +37753,8 @@ CREATE INDEX index_vuln_namespace_hist_statistics_for_traversal_ids_update ON vu
CREATE UNIQUE INDEX index_vuln_namespace_historical_statistics_traversal_ids_date ON vulnerability_namespace_historical_statistics USING btree (traversal_ids, date);
CREATE UNIQUE INDEX index_vuln_namespace_statistics_btree_traversal_ids ON vulnerability_namespace_statistics USING btree (traversal_ids);
CREATE INDEX index_vuln_namespace_statistics_gin_traversal_ids ON vulnerability_namespace_statistics USING gin (traversal_ids);
CREATE UNIQUE INDEX index_vuln_namespace_statistics_on_namespace_id ON vulnerability_namespace_statistics USING btree (namespace_id);
@ -44146,7 +44155,7 @@ ALTER TABLE ONLY external_status_checks
ADD CONSTRAINT fk_rails_1f5a8aa809 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ai_troubleshoot_job_events
ADD CONSTRAINT fk_rails_1fb7e812da FOREIGN KEY (project_id) REFERENCES projects(id);
ADD CONSTRAINT fk_rails_1fb7e812da FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY dora_daily_metrics
ADD CONSTRAINT fk_rails_1fd07aff6f FOREIGN KEY (environment_id) REFERENCES environments(id) ON DELETE CASCADE;

View File

@ -13015,7 +13015,8 @@ Input type: `WorkspaceCreateInput`
| <a id="mutationworkspacecreatemaxhoursbeforetermination"></a>`maxHoursBeforeTermination` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Deprecated:** Field is not used. Deprecated in GitLab 17.9. |
| <a id="mutationworkspacecreateprojectid"></a>`projectId` | [`ProjectID!`](#projectid) | ID of the project that will provide the Devfile for the created workspace. |
| <a id="mutationworkspacecreateprojectref"></a>`projectRef` | [`String`](#string) | Project repo git ref. |
| <a id="mutationworkspacecreatevariables"></a>`variables` | [`[WorkspaceVariableInput!]`](#workspacevariableinput) | Variables to inject into the workspace. |
| <a id="mutationworkspacecreatevariables"></a>`variables` {{< icon name="warning-solid" >}} | [`[WorkspaceVariableInput!]`](#workspacevariableinput) | **Deprecated:** Argument is renamed to workspace_variables. Deprecated in GitLab 18.0. |
| <a id="mutationworkspacecreateworkspacevariables"></a>`workspaceVariables` {{< icon name="warning-solid" >}} | [`[WorkspaceVariableInput!]`](#workspacevariableinput) | **Deprecated:** **Status**: Experiment. Introduced in GitLab 18.0. |
#### Fields
@ -28696,7 +28697,7 @@ four standard [pagination arguments](#pagination-arguments):
##### `Group.projectComplianceRequirementsStatus`
Compliance standards adherence for the projects in a group and its subgroups.
Compliance statuses for the projects in a group and its subgroups.
{{< details >}}
**Introduced** in GitLab 17.10.
@ -28714,6 +28715,7 @@ four standard [pagination arguments](#pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="groupprojectcompliancerequirementsstatusfilters"></a>`filters` | [`GroupProjectRequirementComplianceStatusInput`](#groupprojectrequirementcompliancestatusinput) | Filters applied when retrieving compliance requirement statuses. |
| <a id="groupprojectcompliancerequirementsstatusorderby"></a>`orderBy` | [`ProjectComplianceRequirementStatusOrderBy`](#projectcompliancerequirementstatusorderby) | Field used to sort compliance requirement statuses. |
##### `Group.projectComplianceStandardsAdherence`
@ -30502,7 +30504,18 @@ Represents an instance-level LDAP link.
| <a id="ldapadminrolelinkcn"></a>`cn` | [`String`](#string) | Common Name (CN) of the LDAP group. |
| <a id="ldapadminrolelinkfilter"></a>`filter` | [`String`](#string) | Search filter for the LDAP group. |
| <a id="ldapadminrolelinkid"></a>`id` | [`ID!`](#id) | ID of the LDAP link. |
| <a id="ldapadminrolelinkprovider"></a>`provider` | [`String!`](#string) | LDAP provider for the LDAP link. |
| <a id="ldapadminrolelinkprovider"></a>`provider` | [`LdapProvider!`](#ldapprovider) | LDAP provider for the LDAP link. |
### `LdapProvider`
Represents a LDAP provider.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="ldapproviderid"></a>`id` | [`String`](#string) | ID of the LDAP provider. |
| <a id="ldapproviderlabel"></a>`label` | [`String`](#string) | Display name of the LDAP provider. |
### `LfsObjectRegistry`
@ -44994,6 +45007,16 @@ Compliance status of the project control.
| <a id="projectcompliancecontrolstatuspass"></a>`PASS` | Pass. |
| <a id="projectcompliancecontrolstatuspending"></a>`PENDING` | Pending. |
### `ProjectComplianceRequirementStatusOrderBy`
Values for order_by field for project requirement statuses.
| Value | Description |
| ----- | ----------- |
| <a id="projectcompliancerequirementstatusorderbyframework"></a>`FRAMEWORK` | Order by frameworks. |
| <a id="projectcompliancerequirementstatusorderbyproject"></a>`PROJECT` | Order by projects. |
| <a id="projectcompliancerequirementstatusorderbyrequirement"></a>`REQUIREMENT` | Order by requirements. |
### `ProjectFeatureAccessLevel`
Access level of a project feature.

View File

@ -11,7 +11,7 @@ We have developed a number of utilities to help ease development:
Refer to [`merge_hash.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/utils/merge_hash.rb):
- Deep merges an array of hashes:
- Deep merges an array of elements which can be hashes, arrays, or other objects:
```ruby
Gitlab::Utils::MergeHash.merge(

View File

@ -235,8 +235,7 @@ For more information, see [issue 480328](https://gitlab.com/gitlab-org/gitlab/-/
## Issues to be aware of when upgrading to 17.8
- In GitLab 17.8, three new secrets have been added to support the new encryption framework (started to be used in 17.9).
If you have a multi-node configuration, follow the steps relevant to your installation from
the [17.8.0](#1780) section below.
If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets).
- Migration failures when upgrading to GitLab 17.8.
@ -266,8 +265,7 @@ For more information, see [issue 480328](https://gitlab.com/gitlab-org/gitlab/-/
## Issues to be aware of when upgrading to 17.9
- In GitLab 17.8, three new secrets have been added to support the new encryption framework (started to be used in 17.9).
If you have a multi-node configuration, follow the steps relevant to your installation from
the [17.9.0](#1790) section below.
If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets).
- Runner tags missing when upgrading to GitLab 17.9
@ -289,14 +287,12 @@ For more information, see [issue 480328](https://gitlab.com/gitlab-org/gitlab/-/
## Issues to be aware of when upgrading to 17.10
- In GitLab 17.8, three new secrets have been added to support the new encryption framework (started to be used in 17.9).
If you have a multi-node configuration, follow the steps relevant to your installation from
the [17.9.0](#1790) section below.
If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets).
## Issues to be aware of when upgrading to 17.11
- In GitLab 17.8, three new secrets have been added to support the new encryption framework (started to be used in 17.9).
If you have a multi-node configuration, follow the steps relevant to your installation from
the [17.9.0](#1790) section below.
If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets).
## 17.11.0
@ -308,9 +304,7 @@ In GitLab 17.8, three new secrets have been added to support the new encryption
- `active_record_encryption_deterministic_key`
- `active_record_encryption_key_derivation_salt`
**If you have a multi-node configuration, you must ensure these secrets are the same on all nodes.** Otherwise, the application automatically generates the missing secrets at startup.
Follow the steps relevant to your installation from the [17.9.0](#1790) section below.
If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets).
## 17.10.0
@ -322,9 +316,7 @@ In GitLab 17.8, three new secrets have been added to support the new encryption
- `active_record_encryption_deterministic_key`
- `active_record_encryption_key_derivation_salt`
**If you have a multi-node configuration, you should ensure these secrets are the same on all nodes.** Otherwise, the application automatically generates the missing secrets at startup.
Follow the steps relevant to your installation from the [17.9.0](#1790) section below.
If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets).
## 17.9.0
@ -336,71 +328,7 @@ In GitLab 17.8, three new secrets have been added to support the new encryption
- `active_record_encryption_deterministic_key`
- `active_record_encryption_key_derivation_salt`
**If you have a multi-node configuration, you should ensure these secrets are the same on all nodes.** Otherwise, the application automatically generates the missing secrets at startup.
{{< tabs >}}
{{< tab title="Linux package (Omnibus)" >}}
1. Optional. If possible, put your instance in [maintenance mode](../../administration/maintenance_mode/_index.md) (otherwise if the steps 3 and 6 return `true`, it's fine).
1. Delete all `CloudConnector::Keys` records:
```shell
gitlab-rails r 'CloudConnector::Keys.delete_all'
```
1. Check if any records with encrypted attributes exist:
```shell
gitlab-rails r 'Rails.application.eager_load!; puts ApplicationRecord.descendants.select { |d| d.encrypted_attributes.present? }.index_with { |model| model.count }.values.all?(&:zero?)'
```
If the result is `true`, you can proceed to the next step. Otherwise, we need to check what are the existing records
and decide if we can delete them before proceeding further.
1. Pick one Sidekiq or Rails node as a reference node from which you will copy
`/etc/gitlab/gitlab-secrets.json` to all other Sidekiq and Rails nodes.
1. On all Sidekiq and Rails nodes (except the reference node):
1. Back up your [configuration files](https://docs.gitlab.com/omnibus/settings/backups/#backup-and-restore-configuration-on-a-linux-package-installation):
```shell
sudo gitlab-ctl backup-etc
```
1. Copy `/etc/gitlab/gitlab-secrets.json` from the reference node, and replace the file of the
same name on the current node.
1. Reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
```
1. Check again if any records with encrypted attributes exist (to ensure no records were created while
you performed the previous steps). The return value should be `true`:
```shell
gitlab-rails r 'Rails.application.eager_load!; puts ApplicationRecord.descendants.select { |d| d.encrypted_attributes.present? }.index_with { |model| model.count }.values.all?(&:zero?)'
```
1. Create a new Cloud Connector key: `gitlab-rake cloud_connector:keys:create`
1. Optional. On all Sidekiq and Rails nodes, check that the encrypted attributes can be read
(if no `ActiveRecord::Encryption::Errors::Decryption` exception is raised, it's good):
```shell
gitlab-rails r 'CloudConnector::Keys.first.secret_key; nil'
```
{{< /tab >}}
{{< tab title="Helm chart (Kubernetes)" >}}
If you disabled the [shared-secrets chart](https://docs.gitlab.com/charts/charts/shared-secrets/),
you need to [manually create these secrets](https://docs.gitlab.com/charts/releases/8_0.html).
{{< /tab >}}
{{< /tabs >}}
If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets).
## 17.8.0
@ -412,57 +340,7 @@ In GitLab 17.8, three new secrets have been added to support the new encryption
- `active_record_encryption_deterministic_key`
- `active_record_encryption_key_derivation_salt`
**If you have a multi-node configuration, you should ensure these secrets are the same on all nodes.** Otherwise, the application automatically generates the missing secrets at startup.
{{< tabs >}}
{{< tab title="Linux package (Omnibus)" >}}
1. Optional. If possible, put your instance in [maintenance mode](../../administration/maintenance_mode/_index.md) (otherwise if the steps 2 and 5 return `true`, it's fine).
1. Check if any records with encrypted attributes exist:
```shell
gitlab-rails r 'Rails.application.eager_load!; puts ApplicationRecord.descendants.select { |d| d.encrypted_attributes.present? }.index_with { |model| model.count }.values.all?(&:zero?)'
```
If the result is `true`, proceed to the next step. Otherwise, we need to check what are the existing records
and decide if we can delete them before proceeding further.
1. Pick one Sidekiq or Rails node as a reference node from which you will copy
`/etc/gitlab/gitlab-secrets.json` to all other Sidekiq and Rails nodes.
1. On all Sidekiq and Rails nodes (except the reference node):
1. Back up your [configuration files](https://docs.gitlab.com/omnibus/settings/backups/#backup-and-restore-configuration-on-a-linux-package-installation):
```shell
sudo gitlab-ctl backup-etc
```
1. Copy `/etc/gitlab/gitlab-secrets.json` from the reference node, and replace the file of the
same name on the current node.
1. Reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
```
1. Check again if any records with encrypted attributes exist (to ensure no records were created while
you performed the previous steps). The return value should be `true`:
```shell
gitlab-rails r 'Rails.application.eager_load!; puts ApplicationRecord.descendants.select { |d| d.encrypted_attributes.present? }.index_with { |model| model.count }.values.all?(&:zero?)'
```
{{< /tab >}}
{{< tab title="Helm chart (Kubernetes)" >}}
If you disabled the [shared-secrets chart](https://docs.gitlab.com/charts/charts/shared-secrets/),
you need to [manually create these secrets](https://docs.gitlab.com/charts/releases/8_0.html).
{{< /tab >}}
{{< /tabs >}}
If you have a multi-node configuration, you must [ensure these secrets are the same on all nodes](#unify-new-encryption-secrets).
### Change to the GitLab agent server for Kubernetes
@ -809,3 +687,194 @@ Feedback about this conditional stop on the upgrade path can be provided [in the
| 17.1 | All | 17.1.7 |
| 17.2 | All | 17.2.5 |
| 17.3 | All | 17.3.1 |
## Unify new encryption secrets
[GitLab 17.8 introduced three new secrets](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175154) to support the new encryption framework, [which was introduced in GitLab 17.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/179559):
- `active_record_encryption_primary_key`
- `active_record_encryption_deterministic_key`
- `active_record_encryption_key_derivation_salt`
If you have a multi-node configuration, you must ensure these secrets are the same on all nodes. Otherwise, the application automatically generates the missing secrets at startup.
{{< tabs >}}
{{< tab title="Linux package (Omnibus)" >}}
1. If possible, [enable maintenance mode](../../administration/maintenance_mode/_index.md#enable-maintenance-mode).
1. (Only for GitLab >= 17.9) Delete all `CloudConnector::Keys` records:
```shell
gitlab-rails runner 'CloudConnector::Keys.delete_all'
```
1. On all Sidekiq and GitLab application nodes, gather information about encryption keys and their usage.
On GitLab >= 18.0.0, >= 17.11.2, >= 17.10.6, or >= 17.9.8, run:
```shell
gitlab-rake gitlab:doctor:encryption_keys
```
If you're using other versions, check the content of [`encryption_keys.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/doctor/encryption_keys.rb) and
then download it:
```shell
wget -O encryption_keys.rb https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/doctor/encryption_keys.rb?inline=false
```
Then run the file:
```shell
gitlab-rails runner 'require_relative Pathname(Dir.pwd).join("encryption_keys.rb"); Gitlab::Doctor::EncryptionKeys.new(Logger.new($stdout)).run!'
```
The output of the command indicates which one of three possible processes you must follow.
- If all "Encryption keys usage for \<model\>" report `NONE`, select any Sidekiq or GitLab application node as a reference
node from which to copy `/etc/gitlab/gitlab-secrets.json` to all other Sidekiq and GitLab application nodes.
- If all reported keys usage are for the same key ID, select the node where the key exists as a reference node from which to copy `/etc/gitlab/gitlab-secrets.json` to all
other Sidekiq and GitLab application nodes. For example, let's say you get the following output on node 1:
```shell
Gathering existing encryption keys:
- active_record_encryption_primary_key: ID => `bb32`; truncated secret => `bEt...eBU`
- active_record_encryption_deterministic_key: ID => `445f`; truncated secret => `MJo...yg5`
[... snipped for brevity ...]
Encryption keys usage for VirtualRegistries::Packages::Maven::Upstream: NONE
Encryption keys usage for Ai::ActiveContext::Connection: NONE
Encryption keys usage for CloudConnector::Keys: NONE
Encryption keys usage for DependencyProxy::GroupSetting:
- `bb32` => 8
Encryption keys usage for Ci::PipelineScheduleInput:
- `bb32` => 1
```
And let's say you get the following output on node 2 (the `(UNKNOWN KEY!)` is fine as long as a single key ID is used. For example, `bb32` here):
```shell
Gathering existing encryption keys:
- active_record_encryption_primary_key: ID => `83kf`; truncated secret => `pKq...ikC`
- active_record_encryption_deterministic_key: ID => `b722`; truncated secret => `Lma...iJ7`
[... snipped for brevity ...]
Encryption keys usage for VirtualRegistries::Packages::Maven::Upstream: NONE
Encryption keys usage for Ai::ActiveContext::Connection: NONE
Encryption keys usage for CloudConnector::Keys: NONE
Encryption keys usage for DependencyProxy::GroupSetting:
- `bb32` (UNKNOWN KEY!) => 8
Encryption keys usage for Ci::PipelineScheduleInput:
- `bb32` (UNKNOWN KEY!) => 1
```
With the above examples, you would pick node 1 as the reference node.
1. Not all reported keys usage are for the same key ID. For instance, if node 1 shows `` -`bb32` => 1 `` and node 2 shows
`` - `83kf` => 1 ``. In that case, the resolution is more complex as it involves re-encrypting all data with a single encryption key.
Alternatively, if you're ok losing some data, you can delete records so that all remaining records use the same key ID.
Contact [support](https://about.gitlab.com/support/) for further assistance.
1. After deciding which node is the reference node, decide which of the reference node's secrets must be copied to the other nodes.
1. On all Sidekiq and Rails nodes except the reference node:
1. Back up your [configuration files](https://docs.gitlab.com/omnibus/settings/backups/#backup-and-restore-configuration-on-a-linux-package-installation):
```shell
sudo gitlab-ctl backup-etc
```
1. Copy `/etc/gitlab/gitlab-secrets.json` from the reference node, and replace the file of the
same name on the current node.
1. Reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
```
1. Check again encryption keys and their usage with one of the following command depending on your version:
On GitLab >= 18.0.0, >= 17.11.2, >= 17.10.6, or >= 17.9.8, run:
```shell
gitlab-rake gitlab:doctor:encryption_keys
```
If you're using other versions:
```shell
gitlab-rails runner 'require_relative Pathname(Dir.pwd).join("encryption_keys.rb"); Gitlab::Doctor::EncryptionKeys.new(Logger.new($stdout)).run!'
```
All reported keys usage are for the same key ID. For example, on node 1:
```shell
Gathering existing encryption keys:
- active_record_encryption_primary_key: ID => `bb32`; truncated secret => `bEt...eBU`
- active_record_encryption_deterministic_key: ID => `445f`; truncated secret => `MJo...yg5`
[... snipped for brevity ...]
Encryption keys usage for VirtualRegistries::Packages::Maven::Upstream: NONE
Encryption keys usage for Ai::ActiveContext::Connection: NONE
Encryption keys usage for CloudConnector::Keys:
- `bb32` => 1
Encryption keys usage for DependencyProxy::GroupSetting:
- `bb32` => 8
Encryption keys usage for Ci::PipelineScheduleInput:
- `bb32` => 1
```
And for example, on node 2 (you should not see any `(UNKNOWN KEY!)` this time):
```shell
Gathering existing encryption keys:
- active_record_encryption_primary_key: ID => `bb32`; truncated secret => `bEt...eBU`
- active_record_encryption_deterministic_key: ID => `445f`; truncated secret => `MJo...yg5`
[... snipped for brevity ...]
Encryption keys usage for VirtualRegistries::Packages::Maven::Upstream: NONE
Encryption keys usage for Ai::ActiveContext::Connection: NONE
Encryption keys usage for CloudConnector::Keys:
- `bb32` => 1
Encryption keys usage for DependencyProxy::GroupSetting:
- `bb32` => 8
Encryption keys usage for Ci::PipelineScheduleInput:
- `bb32` => 1
```
1. Remove the `encryption_keys.rb` file if you downloaded it previously:
```shell
rm encryption_keys.rb
```
1. Create a new Cloud Connector key:
For GitLab >= 17.10:
```shell
gitlab-rake cloud_connector:keys:create
```
For GitLab 17.9:
```shell
gitlab-rails runner 'CloudConnector::Keys.create!(secret_key: OpenSSL::PKey::RSA.new(2048).to_pem)'
```
1. [Disable maintenance mode](../../administration/maintenance_mode/_index.md#disable-maintenance-mode).
{{< /tab >}}
{{< tab title="Helm chart (Kubernetes)" >}}
If you disabled the [shared-secrets chart](https://docs.gitlab.com/charts/charts/shared-secrets/),
you need to [manually create these secrets](https://docs.gitlab.com/charts/releases/8_0.html).
{{< /tab >}}
{{< /tabs >}}

View File

@ -6,7 +6,7 @@ module Gitlab
module Utils
module MergeHash
extend self
# Deep merges an array of hashes
# Deep merges an array of elements which can be hashes, arrays, or other objects.
#
# [{ hello: ["world"] },
# { hello: "Everyone" },
@ -49,8 +49,14 @@ module Gitlab
def merge_hash_into_array(array, new_hash)
crushed_new_hash = crush_hash(new_hash)
# Merge the hash into an existing element of the array if there is overlap
if mergeable_index = array.index { |element| crushable?(element) && (crush(element) & crushed_new_hash).any? }
mergeable_index = array.index do |element|
crushed_element = crushable?(element) ? crush(element) : Array.wrap(element)
(crushed_element & crushed_new_hash).any?
end
if mergeable_index
array[mergeable_index] = merge_hash_tree(array[mergeable_index], new_hash)
else
array << new_hash

View File

@ -61196,7 +61196,7 @@ msgstr ""
msgid "The subdomain setting."
msgstr ""
msgid "The subject will be used as the title of the new issue, and the message will be the description. %{quickActionsLinkStart}Quick actions%{quickActionsLinkEnd} and styling with %{markdownLinkStart}Markdown%{markdownLinkEnd} are supported."
msgid "The subject will be used as the title of the new %{name}, and the message will be the description. %{quickActionsLinkStart}Quick actions%{quickActionsLinkEnd} and styling with %{markdownLinkStart}Markdown%{markdownLinkEnd} are supported."
msgstr ""
msgid "The tag name can't be changed for an existing release."

View File

@ -22,6 +22,7 @@ import {
convertWorkItemMutationResponse,
workItemChangeTypeWidgets,
workItemQueryResponse,
workItemWithEpicParentQueryResponse,
} from '../mock_data';
import { designCollectionResponse, mockDesign } from './design_management/mock_data';
@ -78,6 +79,7 @@ describe('WorkItemChangeTypeModal component', () => {
convertWorkItemMutationHandler = convertWorkItemMutationSuccessHandler,
designQueryHandler = noDesignQueryHandler,
allowedConversionTypesEE = [],
hasSubepicsFeature = true,
} = {}) => {
wrapper = mountExtended(WorkItemChangeTypeModal, {
apolloProvider: createMockApollo([
@ -100,6 +102,7 @@ describe('WorkItemChangeTypeModal component', () => {
glFeatures: {
workItemsAlpha,
},
hasSubepicsFeature,
},
stubs: {
GlModal: stubComponent(GlModal, {
@ -141,20 +144,34 @@ describe('WorkItemChangeTypeModal component', () => {
expect(findGlFormSelect().findAll('option')).toHaveLength(2);
});
it('does not allow to change type and disables `Change type` button when the work item has a parent', async () => {
createComponent({ hasParent: true, widgets: workItemQueryResponse.data.workItem.widgets });
describe('work item type change tests', () => {
it.each`
scenario | widgets | hasSubepicsFeature | btnDisabled | parentType
${'epic parent with subepics enabled'} | ${workItemWithEpicParentQueryResponse.data.workItem.widgets} | ${true} | ${false} | ${''}
${'epic parent with subepics disabled'} | ${workItemWithEpicParentQueryResponse.data.workItem.widgets} | ${false} | ${true} | ${'epic'}
${'non-epic parent with subepics enabled'} | ${workItemQueryResponse.data.workItem.widgets} | ${true} | ${true} | ${'issue'}
${'non-epic parent with subepics disabled'} | ${workItemQueryResponse.data.workItem.widgets} | ${false} | ${true} | ${'issue'}
`('$scenario', async ({ widgets, hasSubepicsFeature, btnDisabled, parentType }) => {
createComponent({
hasParent: true,
widgets,
hasSubepicsFeature,
});
await waitForPromises();
await waitForPromises();
findGlFormSelect().vm.$emit('change', issueTypeId);
findGlFormSelect().vm.$emit('change', issueTypeId);
await nextTick();
await nextTick();
expect(findWarningAlert().text()).toBe(
'Parent item type issue is not supported on issue. Remove the parent item to change type.',
);
expect(findChangeTypeModal().props('actionPrimary').attributes.disabled).toBe(true);
const hasWarning = parentType !== '';
expect(findWarningAlert().exists()).toBe(hasWarning);
if (hasWarning) {
const warningText = `Parent item type ${parentType} is not supported on issue. Remove the parent item to change type.`;
expect(findWarningAlert().text()).toBe(warningText);
}
expect(findChangeTypeModal().props('actionPrimary').attributes.disabled).toBe(btnDisabled);
});
});
it('does not allow to change type and disables `Change type` button when the work item has child items', async () => {

View File

@ -10,7 +10,7 @@ import getWorkItemTreeQuery from '~/work_items/graphql/work_item_tree.query.grap
import isExpandedHierarchyTreeChildQuery from '~/work_items/graphql/client/is_expanded_hierarchy_tree_child.query.graphql';
import WorkItemLinkChild from '~/work_items/components/work_item_links/work_item_link_child.vue';
import WorkItemChildrenWrapper from '~/work_items/components/work_item_links/work_item_children_wrapper.vue';
import WorkItemLinkChildContents from '~/work_items/components/shared/work_item_link_child_contents.vue';
import WorkItemLinkChildContents from 'ee_else_ce/work_items/components/shared/work_item_link_child_contents.vue';
import {
WIDGET_TYPE_HIERARCHY,
WORK_ITEM_TYPE_NAME_OBJECTIVE,

View File

@ -4,7 +4,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { visitUrl } from '~/lib/utils/url_utility';
import WorkItemRelationshipList from '~/work_items/components/work_item_relationships/work_item_relationship_list.vue';
import WorkItemLinkChildContents from '~/work_items/components/shared/work_item_link_child_contents.vue';
import WorkItemLinkChildContents from 'ee_else_ce/work_items/components/shared/work_item_link_child_contents.vue';
import removeLinkedItemsMutation from '~/work_items/graphql/remove_linked_items.mutation.graphql';
import addLinkedItemsMutation from '~/work_items/graphql/add_linked_items.mutation.graphql';
@ -87,7 +87,6 @@ describe('WorkItemRelationshipList', () => {
childItem: mockLinkedItems[0].workItem,
canUpdate: true,
workItemFullPath,
showWeight: true,
});
});

View File

@ -333,6 +333,85 @@ export const workItemQueryResponse = {
},
};
export const workItemWithEpicParentQueryResponse = {
data: {
workItem: {
__typename: 'WorkItem',
id: 'gid://gitlab/WorkItem/1',
iid: '1',
archived: false,
title: 'Test',
movedToWorkItemUrl: null,
duplicatedToWorkItemUrl: null,
promotedToEpicUrl: null,
state: 'OPEN',
description: 'description',
confidential: false,
createdAt: '2022-08-03T12:41:54Z',
updatedAt: null,
closedAt: null,
author: {
avatarUrl: 'http://127.0.0.1:3000/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
id: 'gid://gitlab/User/1',
name: 'Administrator',
username: 'root',
webUrl: 'http://127.0.0.1:3000/root',
webPath: '/root',
__typename: 'UserCore',
},
project: {
id: 'gid://gitlab/Project/7',
__typename: 'Project',
},
namespace: {
__typename: 'Project',
id: '1',
fullPath: 'test-project-path',
name: 'Project name',
fullName: 'Group name',
},
workItemType: {
__typename: 'WorkItemType',
id: 'gid://gitlab/WorkItems::Type/5',
name: 'Task',
iconName: 'issue-type-task',
},
userPermissions: {
adminParentLink: false,
adminWorkItemLink: true,
deleteWorkItem: false,
createNote: false,
markNoteAsInternal: true,
moveWorkItem: false,
reportSpam: false,
setWorkItemMetadata: false,
summarizeComments: false,
updateWorkItem: false,
__typename: 'WorkItemPermissions',
},
widgets: [
{
__typename: 'WorkItemWidgetHierarchy',
type: 'HIERARCHY',
hasChildren: true,
parent: {
id: 'gid://gitlab/WorkItem/3',
title: 'Work Item Epic',
webUrl: 'http://127.0.0.1:3000/groups/gitlab-org/-/work_items/130',
__typename: 'WorkItem',
workItemType: {
id: 'gid://gitlab/WorkItems::Type/6',
name: 'Epic',
iconName: 'issue-type-epic',
__typename: 'WorkItemType',
},
},
},
],
},
},
};
export const updateWorkItemMutationResponse = {
data: {
workItemUpdate: {

View File

@ -289,6 +289,7 @@ RSpec.describe IssuesHelper, feature_category: :team_planning do
allow(helper).to receive(:can?).with(current_user, :read_crm_organization, group.crm_group).and_return(false)
allow(helper).to receive(:image_path).and_return('#')
allow(helper).to receive(:url_for).and_return('#')
allow(helper).to receive(:licensed_feature_available?).with(:subepics).and_return(false)
assign(:has_issues, false)
assign(:has_projects, true)
@ -307,7 +308,8 @@ RSpec.describe IssuesHelper, feature_category: :team_planning do
rss_path: '#',
sign_in_path: new_user_session_path,
group_id: group.id,
time_tracking_limit_to_hours: "false"
time_tracking_limit_to_hours: "false",
has_subepics_feature: "false"
}
expect(helper.group_issues_list_data(group, current_user)).to include(expected)
@ -498,4 +500,59 @@ RSpec.describe IssuesHelper, feature_category: :team_planning do
end
end
end
describe '#has_subepics_feature?' do
subject(:has_subepics_feature) { helper.has_subepics_feature?(namespace) }
context 'when namespace is a group project' do
let_it_be(:namespace) { create(:project, namespace: group) }
context 'when subepics feature is licensed and available for group' do
before do
allow(namespace.group).to receive(:licensed_feature_available?).with(:subepics).and_return(true)
end
it { is_expected.to be_truthy }
end
context 'when subepics feature is not licensed for group' do
before do
allow(namespace.group).to receive(:licensed_feature_available?).with(:subepics).and_return(false)
end
it { is_expected.to be_falsey }
end
end
context 'when namespace is a group' do
let_it_be(:namespace) { group }
context 'when subepics feature is licensed and available' do
before do
allow(namespace).to receive(:licensed_feature_available?).with(:subepics).and_return(true)
end
it { is_expected.to be_truthy }
end
context 'when subepics feature is not licensed' do
before do
allow(namespace).to receive(:licensed_feature_available?).with(:subepics).and_return(false)
end
it { is_expected.to be_falsey }
end
end
context 'when namespace is neither a group nor has a group' do
let_it_be(:namespace) { create(:namespace) }
before do
allow(namespace).to receive(:is_a?).with(Group).and_return(false)
allow(namespace).to receive(:respond_to?).with(:group).and_return(false)
end
it { is_expected.to be_falsey }
end
end
end

View File

@ -13,10 +13,15 @@ RSpec.describe Gitlab::Utils::MergeHash do
describe '.elements' do
it 'deep merges an array of elements' do
input = [{ hello: ["world"] },
{ hello: "Everyone" },
{ hello: { greetings: ['Bonjour', 'Hello', 'Hallo', 'Dzień dobry'] } },
"Goodbye", "Hallo"]
input = [
:hello,
"Howdy",
{ hello: ["world"] },
{ hello: "Everyone" },
{ hello: { greetings: ['Bonjour', 'Hello', 'Hallo', 'Dzień dobry'] } },
"Goodbye",
"Hallo"
]
expected_output = [
{
hello:
@ -26,6 +31,7 @@ RSpec.describe Gitlab::Utils::MergeHash do
{ greetings: ['Bonjour', 'Hello', 'Hallo', 'Dzień dobry'] }
]
},
"Howdy",
"Goodbye"
]

View File

@ -103,7 +103,7 @@ RSpec.describe DesignManagement::Version do
it 'has an appropriate cause' do
expect { call_with_empty_actions }
.to raise_error(have_attributes(cause: ActiveRecord::RecordInvalid))
.to raise_error(have_attributes(cause: ActiveRecord::StatementInvalid))
end
it 'provides extra data sentry can consume' do