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`. + + ![Selecting code and asking GitLab Duo Chat to explain using the /explain slash command.](img/code_selection_duo_chat.png) + +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) }