diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index d15596e156a..65a154b8d3a 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-c29c5ba968d141237000185734baa95e122db80b
+1a16fa05c2645a0abba4e2f028e1fdbe5d85be2f
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index 7d8ff13826d..03e954d1859 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -180,7 +180,6 @@ export default {
return {
'is-replying gl-pt-0!': this.isReplying,
'internal-note': this.isDiscussionInternal,
- 'public-note': !this.isDiscussionInternal,
'gl-pt-0!': !this.discussion.diff_discussion && this.isReplying,
};
},
diff --git a/app/assets/javascripts/observability/utils.js b/app/assets/javascripts/observability/utils.js
index 697f67d49c1..6ad3d954266 100644
--- a/app/assets/javascripts/observability/utils.js
+++ b/app/assets/javascripts/observability/utils.js
@@ -1,5 +1,6 @@
import { padWithZeros } from '~/lib/utils/datetime/date_format_utility';
import { isValidDate, differenceInMinutes } from '~/lib/utils/datetime_utility';
+import { mergeUrlParams } from '~/lib/utils/url_utility';
import {
CUSTOM_DATE_RANGE_OPTION,
@@ -193,3 +194,9 @@ export function isTracingDateRangeOutOfBounds({ value, startDate, endDate }) {
}
return false;
}
+
+export function urlWithStringifiedPayloadParam(url, payload, paramName) {
+ return mergeUrlParams({ [paramName]: JSON.stringify(payload) }, url, {
+ spreadArrays: true,
+ });
+}
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index 5628ed3b8fd..4a5a27f83da 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -43,7 +43,7 @@
background-color: $line-target-blue;
}
- + .public-note.discussion-reply-holder {
+ + .discussion-reply-holder {
padding-top: $gl-padding-12 !important;
}
diff --git a/app/models/ci/build_trace_metadata.rb b/app/models/ci/build_trace_metadata.rb
index dd5bf13746f..03ea34f7f35 100644
--- a/app/models/ci/build_trace_metadata.rb
+++ b/app/models/ci/build_trace_metadata.rb
@@ -27,10 +27,20 @@ module Ci
record = find_by(build_id: build_id, partition_id: partition_id)
return record if record
- upsert({ build_id: build_id, partition_id: partition_id }, unique_by: :build_id)
+ upsert({ build_id: build_id, partition_id: partition_id }, unique_by: unique_by_columns_for_upsert)
find_by!(build_id: build_id, partition_id: partition_id)
end
+ # Temporary fix to correctly identify an unique index for upsert
+ # should be replaced with `%w[build_id partition_id]` in %17.5
+ def self.unique_by_columns_for_upsert
+ unique_indexes = connection.schema_cache.indexes(table_name).select(&:unique)
+ columns = %w[partition_id build_id]
+ return columns if unique_indexes.any? { |index| index.columns == columns }
+
+ columns.reverse
+ end
+
# The job is retried around 5 times during the 7 days retention period for
# trace chunks as defined in `Ci::BuildTraceChunks::RedisBase::CHUNK_REDIS_TTL`
def can_attempt_archival_now?
diff --git a/app/policies/virtual_registries/packages/policies/group_policy.rb b/app/policies/virtual_registries/packages/policies/group_policy.rb
index d14e22d50c8..25f7d9e9927 100644
--- a/app/policies/virtual_registries/packages/policies/group_policy.rb
+++ b/app/policies/virtual_registries/packages/policies/group_policy.rb
@@ -18,7 +18,7 @@ module VirtualRegistries
prevent(*create_read_update_admin_destroy(:virtual_registry))
end
- rule { can?(:read_group) }.policy do
+ rule { group.guest | admin | group.has_projects }.policy do
enable :read_virtual_registry
end
diff --git a/app/services/concerns/work_items/widgetable_service.rb b/app/services/concerns/work_items/widgetable_service.rb
index 2f18a183e3a..ca9c585e851 100644
--- a/app/services/concerns/work_items/widgetable_service.rb
+++ b/app/services/concerns/work_items/widgetable_service.rb
@@ -60,16 +60,13 @@ module WorkItems
def interpret_quick_actions!(work_item, widget_params, attributes = {})
return unless work_item.has_widget?(:description)
- widget_description_param = widget_params[::WorkItems::Widgets::Description.api_symbol]
- return unless widget_description_param
+ description_widget_params = widget_params[::WorkItems::Widgets::Description.api_symbol]
+ return unless description_widget_params
- merge_quick_actions_into_params!(work_item, params: widget_description_param)
+ merge_quick_actions_into_params!(work_item, params: description_widget_params)
- # cleanup `description` param so that it is not passed into common params after transform_quick_action_params
- quick_action_params = widget_description_param.dup
- quick_action_params.delete(:description)
-
- parsed_params = work_item.transform_quick_action_params(quick_action_params)
+ # exclude `description` param so that it is not passed into common params after transform_quick_action_params
+ parsed_params = work_item.transform_quick_action_params(description_widget_params.except(:description))
widget_params.merge!(parsed_params[:widgets])
attributes.merge!(parsed_params[:common])
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index fc03c87bda2..8b20bffc4b0 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -217,42 +217,24 @@ class IssuableBaseService < ::BaseContainerService
# If the description has not been edited, then just remove any quick actions
# in the current description.
def merge_quick_actions_into_params!(issuable, params:, only: nil)
- interpret_params = quick_action_options
- unedited_description = issuable.description
- edited_description = params.fetch(:description, issuable.description)
+ target_description = params.fetch(:description, issuable.description)
- target_text = issuable.new_record? || params[:description] ? edited_description : unedited_description
-
- # only set the original_text if we're editing the issuable
- original_text = params[:description] && !issuable.new_record? ? unedited_description : nil
-
- sanitized_description, sanitized_command_params = interpret_quick_actions(target_text, issuable, params: interpret_params, only: only, original_text: original_text)
-
- unless issuable.new_record? || params[:description]
- edited_description = unedited_description
- sanitized_command_params = nil
- end
+ description, command_params = QuickActions::InterpretService.new(
+ container: container,
+ current_user: current_user,
+ params: quick_action_options
+ ).execute_with_original_text(target_description, issuable, only: only, original_text: issuable.description_was)
# Avoid a description already set on an issuable to be overwritten by a nil
- params[:description] = sanitized_description if sanitized_description && sanitized_description != edited_description
+ params[:description] = description if description && description != target_description
- params.merge!(sanitized_command_params) if sanitized_command_params
+ params.merge!(command_params)
end
def quick_action_options
{}
end
- def interpret_quick_actions(new_text, issuable, params:, only:, original_text: nil)
- sanitized_new_text, new_command_params = QuickActions::InterpretService.new(
- container: container,
- current_user: current_user,
- params: params
- ).execute_with_original_text(new_text, issuable, only: only, original_text: original_text)
-
- [sanitized_new_text, new_command_params]
- end
-
def create(issuable, skip_system_notes: false)
initialize_callbacks!(issuable)
diff --git a/config/gitlab_loose_foreign_keys.yml b/config/gitlab_loose_foreign_keys.yml
index 46306971965..d6d2946d945 100644
--- a/config/gitlab_loose_foreign_keys.yml
+++ b/config/gitlab_loose_foreign_keys.yml
@@ -212,6 +212,10 @@ dast_site_validations:
- table: projects
column: project_id
on_delete: async_delete
+dast_sites:
+ - table: projects
+ column: project_id
+ on_delete: async_delete
deployment_clusters:
- table: clusters
column: cluster_id
diff --git a/db/click_house/migrate/main/20240814163200_create_ci_finished_pipelines_hourly.rb b/db/click_house/migrate/main/20240814163200_create_ci_finished_pipelines_hourly.rb
new file mode 100644
index 00000000000..253475b4349
--- /dev/null
+++ b/db/click_house/migrate/main/20240814163200_create_ci_finished_pipelines_hourly.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class CreateCiFinishedPipelinesHourly < ClickHouse::Migration
+ def up
+ execute <<~SQL
+ CREATE TABLE IF NOT EXISTS ci_finished_pipelines_hourly
+ (
+ `path` String DEFAULT '0/',
+
+ `status` LowCardinality(String) DEFAULT '',
+ `source` LowCardinality(String) DEFAULT '',
+ `ref` String DEFAULT '',
+ `started_at_bucket` DateTime64(6, 'UTC') DEFAULT now64(),
+
+ count_pipelines AggregateFunction(count)
+ )
+ ENGINE = AggregatingMergeTree()
+ ORDER BY (started_at_bucket, path, status, source, ref)
+ SQL
+ end
+
+ def down
+ execute <<~SQL
+ DROP TABLE IF EXISTS ci_finished_pipelines_hourly
+ SQL
+ end
+end
diff --git a/db/click_house/migrate/main/20240815170000_create_ci_finished_pipelines_hourly_mv.rb b/db/click_house/migrate/main/20240815170000_create_ci_finished_pipelines_hourly_mv.rb
new file mode 100644
index 00000000000..dd2f8a37f66
--- /dev/null
+++ b/db/click_house/migrate/main/20240815170000_create_ci_finished_pipelines_hourly_mv.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class CreateCiFinishedPipelinesHourlyMv < ClickHouse::Migration
+ def up
+ execute <<~SQL
+ CREATE MATERIALIZED VIEW IF NOT EXISTS ci_finished_pipelines_hourly_mv
+ TO ci_finished_pipelines_hourly
+ AS
+ SELECT
+ path,
+ status,
+ source,
+ ref,
+ toStartOfInterval(started_at, INTERVAL 1 hour) AS started_at_bucket,
+
+ countState() AS count_pipelines
+ FROM ci_finished_pipelines
+ GROUP BY path, status, source, ref, started_at_bucket
+ SQL
+ end
+
+ def down
+ execute <<~SQL
+ DROP VIEW ci_finished_pipelines_hourly_mv
+ SQL
+ end
+end
diff --git a/db/docs/dast_sites.yml b/db/docs/dast_sites.yml
index 19b4ab053db..4cf7af71c71 100644
--- a/db/docs/dast_sites.yml
+++ b/db/docs/dast_sites.yml
@@ -7,7 +7,7 @@ feature_categories:
description: Site to run dast scan on
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36659
milestone: '13.2'
-gitlab_schema: gitlab_main_cell
+gitlab_schema: gitlab_sec
allow_cross_foreign_keys:
- gitlab_main_clusterwide
sharding_key:
diff --git a/db/docs/p_ci_build_trace_metadata.yml b/db/docs/p_ci_build_trace_metadata.yml
new file mode 100644
index 00000000000..7048b25ba98
--- /dev/null
+++ b/db/docs/p_ci_build_trace_metadata.yml
@@ -0,0 +1,11 @@
+---
+table_name: p_ci_build_trace_metadata
+classes:
+- Ci::BuildTraceMetadata
+feature_categories:
+- continuous_integration
+description: Routing table for ci_build_trace_metadata
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/162310
+milestone: '14.2'
+gitlab_schema: gitlab_ci
+sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/458479
diff --git a/db/migrate/20240809134250_create_p_ci_build_trace_metadata.rb b/db/migrate/20240809134250_create_p_ci_build_trace_metadata.rb
new file mode 100644
index 00000000000..82246793993
--- /dev/null
+++ b/db/migrate/20240809134250_create_p_ci_build_trace_metadata.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class CreatePCiBuildTraceMetadata < Gitlab::Database::Migration[2.2]
+ milestone '17.4'
+
+ # rubocop:disable Migration/EnsureFactoryForTable -- No factory needed
+ def change
+ create_table(:p_ci_build_trace_metadata, primary_key: [:build_id, :partition_id],
+ options: 'PARTITION BY LIST (partition_id)', if_not_exists: true) do |t|
+ t.bigint :build_id, null: false
+ t.bigint :partition_id, null: false
+ t.bigint :trace_artifact_id
+ t.datetime_with_timezone :last_archival_attempt_at
+ t.datetime_with_timezone :archived_at
+ t.integer :archival_attempts, default: 0, null: false, limit: 2
+ t.binary :checksum
+ t.binary :remote_checksum
+
+ t.index :trace_artifact_id
+ end
+ end
+ # rubocop:enable Migration/EnsureFactoryForTable -- No factory needed
+end
diff --git a/db/migrate/20240821072055_drop_idx_vulnerability_occurrences_dedup.rb b/db/migrate/20240821072055_drop_idx_vulnerability_occurrences_dedup.rb
new file mode 100644
index 00000000000..c644a6deddd
--- /dev/null
+++ b/db/migrate/20240821072055_drop_idx_vulnerability_occurrences_dedup.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class DropIdxVulnerabilityOccurrencesDedup < Gitlab::Database::Migration[2.2]
+ milestone '17.4'
+
+ disable_ddl_transaction!
+
+ TABLE = :vulnerability_occurrences
+ INDEX_NAME = 'index_vulnerability_occurrences_deduplication'
+ COLUMNS = %i[project_id report_type project_fingerprint]
+
+ def up
+ remove_concurrent_index TABLE, COLUMNS, name: INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index TABLE, COLUMNS, name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20240802140438_remove_projects_dast_sites_project_id_fk.rb b/db/post_migrate/20240802140438_remove_projects_dast_sites_project_id_fk.rb
new file mode 100644
index 00000000000..bcda0993f86
--- /dev/null
+++ b/db/post_migrate/20240802140438_remove_projects_dast_sites_project_id_fk.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class RemoveProjectsDastSitesProjectIdFk < Gitlab::Database::Migration[2.2]
+ milestone '17.4'
+ disable_ddl_transaction!
+
+ FOREIGN_KEY_NAME = "fk_rails_6febb6ea9c"
+
+ def up
+ with_lock_retries do
+ remove_foreign_key_if_exists(:dast_sites, :projects,
+ name: FOREIGN_KEY_NAME, reverse_lock_order: true)
+ end
+ end
+
+ def down
+ add_concurrent_foreign_key(:dast_sites, :projects,
+ name: FOREIGN_KEY_NAME, column: :project_id,
+ target_column: :id, on_delete: :cascade)
+ end
+end
diff --git a/db/post_migrate/20240809135718_fk_ci_builds_ci_build_trace_metadata.rb b/db/post_migrate/20240809135718_fk_ci_builds_ci_build_trace_metadata.rb
new file mode 100644
index 00000000000..23b662bb42c
--- /dev/null
+++ b/db/post_migrate/20240809135718_fk_ci_builds_ci_build_trace_metadata.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class FkCiBuildsCiBuildTraceMetadata < Gitlab::Database::Migration[2.2]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ disable_ddl_transaction!
+ milestone '17.4'
+
+ def up
+ add_concurrent_partitioned_foreign_key(
+ :p_ci_build_trace_metadata, :p_ci_builds,
+ name: :fk_rails_aebc78111f_p,
+ column: [:partition_id, :build_id],
+ target_column: [:partition_id, :id],
+ on_update: :cascade,
+ on_delete: :cascade,
+ reverse_lock_order: true
+ )
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists :p_ci_build_trace_metadata, :p_ci_builds,
+ name: :fk_rails_aebc78111f_p, reverse_lock_order: true
+ end
+ end
+end
diff --git a/db/post_migrate/20240809135746_fk_ci_jobs_artifacts_ci_build_trace_metadata.rb b/db/post_migrate/20240809135746_fk_ci_jobs_artifacts_ci_build_trace_metadata.rb
new file mode 100644
index 00000000000..b99672597ce
--- /dev/null
+++ b/db/post_migrate/20240809135746_fk_ci_jobs_artifacts_ci_build_trace_metadata.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class FkCiJobsArtifactsCiBuildTraceMetadata < Gitlab::Database::Migration[2.2]
+ include Gitlab::Database::PartitioningMigrationHelpers
+
+ disable_ddl_transaction!
+ milestone '17.4'
+
+ def up
+ add_concurrent_partitioned_foreign_key(
+ :p_ci_build_trace_metadata, :p_ci_job_artifacts,
+ name: :fk_21d25cac1a_p,
+ column: [:partition_id, :trace_artifact_id],
+ target_column: [:partition_id, :id],
+ on_update: :cascade,
+ on_delete: :cascade,
+ reverse_lock_order: true
+ )
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists :p_ci_build_trace_metadata, :p_ci_job_artifacts,
+ name: :fk_21d25cac1a_p, reverse_lock_order: true
+ end
+ end
+end
diff --git a/db/post_migrate/20240810135746_partition_ci_build_trace_metadata.rb b/db/post_migrate/20240810135746_partition_ci_build_trace_metadata.rb
new file mode 100644
index 00000000000..1bf2854fcb1
--- /dev/null
+++ b/db/post_migrate/20240810135746_partition_ci_build_trace_metadata.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+class PartitionCiBuildTraceMetadata < Gitlab::Database::Migration[2.2]
+ milestone '17.4'
+ disable_ddl_transaction!
+
+ # rubocop:disable Migration/SchemaAdditionMethodsNoPost -- recreates the table in the same transaction
+ def up
+ with_lock_retries do
+ lock_tables(:p_ci_builds, :p_ci_job_artifacts, :ci_build_trace_metadata, mode: :access_exclusive)
+
+ drop_table(:ci_build_trace_metadata)
+
+ connection.execute(<<~SQL)
+ CREATE TABLE IF NOT EXISTS ci_build_trace_metadata
+ PARTITION OF p_ci_build_trace_metadata
+ FOR VALUES IN (100, 101, 102);
+ SQL
+ end
+ end
+
+ def down
+ drop_table(:ci_build_trace_metadata, if_exists: true)
+
+ create_table(:ci_build_trace_metadata, id: false, if_not_exists: true) do |t|
+ t.bigint :build_id, null: false, default: nil, primary_key: true
+ t.bigint :trace_artifact_id
+ t.integer :archival_attempts, default: 0, null: false, limit: 2
+ t.binary :checksum
+ t.binary :remote_checksum
+ t.datetime_with_timezone :last_archival_attempt_at
+ t.datetime_with_timezone :archived_at
+ t.bigint :partition_id, null: false
+
+ t.index [:trace_artifact_id, :partition_id],
+ name: :index_ci_build_trace_metadata_on_trace_artifact_id_partition_id
+ t.index [:partition_id, :build_id], unique: true,
+ name: :index_ci_build_trace_metadata_on_partition_id_build_id
+ end
+
+ add_concurrent_foreign_key(
+ :ci_build_trace_metadata, :p_ci_builds,
+ column: [:partition_id, :build_id],
+ target_column: [:partition_id, :id],
+ on_update: :cascade,
+ on_delete: :cascade,
+ reverse_lock_order: true,
+ name: :fk_rails_aebc78111f_p
+ )
+
+ add_concurrent_foreign_key(
+ :ci_build_trace_metadata, :p_ci_job_artifacts,
+ column: [:partition_id, :trace_artifact_id],
+ target_column: [:partition_id, :id],
+ on_update: :cascade,
+ on_delete: :cascade,
+ reverse_lock_order: true,
+ name: :fk_21d25cac1a_p
+ )
+ end
+ # rubocop:enable Migration/SchemaAdditionMethodsNoPost
+end
diff --git a/db/schema_migrations/20240802140438 b/db/schema_migrations/20240802140438
new file mode 100644
index 00000000000..b5da8e86211
--- /dev/null
+++ b/db/schema_migrations/20240802140438
@@ -0,0 +1 @@
+519360df6518a135db8b7f543819bfa1984ec0fc3f12cbf3324e0f99602c2987
\ No newline at end of file
diff --git a/db/schema_migrations/20240809134250 b/db/schema_migrations/20240809134250
new file mode 100644
index 00000000000..4b7ade61282
--- /dev/null
+++ b/db/schema_migrations/20240809134250
@@ -0,0 +1 @@
+3711a08edad1fa122bfce2c5c1183e6514430c1ea3253c707dbdf7c84eebeee2
\ No newline at end of file
diff --git a/db/schema_migrations/20240809135718 b/db/schema_migrations/20240809135718
new file mode 100644
index 00000000000..4d6e5cf1361
--- /dev/null
+++ b/db/schema_migrations/20240809135718
@@ -0,0 +1 @@
+76ac52c3eeb536a7a0393bf52d0dfe10029d70927cd28697b7bb57969b3ec47b
\ No newline at end of file
diff --git a/db/schema_migrations/20240809135746 b/db/schema_migrations/20240809135746
new file mode 100644
index 00000000000..d5d3551735f
--- /dev/null
+++ b/db/schema_migrations/20240809135746
@@ -0,0 +1 @@
+24f8129c565f649626fb34a05075a3c43acb3336020add263d0595facd45fc25
\ No newline at end of file
diff --git a/db/schema_migrations/20240810135746 b/db/schema_migrations/20240810135746
new file mode 100644
index 00000000000..e47110240fc
--- /dev/null
+++ b/db/schema_migrations/20240810135746
@@ -0,0 +1 @@
+c7e5dc7f7ca4dbbf62ae0d53eef6beba539ddd75a556a1b20e3f8612997c1204
\ No newline at end of file
diff --git a/db/schema_migrations/20240821072055 b/db/schema_migrations/20240821072055
new file mode 100644
index 00000000000..a749f0b327d
--- /dev/null
+++ b/db/schema_migrations/20240821072055
@@ -0,0 +1 @@
+bf7bd395aac7d28c0982682b5356383d0de4d7b868fa5690c79adc6da4b70022
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 80fc9e425f4..281ca8fc82d 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -7743,15 +7743,27 @@ CREATE SEQUENCE ci_build_trace_chunks_id_seq
ALTER SEQUENCE ci_build_trace_chunks_id_seq OWNED BY ci_build_trace_chunks.id;
-CREATE TABLE ci_build_trace_metadata (
+CREATE TABLE p_ci_build_trace_metadata (
build_id bigint NOT NULL,
+ partition_id bigint NOT NULL,
trace_artifact_id bigint,
- archival_attempts smallint DEFAULT 0 NOT NULL,
- checksum bytea,
- remote_checksum bytea,
last_archival_attempt_at timestamp with time zone,
archived_at timestamp with time zone,
- partition_id bigint NOT NULL
+ archival_attempts smallint DEFAULT 0 NOT NULL,
+ checksum bytea,
+ remote_checksum bytea
+)
+PARTITION BY LIST (partition_id);
+
+CREATE TABLE ci_build_trace_metadata (
+ build_id bigint NOT NULL,
+ partition_id bigint NOT NULL,
+ trace_artifact_id bigint,
+ last_archival_attempt_at timestamp with time zone,
+ archived_at timestamp with time zone,
+ archival_attempts smallint DEFAULT 0 NOT NULL,
+ checksum bytea,
+ remote_checksum bytea
);
CREATE TABLE ci_builds (
@@ -20970,6 +20982,8 @@ ALTER TABLE ONLY namespace_descendants ATTACH PARTITION gitlab_partitions_static
ALTER TABLE ONLY namespace_descendants ATTACH PARTITION gitlab_partitions_static.namespace_descendants_31 FOR VALUES WITH (modulus 32, remainder 31);
+ALTER TABLE ONLY p_ci_build_trace_metadata ATTACH PARTITION ci_build_trace_metadata FOR VALUES IN ('100', '101', '102');
+
ALTER TABLE ONLY p_ci_builds ATTACH PARTITION ci_builds FOR VALUES IN ('100');
ALTER TABLE ONLY p_ci_builds_metadata ATTACH PARTITION ci_builds_metadata FOR VALUES IN ('100');
@@ -23143,8 +23157,11 @@ ALTER TABLE ONLY ci_build_report_results
ALTER TABLE ONLY ci_build_trace_chunks
ADD CONSTRAINT ci_build_trace_chunks_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY p_ci_build_trace_metadata
+ ADD CONSTRAINT p_ci_build_trace_metadata_pkey PRIMARY KEY (build_id, partition_id);
+
ALTER TABLE ONLY ci_build_trace_metadata
- ADD CONSTRAINT ci_build_trace_metadata_pkey PRIMARY KEY (build_id);
+ ADD CONSTRAINT ci_build_trace_metadata_pkey PRIMARY KEY (build_id, partition_id);
ALTER TABLE ONLY p_ci_builds_metadata
ADD CONSTRAINT p_ci_builds_metadata_pkey PRIMARY KEY (id, partition_id);
@@ -26196,6 +26213,10 @@ CREATE INDEX ca_aggregations_last_full_run_at ON analytics_cycle_analytics_aggre
CREATE INDEX ca_aggregations_last_incremental_run_at ON analytics_cycle_analytics_aggregations USING btree (last_incremental_run_at NULLS FIRST) WHERE (enabled IS TRUE);
+CREATE INDEX index_p_ci_build_trace_metadata_on_trace_artifact_id ON ONLY p_ci_build_trace_metadata USING btree (trace_artifact_id);
+
+CREATE INDEX ci_build_trace_metadata_trace_artifact_id_idx ON ci_build_trace_metadata USING btree (trace_artifact_id);
+
CREATE INDEX p_ci_builds_status_created_at_project_id_idx ON ONLY p_ci_builds USING btree (status, created_at, project_id) WHERE ((type)::text = 'Ci::Build'::text);
CREATE INDEX ci_builds_gitlab_monitor_metrics ON ci_builds USING btree (status, created_at, project_id) WHERE ((type)::text = 'Ci::Build'::text);
@@ -27022,10 +27043,6 @@ CREATE UNIQUE INDEX index_ci_build_trace_chunks_on_build_id_and_chunk_index ON c
CREATE INDEX index_ci_build_trace_chunks_on_partition_id_build_id ON ci_build_trace_chunks USING btree (partition_id, build_id);
-CREATE UNIQUE INDEX index_ci_build_trace_metadata_on_partition_id_build_id ON ci_build_trace_metadata USING btree (partition_id, build_id);
-
-CREATE INDEX index_ci_build_trace_metadata_on_trace_artifact_id_partition_id ON ci_build_trace_metadata USING btree (trace_artifact_id, partition_id);
-
CREATE INDEX p_ci_builds_metadata_build_id_idx ON ONLY p_ci_builds_metadata USING btree (build_id) WHERE (has_exposed_artifacts IS TRUE);
CREATE INDEX index_ci_builds_metadata_on_build_id_and_has_exposed_artifacts ON ci_builds_metadata USING btree (build_id) WHERE (has_exposed_artifacts IS TRUE);
@@ -30380,8 +30397,6 @@ CREATE INDEX index_vulnerability_occurrence_pipelines_occurrence_id_and_id ON vu
CREATE INDEX index_vulnerability_occurrence_pipelines_on_pipeline_id ON vulnerability_occurrence_pipelines USING btree (pipeline_id);
-CREATE INDEX index_vulnerability_occurrences_deduplication ON vulnerability_occurrences USING btree (project_id, report_type, project_fingerprint);
-
CREATE INDEX index_vulnerability_occurrences_for_override_uuids_logic ON vulnerability_occurrences USING btree (project_id, report_type, location_fingerprint);
CREATE INDEX index_vulnerability_occurrences_on_initial_pipeline_id ON vulnerability_occurrences USING btree (initial_pipeline_id);
@@ -32254,6 +32269,10 @@ ALTER INDEX index_on_namespace_descendants_outdated ATTACH PARTITION gitlab_part
ALTER INDEX namespace_descendants_pkey ATTACH PARTITION gitlab_partitions_static.namespace_descendants_31_pkey;
+ALTER INDEX p_ci_build_trace_metadata_pkey ATTACH PARTITION ci_build_trace_metadata_pkey;
+
+ALTER INDEX index_p_ci_build_trace_metadata_on_trace_artifact_id ATTACH PARTITION ci_build_trace_metadata_trace_artifact_id_idx;
+
ALTER INDEX p_ci_builds_status_created_at_project_id_idx ATTACH PARTITION ci_builds_gitlab_monitor_metrics;
ALTER INDEX p_ci_builds_metadata_pkey ATTACH PARTITION ci_builds_metadata_pkey;
@@ -32844,7 +32863,7 @@ ALTER TABLE ONLY coverage_fuzzing_corpuses
ALTER TABLE ONLY namespace_settings
ADD CONSTRAINT fk_20cf0eb2f9 FOREIGN KEY (default_compliance_framework_id) REFERENCES compliance_management_frameworks(id) ON DELETE SET NULL;
-ALTER TABLE ONLY ci_build_trace_metadata
+ALTER TABLE p_ci_build_trace_metadata
ADD CONSTRAINT fk_21d25cac1a_p FOREIGN KEY (partition_id, trace_artifact_id) REFERENCES p_ci_job_artifacts(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY users_star_projects
@@ -35028,9 +35047,6 @@ ALTER TABLE ONLY project_compliance_framework_settings
ALTER TABLE ONLY users_security_dashboard_projects
ADD CONSTRAINT fk_rails_6f6cf8e66e FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
-ALTER TABLE ONLY dast_sites
- ADD CONSTRAINT fk_rails_6febb6ea9c FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
-
ALTER TABLE ONLY analytics_dashboards_pointers
ADD CONSTRAINT fk_rails_7027b7eaa9 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
@@ -35502,7 +35518,7 @@ ALTER TABLE ONLY analytics_cycle_analytics_group_stages
ALTER TABLE ONLY metrics_dashboard_annotations
ADD CONSTRAINT fk_rails_aeb11a7643 FOREIGN KEY (environment_id) REFERENCES environments(id) ON DELETE CASCADE;
-ALTER TABLE ONLY ci_build_trace_metadata
+ALTER TABLE p_ci_build_trace_metadata
ADD CONSTRAINT fk_rails_aebc78111f_p FOREIGN KEY (partition_id, build_id) REFERENCES p_ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY bulk_import_trackers
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 5c61e8716e5..30a3785f4c5 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -669,8 +669,7 @@ WARNING:
[very specific cases](https://handbook.gitlab.com/handbook/engineering/workflow/#criteria-for-merging-during-broken-master).
For other cases, follow these [handbook instructions](https://handbook.gitlab.com/handbook/engineering/workflow/#merging-during-broken-master).
- If the latest pipeline was created before the merge request was approved, start a new pipeline to ensure that full RSpec suite has been run. You may skip this step only if the merge request does not contain any backend change.
- - If the **latest [merged results pipeline](../ci/pipelines/merged_results_pipelines.md)** was **created less than 8 hours ago**, you
- may merge without starting a new pipeline as the merge request is close enough to the target branch.
+ - If the **latest [merged results pipeline](../ci/pipelines/merged_results_pipelines.md)** was **created less than 8 hours ago (72 hours for stable branches)**, you may merge without starting a new pipeline as the merge request is close enough to the target branch.
- When you set the MR to auto-merge, you should take over
subsequent revisions for anything that would be spotted after that.
- For merge requests that have had [Squash and merge](../user/project/merge_requests/squash_and_merge.md) set,
diff --git a/doc/development/pipelines/index.md b/doc/development/pipelines/index.md
index 977edd8c282..44824cf1520 100644
--- a/doc/development/pipelines/index.md
+++ b/doc/development/pipelines/index.md
@@ -246,7 +246,7 @@ Merge train pipelines run a single `pre-merge-checks` job which ensures the late
1. A [Merged Results pipeline](../../ci/pipelines/merged_results_pipelines.md)
1. A [`tier-3` pipeline](#pipeline-tiers) (i.e. full pipeline, not predictive one)
-1. Created at most 8 hours ago
+1. Created at most 8 hours ago (72 hours for stable branches)
We opened [a feedback issue](https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/issues/513)
to iterate on this solution.
diff --git a/doc/operations/metrics.md b/doc/operations/metrics.md
index 3905f699bb8..7fb10f6515d 100644
--- a/doc/operations/metrics.md
+++ b/doc/operations/metrics.md
@@ -102,3 +102,23 @@ The following table shows what type of aggregation is used for each search perio
### Data retention
GitLab has a retention limit of 30 days for all ingested metrics.
+
+### Create an issue for a metric
+
+You can create an issue to track any action taken to resolve or investigate a metric. To create an issue for a metric:
+
+1. On the left sidebar, select **Search or go to** and find your project.
+1. Select **Monitor > Metrics**.
+1. From the list of metrics, select a metric.
+1. Select **Create issue**.
+
+The issue is created in the selected project and pre-filled with information from the metric.
+You can edit the issue title and description.
+
+### View issues related to a metric
+
+1. On the left sidebar, select **Search or go to** and find your project.
+1. Select **Monitor > Metrics**.
+1. From the list of metrics, select a metric.
+1. Scroll to **Related issues**.
+1. Optional. To view the issue details, select an issue.
diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md
index c021bafb71b..d3ccc5626e6 100644
--- a/doc/subscriptions/self_managed/index.md
+++ b/doc/subscriptions/self_managed/index.md
@@ -277,7 +277,11 @@ A job is queued. When the job finishes, the subscription details are updated.
## View your subscription
-If you are an administrator, you can view the status of your subscription:
+Prerequisites:
+
+- You must be an administrator.
+
+You can view the status of your subscription:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Subscription**.
diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md
index cbcf01f5cc2..eb22eb652fc 100644
--- a/doc/topics/git/index.md
+++ b/doc/topics/git/index.md
@@ -17,7 +17,7 @@ platform for software development. GitLab adds many powerful
| | | |
|--|--|--|
-| [**Get started**](get_started.md) **{chevron-right}** | [**Install Git**](how_to_install_git/index.md) **{chevron-right}** | [**Tutorial: Create your first commit**](../../tutorials/make_first_git_commit/index.md) **{chevron-right}** |
-| [**Clone a repository to your local machine**](clone.md) **{chevron-right}** | [**Create a branch for your changes**](branch.md) **{chevron-right}** | [**Add files to your branch**](../../gitlab-basics/add-file.md) **{chevron-right}** |
-| [**Stash changes for later**](stash.md) **{chevron-right}** | [**Undo changes**](undo.md) **{chevron-right}** | [**Tutorial: Update Git commit messages**](../../tutorials/update_commit_messages/index.md) **{chevron-right}** |
-| [**Rebase to address merge conflicts**](git_rebase.md) **{chevron-right}** | [**Common Git commands**](../../gitlab-basics/start-using-git.md) **{chevron-right}** | [**Troubleshooting**](troubleshooting_git.md) **{chevron-right}** |
+| [**Get started**](get_started.md) **{chevron-right}**
Overview of how features fit together. | [**Install Git**](how_to_install_git/index.md) **{chevron-right}**
Download, configuration, system requirements. | [**Tutorial: Create your first commit**](../../tutorials/make_first_git_commit/index.md) **{chevron-right}**
Initial commit, Git basics, repository setup. |
+| [**Clone a repository to your local machine**](clone.md) **{chevron-right}**
Local repository, clone, remote repository, SSH. | [**Create a branch for your changes**](branch.md) **{chevron-right}**
Branching, branch switch, checkout. | [**Add files to your branch**](../../gitlab-basics/add-file.md) **{chevron-right}**
Git add, staging changes, file management, commits. |
+| [**Stash changes for later**](stash.md) **{chevron-right}**
Temporary storage, work in progress, context switching. | [**Undo changes**](undo.md) **{chevron-right}**
Reverting commits, removing changes, Git reset, unstage. | [**Tutorial: Update Git commit messages**](../../tutorials/update_commit_messages/index.md) **{chevron-right}**
Commit message editing, version history, best practices. |
+| [**Rebase to address merge conflicts**](git_rebase.md) **{chevron-right}**
Conflict resolution, rebase, branch management. | [**Common Git commands**](../../gitlab-basics/start-using-git.md) **{chevron-right}**
Git cheatsheet, basic operations, command line. | [**Troubleshooting**](troubleshooting_git.md) **{chevron-right}**
Error resolution, common issues, debugging, Git problems. |
diff --git a/doc/topics/git/troubleshooting_git.md b/doc/topics/git/troubleshooting_git.md
index 2f3b3a0e7bb..206d698f6bf 100644
--- a/doc/topics/git/troubleshooting_git.md
+++ b/doc/topics/git/troubleshooting_git.md
@@ -336,10 +336,13 @@ return this error:
To resolve this issue, you can update the password expiration by either:
-- Using the `gitlab-rails console`:
+- Using the [GitLab Rails console](../../administration/operations/rails_console.md)
+ to check and update the user data:
```ruby
- gitlab-rails console
+ user = User.find_by_username('')
+ user.password_expired?
+ user.password_expires_at
user.update!(password_expires_at: nil)
```
diff --git a/doc/topics/manage_code.md b/doc/topics/manage_code.md
index 20fed5bc686..2b02a907958 100644
--- a/doc/topics/manage_code.md
+++ b/doc/topics/manage_code.md
@@ -11,5 +11,5 @@ Store your source files in a repository and create merge requests. Write, debug,
| | | |
|--|--|--|
-| [**Getting started**](../user/get_started/get_started_managing_code.md) **{chevron-right}** | [**Repositories**](../user/project/repository/index.md) **{chevron-right}** | [**Merge requests**](../user/project/merge_requests/index.md) **{chevron-right}** |
-| [**Remote development**](../user/project/remote_development/index.md) **{chevron-right}** | | |
+| [**Getting started**](../user/get_started/get_started_managing_code.md) **{chevron-right}**
Overview of how features fit together. | [**Repositories**](../user/project/repository/index.md) **{chevron-right}**
Version control, code storage, Git repositories, repository monitoring. | [**Merge requests**](../user/project/merge_requests/index.md) **{chevron-right}**
Code review, collaboration, branch merging, commits. |
+| [**Remote development**](../user/project/remote_development/index.md) **{chevron-right}**
Web IDE, workspaces. | | |
diff --git a/doc/topics/plan_and_track.md b/doc/topics/plan_and_track.md
index c7531030f5e..177303a3335 100644
--- a/doc/topics/plan_and_track.md
+++ b/doc/topics/plan_and_track.md
@@ -24,11 +24,11 @@ with [Scaled Agile Framework (SAFe)](https://handbook.gitlab.com/handbook/market
| | | |
|--|--|--|
-| [**Getting started**](../user/get_started/get_started_planning_work.md) **{chevron-right}** | [**Tutorial: Use GitLab for scrum**](../tutorials/scrum_events/index.md) **{chevron-right}** | [**Tutorial: Use GitLab for Kanban**](../tutorials/kanban/index.md) **{chevron-right}** |
-| [**Labels**](../user/project/labels.md) **{chevron-right}** | [**Iterations**](../user/group/iterations/index.md) **{chevron-right}** | [**Milestones**](../user/project/milestones/index.md) **{chevron-right}** |
-| [**Issues**](../user/project/issues/index.md) **{chevron-right}** | [**Issue boards**](../user/project/issue_board.md) **{chevron-right}** | [**Comments and threads**](../user/discussions/index.md) **{chevron-right}** |
-| [**Tasks**](../user/tasks.md) **{chevron-right}** | [**Requirements**](../user/project/requirements/index.md) **{chevron-right}** | [**Time tracking**](../user/project/time_tracking.md) **{chevron-right}** |
-| [**CRM**](../user/crm/index.md) **{chevron-right}** | [**Wikis**](../user/project/wiki/index.md) **{chevron-right}** | [**Epics**](../user/group/epics/index.md) **{chevron-right}** |
-| [**Roadmaps**](../user/group/roadmap/index.md) **{chevron-right}** | [**Planning hierarchies**](../user/group/planning_hierarchy/index.md) **{chevron-right}** | [**Objectives and key results**](../user/okrs.md) **{chevron-right}** |
-| [**Keyboard shortcuts**](../user/shortcuts.md) **{chevron-right}** | [**Quick actions**](../user/project/quick_actions.md) **{chevron-right}** | [**Markdown**](../user/markdown.md) **{chevron-right}** |
-| [**To-Do List**](../user/todos.md) **{chevron-right}** | | |
+| [**Getting started**](../user/get_started/get_started_planning_work.md) **{chevron-right}**
Overview of how features fit together. | [**Tutorial: Use GitLab for scrum**](../tutorials/scrum_events/index.md) **{chevron-right}**
Sprints, backlog, user stories, scrum lifecycle. | [**Tutorial: Use GitLab for Kanban**](../tutorials/kanban/index.md) **{chevron-right}**
Kanban board, columns, work in progress, flow, distribution. |
+| [**Labels**](../user/project/labels.md) **{chevron-right}**
Project labels, group labels, nested scopes, filtering. | [**Iterations**](../user/group/iterations/index.md) **{chevron-right}**
Time-boxed workflow, program increments, cadence, sprints. | [**Milestones**](../user/project/milestones/index.md) **{chevron-right}**
Burndown charts, goals, progress tracking, releases. |
+| [**Issues**](../user/project/issues/index.md) **{chevron-right}**
Tasks, bug reports, feature requests, tracking. | [**Issue boards**](../user/project/issue_board.md) **{chevron-right}**
Visualization, workflow, Kanban, prioritization. | [**Comments and threads**](../user/discussions/index.md) **{chevron-right}**
Mentions, locked discussions, internal notes, thread resolution. |
+| [**Tasks**](../user/tasks.md) **{chevron-right}**
Task labels, confidential tasks, linked items, task weights. | [**Requirements**](../user/project/requirements/index.md) **{chevron-right}**
Acceptance criteria, requirements test reports, CSV import. | [**Time tracking**](../user/project/time_tracking.md) **{chevron-right}**
Estimates, time spent, reporting. |
+| [**CRM**](../user/crm/index.md) **{chevron-right}**
Customer management, organizations, contacts, permissions. | [**Wikis**](../user/project/wiki/index.md) **{chevron-right}**
Documentation, external wikis, wiki events, history. | [**Epics**](../user/group/epics/index.md) **{chevron-right}**
Roadmaps, hierarchies, planning, issue progress. |
+| [**Roadmaps**](../user/group/roadmap/index.md) **{chevron-right}**
Epic progress, timelines, milestones, goals. | [**Planning hierarchies**](../user/group/planning_hierarchy/index.md) **{chevron-right}**
Organization, structure, multi-level epics, nesting. | [**Objectives and key results**](../user/okrs.md) **{chevron-right}**
Goal setting, performance tracking, child objectives, health status. |
+| [**Keyboard shortcuts**](../user/shortcuts.md) **{chevron-right}**
Global shortcuts, navigation, quick access. | [**Quick actions**](../user/project/quick_actions.md) **{chevron-right}**
Commands, shortcuts, inline actions. | [**Markdown**](../user/markdown.md) **{chevron-right}**
Formatting, inline HTML, GitLab-specific references, diagrams and flowcharts. |
+| [**To-Do List**](../user/todos.md) **{chevron-right}**
Task management, actions, access changes. | | |
diff --git a/doc/topics/set_up_organization.md b/doc/topics/set_up_organization.md
index f0116792fa1..34b41ae8aa0 100644
--- a/doc/topics/set_up_organization.md
+++ b/doc/topics/set_up_organization.md
@@ -12,7 +12,7 @@ and give everyone access to the projects they need.
| | | |
|--|--|--|
-| [**Tutorial: Set up your organization**](../tutorials/manage_user/index.md) **{chevron-right}** | [**Namespaces**](../user/namespace/index.md) **{chevron-right}** | [**Members**](../user/project/members/index.md) **{chevron-right}** |
-| [**Organization** (in development)](../user/organization/index.md) **{chevron-right}** | [**Groups**](../user/group/index.md) **{chevron-right}** | [**Sharing projects and groups**](../user/project/members/sharing_projects_groups.md) **{chevron-right}** |
-| [**Compliance**](../administration/compliance.md) **{chevron-right}** | [**Enterprise users**](../user/enterprise_user/index.md) **{chevron-right}** | [**Service accounts**](../user/profile/service_accounts.md) **{chevron-right}** |
-| [**User account options**](../user/profile/index.md) **{chevron-right}** | [**SSH keys**](../user/ssh.md) **{chevron-right}** | [**GitLab.com settings**](../user/gitlab_com/index.md) **{chevron-right}** |
+| [**Tutorial: Set up your organization**](../tutorials/manage_user/index.md) **{chevron-right}**
Setup, configuration, onboarding, organization structure. | [**Namespaces**](../user/namespace/index.md) **{chevron-right}**
Organization, hierarchy, project grouping. | [**Members**](../user/project/members/index.md) **{chevron-right}**
User management, roles, permissions, access levels. |
+| [**Organization** (in development)](../user/organization/index.md) **{chevron-right}**
Namespace hierarchy. | [**Groups**](../user/group/index.md) **{chevron-right}**
Project management, access control, client groups, team groups. | [**Sharing projects and groups**](../user/project/members/sharing_projects_groups.md) **{chevron-right}**
Invitations, group inheritance, project visibility. |
+| [**Compliance**](../administration/compliance.md) **{chevron-right}**
Compliance center, audit events, security policies, compliance frameworks. | [**Enterprise users**](../user/enterprise_user/index.md) **{chevron-right}**
Domain verification, two-factor authentication, enterprise user management, SAML response. | [**Service accounts**](../user/profile/service_accounts.md) **{chevron-right}**
Machine user, rate limits, personal access tokens. |
+| [**User account options**](../user/profile/index.md) **{chevron-right}**
Profile settings, preferences, authentication, notifications. | [**SSH keys**](../user/ssh.md) **{chevron-right}**
Authentication, permissions, key types, ownership. | [**GitLab.com settings**](../user/gitlab_com/index.md) **{chevron-right}**
Instance configurations. |
diff --git a/doc/user/gitlab_duo/index.md b/doc/user/gitlab_duo/index.md
index b8f3f781481..0abb95745d1 100644
--- a/doc/user/gitlab_duo/index.md
+++ b/doc/user/gitlab_duo/index.md
@@ -61,7 +61,7 @@ DETAILS:
- Helps you understand the selected code by explaining it more clearly.
- LLM: Anthropic [Claude 3.5 Sonnet](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
- View documentation for explaining code in:
- - [The IDE](../gitlab_duo_chat/examples.md#explain-code).
+ - [The IDE](../gitlab_duo_chat/examples.md#explain-selected-code).
- [A file](../../user/project/repository/code_explain.md).
- [A merge request](../../user/project/merge_requests/changes.md#explain-code-in-a-merge-request).
diff --git a/doc/user/gitlab_duo_chat/examples.md b/doc/user/gitlab_duo_chat/examples.md
index 1795fb2ad09..62dfea795ac 100644
--- a/doc/user/gitlab_duo_chat/examples.md
+++ b/doc/user/gitlab_duo_chat/examples.md
@@ -34,10 +34,46 @@ its knowledge base is updated daily.
- On GitLab.com the, the most recent version of the documentation is used.
- On Self-managed and GitLab Dedicated, the documentation for the version of the instance is used.
-## Ask about code
+## Explain selected code
DETAILS:
-**Tier GitLab.com and Self-managed:** Premium or Ultimate for a limited time. In the future, Premium with GitLab Duo Pro or Ultimate with [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). **GitLab Dedicated:** GitLab Duo Pro or Enterprise.
+**Tier: GitLab.com and Self-managed:** Premium or Ultimate for a limited time. In the future, Premium with GitLab Duo Pro or Ultimate with [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). **GitLab Dedicated:** GitLab Duo Pro or Enterprise.
+**Offering:** GitLab.com, Self-managed, GitLab Dedicated
+**Editors:** GitLab UI, Web IDE, VS Code, JetBrains IDEs
+**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for GitLab.com in GitLab 16.7.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for self-managed and GitLab Dedicated in GitLab 16.8.
+
+You can ask GitLab Duo Chat to explain selected code:
+
+1. Select some code in your IDE.
+1. In Duo Chat, type `/explain`.
+
+ 
+
+You can also add additional instructions to be considered, for example:
+
+- `/explain the performance`
+- `/explain focus on the algorithm`
+- `/explain the performance gains or losses using this code`
+- `/explain the object inheritance` (classes, object-oriented)
+- `/explain why a static variable is used here` (C++)
+- `/explain how this function would cause a segmentation fault` (C)
+- `/explain how concurrency works in this context` (Go)
+- `/explain how the request reaches the client` (REST API, database)
+
+For more information, see [Use GitLab Duo Chat in VS Code](index.md#use-gitlab-duo-chat-in-vs-code).
+
+In the GitLab UI, you can also explain code in:
+
+- A [file](../project/repository/code_explain.md).
+- A [merge request](../project/merge_requests/changes.md#explain-code-in-a-merge-request).
+
+## Ask about or generate code
+
+DETAILS:
+**Tier: GitLab.com and Self-managed:** Premium or Ultimate for a limited time. In the future, Premium with GitLab Duo Pro or Ultimate with [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). **GitLab Dedicated:** GitLab Duo Pro or Enterprise.
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
**Editors:** GitLab UI, Web IDE, VS Code, JetBrains IDEs
**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
@@ -45,7 +81,15 @@ DETAILS:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122235) for GitLab.com in GitLab 16.1.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122235) for self-managed and GitLab Dedicated in GitLab 16.8.
-You can also ask GitLab Duo Chat to generate code:
+You can ask GitLab Duo Chat questions about code by pasting that code into
+the Duo Chat window. For example:
+
+```plaintext
+Provide a clear explanation of this Ruby code: def sum(a, b) a + b end.
+Describe what this code does and how it works.
+```
+
+You can also ask Chat to generate code. For example:
- `Write a Ruby function that prints 'Hello, World!' when called.`
- `Develop a JavaScript program that simulates a two-player Tic-Tac-Toe game. Provide both game logic and user interface, if applicable.`
@@ -54,11 +98,45 @@ You can also ask GitLab Duo Chat to generate code:
- `Create a product-consumer example with threads and shared memory in C++. Use atomic locks when possible.`
- `Generate Rust code for high performance gRPC calls. Provide a source code example for a server and client.`
-And you can ask GitLab Duo Chat to explain code:
+## Refactor code in the IDE
-- `Provide a clear explanation of the given Ruby code: def sum(a, b) a + b end. Describe what this code does and how it works.`
+DETAILS:
+**Tier: GitLab.com and Self-managed:** Premium or Ultimate for a limited time. In the future, Premium with GitLab Duo Pro or Ultimate with [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). **GitLab Dedicated:** GitLab Duo Pro or Enterprise.
+**Offering:** GitLab.com, Self-managed, GitLab Dedicated
+**Editors:** Web IDE, VS Code, JetBrains IDEs
+**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
-Alternatively, you can use the [`/explain` command](examples.md#explain-code) to explain the selected code in your editor.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for GitLab.com in GitLab 16.7.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for self-managed and GitLab Dedicated in GitLab 16.8.
+
+`/refactor` is a special command to generate a refactoring suggestion for the selected code in your editor.
+You can include additional instructions to be considered. For example:
+
+- Use a specific coding pattern, for example `/refactor with ActiveRecord` or `/refactor into a class providing static functions`.
+- Use a specific library, for example `/refactor using mysql`.
+- Use a specific function/algorithm, for example `/refactor into a stringstream with multiple lines` in C++.
+- Refactor to a different programming language, for example `/refactor to TypeScript`.
+- Focus on performance, for example `/refactor improving performance`.
+- Focus on potential vulnerabilities, for example `/refactor avoiding memory leaks and exploits`.
+
+## Fix code in the IDE
+
+DETAILS:
+**Tier: GitLab.com and Self-managed:** Premium or Ultimate for a limited time. In the future, Premium with GitLab Duo Pro or Ultimate with [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). **GitLab Dedicated:** GitLab Duo Pro or Enterprise.
+**Offering:** GitLab.com, Self-managed, GitLab Dedicated
+**Editors:** Web IDE, VS Code, JetBrains IDEs
+**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for GitLab.com, self-managed and GitLab Dedicated in GitLab 17.3.
+
+`/fix` is a special command to generate a fix suggestion for the selected code in your editor.
+You can include additional instructions to be considered. For example:
+
+- Focus on grammar and typos, for example, `/fix grammar mistakes and typos`.
+- Focus on a concrete algorithm or problem description, for example, `/fix duplicate database inserts` or `/fix race conditions`.
+- Focus on potential bugs that are not directly visible, for example, `/fix potential bugs`.
+- Focus on code performance problems, for example, `/fix performance problems`.
+- Focus on fixing the build when the code does not compile, for example, `/fix the build`.
## Ask about CI/CD
@@ -128,74 +206,6 @@ DETAILS:
[Learn more](../application_security/vulnerabilities/index.md#explaining-a-vulnerability).
-## Explain code
-
-DETAILS:
-**Tier: GitLab.com and Self-managed:** Premium or Ultimate for a limited time. In the future, Premium with GitLab Duo Pro or Ultimate with [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). **GitLab Dedicated:** GitLab Duo Pro or Enterprise.
-**Offering:** GitLab.com, Self-managed, GitLab Dedicated
-**Editors:** GitLab UI, Web IDE, VS Code, JetBrains IDEs
-**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for GitLab.com in GitLab 16.7.
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for self-managed and GitLab Dedicated in GitLab 16.8.
-
-`/explain` is a special command to explain the selected code in your editor.
-You can also add additional instructions to be considered, for example: `/explain the performance`
-See [Use GitLab Duo Chat in VS Code](index.md#use-gitlab-duo-chat-in-vs-code) for more information.
-
-- `/explain focus on the algorithm`
-- `/explain the performance gains or losses using this code`
-- `/explain the object inheritance` (classes, object-oriented)
-- `/explain why a static variable is used here` (C++)
-- `/explain how this function would cause a segmentation fault` (C)
-- `/explain how concurrency works in this context` (Go)
-- `/explain how the request reaches the client` (REST API, database)
-
-You can also use the Web UI to explain code in:
-
-- A [file](../project/repository/code_explain.md).
-- A [merge request](../project/merge_requests/changes.md#explain-code-in-a-merge-request).
-
-## Refactor code in the IDE
-
-DETAILS:
-**Tier: GitLab.com and Self-managed:** Premium or Ultimate for a limited time. In the future, Premium with GitLab Duo Pro or Ultimate with [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). **GitLab Dedicated:** GitLab Duo Pro or Enterprise.
-**Offering:** GitLab.com, Self-managed, GitLab Dedicated
-**Editors:** Web IDE, VS Code, JetBrains IDEs
-**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for GitLab.com in GitLab 16.7.
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for self-managed and GitLab Dedicated in GitLab 16.8.
-
-`/refactor` is a special command to generate a refactoring suggestion for the selected code in your editor.
-You can include additional instructions to be considered. For example:
-
-- Use a specific coding pattern, for example `/refactor with ActiveRecord` or `/refactor into a class providing static functions`.
-- Use a specific library, for example `/refactor using mysql`.
-- Use a specific function/algorithm, for example `/refactor into a stringstream with multiple lines` in C++.
-- Refactor to a different programming language, for example `/refactor to TypeScript`.
-- Focus on performance, for example `/refactor improving performance`.
-- Focus on potential vulnerabilities, for example `/refactor avoiding memory leaks and exploits`.
-
-## Fix code in the IDE
-
-DETAILS:
-**Tier: GitLab.com and Self-managed:** Premium or Ultimate for a limited time. In the future, Premium with GitLab Duo Pro or Ultimate with [GitLab Duo Pro or Enterprise](../../subscriptions/subscription-add-ons.md). **GitLab Dedicated:** GitLab Duo Pro or Enterprise.
-**Offering:** GitLab.com, Self-managed, GitLab Dedicated
-**Editors:** Web IDE, VS Code, JetBrains IDEs
-**LLMs:** Anthropic: [`claude-3-5-sonnet-20240620`](https://console.cloud.google.com/vertex-ai/publishers/anthropic/model-garden/claude-3-5-sonnet)
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429915) for GitLab.com, self-managed and GitLab Dedicated in GitLab 17.3.
-
-`/fix` is a special command to generate a fix suggestion for the selected code in your editor.
-You can include additional instructions to be considered. For example:
-
-- Focus on grammar and typos, for example, `/fix grammar mistakes and typos`.
-- Focus on a concrete algorithm or problem description, for example, `/fix duplicate database inserts` or `/fix race conditions`.
-- Focus on potential bugs that are not directly visible, for example, `/fix potential bugs`.
-- Focus on code performance problems, for example, `/fix performance problems`.
-- Focus on fixing the build when the code does not compile, for example, `/fix the build`.
-
## Write tests in the IDE
DETAILS:
@@ -307,8 +317,8 @@ Use the following commands to quickly accomplish specific tasks.
| /clear | [Delete all conversations permanently and clear the chat window](#delete-or-reset-the-conversation) |
| /reset | [Start a new conversation, but keep the previous conversations visible in the chat window](#delete-or-reset-the-conversation) |
| /tests | [Write tests](#write-tests-in-the-ide) |
-| /explain | [Explain code](../gitlab_duo_chat/examples.md#explain-code) |
-| /vulnerability_explain | [Explain current vulnerability](../gitlab_duo/index.md#vulnerability-explanation) |
-| /refactor | [Refactor the code](../gitlab_duo_chat/examples.md#refactor-code-in-the-ide) |
+| /explain | [Explain code](#explain-selected-code) |
+| /vulnerability_explain | [Explain current vulnerability](../application_security/vulnerabilities/index.md#explaining-a-vulnerability) |
+| /refactor | [Refactor the code](#refactor-code-in-the-ide) |
| /troubleshoot | [Troubleshoot failed CI/CD jobs with root cause analysis](#troubleshoot-failed-cicd-jobs-with-root-cause-analysis) |
-| /fix | [Fix the code](../gitlab_duo_chat/examples.md#fix-code-in-the-ide) |
+| /fix | [Fix the code](#fix-code-in-the-ide) |
diff --git a/doc/user/gitlab_duo_chat/img/code_selection_duo_chat.png b/doc/user/gitlab_duo_chat/img/code_selection_duo_chat.png
new file mode 100644
index 00000000000..b325c2cd9c9
Binary files /dev/null and b/doc/user/gitlab_duo_chat/img/code_selection_duo_chat.png differ
diff --git a/doc/user/gitlab_duo_chat/troubleshooting.md b/doc/user/gitlab_duo_chat/troubleshooting.md
index 45295440dfa..4913990d4a1 100644
--- a/doc/user/gitlab_duo_chat/troubleshooting.md
+++ b/doc/user/gitlab_duo_chat/troubleshooting.md
@@ -82,7 +82,7 @@ For more information about slash commands, refer to the documentation:
- [/tests](../gitlab_duo_chat/examples.md#write-tests-in-the-ide)
- [/refactor](../gitlab_duo_chat/examples.md#refactor-code-in-the-ide)
- [/fix](../gitlab_duo_chat/examples.md#fix-code-in-the-ide)
-- [/explain](../gitlab_duo_chat/examples.md#explain-code)
+- [/explain](../gitlab_duo_chat/examples.md#explain-selected-code)
## `Error M4001`
diff --git a/doc/user/project/merge_requests/changes.md b/doc/user/project/merge_requests/changes.md
index 738c48c4f40..58811e0f5be 100644
--- a/doc/user/project/merge_requests/changes.md
+++ b/doc/user/project/merge_requests/changes.md
@@ -261,7 +261,7 @@ We cannot guarantee that the large language model produces results that are corr
You can also explain code in:
- A [file](../../../user/project/repository/code_explain.md).
-- The [IDE](../../../user/gitlab_duo_chat/examples.md#explain-code).
+- The [IDE](../../../user/gitlab_duo_chat/examples.md#explain-selected-code).
## Expand or collapse comments
diff --git a/doc/user/project/organize_work_with_projects.md b/doc/user/project/organize_work_with_projects.md
index c35895e4b04..88074b1f4b8 100644
--- a/doc/user/project/organize_work_with_projects.md
+++ b/doc/user/project/organize_work_with_projects.md
@@ -17,10 +17,10 @@ GitLab does not limit the number of private projects you can create.
| | | |
|--|--|--|
-| [**Getting started**](../../user/get_started/get_started_projects.md) **{chevron-right}** | [**Create a project**](index.md) **{chevron-right}** | [**Manage projects**](working_with_projects.md) **{chevron-right}** |
-| [**Project visibility**](../public_access.md) **{chevron-right}** | [**Project settings**](working_with_projects.md) **{chevron-right}** | [**Description templates**](../../user/project/description_templates.md) **{chevron-right}** |
-| [**Project access tokens**](../project/settings/project_access_tokens.md) **{chevron-right}** | [**Deploy keys**](../../user/project/deploy_keys/index.md) **{chevron-right}** | [**Deploy tokens**](../../user/project/deploy_tokens/index.md) **{chevron-right}** |
-| [**Share projects**](../project/members/share_project_with_groups.md) **{chevron-right}** | [**Reserved project and group names**](../../user/reserved_names.md) **{chevron-right}** | [**Search**](../../user/search/index.md) **{chevron-right}** |
-| [**Badges**](../../user/project/badges.md) **{chevron-right}** | [**Project topics**](../../user/project/project_topics.md) **{chevron-right}** | [**Code intelligence**](../../user/project/code_intelligence.md) **{chevron-right}** |
-| [**Import and migrate**](../../user/project/import/index.md) **{chevron-right}** | [**System notes**](../../user/project/system_notes.md) **{chevron-right}** | [**Transfer a project to another namespace**](../../user/project/import/index.md) **{chevron-right}** |
-| [**Use a project as a Go package**](../../user/project/use_project_as_go_package.md) **{chevron-right}** | [**Tutorial: Build a protected workflow for your project**](../../tutorials/protected_workflow/index.md) **{chevron-right}** | [**Troubleshooting**](../../user/project/troubleshooting.md) **{chevron-right}** |
+| [**Getting started**](../../user/get_started/get_started_projects.md) **{chevron-right}**
Overview of how features fit together. | [**Create a project**](index.md) **{chevron-right}**
New project, project templates. | [**Manage projects**](working_with_projects.md) **{chevron-right}**
Settings, configuration, project activity, project deletion. |
+| [**Project visibility**](../public_access.md) **{chevron-right}****
Public, private, internal. | [**Project settings**](working_with_projects.md) **{chevron-right}**
Project features, analytics, project permissions. | [**Description templates**](../../user/project/description_templates.md) **{chevron-right}**
Issue templates, merge request templates, instance and group templates. |
+| [**Project access tokens**](../project/settings/project_access_tokens.md) **{chevron-right}**
Authentication, create, revoke, token expiration. | [**Deploy keys**](../../user/project/deploy_keys/index.md) **{chevron-right}**
Public SSH keys, repository access, bot users, read-only access. | [**Deploy tokens**](../../user/project/deploy_tokens/index.md) **{chevron-right}**
Repository cloning, token creation, container registry. |
+| [**Share projects**](../project/members/share_project_with_groups.md)**{chevron-right}**
Member roles, invitations, group access. | [**Reserved project and group names**](../../user/reserved_names.md) **{chevron-right}**
Naming conventions, restrictions, reserved names. | [**Search**](../../user/search/index.md) **{chevron-right}**
Basic, advanced, exact, search scope, commit SHA search. |
+| [**Badges**](../../user/project/badges.md) **{chevron-right}**
Pipeline status, group, project, custom badges. | [**Project topics**](../../user/project/project_topics.md) **{chevron-right}**
Project organization, subscribe, view. | [**Code intelligence**](../../user/project/code_intelligence.md) **{chevron-right}**
Type signatures, symbol documentation, go-to definition. |
+| [**Import and migrate**](../../user/project/import/index.md) **{chevron-right}**
Repository migration, third-party repositories, user contribution mapping. | [**System notes**](../../user/project/system_notes.md) **{chevron-right}**
Event history, activity log, comments history. | [**Transfer a project to another namespace**](../../user/project/import/index.md) **{chevron-right}**
Namespace transfer, subscription transfer. |
+| [**Use a project as a Go package**](../../user/project/use_project_as_go_package.md) **{chevron-right}**
Go modules, import calls. | [**Tutorial: Build a protected workflow for your project**](../../tutorials/protected_workflow/index.md) **{chevron-right}**
Security, approval rules, branch protection. | [**Troubleshooting**](../../user/project/troubleshooting.md) **{chevron-right}**
Problem solving, common issues, debugging, error resolution. |
diff --git a/doc/user/project/repository/code_explain.md b/doc/user/project/repository/code_explain.md
index 840d32d7e41..9c51e3f75c6 100644
--- a/doc/user/project/repository/code_explain.md
+++ b/doc/user/project/repository/code_explain.md
@@ -42,4 +42,4 @@ We cannot guarantee that the large language model produces results that are corr
You can also explain code in:
- A [merge request](../../../user/project/merge_requests/changes.md#explain-code-in-a-merge-request).
-- The [IDE](../../../user/gitlab_duo_chat/examples.md#explain-code).
+- The [IDE](../../../user/gitlab_duo_chat/examples.md#explain-selected-code).
diff --git a/doc/user/project/settings/import_export_troubleshooting.md b/doc/user/project/settings/import_export_troubleshooting.md
index e912731ac24..e0fdd30bb62 100644
--- a/doc/user/project/settings/import_export_troubleshooting.md
+++ b/doc/user/project/settings/import_export_troubleshooting.md
@@ -199,6 +199,13 @@ e.send(:design_repo_saver).send(:save)
# The following line should show you the export_path similar to /var/opt/gitlab/gitlab-rails/shared/tmp/gitlab_exports/@hashed/49/94/4994....
s = Gitlab::ImportExport::Saver.new(exportable: p, shared: p.import_export_shared, user: u)
+# Prior to GitLab 17.0, the `user` parameter was not supported. If you encounter an
+# error with the above or are unsure whether or not to supply the `user`
+# argument, use the following check:
+Gitlab::ImportExport::Saver.instance_method(:initialize).parameters.include?([:keyreq, :user])
+# If the preceding check returns false, omit the user argument:
+s = Gitlab::ImportExport::Saver.new(exportable: p, shared: p.import_export_shared)
+
# To try and upload use:
s.send(:compress_and_save)
s.send(:save_upload)
diff --git a/lib/click_house/models/base_model.rb b/lib/click_house/models/base_model.rb
index 89624076f15..ec453bb638b 100644
--- a/lib/click_house/models/base_model.rb
+++ b/lib/click_house/models/base_model.rb
@@ -32,6 +32,10 @@ module ClickHouse
self.class.new(@query_builder.offset(count))
end
+ def group(...)
+ self.class.new(@query_builder.group(...))
+ end
+
def select(...)
self.class.new(@query_builder.select(...))
end
diff --git a/lib/click_house/models/ci/finished_pipelines_hourly.rb b/lib/click_house/models/ci/finished_pipelines_hourly.rb
new file mode 100644
index 00000000000..7450fbed241
--- /dev/null
+++ b/lib/click_house/models/ci/finished_pipelines_hourly.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module ClickHouse # rubocop:disable Gitlab/BoundedContexts -- Existing module
+ module Models
+ module Ci
+ class FinishedPipelinesHourly < ClickHouse::Models::BaseModel
+ def self.table_name
+ 'ci_finished_pipelines_hourly'
+ end
+
+ def self.for_project(project)
+ new.for_project(project)
+ end
+
+ def self.by_status(statuses)
+ new.by_status(statuses)
+ end
+
+ def self.group_by_status
+ new.group_by_status
+ end
+
+ def for_project(project)
+ where(path: project.project_namespace.traversal_path)
+ end
+
+ def by_status(statuses)
+ where(status: statuses)
+ end
+
+ def group_by_status
+ group([@query_builder.table[:status]])
+ end
+
+ def count_pipelines_function
+ Arel::Nodes::NamedFunction.new('countMerge', [@query_builder.table[:count_pipelines]])
+ end
+ end
+ end
+ end
+end
diff --git a/lib/click_house/query_builder.rb b/lib/click_house/query_builder.rb
index c7f73f7ccef..f9c3fb8e8cb 100644
--- a/lib/click_house/query_builder.rb
+++ b/lib/click_house/query_builder.rb
@@ -90,6 +90,14 @@ module ClickHouse
new_instance
end
+ def group(*columns)
+ new_instance = deep_clone
+
+ new_instance.manager.group(*columns)
+
+ new_instance
+ end
+
def limit(count)
manager.take(count)
self
diff --git a/scripts/pipeline/pre_merge_checks.rb b/scripts/pipeline/pre_merge_checks.rb
index a27b6e9d1d6..d63e041000f 100755
--- a/scripts/pipeline/pre_merge_checks.rb
+++ b/scripts/pipeline/pre_merge_checks.rb
@@ -14,12 +14,14 @@ end
require 'time'
class PreMergeChecks
- DEFAULT_API_ENDPOINT = "https://gitlab.com/api/v4"
- MERGE_TRAIN_REF_REGEX = %r{\Arefs/merge-requests/\d+/train\z}
- TIER_IDENTIFIER_REGEX = /tier:\d/
- REQUIRED_TIER_IDENTIFIER = 'tier:3'
- PREDICTIVE_PIPELINE_IDENTIFIER = 'predictive'
- PIPELINE_FRESHNESS_THRESHOLD_IN_HOURS = 8
+ DEFAULT_API_ENDPOINT = "https://gitlab.com/api/v4"
+ MERGE_TRAIN_REF_REGEX = %r{\Arefs/merge-requests/\d+/train\z}
+ PIPELINE_FRESHNESS_DEFAULT_THRESHOLD_IN_HOURS = 8
+ PIPELINE_FRESHNESS_STABLE_BRANCHES_THRESHOLD_IN_HOURS = 72
+ PREDICTIVE_PIPELINE_IDENTIFIER = 'predictive'
+ REQUIRED_TIER_IDENTIFIER = 'tier:3'
+ STABLE_BRANCH_SUFFIX = '-stable-ee'
+ TIER_IDENTIFIER_REGEX = /tier:\d/
PreMergeChecksFailedError = Class.new(StandardError)
PreMergeChecksStatus = Struct.new(:exitstatus, :message) do
@@ -31,10 +33,12 @@ class PreMergeChecks
def initialize(
api_endpoint: ENV.fetch('CI_API_V4_URL', DEFAULT_API_ENDPOINT),
project_id: ENV['CI_PROJECT_ID'],
- merge_request_iid: ENV['CI_MERGE_REQUEST_IID'])
+ merge_request_iid: ENV['CI_MERGE_REQUEST_IID'],
+ target_branch: ENV['CI_MERGE_REQUEST_TARGET_BRANCH_NAME'])
@api_endpoint = api_endpoint
@project_id = project_id
@merge_request_iid = merge_request_iid.to_i
+ @target_branch = target_branch
end
def execute
@@ -62,7 +66,7 @@ class PreMergeChecks
private
- attr_reader :api_endpoint, :project_id, :merge_request_iid
+ attr_reader :api_endpoint, :project_id, :merge_request_iid, :target_branch
def api_client
@api_client ||= begin
@@ -77,6 +81,7 @@ class PreMergeChecks
def check_required_ids!
fail_check!("Missing project_id!") unless project_id
fail_check!("Missing merge_request_iid!") if merge_request_iid == 0
+ fail_check!("Missing target_branch!") unless target_branch
end
def check_pipeline_for_merged_results!(pipeline)
@@ -101,10 +106,17 @@ class PreMergeChecks
def check_pipeline_freshness!(pipeline)
hours_ago = ((Time.now - Time.parse(pipeline.created_at)) / 3600).ceil(2)
- return if hours_ago < PIPELINE_FRESHNESS_THRESHOLD_IN_HOURS
+ threshold =
+ if target_branch_is_stable_branch?
+ PIPELINE_FRESHNESS_STABLE_BRANCHES_THRESHOLD_IN_HOURS
+ else
+ PIPELINE_FRESHNESS_DEFAULT_THRESHOLD_IN_HOURS
+ end
+
+ return if hours_ago < threshold
fail_check! <<~TEXT
- Expected latest pipeline (#{pipeline.web_url}) to be created within the last #{PIPELINE_FRESHNESS_THRESHOLD_IN_HOURS} hours (it was created #{hours_ago} hours ago)!
+ Expected latest pipeline (#{pipeline.web_url}) to be created within the last #{threshold} hours (it was created #{hours_ago} hours ago)!
Please start a new pipeline.
TEXT
@@ -126,6 +138,10 @@ class PreMergeChecks
end
end
+ def target_branch_is_stable_branch?
+ target_branch.end_with?(STABLE_BRANCH_SUFFIX)
+ end
+
def fail_check!(text)
raise PreMergeChecksFailedError, text
end
@@ -144,12 +160,18 @@ if $PROGRAM_NAME == __FILE__
options[:merge_request_iid] = value
end
+ opts.on("-t", "--target-branch [string]", String, "Target branch name") do |value|
+ options[:target_branch] = value
+ end
+
opts.on("-h", "--help") do
- puts "Usage: #{File.basename(__FILE__)} [--project_id ] [--merge_request_iid ]"
+ puts "Usage: #{File.basename(__FILE__)} [--project_id ] " \
+ "[--merge_request_iid ] [--target-branch ]"
puts
puts "Examples:"
puts
- puts "#{File.basename(__FILE__)} --project_id \"gitlab-org/gitlab\" --merge_request_iid \"1\""
+ puts "#{File.basename(__FILE__)} --project_id \"gitlab-org/gitlab\" " \
+ "--merge_request_iid \"1\" --target-branch \"master\""
exit
end
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index 1bd6c965ba9..6163dd44ce1 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -10,31 +10,33 @@ RSpec.describe 'Database schema', feature_category: :database do
let(:columns_name_with_jsonb) { retrieve_columns_name_with_jsonb }
IGNORED_INDEXES_ON_FKS = {
+ ai_testing_terms_acceptances: %w[user_id], # testing terms only have 1 entry, and if the user is deleted the record should remain
application_settings: %w[instance_administration_project_id instance_administrators_group_id],
+ ci_build_trace_metadata: [%w[partition_id build_id], %w[partition_id trace_artifact_id]], # the index on build_id is enough
+ ci_builds: [%w[partition_id stage_id], %w[partition_id execution_config_id], %w[auto_canceled_by_partition_id auto_canceled_by_id], %w[upstream_pipeline_partition_id upstream_pipeline_id], %w[partition_id commit_id]], # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142804#note_1745483081
+ ci_daily_build_group_report_results: [%w[partition_id last_pipeline_id]], # index on last_pipeline_id is sufficient
+ ci_pipeline_artifacts: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
ci_pipeline_chat_data: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
+ ci_pipeline_messages: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
+ ci_pipeline_metadata: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
+ ci_pipeline_variables: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
+ ci_pipelines: [%w[auto_canceled_by_partition_id auto_canceled_by_id]], # index on auto_canceled_by_id is sufficient
+ ci_pipelines_config: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
+ ci_sources_pipelines: [%w[source_partition_id source_pipeline_id], %w[partition_id pipeline_id]],
+ ci_sources_projects: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
+ ci_stages: [%w[partition_id pipeline_id]], # the index on pipeline_id is sufficient
+ notes: %w[namespace_id], # this index is added in an async manner, hence it needs to be ignored in the first phase.
+ p_ci_build_trace_metadata: [%w[partition_id build_id], %w[partition_id trace_artifact_id]], # the index on build_id is enough
+ p_ci_builds: [%w[partition_id stage_id], %w[partition_id execution_config_id], %w[auto_canceled_by_partition_id auto_canceled_by_id], %w[upstream_pipeline_partition_id upstream_pipeline_id], %w[partition_id commit_id]], # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142804#note_1745483081
+ p_ci_builds_execution_configs: [%w[partition_id pipeline_id]], # the index on pipeline_id is enough
+ p_ci_pipeline_variables: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
+ p_ci_stages: [%w[partition_id pipeline_id]], # the index on pipeline_id is sufficient
# `search_index_id index_type` is the composite foreign key configured for `search_namespace_index_assignments`,
# but in Search::NamespaceIndexAssignment model, only `search_index_id` is used as foreign key and indexed
search_namespace_index_assignments: [%w[search_index_id index_type]],
slack_integrations_scopes: [%w[slack_api_scope_id]],
- notes: %w[namespace_id], # this index is added in an async manner, hence it needs to be ignored in the first phase.
- users: [%w[accepted_term_id]],
- ci_pipeline_artifacts: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
- ci_sources_projects: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
- ci_daily_build_group_report_results: [%w[partition_id last_pipeline_id]], # index on last_pipeline_id is sufficient
- ci_builds: [%w[partition_id stage_id], %w[partition_id execution_config_id], %w[auto_canceled_by_partition_id auto_canceled_by_id], %w[upstream_pipeline_partition_id upstream_pipeline_id], %w[partition_id commit_id]], # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142804#note_1745483081
- ci_pipeline_variables: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
- p_ci_pipeline_variables: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
- ci_pipelines: [%w[auto_canceled_by_partition_id auto_canceled_by_id]], # index on auto_canceled_by_id is sufficient
- ci_pipelines_config: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
- ci_pipeline_metadata: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
- ci_pipeline_messages: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
- p_ci_builds: [%w[partition_id stage_id], %w[partition_id execution_config_id], %w[auto_canceled_by_partition_id auto_canceled_by_id], %w[upstream_pipeline_partition_id upstream_pipeline_id], %w[partition_id commit_id]], # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142804#note_1745483081
- ci_stages: [%w[partition_id pipeline_id]], # the index on pipeline_id is sufficient
- p_ci_stages: [%w[partition_id pipeline_id]], # the index on pipeline_id is sufficient
- ai_testing_terms_acceptances: %w[user_id], # testing terms only have 1 entry, and if the user is deleted the record should remain
- p_ci_builds_execution_configs: [%w[partition_id pipeline_id]], # the index on pipeline_id is enough
- ci_sources_pipelines: [%w[source_partition_id source_pipeline_id], %w[partition_id pipeline_id]],
- snippets: %w[organization_id] # this index is added in an async manner, hence it needs to be ignored in the first phase.
+ snippets: %w[organization_id], # this index is added in an async manner, hence it needs to be ignored in the first phase.
+ users: [%w[accepted_term_id]]
}.with_indifferent_access.freeze
# If splitting FK and table removal into two MRs as suggested in the docs, use this constant in the initial FK removal MR.
diff --git a/spec/frontend/notes/components/noteable_discussion_spec.js b/spec/frontend/notes/components/noteable_discussion_spec.js
index e7a201cc8d4..ef79243fe39 100644
--- a/spec/frontend/notes/components/noteable_discussion_spec.js
+++ b/spec/frontend/notes/components/noteable_discussion_spec.js
@@ -207,23 +207,6 @@ describe('noteable_discussion component', () => {
expect(replyWrapper.exists()).toBe(true);
expect(replyWrapper.classes('internal-note')).toBe(true);
});
-
- it('should add `public-note` class when the discussion is not internal', async () => {
- const softCopyInternalNotes = [...discussionMock.notes];
- const mockPublicNotes = softCopyInternalNotes.splice(0, 2);
- mockPublicNotes[0].internal = false;
-
- const mockDiscussion = {
- ...discussionMock,
- notes: [...mockPublicNotes],
- };
- wrapper.setProps({ discussion: mockDiscussion });
- await nextTick();
-
- const replyWrapper = wrapper.find('[data-testid="reply-wrapper"]');
- expect(replyWrapper.exists()).toBe(true);
- expect(replyWrapper.classes('public-note')).toBe(true);
- });
});
describe('for resolved thread', () => {
diff --git a/spec/frontend/observability/utils_spec.js b/spec/frontend/observability/utils_spec.js
index d0c42e38436..c84f2a098c9 100644
--- a/spec/frontend/observability/utils_spec.js
+++ b/spec/frontend/observability/utils_spec.js
@@ -7,6 +7,7 @@ import {
formattedTimeFromDate,
isTracingDateRangeOutOfBounds,
validatedDateRangeQuery,
+ urlWithStringifiedPayloadParam,
} from '~/observability/utils';
import {
CUSTOM_DATE_RANGE_OPTION,
@@ -271,3 +272,15 @@ describe('validatedDateRangeQuery', () => {
expect(result).toEqual({ value: '1h' });
});
});
+
+describe('urlWithStringifiedPayloadParam', () => {
+ it('returns the url with a stringified param', () => {
+ expect(
+ urlWithStringifiedPayloadParam(
+ 'http://gdk.test:3443/?foo=bar',
+ { a: 'b', c: 'd' },
+ 'my_param',
+ ),
+ ).toBe('http://gdk.test:3443/?foo=bar&my_param=%7B%22a%22%3A%22b%22%2C%22c%22%3A%22d%22%7D');
+ });
+});
diff --git a/spec/lib/click_house/models/base_model_spec.rb b/spec/lib/click_house/models/base_model_spec.rb
index 376300d7781..fcee40d48e3 100644
--- a/spec/lib/click_house/models/base_model_spec.rb
+++ b/spec/lib/click_house/models/base_model_spec.rb
@@ -90,6 +90,19 @@ RSpec.describe ClickHouse::Models::BaseModel, feature_category: :database do
end
end
+ describe '#group' do
+ it 'returns a new instance with grouped results' do
+ dummy_instance = dummy_class.new(query_builder)
+
+ expect(query_builder).to receive(:group).with(:id, :name).and_return(updated_query_builder)
+
+ new_instance = dummy_instance.group(:id, :name)
+
+ expect(new_instance).to be_a(dummy_class)
+ expect(new_instance).not_to eq(dummy_instance)
+ end
+ end
+
describe '#select' do
it 'returns a new instance with selected fields' do
dummy_instance = dummy_class.new(query_builder)
diff --git a/spec/lib/click_house/models/ci/finished_pipelines_hourly_spec.rb b/spec/lib/click_house/models/ci/finished_pipelines_hourly_spec.rb
new file mode 100644
index 00000000000..702d97565f7
--- /dev/null
+++ b/spec/lib/click_house/models/ci/finished_pipelines_hourly_spec.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ClickHouse::Models::Ci::FinishedPipelinesHourly, feature_category: :fleet_visibility do
+ let(:instance) { described_class.new }
+
+ let_it_be(:group) { create(:group, :nested) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:path) { project.reload.project_namespace.traversal_path }
+
+ specify { expect(path).to match(%r{\A(\d+/){3}\z}) }
+
+ describe '#for_project' do
+ it 'builds the correct SQL' do
+ expected_sql = <<~SQL.lines(chomp: true).join(' ')
+ SELECT * FROM "ci_finished_pipelines_hourly"
+ WHERE "ci_finished_pipelines_hourly"."path" = '#{path}'
+ SQL
+
+ result_sql = instance.for_project(project).to_sql
+
+ expect(result_sql.strip).to eq(expected_sql.strip)
+ end
+ end
+
+ describe '#by_status' do
+ it 'builds the correct SQL' do
+ expected_sql = <<~SQL.lines(chomp: true).join(' ')
+ SELECT * FROM "ci_finished_pipelines_hourly"
+ WHERE "ci_finished_pipelines_hourly"."status" IN ('failed', 'success')
+ SQL
+
+ result_sql = instance.by_status(%i[failed success]).to_sql
+
+ expect(result_sql.strip).to eq(expected_sql.strip)
+ end
+ end
+
+ describe '#group_by_status' do
+ it 'builds the correct SQL' do
+ expected_sql = <<~SQL.lines(chomp: true).join(' ')
+ SELECT "ci_finished_pipelines_hourly"."status"
+ FROM "ci_finished_pipelines_hourly"
+ GROUP BY "ci_finished_pipelines_hourly"."status"
+ SQL
+
+ result_sql = instance.select(:status).group_by_status.to_sql
+
+ expect(result_sql.strip).to eq(expected_sql.strip)
+ end
+ end
+
+ describe '#count_pipelines_function' do
+ it 'builds the correct SQL' do
+ expected_sql = <<~SQL.lines(chomp: true).join(' ')
+ SELECT "ci_finished_pipelines_hourly"."status", countMerge("ci_finished_pipelines_hourly"."count_pipelines")
+ FROM "ci_finished_pipelines_hourly"
+ SQL
+
+ result_sql = instance.select(:status, instance.count_pipelines_function).to_sql
+
+ expect(result_sql.strip).to eq(expected_sql.strip)
+ end
+ end
+
+ describe 'class methods' do
+ before do
+ allow(described_class).to receive(:new).and_return(instance)
+ end
+
+ describe '.for_project' do
+ it 'calls the corresponding instance method' do
+ expect(instance).to receive(:for_project).with(project)
+
+ described_class.for_project(project)
+ end
+ end
+
+ describe '.by_status' do
+ it 'calls the corresponding instance method' do
+ expect(instance).to receive(:by_status).with(:success)
+
+ described_class.by_status(:success)
+ end
+ end
+
+ describe '.group_by_status' do
+ it 'calls the corresponding instance method' do
+ expect(instance).to receive(:group_by_status)
+
+ described_class.group_by_status
+ end
+ end
+ end
+
+ describe 'method chaining' do
+ it 'builds the correct SQL with chained methods' do
+ expected_sql = <<~SQL.lines(chomp: true).join(' ')
+ SELECT "ci_finished_pipelines_hourly"."status" FROM "ci_finished_pipelines_hourly"
+ WHERE "ci_finished_pipelines_hourly"."path" = '#{path}'
+ AND "ci_finished_pipelines_hourly"."status" IN ('failed', 'success')
+ GROUP BY "ci_finished_pipelines_hourly"."status"
+ SQL
+
+ result_sql = instance.for_project(project).select(:status).by_status(%i[failed success]).group_by_status.to_sql
+
+ expect(result_sql.strip).to eq(expected_sql.strip)
+ end
+ end
+end
diff --git a/spec/lib/click_house/query_builder_spec.rb b/spec/lib/click_house/query_builder_spec.rb
index f5e1d53e7c1..13b51f432cc 100644
--- a/spec/lib/click_house/query_builder_spec.rb
+++ b/spec/lib/click_house/query_builder_spec.rb
@@ -278,6 +278,30 @@ RSpec.describe ClickHouse::QueryBuilder, feature_category: :database do
end
end
+ describe '#group' do
+ it 'builds correct group query' do
+ expected_sql = <<~SQL.lines(chomp: true).join(' ')
+ SELECT * FROM "test_table"
+ GROUP BY column1
+ SQL
+
+ sql = builder.group(:column1).to_sql
+
+ expect(sql).to eq(expected_sql)
+ end
+
+ it 'chains multiple groups when called multiple times' do
+ expected_sql = <<~SQL.lines(chomp: true).join(' ')
+ SELECT * FROM "test_table"
+ GROUP BY column1, column2
+ SQL
+
+ sql = builder.group(:column1).group(:column2).to_sql
+
+ expect(sql).to eq(expected_sql)
+ end
+ end
+
describe '#to_sql' do
it 'delegates to the Arel::SelectManager' do
expect(builder.send(:manager)).to receive(:to_sql)
diff --git a/spec/lib/gitlab/database/postgres_partition_spec.rb b/spec/lib/gitlab/database/postgres_partition_spec.rb
index 4b7566794cb..75d11ed9b7d 100644
--- a/spec/lib/gitlab/database/postgres_partition_spec.rb
+++ b/spec/lib/gitlab/database/postgres_partition_spec.rb
@@ -46,7 +46,11 @@ RSpec.describe Gitlab::Database::PostgresPartition, type: :model, feature_catego
end
describe '.with_list_constraint' do
- subject(:with_list_constraint) { described_class.with_list_constraint(partition_id) }
+ subject(:with_list_constraint) do
+ described_class
+ .with_parent_tables(Ci::Partitionable.registered_models.map(&:table_name))
+ .with_list_constraint(partition_id)
+ end
context 'when condition matches' do
let(:partition_id) { '102' }
diff --git a/spec/policies/virtual_registries/packages/policies/group_policy_spec.rb b/spec/policies/virtual_registries/packages/policies/group_policy_spec.rb
index e1e4eeb2821..5e5a7ba739c 100644
--- a/spec/policies/virtual_registries/packages/policies/group_policy_spec.rb
+++ b/spec/policies/virtual_registries/packages/policies/group_policy_spec.rb
@@ -13,29 +13,32 @@ RSpec.describe VirtualRegistries::Packages::Policies::GroupPolicy, feature_categ
describe 'read_virtual_registry' do
where(:group_visibility, :current_user, :allowed?) do
- 'PUBLIC' | nil | false
- 'PUBLIC' | ref(:non_group_member) | true
- 'PUBLIC' | ref(:guest) | true
- 'PUBLIC' | ref(:reporter) | true
- 'PUBLIC' | ref(:developer) | true
- 'PUBLIC' | ref(:maintainer) | true
- 'PUBLIC' | ref(:owner) | true
+ 'PUBLIC' | nil | false
+ 'PUBLIC' | ref(:non_group_member) | false
+ 'PUBLIC' | ref(:guest) | true
+ 'PUBLIC' | ref(:reporter) | true
+ 'PUBLIC' | ref(:developer) | true
+ 'PUBLIC' | ref(:maintainer) | true
+ 'PUBLIC' | ref(:owner) | true
+ 'PUBLIC' | ref(:organization_owner) | true
- 'INTERNAL' | nil | false
- 'INTERNAL' | ref(:non_group_member) | true
- 'INTERNAL' | ref(:guest) | true
- 'INTERNAL' | ref(:reporter) | true
- 'INTERNAL' | ref(:developer) | true
- 'INTERNAL' | ref(:maintainer) | true
- 'INTERNAL' | ref(:owner) | true
+ 'INTERNAL' | nil | false
+ 'INTERNAL' | ref(:non_group_member) | false
+ 'INTERNAL' | ref(:guest) | true
+ 'INTERNAL' | ref(:reporter) | true
+ 'INTERNAL' | ref(:developer) | true
+ 'INTERNAL' | ref(:maintainer) | true
+ 'INTERNAL' | ref(:owner) | true
+ 'INTERNAL' | ref(:organization_owner) | true
- 'PRIVATE' | nil | false
- 'PRIVATE' | ref(:non_group_member) | false
- 'PRIVATE' | ref(:guest) | true
- 'PRIVATE' | ref(:reporter) | true
- 'PRIVATE' | ref(:developer) | true
- 'PRIVATE' | ref(:maintainer) | true
- 'PRIVATE' | ref(:owner) | true
+ 'PRIVATE' | nil | false
+ 'PRIVATE' | ref(:non_group_member) | false
+ 'PRIVATE' | ref(:guest) | true
+ 'PRIVATE' | ref(:reporter) | true
+ 'PRIVATE' | ref(:developer) | true
+ 'PRIVATE' | ref(:maintainer) | true
+ 'PRIVATE' | ref(:owner) | true
+ 'PRIVATE' | ref(:organization_owner) | true
end
with_them do
@@ -46,14 +49,42 @@ RSpec.describe VirtualRegistries::Packages::Policies::GroupPolicy, feature_categ
it { is_expected.to public_send(allowed? ? :be_allowed : :be_disallowed, :read_virtual_registry) }
end
- context 'for deploy token' do
- let(:deploy_token) do
- create(:deploy_token, :group).tap do |token|
- create(:group_deploy_token, group: target, deploy_token: token)
+ context 'with project membership' do
+ let_it_be(:project) { create(:project, group: group) }
+ let(:current_user) { non_group_member }
+
+ %i[
+ guest
+ reporter
+ developer
+ maintainer
+ owner
+ ].each do |role|
+ context "for #{role}" do
+ before do
+ project.send(:"add_#{role}", current_user)
+ end
+
+ it { expect_allowed(:read_virtual_registry) }
end
end
+ end
- subject { described_class.new(deploy_token, policy_subject) }
+ context 'for admin' do
+ let(:current_user) { admin }
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it { expect_allowed(:read_virtual_registry) }
+ end
+
+ context 'when admin mode is disabled' do
+ it { expect_disallowed(:read_virtual_registry) }
+ end
+ end
+
+ context 'for deploy token' do
+ let(:deploy_token) { create(:deploy_token, :group, groups: [target]) }
+ let(:current_user) { deploy_token }
where(:target, :group_visibility, :read_virtual_registry, :allowed?) do
ref(:group) | 'PUBLIC' | true | true
@@ -85,29 +116,32 @@ RSpec.describe VirtualRegistries::Packages::Policies::GroupPolicy, feature_categ
%i[create update destroy].each do |action|
describe "#{action}_virtual_registry" do
where(:group_visibility, :current_user, :allowed?) do
- 'PUBLIC' | nil | false
- 'PUBLIC' | ref(:non_group_member) | false
- 'PUBLIC' | ref(:guest) | false
- 'PUBLIC' | ref(:reporter) | false
- 'PUBLIC' | ref(:developer) | false
- 'PUBLIC' | ref(:maintainer) | true
- 'PUBLIC' | ref(:owner) | true
+ 'PUBLIC' | nil | false
+ 'PUBLIC' | ref(:non_group_member) | false
+ 'PUBLIC' | ref(:guest) | false
+ 'PUBLIC' | ref(:reporter) | false
+ 'PUBLIC' | ref(:developer) | false
+ 'PUBLIC' | ref(:maintainer) | true
+ 'PUBLIC' | ref(:owner) | true
+ 'PUBLIC' | ref(:organization_owner) | true
- 'INTERNAL' | nil | false
- 'INTERNAL' | ref(:non_group_member) | false
- 'INTERNAL' | ref(:guest) | false
- 'INTERNAL' | ref(:reporter) | false
- 'INTERNAL' | ref(:developer) | false
- 'INTERNAL' | ref(:maintainer) | true
- 'INTERNAL' | ref(:owner) | true
+ 'INTERNAL' | nil | false
+ 'INTERNAL' | ref(:non_group_member) | false
+ 'INTERNAL' | ref(:guest) | false
+ 'INTERNAL' | ref(:reporter) | false
+ 'INTERNAL' | ref(:developer) | false
+ 'INTERNAL' | ref(:maintainer) | true
+ 'INTERNAL' | ref(:owner) | true
+ 'INTERNAL' | ref(:organization_owner) | true
- 'PRIVATE' | nil | false
- 'PRIVATE' | ref(:non_group_member) | false
- 'PRIVATE' | ref(:guest) | false
- 'PRIVATE' | ref(:reporter) | false
- 'PRIVATE' | ref(:developer) | false
- 'PRIVATE' | ref(:maintainer) | true
- 'PRIVATE' | ref(:owner) | true
+ 'PRIVATE' | nil | false
+ 'PRIVATE' | ref(:non_group_member) | false
+ 'PRIVATE' | ref(:guest) | false
+ 'PRIVATE' | ref(:reporter) | false
+ 'PRIVATE' | ref(:developer) | false
+ 'PRIVATE' | ref(:maintainer) | true
+ 'PRIVATE' | ref(:owner) | true
+ 'PRIVATE' | ref(:organization_owner) | true
end
with_them do
@@ -118,5 +152,17 @@ RSpec.describe VirtualRegistries::Packages::Policies::GroupPolicy, feature_categ
it { is_expected.to public_send(allowed? ? :be_allowed : :be_disallowed, :"#{action}_virtual_registry") }
end
end
+
+ context 'for admin' do
+ let(:current_user) { admin }
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it { expect_allowed(:"#{action}_virtual_registry") }
+ end
+
+ context 'when admin mode is disabled' do
+ it { expect_disallowed(:"#{action}_virtual_registry") }
+ end
+ end
end
end
diff --git a/spec/requests/api/virtual_registries/packages/maven_spec.rb b/spec/requests/api/virtual_registries/packages/maven_spec.rb
index a46a9931d99..14a00c31b65 100644
--- a/spec/requests/api/virtual_registries/packages/maven_spec.rb
+++ b/spec/requests/api/virtual_registries/packages/maven_spec.rb
@@ -7,18 +7,18 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
include WorkhorseHelpers
include HttpBasicAuthHelpers
- let_it_be_with_reload(:registry) { create(:virtual_registries_packages_maven_registry) }
+ let_it_be(:group) { create(:group) }
+ let_it_be_with_reload(:registry) { create(:virtual_registries_packages_maven_registry, group: group) }
let_it_be(:upstream) { create(:virtual_registries_packages_maven_upstream, registry: registry) }
- let_it_be(:group) { registry.group }
let_it_be(:project) { create(:project, namespace: group) }
- let_it_be(:user) { project.creator }
- let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
+ let_it_be(:user) { create(:user, owner_of: project) }
let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
let_it_be(:deploy_token) do
create(:deploy_token, :group, groups: [group], read_virtual_registry: true)
end
- let_it_be(:headers) { user_basic_auth_header(user, personal_access_token) }
+ let(:personal_access_token) { create(:personal_access_token, user: user) }
+ let(:headers) { user_basic_auth_header(user, personal_access_token) }
shared_examples 'disabled feature flag' do
before do
@@ -95,11 +95,11 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
end
context 'with a non member user' do
- let_it_be(:user) { build_stubbed(:user) }
+ let_it_be(:user) { create(:user) }
where(:group_access_level, :status) do
- 'PUBLIC' | :ok
- 'INTERNAL' | :ok
+ 'PUBLIC' | :forbidden
+ 'INTERNAL' | :forbidden
'PRIVATE' | :not_found
end
@@ -108,11 +108,7 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false))
end
- if params[:status] == :ok
- it_behaves_like 'successful response'
- else
- it_behaves_like 'returning response status', params[:status]
- end
+ it_behaves_like 'returning response status', params[:status]
end
end
@@ -323,11 +319,11 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
end
context 'with a non member user' do
- let_it_be(:user) { build_stubbed(:user) }
+ let_it_be(:user) { create(:user) }
where(:group_access_level, :status) do
- 'PUBLIC' | :ok
- 'INTERNAL' | :ok
+ 'PUBLIC' | :forbidden
+ 'INTERNAL' | :forbidden
'PRIVATE' | :forbidden
end
with_them do
@@ -335,11 +331,7 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false))
end
- if params[:status] == :ok
- it_behaves_like 'successful response'
- else
- it_behaves_like 'returning response status', params[:status]
- end
+ it_behaves_like 'returning response status', params[:status]
end
end
@@ -588,8 +580,8 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
let_it_be(:user) { create(:user) }
where(:group_access_level, :status) do
- 'PUBLIC' | :ok
- 'INTERNAL' | :ok
+ 'PUBLIC' | :forbidden
+ 'INTERNAL' | :forbidden
'PRIVATE' | :forbidden
end
@@ -598,11 +590,7 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false))
end
- if params[:status] == :ok
- it_behaves_like 'successful response'
- else
- it_behaves_like 'returning response status', params[:status]
- end
+ it_behaves_like 'returning response status', params[:status]
end
end
@@ -782,11 +770,11 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
end
context 'with a non member user' do
- let_it_be(:user) { build_stubbed(:user) }
+ let_it_be(:user) { create(:user) }
where(:group_access_level, :status) do
- 'PUBLIC' | :ok
- 'INTERNAL' | :ok
+ 'PUBLIC' | :forbidden
+ 'INTERNAL' | :forbidden
'PRIVATE' | :forbidden
end
@@ -795,11 +783,7 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false))
end
- if params[:status] == :ok
- it_behaves_like 'successful response'
- else
- it_behaves_like 'returning response status', params[:status]
- end
+ it_behaves_like 'returning response status', params[:status]
end
end
diff --git a/spec/scripts/pipeline/pre_merge_checks_spec.rb b/spec/scripts/pipeline/pre_merge_checks_spec.rb
index eeb06afe4c9..6298a4a06de 100644
--- a/spec/scripts/pipeline/pre_merge_checks_spec.rb
+++ b/spec/scripts/pipeline/pre_merge_checks_spec.rb
@@ -8,16 +8,17 @@ require_relative '../../../scripts/pipeline/pre_merge_checks'
RSpec.describe PreMergeChecks, time_travel_to: Time.parse('2024-05-29T10:00:00 UTC'), feature_category: :tooling do
include StubENV
- let(:instance) { described_class.new }
- let(:project_id) { '42' }
- let(:merge_request_iid) { '1' }
- let(:mr_pipelines_url) { "https://gitlab.test/api/v4/projects/#{project_id}/merge_requests/#{merge_request_iid}/pipelines" }
+ let(:instance) { described_class.new }
+ let(:project_id) { '42' }
+ let(:merge_request_iid) { '1' }
+ let(:target_branch) { 'master' }
+ let(:mr_pipelines_url) { "https://gitlab.test/api/v4/projects/#{project_id}/merge_requests/#{merge_request_iid}/pipelines" }
- let(:latest_mr_pipeline_ref) { "refs/merge-requests/1/merge" }
- let(:latest_mr_pipeline_status) { "success" }
+ let(:latest_mr_pipeline_ref) { "refs/merge-requests/1/merge" }
+ let(:latest_mr_pipeline_status) { "success" }
let(:latest_mr_pipeline_created_at) { "2024-05-29T07:15:00 UTC" }
- let(:latest_mr_pipeline_web_url) { "https://gitlab.com/gitlab-org/gitlab/-/pipelines/1310472835" }
- let(:latest_mr_pipeline_name) { "Ruby 3.2 MR [tier:3, gdk]" }
+ let(:latest_mr_pipeline_web_url) { "https://gitlab.com/gitlab-org/gitlab/-/pipelines/1310472835" }
+ let(:latest_mr_pipeline_name) { "Ruby 3.2 MR [tier:3, gdk]" }
let(:latest_mr_pipeline_short) do
{
id: 1309901620,
@@ -78,7 +79,8 @@ RSpec.describe PreMergeChecks, time_travel_to: Time.parse('2024-05-29T10:00:00 U
stub_env(
'CI_API_V4_URL' => 'https://gitlab.test/api/v4',
'CI_PROJECT_ID' => project_id,
- 'CI_MERGE_REQUEST_IID' => merge_request_iid
+ 'CI_MERGE_REQUEST_IID' => merge_request_iid,
+ 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME' => target_branch
)
end
@@ -102,6 +104,16 @@ RSpec.describe PreMergeChecks, time_travel_to: Time.parse('2024-05-29T10:00:00 U
expect(instance.execute.message).to include("Missing merge_request_iid")
end
end
+
+ context 'when target_branch is missing' do
+ let(:target_branch) { nil }
+
+ it 'returns a failed PreMergeChecksStatus' do
+ expect(instance.execute).to be_a(described_class::PreMergeChecksStatus)
+ expect(instance.execute).not_to be_success
+ expect(instance.execute.message).to include("Missing target_branch")
+ end
+ end
end
describe '#execute' do
@@ -138,6 +150,33 @@ RSpec.describe PreMergeChecks, time_travel_to: Time.parse('2024-05-29T10:00:00 U
allow(api_client).to receive(:pipeline).with(project_id, mr_pipelines[2][:id]).and_return(latest_mr_pipeline)
end
+ context 'and the target branch is a stable branch' do
+ let(:target_branch) { 'a-stable-branch-stable-ee' }
+
+ context 'and the latest pipeline is not fresh enough' do
+ let(:latest_mr_pipeline_created_at) { "2024-05-26T09:30:00 UTC" }
+
+ it 'returns a failed PreMergeChecksStatus' do
+ expect(instance.execute).to be_a(described_class::PreMergeChecksStatus)
+ expect(instance.execute).not_to be_success
+ expect(instance.execute.message)
+ .to include(
+ "Expected latest pipeline (#{latest_mr_pipeline_web_url}) to be created within the last 72 hours " \
+ "(it was created 72.5 hours ago)!"
+ )
+ end
+ end
+
+ context 'and the latest pipeline is fresh enough' do
+ let(:latest_mr_pipeline_created_at) { "2024-05-26T10:30:00 UTC" }
+
+ it 'returns a successful PreMergeChecksStatus' do
+ expect(instance.execute).to be_a(described_class::PreMergeChecksStatus)
+ expect(instance.execute).to be_success
+ end
+ end
+ end
+
context 'and it passes all the checks' do
it 'returns a successful PreMergeChecksStatus' do
expect(instance.execute).to be_a(described_class::PreMergeChecksStatus)
diff --git a/spec/services/virtual_registries/packages/maven/handle_file_request_service_spec.rb b/spec/services/virtual_registries/packages/maven/handle_file_request_service_spec.rb
index bee0d9bb58a..d87a8eb3bb6 100644
--- a/spec/services/virtual_registries/packages/maven/handle_file_request_service_spec.rb
+++ b/spec/services/virtual_registries/packages/maven/handle_file_request_service_spec.rb
@@ -5,8 +5,8 @@ require 'spec_helper'
RSpec.describe VirtualRegistries::Packages::Maven::HandleFileRequestService, :aggregate_failures, feature_category: :virtual_registry do
let_it_be(:registry) { create(:virtual_registries_packages_maven_registry, :with_upstream) }
let_it_be(:project) { create(:project, namespace: registry.group) }
+ let_it_be(:user) { create(:user, owner_of: project) }
- let(:user) { project.creator }
let(:upstream) { registry.upstream }
let(:path) { 'com/test/package/1.2.3/package-1.2.3.pom' }
let(:upstream_resource_url) { upstream.url_for(path) }