diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index e65c8834ada..ff30ca95eda 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -6ed17e45a50357b3cece084cd26e833bc3443089 +575e8b7baeb800a8d0d0f28a80c0461a7a0db741 diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue index 1fce55ce804..6b9b96741fb 100644 --- a/app/assets/javascripts/diffs/components/tree_list.vue +++ b/app/assets/javascripts/diffs/components/tree_list.vue @@ -12,6 +12,7 @@ import micromatch from 'micromatch'; import { getModifierKey } from '~/constants'; import { s__, sprintf } from '~/locale'; import { RecycleScroller } from 'vendor/vue-virtual-scroller'; +import { isElementClipped } from '~/lib/utils/common_utils'; import DiffFileRow from './diff_file_row.vue'; import TreeListHeight from './tree_list_height.vue'; @@ -152,6 +153,8 @@ export default { ...mapActions('diffs', ['toggleTreeOpen', 'goToFile', 'setRenderTreeList', 'setTreeOpen']), scrollVirtualScrollerToFileHash(hash) { + const item = document.querySelector(`[data-file-row="${hash}"]`); + if (item && !isElementClipped(item, this.$refs.scroller.$el)) return; const index = this.treeList.findIndex((f) => f.fileHash === hash); if (index !== -1) { this.$refs.scroller.scrollToItem?.(index); @@ -234,6 +237,7 @@ export default { :class="{ 'tree-list-parent': item.level > 0 }" :tabindex="0" class="gl-relative !gl-m-1" + :data-file-row="item.fileHash" @toggleTreeOpen="toggleTreeOpen" @clickFile="(path) => goToFile({ path })" /> diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 6ff002eb76a..c99726e076f 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -149,6 +149,18 @@ export const isInViewport = (el, offset = {}) => { ); }; +export const isElementClipped = (element, scrollContainer) => { + const elementRect = element.getBoundingClientRect(); + const containerRect = scrollContainer.getBoundingClientRect(); + + return ( + elementRect.top < containerRect.top || + elementRect.bottom > containerRect.bottom || + elementRect.left < containerRect.left || + elementRect.right > containerRect.right + ); +}; + export const isModifierKey = (e) => e.metaKey || e.ctrlKey || e.altKey || e.shiftKey; export const isMetaKey = (e) => e.metaKey || e.ctrlKey; diff --git a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue index 344d4ddf829..0ffafff7b40 100644 --- a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue +++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue @@ -439,11 +439,6 @@ export default {
'.html_safe, code_end: '
'.html_safe },
help_text: _('Authenticate user SSH keys without requiring additional configuration. Performance of GitLab can be improved by using the GitLab database instead. %{link_start}How do I configure authentication using the GitLab database? %{link_end}').html_safe % { link_start: help_link_start, link_end: ''.html_safe}
.form-group
= f.label :raw_blob_request_limit, _('Raw blob request rate limit per minute'), class: 'label-bold'
diff --git a/config/application_setting_columns/secret_push_protection_available.yml b/config/application_setting_columns/secret_push_protection_available.yml
new file mode 100644
index 00000000000..8c2f8351c9d
--- /dev/null
+++ b/config/application_setting_columns/secret_push_protection_available.yml
@@ -0,0 +1,15 @@
+---
+api_type: boolean
+attr: secret_push_protection_available
+clusterwide: true
+column: secret_push_protection_available
+db_type: boolean
+default: 'false'
+description: Allow projects to enable secret push protection. This does not enable
+ secret push protection. When you enable this feature, you accept the [GitLab Testing
+ Agreement](https://handbook.gitlab.com/handbook/legal/testing-agreement/). Ultimate
+ only.
+encrypted: false
+gitlab_com_different_than_default: true
+jihu: false
+not_null: true
diff --git a/config/initializers/7_gitlab_http.rb b/config/initializers/7_gitlab_http.rb
index 567f0b726db..2b2c1b80304 100644
--- a/config/initializers/7_gitlab_http.rb
+++ b/config/initializers/7_gitlab_http.rb
@@ -23,7 +23,7 @@ Gitlab::HTTP_V2.configure do |config|
Gitlab::SilentMode.log_info(message: message, outbound_http_request_method: http_method)
end
config.log_with_level_proc = ->(log_level, message_params) do
- Gitlab::AppJsonLogger.public_send(log_level, message_params)
+ Gitlab::AppJsonLogger.public_send(log_level, Gitlab::ApplicationContext.current.merge(message_params))
end
end
diff --git a/db/docs/merge_requests_approval_rules.yml b/db/docs/merge_requests_approval_rules.yml
new file mode 100644
index 00000000000..fc3ebd5900c
--- /dev/null
+++ b/db/docs/merge_requests_approval_rules.yml
@@ -0,0 +1,13 @@
+---
+table_name: merge_requests_approval_rules
+classes:
+ - MergeRequests::ApprovalRule
+feature_categories:
+ - code_review_workflow
+description: Main table that stores information about approval rules v2.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/179839
+milestone: '17.9'
+gitlab_schema: gitlab_main_cell
+sharding_key:
+ group_id: namespaces
+ project_id: projects
diff --git a/db/migrate/20250114030220_rename_pre_receive_secret_detection_enabled_to_secret_push_protection_enabled.rb b/db/migrate/20250114030220_rename_pre_receive_secret_detection_enabled_to_secret_push_protection_enabled.rb
new file mode 100644
index 00000000000..a7966414a7f
--- /dev/null
+++ b/db/migrate/20250114030220_rename_pre_receive_secret_detection_enabled_to_secret_push_protection_enabled.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class RenamePreReceiveSecretDetectionEnabledToSecretPushProtectionEnabled < Gitlab::Database::Migration[2.2]
+ milestone '17.9'
+ disable_ddl_transaction!
+
+ TABLE = :project_security_settings
+
+ def up
+ rename_column_concurrently TABLE, :pre_receive_secret_detection_enabled,
+ :secret_push_protection_enabled, batch_column_name: :project_id
+ end
+
+ def down
+ undo_rename_column_concurrently TABLE, :pre_receive_secret_detection_enabled,
+ :secret_push_protection_enabled
+ end
+end
diff --git a/db/migrate/20250114030822_rename_pre_receive_secret_detection_enabled_to_secret_push_protection_available.rb b/db/migrate/20250114030822_rename_pre_receive_secret_detection_enabled_to_secret_push_protection_available.rb
new file mode 100644
index 00000000000..59285556da9
--- /dev/null
+++ b/db/migrate/20250114030822_rename_pre_receive_secret_detection_enabled_to_secret_push_protection_available.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class RenamePreReceiveSecretDetectionEnabledToSecretPushProtectionAvailable < Gitlab::Database::Migration[2.2]
+ milestone '17.9'
+ disable_ddl_transaction!
+
+ TABLE = :application_settings
+
+ def up
+ rename_column_concurrently TABLE, :pre_receive_secret_detection_enabled, :secret_push_protection_available
+ end
+
+ def down
+ undo_rename_column_concurrently TABLE, :pre_receive_secret_detection_enabled, :secret_push_protection_available
+ end
+end
diff --git a/db/migrate/20250123151650_create_merge_requests_approval_rules.rb b/db/migrate/20250123151650_create_merge_requests_approval_rules.rb
new file mode 100644
index 00000000000..700e18d4ded
--- /dev/null
+++ b/db/migrate/20250123151650_create_merge_requests_approval_rules.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class CreateMergeRequestsApprovalRules < Gitlab::Database::Migration[2.2]
+ milestone '17.9'
+
+ def change
+ create_table :merge_requests_approval_rules do |t| # -- Migration/EnsureFactoryForTable false positive
+ t.text :name, limit: 255, null: false
+ t.integer :approvals_required, null: false, default: 0
+ t.integer :rule_type, null: false, default: 0, limit: 2
+ t.integer :origin, null: false, default: 0, limit: 2
+ t.bigint :project_id, null: true
+ t.bigint :group_id, null: true
+ t.bigint :source_rule_id, null: true
+ t.index :project_id
+ t.index :group_id
+ t.index :source_rule_id
+ t.timestamps_with_timezone null: false
+ end
+ end
+end
diff --git a/db/migrate/20250205094214_add_merge_requests_approval_rules_source_rule_fk.rb b/db/migrate/20250205094214_add_merge_requests_approval_rules_source_rule_fk.rb
new file mode 100644
index 00000000000..03a6a9ba8a7
--- /dev/null
+++ b/db/migrate/20250205094214_add_merge_requests_approval_rules_source_rule_fk.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddMergeRequestsApprovalRulesSourceRuleFk < Gitlab::Database::Migration[2.2]
+ milestone '17.9'
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :merge_requests_approval_rules, :merge_requests_approval_rules, column: :source_rule_id,
+ on_delete: :nullify
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :merge_requests_approval_rules, column: :source_rule_id
+ end
+ end
+end
diff --git a/db/migrate/20250205094243_add_merge_requests_approval_rules_project_fk.rb b/db/migrate/20250205094243_add_merge_requests_approval_rules_project_fk.rb
new file mode 100644
index 00000000000..40a1ee3859b
--- /dev/null
+++ b/db/migrate/20250205094243_add_merge_requests_approval_rules_project_fk.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddMergeRequestsApprovalRulesProjectFk < Gitlab::Database::Migration[2.2]
+ milestone '17.9'
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :merge_requests_approval_rules, :projects, column: :project_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :merge_requests_approval_rules, column: :project_id
+ end
+ end
+end
diff --git a/db/migrate/20250205094302_add_merge_requests_approval_rules_group_fk.rb b/db/migrate/20250205094302_add_merge_requests_approval_rules_group_fk.rb
new file mode 100644
index 00000000000..b558885fda6
--- /dev/null
+++ b/db/migrate/20250205094302_add_merge_requests_approval_rules_group_fk.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddMergeRequestsApprovalRulesGroupFk < Gitlab::Database::Migration[2.2]
+ milestone '17.9'
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :merge_requests_approval_rules, :namespaces, column: :group_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :merge_requests_approval_rules, column: :group_id
+ end
+ end
+end
diff --git a/db/migrate/20250205094331_add_merge_requests_approval_rules_multi_column_not_null_constraint.rb b/db/migrate/20250205094331_add_merge_requests_approval_rules_multi_column_not_null_constraint.rb
new file mode 100644
index 00000000000..b34c7ea12a4
--- /dev/null
+++ b/db/migrate/20250205094331_add_merge_requests_approval_rules_multi_column_not_null_constraint.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class AddMergeRequestsApprovalRulesMultiColumnNotNullConstraint < Gitlab::Database::Migration[2.2]
+ milestone '17.9'
+ disable_ddl_transaction!
+
+ def up
+ add_multi_column_not_null_constraint(:merge_requests_approval_rules, :group_id, :project_id)
+ end
+
+ def down
+ remove_multi_column_not_null_constraint(:merge_requests_approval_rules, :group_id, :project_id)
+ end
+end
diff --git a/db/schema_migrations/20250114030220 b/db/schema_migrations/20250114030220
new file mode 100644
index 00000000000..2244a495a22
--- /dev/null
+++ b/db/schema_migrations/20250114030220
@@ -0,0 +1 @@
+555bf26fb6ee2d65df9aa7ebde9be19a8681563a053b74d3c9c2df1e2dc9dda9
\ No newline at end of file
diff --git a/db/schema_migrations/20250114030822 b/db/schema_migrations/20250114030822
new file mode 100644
index 00000000000..2a744efc277
--- /dev/null
+++ b/db/schema_migrations/20250114030822
@@ -0,0 +1 @@
+2d74e09c4f8df3a0e49762bc9bde7c802c33c0f1ac1498b83a3080dc35b93ba2
\ No newline at end of file
diff --git a/db/schema_migrations/20250123151650 b/db/schema_migrations/20250123151650
new file mode 100644
index 00000000000..6fbb499a264
--- /dev/null
+++ b/db/schema_migrations/20250123151650
@@ -0,0 +1 @@
+d149047a5d5fa4fa8242fde7f0d266cebef8d56e0db282d745c6da6e0f9f1c1d
\ No newline at end of file
diff --git a/db/schema_migrations/20250205094214 b/db/schema_migrations/20250205094214
new file mode 100644
index 00000000000..d8a44e1c1b5
--- /dev/null
+++ b/db/schema_migrations/20250205094214
@@ -0,0 +1 @@
+b22251b9e81427177253b4121f677e6a690378b55cc2836e3da0019b71822629
\ No newline at end of file
diff --git a/db/schema_migrations/20250205094243 b/db/schema_migrations/20250205094243
new file mode 100644
index 00000000000..45f04e8079f
--- /dev/null
+++ b/db/schema_migrations/20250205094243
@@ -0,0 +1 @@
+6d8e72cc35740ab5214c24b9d2094bce9289edd5c999ef89d4c2aa9b4d949a07
\ No newline at end of file
diff --git a/db/schema_migrations/20250205094302 b/db/schema_migrations/20250205094302
new file mode 100644
index 00000000000..b54934da8ad
--- /dev/null
+++ b/db/schema_migrations/20250205094302
@@ -0,0 +1 @@
+89018d03700f67c3d24a34d03529697d7e75dcffad09c6234239a7c661746071
\ No newline at end of file
diff --git a/db/schema_migrations/20250205094331 b/db/schema_migrations/20250205094331
new file mode 100644
index 00000000000..c5eb507d3af
--- /dev/null
+++ b/db/schema_migrations/20250205094331
@@ -0,0 +1 @@
+660df89c5562b36729e4f93d57ad28b761a275105a806eb91258fb01f3b3703f
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 0943c8d31fe..f40d29d46d7 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -403,6 +403,74 @@ BEGIN
END;
$$;
+CREATE FUNCTION function_for_trigger_1baf8c8e1f66() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ NEW."pre_receive_secret_detection_enabled" := NEW."secret_push_protection_available";
+ RETURN NEW;
+END
+$$;
+
+CREATE FUNCTION function_for_trigger_7f41427eda69() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ NEW."secret_push_protection_available" := NEW."pre_receive_secret_detection_enabled";
+ RETURN NEW;
+END
+$$;
+
+CREATE FUNCTION function_for_trigger_7fbecfcdf89a() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ NEW."pre_receive_secret_detection_enabled" := NEW."secret_push_protection_enabled";
+ RETURN NEW;
+END
+$$;
+
+CREATE FUNCTION function_for_trigger_897f35481f9a() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ NEW."secret_push_protection_enabled" := NEW."pre_receive_secret_detection_enabled";
+ RETURN NEW;
+END
+$$;
+
+CREATE FUNCTION function_for_trigger_b9839c6d713f() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ IF NEW."pre_receive_secret_detection_enabled" IS NOT DISTINCT FROM 'false' AND NEW."secret_push_protection_available" IS DISTINCT FROM 'false' THEN
+ NEW."pre_receive_secret_detection_enabled" = NEW."secret_push_protection_available";
+ END IF;
+
+ IF NEW."secret_push_protection_available" IS NOT DISTINCT FROM 'false' AND NEW."pre_receive_secret_detection_enabled" IS DISTINCT FROM 'false' THEN
+ NEW."secret_push_protection_available" = NEW."pre_receive_secret_detection_enabled";
+ END IF;
+
+ RETURN NEW;
+END
+$$;
+
+CREATE FUNCTION function_for_trigger_cbecfadbc3e8() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ IF NEW."pre_receive_secret_detection_enabled" IS NOT DISTINCT FROM 'false' AND NEW."secret_push_protection_enabled" IS DISTINCT FROM 'false' THEN
+ NEW."pre_receive_secret_detection_enabled" = NEW."secret_push_protection_enabled";
+ END IF;
+
+ IF NEW."secret_push_protection_enabled" IS NOT DISTINCT FROM 'false' AND NEW."pre_receive_secret_detection_enabled" IS DISTINCT FROM 'false' THEN
+ NEW."secret_push_protection_enabled" = NEW."pre_receive_secret_detection_enabled";
+ END IF;
+
+ RETURN NEW;
+END
+$$;
+
CREATE FUNCTION gitlab_schema_prevent_write() RETURNS trigger
LANGUAGE plpgsql
AS $$
@@ -8037,6 +8105,7 @@ CREATE TABLE application_settings (
observability_settings jsonb DEFAULT '{}'::jsonb NOT NULL,
search jsonb DEFAULT '{}'::jsonb NOT NULL,
anti_abuse_settings jsonb DEFAULT '{}'::jsonb NOT NULL,
+ secret_push_protection_available boolean DEFAULT false,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
CONSTRAINT app_settings_ext_pipeline_validation_service_url_text_limit CHECK ((char_length(external_pipeline_validation_service_url) <= 255)),
@@ -8104,6 +8173,7 @@ CREATE TABLE application_settings (
CONSTRAINT check_application_settings_transactional_emails_is_hash CHECK ((jsonb_typeof(transactional_emails) = 'object'::text)),
CONSTRAINT check_b8c74ea5b3 CHECK ((char_length(deactivation_email_additional_text) <= 1000)),
CONSTRAINT check_babd774f3c CHECK ((char_length(secret_detection_service_url) <= 255)),
+ CONSTRAINT check_be6ab41dcc CHECK ((secret_push_protection_available IS NOT NULL)),
CONSTRAINT check_bf5157a366 CHECK ((char_length(required_instance_ci_template) <= 1024)),
CONSTRAINT check_cdfbd99405 CHECK ((char_length(security_txt_content) <= 2048)),
CONSTRAINT check_d03919528d CHECK ((char_length(container_registry_vendor) <= 255)),
@@ -15972,6 +16042,30 @@ CREATE TABLE merge_requests (
CONSTRAINT check_970d272570 CHECK ((lock_version IS NOT NULL))
);
+CREATE TABLE merge_requests_approval_rules (
+ id bigint NOT NULL,
+ name text NOT NULL,
+ approvals_required integer DEFAULT 0 NOT NULL,
+ rule_type smallint DEFAULT 0 NOT NULL,
+ origin smallint DEFAULT 0 NOT NULL,
+ project_id bigint,
+ group_id bigint,
+ source_rule_id bigint,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ CONSTRAINT check_ba7b03c61a CHECK ((num_nonnulls(group_id, project_id) = 1)),
+ CONSTRAINT check_c7c36145b7 CHECK ((char_length(name) <= 255))
+);
+
+CREATE SEQUENCE merge_requests_approval_rules_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE merge_requests_approval_rules_id_seq OWNED BY merge_requests_approval_rules.id;
+
CREATE TABLE merge_requests_closing_issues (
id bigint NOT NULL,
merge_request_id bigint NOT NULL,
@@ -19539,7 +19633,9 @@ CREATE TABLE project_security_settings (
auto_fix_sast boolean DEFAULT true NOT NULL,
continuous_vulnerability_scans_enabled boolean DEFAULT false NOT NULL,
container_scanning_for_registry_enabled boolean DEFAULT false NOT NULL,
- pre_receive_secret_detection_enabled boolean DEFAULT false NOT NULL
+ pre_receive_secret_detection_enabled boolean DEFAULT false NOT NULL,
+ secret_push_protection_enabled boolean DEFAULT false,
+ CONSTRAINT check_20a23efdb6 CHECK ((secret_push_protection_enabled IS NOT NULL))
);
CREATE SEQUENCE project_security_settings_project_id_seq
@@ -25202,6 +25298,8 @@ ALTER TABLE ONLY merge_request_user_mentions ALTER COLUMN id SET DEFAULT nextval
ALTER TABLE ONLY merge_requests ALTER COLUMN id SET DEFAULT nextval('merge_requests_id_seq'::regclass);
+ALTER TABLE ONLY merge_requests_approval_rules ALTER COLUMN id SET DEFAULT nextval('merge_requests_approval_rules_id_seq'::regclass);
+
ALTER TABLE ONLY merge_requests_closing_issues ALTER COLUMN id SET DEFAULT nextval('merge_requests_closing_issues_id_seq'::regclass);
ALTER TABLE ONLY merge_requests_compliance_violations ALTER COLUMN id SET DEFAULT nextval('merge_requests_compliance_violations_id_seq'::regclass);
@@ -27760,6 +27858,9 @@ ALTER TABLE ONLY merge_request_reviewers
ALTER TABLE ONLY merge_request_user_mentions
ADD CONSTRAINT merge_request_user_mentions_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY merge_requests_approval_rules
+ ADD CONSTRAINT merge_requests_approval_rules_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY merge_requests_closing_issues
ADD CONSTRAINT merge_requests_closing_issues_pkey PRIMARY KEY (id);
@@ -33143,6 +33244,12 @@ CREATE INDEX index_merge_request_reviewers_on_user_id ON merge_request_reviewers
CREATE UNIQUE INDEX index_merge_request_user_mentions_on_note_id ON merge_request_user_mentions USING btree (note_id) WHERE (note_id IS NOT NULL);
+CREATE INDEX index_merge_requests_approval_rules_on_group_id ON merge_requests_approval_rules USING btree (group_id);
+
+CREATE INDEX index_merge_requests_approval_rules_on_project_id ON merge_requests_approval_rules USING btree (project_id);
+
+CREATE INDEX index_merge_requests_approval_rules_on_source_rule_id ON merge_requests_approval_rules USING btree (source_rule_id);
+
CREATE INDEX index_merge_requests_closing_issues_on_issue_id ON merge_requests_closing_issues USING btree (issue_id);
CREATE INDEX index_merge_requests_closing_issues_on_merge_request_id ON merge_requests_closing_issues USING btree (merge_request_id);
@@ -37729,6 +37836,8 @@ CREATE TRIGGER trigger_174b23fa3dfb BEFORE INSERT OR UPDATE ON approval_project_
CREATE TRIGGER trigger_18bc439a6741 BEFORE INSERT OR UPDATE ON packages_conan_metadata FOR EACH ROW EXECUTE FUNCTION trigger_18bc439a6741();
+CREATE TRIGGER trigger_1baf8c8e1f66 BEFORE UPDATE OF secret_push_protection_available ON application_settings FOR EACH ROW EXECUTE FUNCTION function_for_trigger_1baf8c8e1f66();
+
CREATE TRIGGER trigger_1c0f1ca199a3 BEFORE INSERT OR UPDATE ON ci_resources FOR EACH ROW EXECUTE FUNCTION trigger_1c0f1ca199a3();
CREATE TRIGGER trigger_1ed40f4d5f4e BEFORE INSERT OR UPDATE ON packages_maven_metadata FOR EACH ROW EXECUTE FUNCTION trigger_1ed40f4d5f4e();
@@ -37853,6 +37962,10 @@ CREATE TRIGGER trigger_7de792ddbc05 BEFORE INSERT OR UPDATE ON dast_site_validat
CREATE TRIGGER trigger_7e2eed79e46e BEFORE INSERT OR UPDATE ON abuse_reports FOR EACH ROW EXECUTE FUNCTION trigger_7e2eed79e46e();
+CREATE TRIGGER trigger_7f41427eda69 BEFORE UPDATE OF pre_receive_secret_detection_enabled ON application_settings FOR EACH ROW EXECUTE FUNCTION function_for_trigger_7f41427eda69();
+
+CREATE TRIGGER trigger_7fbecfcdf89a BEFORE UPDATE OF secret_push_protection_enabled ON project_security_settings FOR EACH ROW EXECUTE FUNCTION function_for_trigger_7fbecfcdf89a();
+
CREATE TRIGGER trigger_81b4c93e7133 BEFORE INSERT OR UPDATE ON pages_deployment_states FOR EACH ROW EXECUTE FUNCTION trigger_81b4c93e7133();
CREATE TRIGGER trigger_8204480b3a2e BEFORE INSERT OR UPDATE ON incident_management_escalation_rules FOR EACH ROW EXECUTE FUNCTION trigger_8204480b3a2e();
@@ -37861,6 +37974,8 @@ CREATE TRIGGER trigger_84d67ad63e93 BEFORE INSERT OR UPDATE ON wiki_page_slugs F
CREATE TRIGGER trigger_85d89f0f11db BEFORE INSERT OR UPDATE ON issue_metrics FOR EACH ROW EXECUTE FUNCTION trigger_85d89f0f11db();
+CREATE TRIGGER trigger_897f35481f9a BEFORE UPDATE OF pre_receive_secret_detection_enabled ON project_security_settings FOR EACH ROW EXECUTE FUNCTION function_for_trigger_897f35481f9a();
+
CREATE TRIGGER trigger_8a38ce2327de BEFORE INSERT OR UPDATE ON boards_epic_user_preferences FOR EACH ROW EXECUTE FUNCTION trigger_8a38ce2327de();
CREATE TRIGGER trigger_8ac78f164b2d BEFORE INSERT OR UPDATE ON design_management_repositories FOR EACH ROW EXECUTE FUNCTION trigger_8ac78f164b2d();
@@ -37923,6 +38038,8 @@ CREATE TRIGGER trigger_b7abb8fc4cf0 BEFORE INSERT OR UPDATE ON work_item_progres
CREATE TRIGGER trigger_b8eecea7f351 BEFORE INSERT OR UPDATE ON dependency_proxy_manifest_states FOR EACH ROW EXECUTE FUNCTION trigger_b8eecea7f351();
+CREATE TRIGGER trigger_b9839c6d713f BEFORE INSERT ON application_settings FOR EACH ROW EXECUTE FUNCTION function_for_trigger_b9839c6d713f();
+
CREATE TRIGGER trigger_c17a166692a2 BEFORE INSERT OR UPDATE ON audit_events_streaming_headers FOR EACH ROW EXECUTE FUNCTION trigger_c17a166692a2();
CREATE TRIGGER trigger_c59fe6f31e71 BEFORE INSERT OR UPDATE ON security_orchestration_policy_rule_schedules FOR EACH ROW EXECUTE FUNCTION trigger_c59fe6f31e71();
@@ -37937,6 +38054,8 @@ CREATE TRIGGER trigger_cac7c0698291 BEFORE INSERT OR UPDATE ON evidences FOR EAC
CREATE TRIGGER trigger_catalog_resource_sync_event_on_project_update AFTER UPDATE ON projects FOR EACH ROW WHEN ((((old.name)::text IS DISTINCT FROM (new.name)::text) OR (old.description IS DISTINCT FROM new.description) OR (old.visibility_level IS DISTINCT FROM new.visibility_level))) EXECUTE FUNCTION insert_catalog_resource_sync_event();
+CREATE TRIGGER trigger_cbecfadbc3e8 BEFORE INSERT ON project_security_settings FOR EACH ROW EXECUTE FUNCTION function_for_trigger_cbecfadbc3e8();
+
CREATE TRIGGER trigger_cd50823537a3 BEFORE INSERT OR UPDATE ON issuable_slas FOR EACH ROW EXECUTE FUNCTION trigger_cd50823537a3();
CREATE TRIGGER trigger_cf646a118cbb BEFORE INSERT OR UPDATE ON milestone_releases FOR EACH ROW EXECUTE FUNCTION trigger_cf646a118cbb();
@@ -38070,6 +38189,9 @@ ALTER TABLE ONLY cluster_agent_url_configurations
ALTER TABLE ONLY incident_management_escalation_rules
ADD CONSTRAINT fk_0314ee86eb FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+ALTER TABLE ONLY merge_requests_approval_rules
+ ADD CONSTRAINT fk_03983bf729 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY audit_events_instance_google_cloud_logging_configurations
ADD CONSTRAINT fk_03a15ca4fa FOREIGN KEY (stream_destination_id) REFERENCES audit_events_instance_external_streaming_destinations(id) ON DELETE SET NULL;
@@ -38943,6 +39065,9 @@ ALTER TABLE ONLY scan_result_policies
ALTER TABLE ONLY catalog_resource_versions
ADD CONSTRAINT fk_7ad8849db4 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY merge_requests_approval_rules
+ ADD CONSTRAINT fk_7af76dbd21 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY issue_customer_relations_contacts
ADD CONSTRAINT fk_7b92f835bb FOREIGN KEY (contact_id) REFERENCES customer_relations_contacts(id) ON DELETE CASCADE;
@@ -39897,6 +40022,9 @@ ALTER TABLE ONLY application_settings
ALTER TABLE ONLY issuable_severities
ADD CONSTRAINT fk_f9df19ecb6 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY merge_requests_approval_rules
+ ADD CONSTRAINT fk_fa5b38e373 FOREIGN KEY (source_rule_id) REFERENCES merge_requests_approval_rules(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY clusters_managed_resources
ADD CONSTRAINT fk_fad3c3b2e2 FOREIGN KEY (environment_id) REFERENCES environments(id) ON DELETE CASCADE;
diff --git a/doc/administration/gitlab_duo_self_hosted/configuration_types.md b/doc/administration/gitlab_duo_self_hosted/configuration_types.md
new file mode 100644
index 00000000000..4fc3253b102
--- /dev/null
+++ b/doc/administration/gitlab_duo_self_hosted/configuration_types.md
@@ -0,0 +1,111 @@
+---
+stage: AI-Powered
+group: Custom Models
+description: Get started with GitLab Duo Self-Hosted.
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
+title: GitLab Duo Self-Hosted configuration and authentication
+---
+
+DETAILS:
+**Tier:** Ultimate with GitLab Duo Enterprise - [Start a trial](https://about.gitlab.com/solutions/gitlab-duo-pro/sales/?type=free-trial)
+**Offering:** GitLab Self-Managed
+**Status:** Beta
+
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/12972) in GitLab 17.1 [with a flag](../feature_flags.md) named `ai_custom_model`. Disabled by default.
+> - [Enabled on GitLab Self-Managed](https://gitlab.com/groups/gitlab-org/-/epics/15176) in GitLab 17.6.
+> - Changed to require GitLab Duo add-on in GitLab 17.6 and later.
+> - Feature flag `ai_custom_model` removed in GitLab 17.8
+
+There are two configuration options for self-managed customers:
+
+- **GitLab.com AI gateway**: Use the GitLab-managed AI gateway with default external
+ large language model (LLM) providers (for example, Google Vertex or Anthropic).
+- **Self-hosted AI gateway**: Deploy and manage your own AI gateway and language models in your infrastructure,
+ without depending on GitLab-provided external language providers.
+
+## GitLab.com AI gateway
+
+In this configuration, your GitLab instance depends on and sends requests to the external GitLab AI gateway, which communicates with external AI vendors such as Google Vertex or Anthropic. The response is then forwarded back to your GitLab instance.
+
+```mermaid
+%%{init: { "theme": "default", "fontFamily": "GitLab Sans", "sequence": { "actorFontSize": 12, "participantFontSize": 12, "messageFontSize": 12 } }}%%
+sequenceDiagram
+ actor User as User
+ participant SelfHostedGitLab as Self-hosted GitLab (Your Instance)
+ participant GitLabAIGateway as GitLab AI gateway (External)
+ participant GitLabAIVendor as GitLab AI Vendor (External)
+
+ User ->> SelfHostedGitLab: Send request
+ SelfHostedGitLab ->> SelfHostedGitLab: Check if self-hosted model is configured
+ SelfHostedGitLab ->> GitLabAIGateway: Forward request for AI processing
+ GitLabAIGateway ->> GitLabAIVendor: Create prompt and send request to AI model server
+ GitLabAIVendor -->> GitLabAIGateway: Respond to the prompt
+ GitLabAIGateway -->> SelfHostedGitLab: Forward AI response
+ SelfHostedGitLab -->> User: Forward AI response
+```
+
+## Self-hosted AI gateway
+
+In this configuration, the entire system is isolated within the enterprise, ensuring a fully self-hosted environment that safeguards data privacy.
+
+```mermaid
+%%{init: { "theme": "default", "fontFamily": "GitLab Sans", "sequence": { "actorFontSize": 12, "participantFontSize": 12, "messageFontSize": 12 } }}%%
+sequenceDiagram
+ actor User as User
+ participant SelfHostedGitLab as Self-hosted GitLab
+ participant SelfHostedAIGateway as Self-hosted AI gateway
+ participant SelfHostedModel as Self-hosted model
+
+ User ->> SelfHostedGitLab: Send request
+ SelfHostedGitLab ->> SelfHostedGitLab: Check if self-hosted model is configured
+ SelfHostedAIGateway ->> SelfHostedModel: Create prompt and perform request to AI model server
+ SelfHostedGitLab ->> SelfHostedAIGateway: Forward request for AI processing
+ SelfHostedModel -->> SelfHostedAIGateway: Respond to the prompt
+ SelfHostedAIGateway -->> SelfHostedGitLab: Forward AI response
+ SelfHostedGitLab -->> User: Forward AI response
+```
+
+For more information, see the [self-hosted model deployment blueprint](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/custom_models/).
+
+## Authentication for GitLab Duo Self-Hosted
+
+The authentication process for GitLab Duo Self-Hosted is secure, efficient, and made up of the following key components:
+
+- **Self-issued tokens**: In this architecture, access credentials are not synchronized with `cloud.gitlab.com`. Instead, tokens are self-issued dynamically, similar to the functionality on GitLab.com. This method provides users with immediate access while maintaining a high level of security.
+
+- **Offline environments**: In offline setups, there are no connections to `cloud.gitlab.com`. All requests are routed exclusively to the self-hosted AI gateway.
+
+- **Token minting and verification**: The instance mints the token, which is then verified by the AI gateway against the GitLab instance.
+
+- **Model configuration and security**: When an administrator configures a model, they can incorporate an API key to authenticate requests. Additionally, you can enhance security by specifying connection IP addresses within your network, ensuring that only trusted IPs can interact with the model.
+
+As illustrated in the following diagram:
+
+1. The authentication flow begins when the user configures the model through the GitLab instance and submits a request to access the GitLab Duo feature.
+1. The GitLab instance mints an access token, which the user forwards to GitLab and then to the AI gateway for verification.
+1. Upon confirming the token's validity, the AI gateway sends a request to the AI model, which uses the API key to authenticate the request and process it.
+1. The results are then relayed back to the GitLab instance, completing the flow by sending the response to the user, which is designed to be secure and efficient.
+
+```mermaid
+%%{init: { "theme": "default", "fontFamily": "GitLab Sans", "sequence": { "actorFontSize": 12, "participantFontSize": 12, "messageFontSize": 12 } }}%%
+ sequenceDiagram
+ participant User as User
+ participant GitLab as GitLab Instance
+ participant AI gateway as AI gateway
+ participant AIModel as AI Model
+
+ User->>GitLab: Configure Model
+ User->>GitLab: Request Access
+ GitLab->>GitLab: Mint Token
+ GitLab->>User: Send Token
+ User->>GitLab: Forward Minted Token
+ GitLab->>AI gateway: Verify Token
+ AI gateway->>GitLab: Token Validated
+ GitLab->>AI gateway: Send Request to Model
+ AI gateway->>AIModel: Send Request to Model
+ AIModel->>AIModel: Authenticate using API Key
+ AIModel->>AI gateway: Process Request
+ AI gateway->>GitLab: Send Result to GitLab
+ GitLab->>User: Send Response
+
+```
diff --git a/doc/administration/gitlab_duo_self_hosted/configure_duo_features.md b/doc/administration/gitlab_duo_self_hosted/configure_duo_features.md
new file mode 100644
index 00000000000..1eabf1823ab
--- /dev/null
+++ b/doc/administration/gitlab_duo_self_hosted/configure_duo_features.md
@@ -0,0 +1,158 @@
+---
+stage: AI-Powered
+group: Custom Models
+description: Configure your GitLab instance to use GitLab Duo Self-Hosted.
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
+title: Configure GitLab to access GitLab Duo Self-Hosted
+---
+
+DETAILS:
+**Tier:** Ultimate with GitLab Duo Enterprise - [Start a trial](https://about.gitlab.com/solutions/gitlab-duo-pro/sales/?type=free-trial)
+**Offering:** GitLab Self-Managed
+**Status:** Beta
+
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/12972) in GitLab 17.1 [with a flag](../feature_flags.md) named `ai_custom_model`. Disabled by default.
+> - [Enabled on GitLab Self-Managed](https://gitlab.com/groups/gitlab-org/-/epics/15176) in GitLab 17.6.
+> - Changed to require GitLab Duo add-on in GitLab 17.6 and later.
+> - Feature flag `ai_custom_model` removed in GitLab 17.8
+
+To configure your GitLab instance to access the available self-hosted models in your infrastructure:
+
+1. [Confirm that a fully self-hosted configuration is appropriate for your use case](index.md#decide-on-your-configuration-type).
+1. Configure your GitLab instance.
+1. Configure the self-hosted model.
+1. Configure the GitLab Duo features to use your self-hosted model.
+
+## Configure your GitLab instance
+
+Prerequisites:
+
+- [Upgrade to the latest version of GitLab](../../update/_index.md).
+
+To configure your GitLab instance to access the AI gateway:
+
+::Tabs
+
+:::TabTitle Linux package
+
+1. Where your GitLab instance is installed, update the `/etc/gitlab/gitlab.rb` file:
+
+ ```shell
+ sudo vim /etc/gitlab/gitlab.rb
+ ```
+
+1. Add and save the following environment variables:
+
+ ```ruby
+ gitlab_rails['env'] = {
+ 'AI_GATEWAY_URL' => '