diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index 1e449a535e3..3dcccc3ee59 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-fd923fb927c727b14365468890dfc41a53794682
+f5b045d8694528efbe2e08984ac5867bdfe41aee
diff --git a/app/services/packages/create_package_service.rb b/app/services/packages/create_package_service.rb
index 7565b017a64..61d26472430 100644
--- a/app/services/packages/create_package_service.rb
+++ b/app/services/packages/create_package_service.rb
@@ -30,6 +30,10 @@ module Packages
end
end
+ def can_create_package?
+ can?(current_user, :create_package, project)
+ end
+
private
def package_attrs(attrs)
diff --git a/app/services/packages/pypi/create_package_service.rb b/app/services/packages/pypi/create_package_service.rb
index 422815cd118..975073b87cc 100644
--- a/app/services/packages/pypi/create_package_service.rb
+++ b/app/services/packages/pypi/create_package_service.rb
@@ -6,13 +6,13 @@ module Packages
include ::Gitlab::Utils::StrongMemoize
ERROR_REASON_INVALID_PARAMETER = :invalid_parameter
- ERROR_REASON_PACKAGE_PROTECTED = :package_protected
+ ERROR_RESPONSE_PACKAGE_PROTECTED =
+ ServiceResponse.error(message: 'Package protected.', reason: :package_protected)
+ ERROR_RESPONSE_UNAUTHORIZED = ServiceResponse.error(message: 'Unauthorized', reason: :unauthorized)
def execute
- if current_package_protected?
- return ServiceResponse.error(message: 'Package protected.',
- reason: ERROR_REASON_PACKAGE_PROTECTED)
- end
+ return ERROR_RESPONSE_UNAUTHORIZED unless can_create_package?
+ return ERROR_RESPONSE_PACKAGE_PROTECTED if current_package_protected?
::Packages::Package.transaction do
meta = Packages::Pypi::Metadatum.new(
diff --git a/db/docs/batched_background_migrations/backfill_incident_management_pending_alert_escalations_project_id.yml b/db/docs/batched_background_migrations/backfill_incident_management_pending_alert_escalations_project_id.yml
index a5c9fd604eb..ac8123c8ee8 100644
--- a/db/docs/batched_background_migrations/backfill_incident_management_pending_alert_escalations_project_id.yml
+++ b/db/docs/batched_background_migrations/backfill_incident_management_pending_alert_escalations_project_id.yml
@@ -3,7 +3,6 @@ migration_job_name: BackfillIncidentManagementPendingAlertEscalationsProjectId
description: Backfills sharding key `incident_management_pending_alert_escalations.project_id` from `alert_management_alerts`.
feature_category: incident_management
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166816
-milestone: '17.5'
-queued_migration_version: 20240922141648
-finalize_after: '2024-10-22'
+milestone: '17.6'
+queued_migration_version: 20241025073808
finalized_by: # version of the migration that finalized this BBM
diff --git a/db/migrate/20241018071109_add_created_by_to_custom_fields.rb b/db/migrate/20241018071109_add_created_by_to_custom_fields.rb
new file mode 100644
index 00000000000..2c89d0acf20
--- /dev/null
+++ b/db/migrate/20241018071109_add_created_by_to_custom_fields.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddCreatedByToCustomFields < Gitlab::Database::Migration[2.2]
+ milestone '17.6'
+
+ def change
+ add_reference :custom_fields, :created_by, index: true, foreign_key: { on_delete: :nullify, to_table: :users } # rubocop:disable Migration/AddReference -- table is empty
+ end
+end
diff --git a/db/migrate/20241018072227_add_updated_by_to_custom_fields.rb b/db/migrate/20241018072227_add_updated_by_to_custom_fields.rb
new file mode 100644
index 00000000000..1542c2ba659
--- /dev/null
+++ b/db/migrate/20241018072227_add_updated_by_to_custom_fields.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddUpdatedByToCustomFields < Gitlab::Database::Migration[2.2]
+ milestone '17.6'
+
+ def change
+ add_reference :custom_fields, :updated_by, index: true, foreign_key: { on_delete: :nullify, to_table: :users } # rubocop:disable Migration/AddReference -- table is empty
+ end
+end
diff --git a/db/post_migrate/20240922141646_add_incident_management_pending_alert_escalations_project_id_fk.rb b/db/post_migrate/20240922141646_add_incident_management_pending_alert_escalations_project_id_fk.rb
index d24c3856a21..e612b287b42 100644
--- a/db/post_migrate/20240922141646_add_incident_management_pending_alert_escalations_project_id_fk.rb
+++ b/db/post_migrate/20240922141646_add_incident_management_pending_alert_escalations_project_id_fk.rb
@@ -7,13 +7,12 @@ class AddIncidentManagementPendingAlertEscalationsProjectIdFk < Gitlab::Database
disable_ddl_transaction!
def up
- add_concurrent_partitioned_foreign_key :incident_management_pending_alert_escalations, :projects,
- column: :project_id, on_delete: :cascade
+ # no-op because there was a bug in the original migration, which has been
+ # fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/168212
end
def down
- with_lock_retries do
- remove_foreign_key :incident_management_pending_alert_escalations, column: :project_id
- end
+ # no-op because there was a bug in the original migration, which has been
+ # fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/168212
end
end
diff --git a/db/post_migrate/20240922141647_add_incident_management_pending_alert_escalations_project_id_trigger.rb b/db/post_migrate/20240922141647_add_incident_management_pending_alert_escalations_project_id_trigger.rb
index 4c0b357d0d4..bac6376b96c 100644
--- a/db/post_migrate/20240922141647_add_incident_management_pending_alert_escalations_project_id_trigger.rb
+++ b/db/post_migrate/20240922141647_add_incident_management_pending_alert_escalations_project_id_trigger.rb
@@ -4,22 +4,12 @@ class AddIncidentManagementPendingAlertEscalationsProjectIdTrigger < Gitlab::Dat
milestone '17.5'
def up
- install_sharding_key_assignment_trigger(
- table: :incident_management_pending_alert_escalations,
- sharding_key: :project_id,
- parent_table: :alert_management_alerts,
- parent_sharding_key: :project_id,
- foreign_key: :alert_id
- )
+ # no-op because there was a bug in the original migration, which has been
+ # fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/168212
end
def down
- remove_sharding_key_assignment_trigger(
- table: :incident_management_pending_alert_escalations,
- sharding_key: :project_id,
- parent_table: :alert_management_alerts,
- parent_sharding_key: :project_id,
- foreign_key: :alert_id
- )
+ # no-op because there was a bug in the original migration, which has been
+ # fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/168212
end
end
diff --git a/db/post_migrate/20240922141648_queue_backfill_incident_management_pending_alert_escalations_project_id.rb b/db/post_migrate/20240922141648_queue_backfill_incident_management_pending_alert_escalations_project_id.rb
index 25cc40b49a8..a95af4a90b0 100644
--- a/db/post_migrate/20240922141648_queue_backfill_incident_management_pending_alert_escalations_project_id.rb
+++ b/db/post_migrate/20240922141648_queue_backfill_incident_management_pending_alert_escalations_project_id.rb
@@ -10,32 +10,12 @@ class QueueBackfillIncidentManagementPendingAlertEscalationsProjectId < Gitlab::
SUB_BATCH_SIZE = 100
def up
- queue_batched_background_migration(
- MIGRATION,
- :incident_management_pending_alert_escalations,
- :id,
- :project_id,
- :alert_management_alerts,
- :project_id,
- :alert_id,
- job_interval: DELAY_INTERVAL,
- batch_size: BATCH_SIZE,
- batch_class_name: 'LooseIndexScanBatchingStrategy',
- sub_batch_size: SUB_BATCH_SIZE
- )
+ # no-op because there was a bug in the original migration, which has been
+ # fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/168212
end
def down
- delete_batched_background_migration(
- MIGRATION,
- :incident_management_pending_alert_escalations,
- :id,
- [
- :project_id,
- :alert_management_alerts,
- :project_id,
- :alert_id
- ]
- )
+ # no-op because there was a bug in the original migration, which has been
+ # fixed by https://gitlab.com/gitlab-org/gitlab/-/merge_requests/168212
end
end
diff --git a/db/post_migrate/20241023060148_remove_bbm_incident_management_pending_alert_esc_project_id.rb b/db/post_migrate/20241023060148_remove_bbm_incident_management_pending_alert_esc_project_id.rb
new file mode 100644
index 00000000000..4575dbd4f56
--- /dev/null
+++ b/db/post_migrate/20241023060148_remove_bbm_incident_management_pending_alert_esc_project_id.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class RemoveBbmIncidentManagementPendingAlertEscProjectId < Gitlab::Database::Migration[2.2]
+ milestone '17.6'
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
+
+ MIGRATION = "BackfillIncidentManagementPendingAlertEscalationsProjectId"
+
+ def up
+ delete_batched_background_migration(MIGRATION, :incident_management_pending_alert_escalations, :id,
+ [:project_id, :alert_management_alerts, :project_id, :alert_id])
+ end
+
+ def down; end
+end
diff --git a/db/post_migrate/20241025072621_remove_incident_management_pending_alert_esc_project_id_fk.rb b/db/post_migrate/20241025072621_remove_incident_management_pending_alert_esc_project_id_fk.rb
new file mode 100644
index 00000000000..c3687ab5cb4
--- /dev/null
+++ b/db/post_migrate/20241025072621_remove_incident_management_pending_alert_esc_project_id_fk.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class RemoveIncidentManagementPendingAlertEscProjectIdFk < Gitlab::Database::Migration[2.2]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ milestone '17.6'
+ disable_ddl_transaction!
+
+ CONSTRAINT_NAME = 'fk_rails_2bbafb00ef'
+
+ def up
+ with_lock_retries do
+ remove_foreign_key_if_exists(:incident_management_pending_alert_escalations, :projects, name: CONSTRAINT_NAME)
+ end
+ end
+
+ def down
+ add_concurrent_partitioned_foreign_key :incident_management_pending_alert_escalations, :projects,
+ column: :project_id, on_delete: :cascade, name: CONSTRAINT_NAME
+ end
+end
diff --git a/db/post_migrate/20241025073508_remove_incident_management_pending_alert_esc_project_id_trigger.rb b/db/post_migrate/20241025073508_remove_incident_management_pending_alert_esc_project_id_trigger.rb
new file mode 100644
index 00000000000..1665f0b7d88
--- /dev/null
+++ b/db/post_migrate/20241025073508_remove_incident_management_pending_alert_esc_project_id_trigger.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+class RemoveIncidentManagementPendingAlertEscProjectIdTrigger < Gitlab::Database::Migration[2.2]
+ milestone '17.6'
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ execute <<~SQL
+ DROP TRIGGER IF EXISTS trigger_2a994bb5629f ON incident_management_pending_alert_escalations;
+ DROP FUNCTION IF EXISTS trigger_2a994bb5629f();
+ SQL
+ end
+ end
+
+ def down
+ execute <<~SQL
+ CREATE FUNCTION trigger_2a994bb5629f() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+ BEGIN
+ IF NEW."project_id" IS NULL THEN
+ SELECT "project_id"
+ INTO NEW."project_id"
+ FROM "alert_management_alerts"
+ WHERE "alert_management_alerts"."id" = NEW."alert_id";
+ END IF;
+ RETURN NEW;
+ END;
+ $$;
+
+ CREATE TRIGGER trigger_2a994bb5629f
+ BEFORE INSERT OR UPDATE ON incident_management_pending_alert_escalations
+ FOR EACH ROW
+ EXECUTE FUNCTION trigger_2a994bb5629f();
+ SQL
+ end
+end
diff --git a/db/post_migrate/20241025073638_add_incident_management_pending_alert_esc_project_id_fk.rb b/db/post_migrate/20241025073638_add_incident_management_pending_alert_esc_project_id_fk.rb
new file mode 100644
index 00000000000..09ff8837a02
--- /dev/null
+++ b/db/post_migrate/20241025073638_add_incident_management_pending_alert_esc_project_id_fk.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class AddIncidentManagementPendingAlertEscProjectIdFk < Gitlab::Database::Migration[2.2]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ milestone '17.6'
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_partitioned_foreign_key :incident_management_pending_alert_escalations, :projects,
+ column: :project_id, on_delete: :cascade, reverse_lock_order: true
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists :incident_management_pending_alert_escalations, column: :project_id,
+ reverse_lock_order: true
+ end
+ end
+end
diff --git a/db/post_migrate/20241025073721_add_incident_management_pending_alert_esc_project_id_trigger.rb b/db/post_migrate/20241025073721_add_incident_management_pending_alert_esc_project_id_trigger.rb
new file mode 100644
index 00000000000..e9b22b7346c
--- /dev/null
+++ b/db/post_migrate/20241025073721_add_incident_management_pending_alert_esc_project_id_trigger.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class AddIncidentManagementPendingAlertEscProjectIdTrigger < Gitlab::Database::Migration[2.2]
+ milestone '17.6'
+
+ def up
+ install_sharding_key_assignment_trigger(
+ table: :incident_management_pending_alert_escalations,
+ sharding_key: :project_id,
+ parent_table: :alert_management_alerts,
+ parent_sharding_key: :project_id,
+ foreign_key: :alert_id
+ )
+ end
+
+ def down
+ remove_sharding_key_assignment_trigger(
+ table: :incident_management_pending_alert_escalations,
+ sharding_key: :project_id,
+ parent_table: :alert_management_alerts,
+ parent_sharding_key: :project_id,
+ foreign_key: :alert_id
+ )
+ end
+end
diff --git a/db/post_migrate/20241025073808_queue_backfill_incident_management_pending_alert_esc_project_id.rb b/db/post_migrate/20241025073808_queue_backfill_incident_management_pending_alert_esc_project_id.rb
new file mode 100644
index 00000000000..ba3b6d08a95
--- /dev/null
+++ b/db/post_migrate/20241025073808_queue_backfill_incident_management_pending_alert_esc_project_id.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+class QueueBackfillIncidentManagementPendingAlertEscProjectId < Gitlab::Database::Migration[2.2]
+ milestone '17.6'
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main_cell
+
+ MIGRATION = "BackfillIncidentManagementPendingAlertEscalationsProjectId"
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 1000
+ SUB_BATCH_SIZE = 100
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :incident_management_pending_alert_escalations,
+ :id,
+ :project_id,
+ :alert_management_alerts,
+ :project_id,
+ :alert_id,
+ job_interval: DELAY_INTERVAL,
+ batch_size: BATCH_SIZE,
+ batch_class_name: 'LooseIndexScanBatchingStrategy',
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+ end
+
+ def down
+ delete_batched_background_migration(
+ MIGRATION,
+ :incident_management_pending_alert_escalations,
+ :id,
+ [
+ :project_id,
+ :alert_management_alerts,
+ :project_id,
+ :alert_id
+ ]
+ )
+ end
+end
diff --git a/db/schema_migrations/20241018071109 b/db/schema_migrations/20241018071109
new file mode 100644
index 00000000000..183dc667a03
--- /dev/null
+++ b/db/schema_migrations/20241018071109
@@ -0,0 +1 @@
+a0e4e34019e63322618d0bad2a1c271f07dfb99852ef913045e35bbba7f2a6a1
\ No newline at end of file
diff --git a/db/schema_migrations/20241018072227 b/db/schema_migrations/20241018072227
new file mode 100644
index 00000000000..64c698ecdfd
--- /dev/null
+++ b/db/schema_migrations/20241018072227
@@ -0,0 +1 @@
+0f9f28442fc0a89d1beb1f89b17df636a409e6841f9ab234dc8297a6fb4625aa
\ No newline at end of file
diff --git a/db/schema_migrations/20241023060148 b/db/schema_migrations/20241023060148
new file mode 100644
index 00000000000..f072c1a78c0
--- /dev/null
+++ b/db/schema_migrations/20241023060148
@@ -0,0 +1 @@
+d9f0d28c35d08427014710004597625e351f8c8ad9fb2e244887c9201e0177ea
\ No newline at end of file
diff --git a/db/schema_migrations/20241025072621 b/db/schema_migrations/20241025072621
new file mode 100644
index 00000000000..4e4309f7852
--- /dev/null
+++ b/db/schema_migrations/20241025072621
@@ -0,0 +1 @@
+d30a5ae139557ab677d23941ce5b55ecb0f6d67f7b55035ea33fb229eb236cc6
\ No newline at end of file
diff --git a/db/schema_migrations/20241025073508 b/db/schema_migrations/20241025073508
new file mode 100644
index 00000000000..eb424d4d60d
--- /dev/null
+++ b/db/schema_migrations/20241025073508
@@ -0,0 +1 @@
+345f6b90e74d6c7a5e6133d4f895717f76aa4156ac877cd494c5e16c320a053a
\ No newline at end of file
diff --git a/db/schema_migrations/20241025073638 b/db/schema_migrations/20241025073638
new file mode 100644
index 00000000000..842f688bb81
--- /dev/null
+++ b/db/schema_migrations/20241025073638
@@ -0,0 +1 @@
+0f7f885d6493a5c9b6a13641d5e516190511a077f0b91f4d7f2e0cdf4c99e135
\ No newline at end of file
diff --git a/db/schema_migrations/20241025073721 b/db/schema_migrations/20241025073721
new file mode 100644
index 00000000000..7e2a103156c
--- /dev/null
+++ b/db/schema_migrations/20241025073721
@@ -0,0 +1 @@
+55055788e7c493b41c9d18f5cb03dacd157827cd92d31dd40d4790e2b3a6373d
\ No newline at end of file
diff --git a/db/schema_migrations/20241025073808 b/db/schema_migrations/20241025073808
new file mode 100644
index 00000000000..9ce2cb3dc46
--- /dev/null
+++ b/db/schema_migrations/20241025073808
@@ -0,0 +1 @@
+c4a17ceba5f4174bd71068b5ff12d2f38b1c744e6304cbdd635320929fbb868b
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 59f763ec168..f3aaf7324bc 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -10162,6 +10162,8 @@ CREATE TABLE custom_fields (
archived_at timestamp with time zone,
field_type smallint NOT NULL,
name text NOT NULL,
+ created_by_id bigint,
+ updated_by_id bigint,
CONSTRAINT check_b047b04af9 CHECK ((char_length(name) <= 255))
);
@@ -28765,6 +28767,10 @@ CREATE UNIQUE INDEX index_custom_emoji_on_namespace_id_and_name ON custom_emoji
CREATE INDEX index_custom_field_select_options_on_namespace_id ON custom_field_select_options USING btree (namespace_id);
+CREATE INDEX index_custom_fields_on_created_by_id ON custom_fields USING btree (created_by_id);
+
+CREATE INDEX index_custom_fields_on_updated_by_id ON custom_fields USING btree (updated_by_id);
+
CREATE UNIQUE INDEX index_custom_software_licenses_on_project_id_and_name ON custom_software_licenses USING btree (project_id, name);
CREATE INDEX index_customer_relations_contacts_on_group_id ON customer_relations_contacts USING btree (group_id);
@@ -36232,6 +36238,9 @@ ALTER TABLE ONLY ci_builds
ALTER TABLE ONLY upcoming_reconciliations
ADD CONSTRAINT fk_rails_497b4938ac FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY custom_fields
+ ADD CONSTRAINT fk_rails_4a74c8558e FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY group_deletion_schedules
ADD CONSTRAINT fk_rails_4b8c694a6c FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
@@ -36628,6 +36637,9 @@ ALTER TABLE ONLY dependency_proxy_manifest_states
ALTER TABLE ONLY ci_job_artifact_states
ADD CONSTRAINT fk_rails_80a9cba3b2_p FOREIGN KEY (partition_id, job_artifact_id) REFERENCES p_ci_job_artifacts(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
+ALTER TABLE ONLY custom_fields
+ ADD CONSTRAINT fk_rails_80c4a47616 FOREIGN KEY (updated_by_id) REFERENCES users(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY approval_merge_request_rules_users
ADD CONSTRAINT fk_rails_80e6801803 FOREIGN KEY (approval_merge_request_rule_id) REFERENCES approval_merge_request_rules(id) ON DELETE CASCADE;
diff --git a/doc/administration/settings/usage_statistics.md b/doc/administration/settings/usage_statistics.md
index 6b2d27b3502..0575b45ed3f 100644
--- a/doc/administration/settings/usage_statistics.md
+++ b/doc/administration/settings/usage_statistics.md
@@ -102,7 +102,7 @@ for all authenticated users, and on the **Admin** area pages. The statuses are:
- Red: The version of GitLab you are running is vulnerable. You should install
the latest version with security fixes as soon as possible.
-
+
### Enable or disable version check
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 1914c39ce20..a79ce489531 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -3880,6 +3880,55 @@ Input type: `CreateTestCaseInput`
| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| `testCase` | [`Issue`](#issue) | Test case created. |
+### `Mutation.customFieldCreate`
+
+DETAILS:
+**Introduced** in GitLab 17.6.
+**Status**: Experiment.
+
+Input type: `CustomFieldCreateInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `fieldType` | [`CustomFieldType!`](#customfieldtype) | Type of custom field. |
+| `groupPath` | [`ID!`](#id) | Group path where the custom field is created. |
+| `name` | [`String!`](#string) | Name of the custom field. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `customField` | [`CustomField`](#customfield) | Created custom field. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+
+### `Mutation.customFieldUpdate`
+
+DETAILS:
+**Introduced** in GitLab 17.6.
+**Status**: Experiment.
+
+Input type: `CustomFieldUpdateInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `id` | [`IssuablesCustomFieldID!`](#issuablescustomfieldid) | Global ID of the custom field. |
+| `name` | [`String!`](#string) | Name of the custom field. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `customField` | [`CustomField`](#customfield) | Updated custom field. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+
### `Mutation.customerRelationsContactCreate`
Input type: `CustomerRelationsContactCreateInput`
@@ -21420,11 +21469,13 @@ Represents a custom field.
| ---- | ---- | ----------- |
| `active` | [`Boolean!`](#boolean) | Whether the custom field is active. |
| `createdAt` | [`Time!`](#time) | Timestamp when the custom field was created. |
+| `createdBy` | [`UserCore`](#usercore) | User that created the custom field. |
| `fieldType` | [`CustomFieldType!`](#customfieldtype) | Type of custom field. |
| `id` | [`IssuablesCustomFieldID!`](#issuablescustomfieldid) | Global ID of the custom field. |
| `name` | [`String!`](#string) | Name of the custom field. |
| `selectOptions` | [`[CustomFieldSelectOption!]`](#customfieldselectoption) | Available options for a select field. |
| `updatedAt` | [`Time!`](#time) | Timestamp when the custom field was last updated. |
+| `updatedBy` | [`UserCore`](#usercore) | User that last updated the custom field. |
| `workItemTypes` | [`[WorkItemType!]`](#workitemtype) | Work item types that the custom field is available on. |
### `CustomFieldSelectOption`
diff --git a/doc/user/analytics/dora_metrics.md b/doc/user/analytics/dora_metrics.md
index 16ecefa6fe5..b3f3241d7a3 100644
--- a/doc/user/analytics/dora_metrics.md
+++ b/doc/user/analytics/dora_metrics.md
@@ -76,7 +76,7 @@ The first step is to benchmark the cadence of code releases between groups and p
Lead time for changes is the amount of time it takes a code change to get into production.
-**Lead time for changes** is not the same as **Lead time**. In the value stream, lead time measures the time it takes for work on an issue to move from the moment it's requested (Issue created) to the moment it's fulfilled and delivered (Issue closed).
+**Lead time for changes** is not the same as **Lead time**. In value stream analytics, lead time measures the time it takes for work on an issue to move from the moment it's requested (Issue created) to the moment it's fulfilled and delivered (Issue closed).
For software leaders, lead time for changes reflects the efficiency of CI/CD pipelines and visualizes how quickly work is delivered to customers.
Over time, the lead time for changes should decrease, while your team's performance should increase. Low lead time for changes means more efficient CI/CD pipelines.
@@ -94,6 +94,7 @@ The first step is to benchmark the CI/CD pipelines' efficiency between groups an
- Using Value Stream Analytics to identify bottlenecks in the processes.
- Breaking the changes down into smaller iterations.
- Adding automation.
+- Improving the performance of your pipelines.
## Time to restore service
@@ -104,7 +105,7 @@ Low time to restore service means the organization can take risks with new innov
### How time to restore service is calculated
-In GitLab, time to restore service is measured as the median time an incident was open for on a production environment.
+In GitLab, time to restore service is measured as the median time an incident was open on a production environment.
GitLab calculates the number of seconds an incident was open on a production environment in the given time period. This assumes:
- [GitLab incidents](../../operations/incident_management/incidents.md) are tracked.
@@ -117,17 +118,18 @@ The first step is to benchmark the team response and recover from service interr
- Improving the observability into the production environment.
- Improving response workflows.
+- Improving deployment frequency and lead time for changes so fixes can get into production more efficiently.
## Change failure rate
-Change failure rate is how often a change cause failure in production.
+Change failure rate is how often a change causes a failure in production.
Software leaders can use the change failure rate metric to gain insights into the quality of the code being shipped.
High change failure rate may indicate an inefficient deployment process or insufficient automated testing coverage.
### How change failure rate is calculated
-In GitLab, change failure rate is measured as the percentage of deployments that cause an incident in production in the given time period.
+In GitLab, change failure rate is measured as the percentage of deployments that cause an incident in production in a given time period.
GitLab calculates change failure rate as the number of incidents divided by the number of deployments to a production environment. This calculation assumes:
- [GitLab incidents](../../operations/incident_management/incidents.md) are tracked.
@@ -227,7 +229,7 @@ These deployment records are not created for pull-based deployments, for example
To track DORA metrics in these cases, you can [create a deployment record](../../api/deployments.md#create-a-deployment) using the Deployments API.
You must set the environment name where the deployment tier is configured, because the tier variable is specified for the given environment, not for the deployments.
-For more information, see [Track deployments of an external deployment tool](../../ci/environments/external_deployment_tools.md).
+For more information, see [track deployments of an external deployment tool](../../ci/environments/external_deployment_tools.md).
### Measure DORA metrics with Jira
@@ -239,7 +241,7 @@ For more information, see [Track deployments of an external deployment tool](../
For PagerDuty, you can set up a [webhook to automatically create a GitLab incident for each PagerDuty incident](../../operations/incident_management/manage_incidents.md#using-the-pagerduty-webhook).
This configuration requires you to make changes in both PagerDuty and GitLab.
-For others incident management tools, you can set up the
+For other incident management tools, you can set up the
[HTTP integration](../../operations/incident_management/integrations.md#http-endpoints),
and use it to automatically:
@@ -252,8 +254,8 @@ The table below provides an overview of the DORA metrics' availability in projec
| Metric | Level | Comments |
|---------------------------|-------------------|----------|
-| `deployment_frequency` | Project | |
-| `deployment_frequency` | Group | |
+| `deployment_frequency` | Project | Unit in deployment count. |
+| `deployment_frequency` | Group | Unit in deployment count. Aggregation method is average. |
| `lead_time_for_changes` | Project | Unit in seconds. Aggregation method is median. |
| `lead_time_for_changes` | Group | Unit in seconds. Aggregation method is median. |
| `time_to_restore_service` | Project and group | Unit in days. Aggregation method is median. (Available in UI chart in GitLab 15.1 and later) |
diff --git a/doc/user/group/contribution_analytics/index.md b/doc/user/group/contribution_analytics/index.md
index 31ca178a30c..d3f15fc128b 100644
--- a/doc/user/group/contribution_analytics/index.md
+++ b/doc/user/group/contribution_analytics/index.md
@@ -27,8 +27,8 @@ To view contribution analytics:
Three bar charts and a table illustrate the number of contributions made by each group member:
- Push events
-- Merge requests
-- Closed issues
+- Created, merged, and closed merge requests
+- Created and closed issues
### View a member's contributions
diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb
index 65edbb8b390..9cd114994b2 100644
--- a/lib/api/pypi_packages.rb
+++ b/lib/api/pypi_packages.rb
@@ -306,7 +306,7 @@ module API
.new(project, current_user, declared_params.merge(build: current_authenticated_job))
.execute
- if service_response.error? && service_response.reason == Packages::Pypi::CreatePackageService::ERROR_REASON_PACKAGE_PROTECTED
+ if service_response.error? && service_response.reason == Packages::Pypi::CreatePackageService::ERROR_RESPONSE_PACKAGE_PROTECTED.reason
forbidden!(service_response.message)
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e4c530fc7f7..715ce9f57b5 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -64535,6 +64535,9 @@ msgstr ""
msgid "can only be changed by a group admin."
msgstr ""
+msgid "can only have a maximum of %{limit} active custom fields."
+msgstr ""
+
msgid "can only have one escalation policy"
msgstr ""
diff --git a/spec/migrations/20240922141648_queue_backfill_incident_management_pending_alert_escalations_project_id_spec.rb b/spec/migrations/20241025073808_queue_backfill_incident_management_pending_alert_esc_project_id_spec.rb
similarity index 88%
rename from spec/migrations/20240922141648_queue_backfill_incident_management_pending_alert_escalations_project_id_spec.rb
rename to spec/migrations/20241025073808_queue_backfill_incident_management_pending_alert_esc_project_id_spec.rb
index ba9f8f19683..7a1342ffeed 100644
--- a/spec/migrations/20240922141648_queue_backfill_incident_management_pending_alert_escalations_project_id_spec.rb
+++ b/spec/migrations/20241025073808_queue_backfill_incident_management_pending_alert_esc_project_id_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe QueueBackfillIncidentManagementPendingAlertEscalationsProjectId, feature_category: :incident_management do
+RSpec.describe QueueBackfillIncidentManagementPendingAlertEscProjectId, feature_category: :incident_management do
let!(:batched_migration) { described_class::MIGRATION }
it 'schedules a new batched migration' do
diff --git a/spec/services/packages/pypi/create_package_service_spec.rb b/spec/services/packages/pypi/create_package_service_spec.rb
index 9f7fc15ae46..a18f6833071 100644
--- a/spec/services/packages/pypi/create_package_service_spec.rb
+++ b/spec/services/packages/pypi/create_package_service_spec.rb
@@ -24,9 +24,21 @@ RSpec.describe Packages::Pypi::CreatePackageService, :aggregate_failures, featur
}
end
- describe '#execute' do
- subject(:execute_service) { described_class.new(project, user, params).execute }
+ subject(:execute_service) { described_class.new(project, user, params).execute }
+ shared_examples 'an error response while not creating a pypi package' do |message:, reason:|
+ it_behaves_like 'returning an error service response', message: message
+ it { is_expected.to have_attributes(reason: reason) }
+
+ it 'does not create any pypi-related package records' do
+ expect { execute_service }
+ .to not_change { Packages::Package.count }
+ .and not_change { Packages::Package.pypi.count }
+ .and not_change { Packages::PackageFile.count }
+ end
+ end
+
+ describe '#execute' do
let(:created_package) { Packages::Package.pypi.last }
context 'without an existing package' do
@@ -150,16 +162,9 @@ RSpec.describe Packages::Pypi::CreatePackageService, :aggregate_failures, featur
params[:md5_digest] = md5
end
- it_behaves_like 'returning an error service response',
- message: 'Validation failed: File name has already been taken' do
- it { is_expected.to have_attributes(reason: :invalid_parameter) }
- end
-
- it 'does not create a pypi package' do
- expect { execute_service }
- .to change { Packages::Package.pypi.count }.by(0)
- .and change { Packages::PackageFile.count }.by(0)
- end
+ it_behaves_like 'an error response while not creating a pypi package',
+ message: 'Validation failed: File name has already been taken',
+ reason: :invalid_parameter
context 'with a pending_destruction package' do
before do
@@ -199,6 +204,22 @@ RSpec.describe Packages::Pypi::CreatePackageService, :aggregate_failures, featur
end
end
+ context 'with unauthorized user' do
+ let_it_be(:user) { create(:user) }
+
+ it_behaves_like 'an error response while not creating a pypi package',
+ message: 'Unauthorized',
+ reason: :unauthorized
+ end
+
+ context 'without user' do
+ let_it_be(:user) { nil }
+
+ it_behaves_like 'an error response while not creating a pypi package',
+ message: 'Unauthorized',
+ reason: :unauthorized
+ end
+
context 'with package protection rule for different roles and package_name_patterns' do
let_it_be_with_reload(:package_protection_rule) do
create(:package_protection_rule, package_type: :pypi, project: project)
@@ -245,8 +266,9 @@ RSpec.describe Packages::Pypi::CreatePackageService, :aggregate_failures, featur
end
shared_examples 'an error service response for unauthorized' do
- it_behaves_like 'returning an error service response', message: 'Unauthorized'
- it { is_expected.to have_attributes(reason: :invalid_parameter) }
+ it_behaves_like 'an error response while not creating a pypi package',
+ message: 'Unauthorized',
+ reason: :unauthorized
end
before do