diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql
index 35f031e525d..ae105b80db0 100644
--- a/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql
@@ -23,12 +23,6 @@ query getState($projectPath: ID!, $iid: String!) {
status
warnings
}
- pipelines(first: 1) {
- nodes {
- id
- mergeRequestEventType
- }
- }
shouldBeRebased
sourceBranchExists
state
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/merge_request_event_type.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/merge_request_event_type.query.graphql
new file mode 100644
index 00000000000..a4dc6ba3fdf
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/merge_request_event_type.query.graphql
@@ -0,0 +1,14 @@
+query mergeRequestEventTypeQuery($projectPath: ID!, $iid: String!) {
+ project(fullPath: $projectPath) {
+ id
+ mergeRequest(iid: $iid) {
+ id
+ pipelines(first: 1) {
+ nodes {
+ id
+ mergeRequestEventType
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index efcbdadf76a..4c0bcb9c241 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -182,7 +182,6 @@ export default class MergeRequestStore {
setGraphqlData(project) {
const { mergeRequest } = project;
const pipeline = mergeRequest.headPipeline;
- const pipelines = mergeRequest.pipelines?.nodes;
this.updateStatusState(mergeRequest.state);
@@ -200,7 +199,6 @@ export default class MergeRequestStore {
this.ciStatus = `${this.ciStatus}-with-warnings`;
}
- this.detatchedPipeline = pipelines.length ? pipelines[0].mergeRequestEventType : null;
this.commitsCount = mergeRequest.commitCount;
this.branchMissing =
mergeRequest.detailedMergeStatus !== 'NOT_OPEN' &&
diff --git a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue
index e3508091877..ee27a60a24b 100644
--- a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue
+++ b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue
@@ -191,7 +191,7 @@ export default {
},
},
i18n: {
- title: s__('WorkItem|Linked Items'),
+ title: s__('WorkItem|Linked items'),
fetchError: s__('WorkItem|Something went wrong when fetching items. Please refresh this page.'),
emptyStateMessage: s__(
"WorkItem|Link items together to show that they're related or that one is blocking others.",
diff --git a/app/graphql/types/ci/runner_job_execution_status_enum.rb b/app/graphql/types/ci/runner_job_execution_status_enum.rb
index 7e40efd86d4..3eda1e379b2 100644
--- a/app/graphql/types/ci/runner_job_execution_status_enum.rb
+++ b/app/graphql/types/ci/runner_job_execution_status_enum.rb
@@ -10,10 +10,10 @@ module Types
value: :idle,
alpha: { milestone: '15.7' }
- value 'RUNNING',
- description: 'Runner is executing jobs.',
- value: :running,
- alpha: { milestone: '15.7' }
+ value 'ACTIVE',
+ description: 'Runner is busy.',
+ value: :active,
+ alpha: { milestone: '17.2' }
end
end
end
diff --git a/app/graphql/types/ci/runner_manager_type.rb b/app/graphql/types/ci/runner_manager_type.rb
index 984905b2fa5..6d83e989a42 100644
--- a/app/graphql/types/ci/runner_manager_type.rb
+++ b/app/graphql/types/ci/runner_manager_type.rb
@@ -55,7 +55,7 @@ module Types
statuses = ::Ci::RunnerManager.id_in(runner_manager_ids).with_executing_builds.index_by(&:id)
runner_manager_ids.each do |runner_manager_id|
- loader.call(runner_manager_id, statuses[runner_manager_id] ? :running : :idle)
+ loader.call(runner_manager_id, statuses[runner_manager_id] ? :active : :idle)
end
end
end
diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb
index 17b3ca85afc..4bb8ec55ec0 100644
--- a/app/graphql/types/ci/runner_type.rb
+++ b/app/graphql/types/ci/runner_type.rb
@@ -158,7 +158,7 @@ module Types
statuses = ::Ci::Runner.id_in(runner_ids).with_executing_builds.index_by(&:id)
runner_ids.each do |runner_id|
- loader.call(runner_id, statuses[runner_id] ? :running : :idle)
+ loader.call(runner_id, statuses[runner_id] ? :active : :idle)
end
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index f28a5b3229d..a5c2c2340b8 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -13,7 +13,7 @@ module ProjectsHelper
end
def link_to_project(project)
- link_to namespace_project_path(namespace_id: project.namespace, id: project), title: h(project.name), class: 'gl-link gl-text-truncate' do
+ link_to namespace_project_path(namespace_id: project.namespace, id: project), title: h(project.name), class: 'gl-link gl-truncate' do
title = content_tag(:span, project.name, class: 'project-name')
if project.namespace
@@ -56,7 +56,7 @@ module ProjectsHelper
content_tag(:span, username, name_tag_options)
end
- def link_to_member(project, author, opts = {}, &block)
+ def link_to_member(_project, author, opts = {}, &block)
default_opts = { avatar: true, name: true, title: ":name" }
opts = default_opts.merge(opts)
@@ -394,7 +394,8 @@ module ProjectsHelper
end
def show_terraform_banner?(project)
- Feature.enabled?(:show_terraform_banner, type: :ops) && project.repository_languages.with_programming_language('HCL').exists? && project.terraform_states.empty?
+ Feature.enabled?(:show_terraform_banner, type: :ops) &&
+ project.repository_languages.with_programming_language('HCL').exists? && project.terraform_states.empty?
end
def project_permissions_panel_data(project)
diff --git a/app/services/work_items/widgets/labels_service/base_service.rb b/app/services/work_items/widgets/labels_service/base_service.rb
index 2d679c1f18c..a8ce195a7da 100644
--- a/app/services/work_items/widgets/labels_service/base_service.rb
+++ b/app/services/work_items/widgets/labels_service/base_service.rb
@@ -7,18 +7,11 @@ module WorkItems
private
def prepare_params(params: {}, permitted_params: [])
- clear_label_params(params) if new_type_excludes_widget?
-
return if params.blank?
return unless has_permission?(:set_work_item_metadata)
service_params.merge!(params.slice(*permitted_params))
end
-
- def clear_label_params(params)
- params[:remove_label_ids] = @work_item.labels.map(&:id)
- params[:add_label_ids] = []
- end
end
end
end
diff --git a/app/services/work_items/widgets/labels_service/update_service.rb b/app/services/work_items/widgets/labels_service/update_service.rb
index 780451e3eae..241a156aa06 100644
--- a/app/services/work_items/widgets/labels_service/update_service.rb
+++ b/app/services/work_items/widgets/labels_service/update_service.rb
@@ -5,8 +5,17 @@ module WorkItems
module LabelsService
class UpdateService < BaseService
def prepare_update_params(params: {})
+ clear_label_params(params) if new_type_excludes_widget?
+
prepare_params(params: params, permitted_params: %i[add_label_ids remove_label_ids])
end
+
+ private
+
+ def clear_label_params(params)
+ params[:remove_label_ids] = @work_item.labels.map(&:id)
+ params[:add_label_ids] = []
+ end
end
end
end
diff --git a/app/views/admin/background_jobs/show.html.haml b/app/views/admin/background_jobs/show.html.haml
index 2d04ff8dd64..18fa590c8ed 100644
--- a/app/views/admin/background_jobs/show.html.haml
+++ b/app/views/admin/background_jobs/show.html.haml
@@ -1,8 +1,8 @@
-- page_title _("Background Jobs")
+- page_title _("Background jobs")
- sidekiq_link = link_to('', 'https://sidekiq.org/', target: '_blank', rel: 'noopener noreferrer')
- page_description = safe_format(_('GitLab uses %{link_start}Sidekiq%{link_end} to process background jobs'), tag_pair(sidekiq_link, :link_start, :link_end))
-= render ::Layouts::PageHeadingComponent.new(_('Background Jobs'),
+= render ::Layouts::PageHeadingComponent.new(_('Background jobs'),
description: page_description,
options: { data: { event_tracking_load: 'true', event_tracking: 'view_admin_background_jobs_pageload' } })
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index a33413907ed..3c37cbb490f 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -36,13 +36,13 @@
= _('You can also upload existing files from your computer using the instructions below.')
.git-empty.js-git-empty
%h5= _('Git global setup')
- %pre.gl-bg-gray-10
+ %pre.code.js-syntax-highlight
:preserve
git config --global user.name "#{h git_user_name}"
git config --global user.email "#{h git_user_email}"
%h5= _('Create a new repository')
- %pre.gl-bg-gray-10
+ %pre.code.js-syntax-highlight
:preserve
git clone #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
cd #{h @project.path}
@@ -55,7 +55,7 @@
git push --set-upstream origin #{h escaped_default_branch_name }
%h5= _('Push an existing folder')
- %pre.gl-bg-gray-10
+ %pre.code.js-syntax-highlight
:preserve
cd existing_folder
git init --initial-branch=#{h escaped_default_branch_name}
@@ -67,7 +67,7 @@
git push --set-upstream origin #{h escaped_default_branch_name }
%h5= _('Push an existing Git repository')
- %pre.gl-bg-gray-10
+ %pre.code.js-syntax-highlight
:preserve
cd existing_repo
git remote rename origin old-origin
@@ -76,6 +76,5 @@
%span><
git push --set-upstream origin --all
git push --set-upstream origin --tags
-
.project-page-layout-sidebar.js-show-on-project-root.gl-mt-5
= render "sidebar"
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index f05beeb869e..38bb3757334 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -264,6 +264,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: cronjob:ci_click_house_finished_pipelines_sync_cron
+ :worker_name: Ci::ClickHouse::FinishedPipelinesSyncCronWorker
+ :feature_category: :fleet_visibility
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: cronjob:ci_delete_unit_tests
:worker_name: Ci::DeleteUnitTestsWorker
:feature_category: :code_testing
@@ -2820,6 +2829,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: ci_click_house_finished_pipelines_sync
+ :worker_name: Ci::ClickHouse::FinishedPipelinesSyncWorker
+ :feature_category: :fleet_visibility
+ :has_external_dependencies: true
+ :urgency: :throttled
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: ci_delete_objects
:worker_name: Ci::DeleteObjectsWorker
:feature_category: :continuous_integration
diff --git a/app/workers/ci/click_house/finished_pipelines_sync_cron_worker.rb b/app/workers/ci/click_house/finished_pipelines_sync_cron_worker.rb
new file mode 100644
index 00000000000..210005410fc
--- /dev/null
+++ b/app/workers/ci/click_house/finished_pipelines_sync_cron_worker.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Ci
+ module ClickHouse
+ class FinishedPipelinesSyncCronWorker
+ include ApplicationWorker
+
+ idempotent!
+ queue_namespace :cronjob
+ data_consistency :delayed
+ feature_category :fleet_visibility
+ loggable_arguments 1
+
+ def perform(*args)
+ return unless ::Ci::ClickHouse::DataIngestion::FinishedPipelinesSyncService.enabled?
+
+ total_workers = args.first || 1
+
+ total_workers.times do |worker_index|
+ FinishedPipelinesSyncWorker.perform_async(worker_index, total_workers)
+ end
+
+ nil
+ end
+ end
+ end
+end
diff --git a/app/workers/ci/click_house/finished_pipelines_sync_worker.rb b/app/workers/ci/click_house/finished_pipelines_sync_worker.rb
new file mode 100644
index 00000000000..80ae571e216
--- /dev/null
+++ b/app/workers/ci/click_house/finished_pipelines_sync_worker.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Ci
+ module ClickHouse
+ class FinishedPipelinesSyncWorker
+ include ApplicationWorker
+ include ClickHouseWorker
+
+ idempotent!
+ data_consistency :delayed
+ urgency :throttled
+ feature_category :fleet_visibility
+ loggable_arguments 1, 2
+
+ def perform(worker_index = 0, total_workers = 1)
+ response = ::Ci::ClickHouse::DataIngestion::FinishedPipelinesSyncService.new(
+ worker_index: worker_index, total_workers: total_workers
+ ).execute
+
+ result = response.success? ? response.payload : response.deconstruct_keys(%i[message reason])
+ log_extra_metadata_on_done(:result, result)
+ end
+ end
+ end
+end
diff --git a/app/workers/click_house/audit_event_partition_sync_worker.rb b/app/workers/click_house/audit_event_partition_sync_worker.rb
index 510b3ce4d91..b756360fca1 100644
--- a/app/workers/click_house/audit_event_partition_sync_worker.rb
+++ b/app/workers/click_house/audit_event_partition_sync_worker.rb
@@ -7,7 +7,6 @@ module ClickHouse
idempotent!
data_consistency :delayed
- worker_has_external_dependencies! # the worker interacts with a ClickHouse database
feature_category :compliance_management
deduplicate :until_executed, including_scheduled: true # The second job can be skipped if first job hasn't run yet.
diff --git a/app/workers/click_house/audit_events_sync_worker.rb b/app/workers/click_house/audit_events_sync_worker.rb
index d6b1b38c6cc..67112162e08 100644
--- a/app/workers/click_house/audit_events_sync_worker.rb
+++ b/app/workers/click_house/audit_events_sync_worker.rb
@@ -8,7 +8,6 @@ module ClickHouse
idempotent!
queue_namespace :cronjob
data_consistency :delayed
- worker_has_external_dependencies! # the worker interacts with a ClickHouse database
feature_category :compliance_management
def perform
diff --git a/app/workers/concerns/click_house_worker.rb b/app/workers/concerns/click_house_worker.rb
index 6399796f6df..bc6c4b4cf7b 100644
--- a/app/workers/concerns/click_house_worker.rb
+++ b/app/workers/concerns/click_house_worker.rb
@@ -25,6 +25,7 @@ module ClickHouseWorker
included do
click_house_migration_lock(ClickHouse::MigrationSupport::ExclusiveLock::DEFAULT_CLICKHOUSE_WORKER_TTL)
+ worker_has_external_dependencies! # the worker interacts with a ClickHouse database
pause_control :click_house_migration
end
end
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index e1163684755..b98f39b1c08 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -603,6 +603,10 @@ production: &base
ci_runners_stale_machines_cleanup_worker:
cron: "36 * * * *"
+ # Periodically queue syncing of finished pipelines from p_ci_finished_pipeline_ch_sync_events to ClickHouse
+ ci_click_house_finished_pipelines_sync_worker:
+ cron: "*/4 * * * *"
+
# GitLab EE only jobs. These jobs are automatically enabled for an EE
# installation, and ignored for a CE installation.
ee_cron_jobs:
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 1b875f94152..4fb035c8058 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -710,6 +710,10 @@ Settings.cron_jobs['performance_bar_stats']['job_class'] = 'GitlabPerformanceBar
Settings.cron_jobs['ci_catalog_resources_aggregate_last30_day_usage_worker'] ||= {}
Settings.cron_jobs['ci_catalog_resources_aggregate_last30_day_usage_worker']['cron'] ||= '*/4 * * * *'
Settings.cron_jobs['ci_catalog_resources_aggregate_last30_day_usage_worker']['job_class'] = 'Ci::Catalog::Resources::AggregateLast30DayUsageWorker'
+Settings.cron_jobs['ci_click_house_finished_pipelines_sync_worker'] ||= {}
+Settings.cron_jobs['ci_click_house_finished_pipelines_sync_worker']['cron'] ||= '*/4 * * * *'
+Settings.cron_jobs['ci_click_house_finished_pipelines_sync_worker']['args'] ||= [1]
+Settings.cron_jobs['ci_click_house_finished_pipelines_sync_worker']['job_class'] = 'Ci::ClickHouse::FinishedPipelinesSyncCronWorker'
Gitlab.ee do
Settings.cron_jobs['analytics_devops_adoption_create_all_snapshots_worker'] ||= {}
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index db8f9b58565..8790aab1495 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -169,6 +169,8 @@
- 1
- - ci_cancel_redundant_pipelines
- 1
+- - ci_click_house_finished_pipelines_sync
+ - 1
- - ci_delete_objects
- 1
- - ci_initialize_pipelines_iid_sequence
diff --git a/db/post_migrate/20240711092719_ensure_unique_id_for_p_ci_job_annotation.rb b/db/post_migrate/20240711092719_ensure_unique_id_for_p_ci_job_annotation.rb
new file mode 100644
index 00000000000..c7985a8cb1b
--- /dev/null
+++ b/db/post_migrate/20240711092719_ensure_unique_id_for_p_ci_job_annotation.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class EnsureUniqueIdForPCiJobAnnotation < Gitlab::Database::Migration[2.2]
+ include Gitlab::Database::PartitioningMigrationHelpers::UniquenessHelpers
+
+ milestone '17.3'
+
+ TABLE_NAME = :p_ci_job_annotations
+ SEQ_NAME = :p_ci_job_annotations_id_seq
+
+ def up
+ ensure_unique_id(TABLE_NAME, seq: SEQ_NAME)
+ end
+
+ def down
+ revert_ensure_unique_id(TABLE_NAME, seq: SEQ_NAME)
+ end
+end
diff --git a/db/schema_migrations/20240711092719 b/db/schema_migrations/20240711092719
new file mode 100644
index 00000000000..d8ff6d2ccee
--- /dev/null
+++ b/db/schema_migrations/20240711092719
@@ -0,0 +1 @@
+abb93f7fc389219ea418d08c3c3cbb562d78e67e58971b56f10b65d8a096a379
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 0d64a793779..e127e7f413a 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -49,6 +49,19 @@ RETURN NEW;
END
$$;
+CREATE FUNCTION assign_p_ci_job_annotations_id_value() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+IF NEW."id" IS NOT NULL THEN
+ RAISE WARNING 'Manually assigning ids is not allowed, the value will be ignored';
+END IF;
+NEW."id" := nextval('p_ci_job_annotations_id_seq'::regclass);
+RETURN NEW;
+
+END
+$$;
+
CREATE FUNCTION assign_p_ci_job_artifacts_id_value() RETURNS trigger
LANGUAGE plpgsql
AS $$
@@ -31708,6 +31721,8 @@ CREATE TRIGGER assign_p_ci_builds_execution_configs_id_trigger BEFORE INSERT ON
CREATE TRIGGER assign_p_ci_builds_id_trigger BEFORE INSERT ON p_ci_builds FOR EACH ROW EXECUTE FUNCTION assign_p_ci_builds_id_value();
+CREATE TRIGGER assign_p_ci_job_annotations_id_trigger BEFORE INSERT ON p_ci_job_annotations FOR EACH ROW EXECUTE FUNCTION assign_p_ci_job_annotations_id_value();
+
CREATE TRIGGER assign_p_ci_job_artifacts_id_trigger BEFORE INSERT ON p_ci_job_artifacts FOR EACH ROW EXECUTE FUNCTION assign_p_ci_job_artifacts_id_value();
CREATE TRIGGER assign_p_ci_pipeline_variables_id_trigger BEFORE INSERT ON p_ci_pipeline_variables FOR EACH ROW EXECUTE FUNCTION assign_p_ci_pipeline_variables_id_value();
diff --git a/doc/administration/admin_area.md b/doc/administration/admin_area.md
index a22d8fbad8b..a5ee9b9a4a6 100644
--- a/doc/administration/admin_area.md
+++ b/doc/administration/admin_area.md
@@ -446,9 +446,9 @@ The **System information** page provides the following statistics:
These statistics are updated only when you go to the **System information** page, or you refresh the page in your browser.
-### Background Jobs
+### Background jobs
-The **Background Jobs** page displays the Sidekiq dashboard. Sidekiq is used by GitLab to
+The **Background jobs** page displays the Sidekiq dashboard. Sidekiq is used by GitLab to
perform processing in the background.
The Sidekiq dashboard consists of the following elements:
diff --git a/doc/administration/backup_restore/migrate_to_new_server.md b/doc/administration/backup_restore/migrate_to_new_server.md
index 19a17890812..5ab24467f0d 100644
--- a/doc/administration/backup_restore/migrate_to_new_server.md
+++ b/doc/administration/backup_restore/migrate_to_new_server.md
@@ -89,14 +89,14 @@ To prepare the new server:
1. Disable periodic background jobs:
1. On the left sidebar, at the bottom, select **Admin Area**.
- 1. On the left sidebar, select **Monitoring > Background Jobs**.
+ 1. On the left sidebar, select **Monitoring > Background jobs**.
1. Under the Sidekiq dashboard, select **Cron** tab and then
**Disable All**.
1. Wait for the currently running CI/CD jobs to finish, or accept that jobs that have not completed may be lost.
To view jobs currently running, on the left sidebar, select **Overviews > Jobs**,
and then select **Running**.
1. Wait for Sidekiq jobs to finish:
- 1. On the left sidebar, select **Monitoring > Background Jobs**.
+ 1. On the left sidebar, select **Monitoring > Background jobs**.
1. Under the Sidekiq dashboard, select **Queues** and then **Live Poll**.
Wait for **Busy** and **Enqueued** to drop to 0.
These queues contain work that has been submitted by your users;
@@ -200,7 +200,7 @@ to the new environment.
1. [Restore the GitLab backup](restore_gitlab.md).
1. Verify that the Redis database restored correctly:
1. On the left sidebar, at the bottom, select **Admin Area**.
- 1. On the left sidebar, select **Monitoring > Background Jobs**.
+ 1. On the left sidebar, select **Monitoring > Background jobs**.
1. Under the Sidekiq dashboard, verify that the numbers
match with what was shown on the old server.
1. While still under the Sidekiq dashboard, select **Cron** and then **Enable All**
diff --git a/doc/administration/geo/disaster_recovery/planned_failover.md b/doc/administration/geo/disaster_recovery/planned_failover.md
index 4303792b112..6a6b192c0ac 100644
--- a/doc/administration/geo/disaster_recovery/planned_failover.md
+++ b/doc/administration/geo/disaster_recovery/planned_failover.md
@@ -205,7 +205,7 @@ be disabled on the **primary** site:
1. Enable [maintenance mode](../../maintenance_mode/index.md) on the **primary** site.
1. On the left sidebar, at the bottom, select **Admin Area**.
-1. Select **Monitoring > Background Jobs**.
+1. Select **Monitoring > Background jobs**.
1. On the Sidekiq dashboard, select **Cron**.
1. Select `Disable All` to disable non-Geo periodic background jobs.
1. Select `Enable` for the following cronjobs:
@@ -224,7 +224,7 @@ be disabled on the **primary** site:
final replication process now.
1. On the **primary** site:
1. On the left sidebar, at the bottom, select **Admin Area**.
- 1. On the left sidebar, select **Monitoring > Background Jobs**.
+ 1. On the left sidebar, select **Monitoring > Background jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all queues except
those with `geo` in the name to drop to 0.
These queues contain work that has been submitted by your users; failing over
@@ -239,7 +239,7 @@ be disabled on the **primary** site:
1. On the **secondary** site:
1. On the left sidebar, at the bottom, select **Admin Area**.
- 1. On the left sidebar, select **Monitoring > Background Jobs**.
+ 1. On the left sidebar, select **Monitoring > Background jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all the `geo`
queues to drop to 0 queued and 0 running jobs.
1. [Run an integrity check](../../raketasks/check.md) to verify the integrity
diff --git a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
index 2419b66e7e3..c40960cbc00 100644
--- a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
+++ b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_multi_node.md
@@ -109,7 +109,7 @@ follow these steps to avoid unnecessary data loss:
trigger the final replication process now.
1. On the **primary** site:
1. On the left sidebar, at the bottom, select **Admin Area**.
- 1. On the left sidebar, select **Monitoring > Background Jobs**.
+ 1. On the left sidebar, select **Monitoring > Background jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all queues except
those with `geo` in the name to drop to 0.
These queues contain work that has been submitted by your users; failing over
@@ -124,7 +124,7 @@ follow these steps to avoid unnecessary data loss:
1. On the **secondary** site:
1. On the left sidebar, at the bottom, select **Admin Area**.
- 1. On the left sidebar, select **Monitoring > Background Jobs**.
+ 1. On the left sidebar, select **Monitoring > Background jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all the `geo`
queues to drop to 0 queued and 0 running jobs.
1. [Run an integrity check](../../../raketasks/check.md) to verify the integrity
diff --git a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
index 5d34ed289c0..3e584705899 100644
--- a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
+++ b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
@@ -120,7 +120,7 @@ follow these steps to avoid unnecessary data loss:
1. On the **primary** site:
1. On the left sidebar, at the bottom, select **Admin Area**..
- 1. On the left sidebar, select **Monitoring > Background Jobs**.
+ 1. On the left sidebar, select **Monitoring > Background jobs**.
1. On the Sidekiq dashboard, select **Cron**.
1. Select `Disable All` to disable any non-Geo periodic background jobs.
1. Select `Enable` for the `geo_sidekiq_cron_config_worker` cron job.
@@ -138,7 +138,7 @@ follow these steps to avoid unnecessary data loss:
trigger the final replication process now.
1. On the **primary** site:
1. On the left sidebar, at the bottom, select **Admin Area**.
- 1. On the left sidebar, select **Monitoring > Background Jobs**.
+ 1. On the left sidebar, select **Monitoring > Background jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all queues except
those with `geo` in the name to drop to 0.
These queues contain work that has been submitted by your users; failing over
@@ -153,7 +153,7 @@ follow these steps to avoid unnecessary data loss:
1. On the **secondary** site:
1. On the left sidebar, at the bottom, select **Admin Area**.
- 1. On the left sidebar, select **Monitoring > Background Jobs**.
+ 1. On the left sidebar, select **Monitoring > Background jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all the `geo`
queues to drop to 0 queued and 0 running jobs.
1. [Run an integrity check](../../../raketasks/check.md) to verify the integrity
diff --git a/doc/administration/maintenance_mode/index.md b/doc/administration/maintenance_mode/index.md
index 6d95ddcc421..806ea0182fe 100644
--- a/doc/administration/maintenance_mode/index.md
+++ b/doc/administration/maintenance_mode/index.md
@@ -182,7 +182,7 @@ you should disable all cron jobs except for those related to Geo.
To monitor queues and disable jobs:
1. On the left sidebar, at the bottom, select **Admin Area**.
-1. Select **Monitoring > Background Jobs**.
+1. Select **Monitoring > Background jobs**.
1. In the Sidekiq dashboard, select **Cron** and disable jobs individually or all at once by selecting **Disable All**.
### Incident management
diff --git a/doc/administration/self_hosted_models/index.md b/doc/administration/self_hosted_models/index.md
index 47fbfb6e766..f645ce30a05 100644
--- a/doc/administration/self_hosted_models/index.md
+++ b/doc/administration/self_hosted_models/index.md
@@ -46,7 +46,7 @@ When you use self-hosted models, you:
You can connect supported models to LLM features. Model-specific prompts
and GitLab Duo feature support is provided by the self-hosted models feature. For
-more information about this offering, see [subscriptions](../../subscriptions/self_managed/index.md).
+more information about this offering, see [subscriptions](../../subscriptions/self_managed/index.md) and the [Blueprint](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/custom_models/).
## Prerequisites
diff --git a/doc/administration/self_hosted_models/install_infrastructure.md b/doc/administration/self_hosted_models/install_infrastructure.md
index 414c86a1398..03eab4b503c 100644
--- a/doc/administration/self_hosted_models/install_infrastructure.md
+++ b/doc/administration/self_hosted_models/install_infrastructure.md
@@ -96,23 +96,46 @@ sudo mkdir -p /srv/gitlab-agw
If you're running Docker with a user other than `root`, ensure appropriate
permissions have been granted to that directory.
+#### Optional: Download documentation index
+
+To improve results when asking GitLab Duo Chat questions about GitLab, you can
+index GitLab documentation and provide it as a file to the AI Gateway.
+
+To index the documentation in your local installation,run:
+
+```shell
+pip install requests langchain langchain_text_splitters
+python3 scripts/custom_models/create_index.py -o
+```
+
+This creates a file `docs.db` at the specified path.
+
+You can also create an index for a specific GitLab version:
+
+```shell
+python3 scripts/custom_models/create_index.py --version_tag="{gitlab-version}"
+```
+
#### Find the AI Gateway release
In a production environment, you should set your deployment to a specific
GitLab AI Gateway release. Find the release to use in [GitLab AI Gateway releases](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/-/releases), for example:
```shell
-docker run -p 5000:500 -e AIGW_CUSTOM_MODELS__ENABLED=true registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway:v1.4.0`
+docker run -e AIGW_CUSTOM_MODELS__ENABLED=true \
+ -v path/to/created/index/docs.db:/app/tmp/docs.db \
+ -e AIGW_FASTAPI__OPENAPI_URL="/openapi.json" \
+ -e AIGW_AUTH__BYPASS_EXTERNAL=true \
+ -e AIGW_FASTAPI__DOCS_URL="/docs"\
+ -e AIGW_FASTAPI__API_PORT=5052 \
+ registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway:v1.11.0`
```
-To set your deployment to the latest stable release, use the `latest` tag to run the latest stable release:
+To set your deployment to the latest stable release, use the `latest` tag to run the latest stable release.
-```shell
-docker run -p 5000:500 -e AIGW_CUSTOM_MODELS__ENABLED=true registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway:latest`
-```
-
-NOTE:
-We do not yet support multi-arch image, only `linux/amd64`. If you try to run this on Apple chip, adding `--platform linux/amd64` to the `docker run` command will help.
+The arguments `AIGW_FASTAPI__OPENAPI_URL` and `AIGW_FASTAPI__DOCS_URL` are not
+mandatory, but are useful for debugging. From the host, accessing `http://localhost:5052/docs`
+should open the AI Gateway API documentation.
### Install by using Docker Engine
@@ -172,7 +195,13 @@ To upgrade the AI Gateway, download the newest Docker image tag.
1. Pull the new image:
```shell
- docker run -p 5000:500 -e AIGW_CUSTOM_MODELS__ENABLED=true registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway:latest
+ docker run -e AIGW_CUSTOM_MODELS__ENABLED=true \
+ -v path/to/created/index/docs.db:/app/tmp/docs.db \
+ -e AIGW_FASTAPI__OPENAPI_URL="/openapi.json" \
+ -e AIGW_AUTH__BYPASS_EXTERNAL=true \
+ -e AIGW_FASTAPI__DOCS_URL="/docs"\
+ -e AIGW_FASTAPI__API_PORT=5052 \
+ registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway:v1.11.0`
```
1. Ensure that the environment variables are all set correctly
@@ -180,3 +209,21 @@ To upgrade the AI Gateway, download the newest Docker image tag.
## Alternative installation methods
For information on alternative ways to install the AI Gateway, see [issue 463773](https://gitlab.com/gitlab-org/gitlab/-/issues/463773).
+
+## Troubleshooting
+
+### The image's platform does not match the host
+
+When [finding the AI Gateway release](#find-the-ai-gateway-release), you might get an error that states `The requested image’s platform (linux/amd64) does not match the detected host`.
+
+To work around this error, add `--platform linux/amd64` to the `docker run` command:
+
+```shell
+docker run -e AIGW_CUSTOM_MODELS__ENABLED=true --platform linux/amd64 \
+ -v path/to/created/index/docs.db:/app/tmp/docs.db \
+ -e AIGW_FASTAPI__OPENAPI_URL="/openapi.json" \
+ -e AIGW_AUTH__BYPASS_EXTERNAL=true \
+ -e AIGW_FASTAPI__DOCS_URL="/docs"\
+ -e AIGW_FASTAPI__API_PORT=5052 \
+ registry.gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist/model-gateway:v1.11.0`
+```
diff --git a/doc/administration/sidekiq/extra_sidekiq_processes.md b/doc/administration/sidekiq/extra_sidekiq_processes.md
index b0ee4047061..ff87d84a738 100644
--- a/doc/administration/sidekiq/extra_sidekiq_processes.md
+++ b/doc/administration/sidekiq/extra_sidekiq_processes.md
@@ -49,7 +49,7 @@ to all available queues:
To view the Sidekiq processes in GitLab:
1. On the left sidebar, at the bottom, select **Admin Area**.
-1. Select **Monitoring > Background Jobs**.
+1. Select **Monitoring > Background jobs**.
## Concurrency
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 244cf7966c5..f2f4a77cfcd 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -34066,8 +34066,8 @@ Runner cloud provider.
| Value | Description |
| ----- | ----------- |
+| `ACTIVE` **{warning-solid}** | **Introduced** in GitLab 17.2. **Status**: Experiment. Runner is busy. |
| `IDLE` **{warning-solid}** | **Introduced** in GitLab 15.7. **Status**: Experiment. Runner is idle. |
-| `RUNNING` **{warning-solid}** | **Introduced** in GitLab 15.7. **Status**: Experiment. Runner is executing jobs. |
### `CiRunnerMembershipFilter`
diff --git a/doc/integration/advanced_search/elasticsearch.md b/doc/integration/advanced_search/elasticsearch.md
index 91a4315b645..9cc5c39312e 100644
--- a/doc/integration/advanced_search/elasticsearch.md
+++ b/doc/integration/advanced_search/elasticsearch.md
@@ -347,7 +347,7 @@ To enable advanced search:
```
1. Optional. Monitor the status of background jobs.
- 1. On the left sidebar, select **Monitoring > Background Jobs**.
+ 1. On the left sidebar, select **Monitoring > Background jobs**.
1. On the Sidekiq dashboard, select **Queues** and wait for the `elastic_commit_indexer`
and `elastic_wiki_indexer` queues to drop to `0`.
These queues contain jobs to index code and wiki data for groups and projects.
@@ -972,7 +972,7 @@ due to large volumes of data being indexed, follow these steps:
```
This enqueues a Sidekiq job for each project that needs to be indexed.
- You can view the jobs in the Admin Area under **Monitoring > Background Jobs > Queues Tab**
+ You can view the jobs in the Admin Area under **Monitoring > Background jobs > Queues Tab**
and select `elastic_commit_indexer`, or you can query indexing status using a Rake task:
```shell
diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md
index 349e297d560..de89a994dce 100644
--- a/doc/user/project/pages/index.md
+++ b/doc/user/project/pages/index.md
@@ -313,6 +313,8 @@ With this configuration, users will have the access to each GitLab Pages deploym
When using [environments](../../../ci/environments/index.md) for pages, all pages environments are
listed on the project environment list.
+You can also [group similar environments](../../../ci/environments/index.md#group-similar-environments) together.
+
### Deployments deletion
#### Auto-clean
diff --git a/doc/user/project/repository/code_suggestions/index.md b/doc/user/project/repository/code_suggestions/index.md
index a50fc6cd6d7..a22b26514f3 100644
--- a/doc/user/project/repository/code_suggestions/index.md
+++ b/doc/user/project/repository/code_suggestions/index.md
@@ -24,10 +24,24 @@ Write code more efficiently by using generative AI to suggest code while you're
With GitLab Duo Code Suggestions, you get:
-- Code completion, which suggests completions to the current line you are typing.
+- Code completion, which suggests completions to the current line you are
+ typing. Code completion is used in most situations to quickly complete one
+ or a few lines of code.
- Code generation, which generates code based on a natural language code
- comment block. Write a comment like `# Type more here`, then press Enter to generate
- code based on the context of your comment and the rest of your code.
+ comment block. Write a comment like `# check if code suggestions are
+ enabled for current user`, then press Enter to generate code based
+ on the context of your comment and the rest of your code.
+
+ Code generation requests are slower than code completion requests, but provide
+ more accurate responses because:
+ - A larger LLM is used.
+ - Additional context is sent in the request, for example,
+ the libraries used by the project.
+
+ Code generation is used when the:
+ - User writes a comment and hits Enter.
+ - File being edited is less than five lines of code.
+ - User enters an empty function or method.
[View a click-through demo](https://gitlab.navattic.com/code-suggestions).
diff --git a/lib/gitlab/import/import_user_creator.rb b/lib/gitlab/import/import_user_creator.rb
index 62b4620b7d2..049d82d96a0 100644
--- a/lib/gitlab/import/import_user_creator.rb
+++ b/lib/gitlab/import/import_user_creator.rb
@@ -42,7 +42,7 @@ module Gitlab
username: username_and_email_generator.username,
email: username_and_email_generator.email
) do |u|
- u.assign_personal_namespace(Organizations::Organization.default_organization)
+ u.assign_personal_namespace(root_ancestor.organization)
end
end
diff --git a/lib/sidebars/admin/menus/monitoring_menu.rb b/lib/sidebars/admin/menus/monitoring_menu.rb
index 5ea2bd56ce1..0dde08f2690 100644
--- a/lib/sidebars/admin/menus/monitoring_menu.rb
+++ b/lib/sidebars/admin/menus/monitoring_menu.rb
@@ -51,7 +51,7 @@ module Sidebars
def background_jobs_menu_item
::Sidebars::MenuItem.new(
- title: _('Background Jobs'),
+ title: _('Background jobs'),
link: admin_background_jobs_path,
active_routes: { controller: 'background_jobs' },
item_id: :background_jobs
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 123ce3746b6..45c3dd3692b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -7858,10 +7858,10 @@ msgstr ""
msgid "Background Color"
msgstr ""
-msgid "Background Jobs"
+msgid "Background color"
msgstr ""
-msgid "Background color"
+msgid "Background jobs"
msgstr ""
msgid "Background migrations"
@@ -60295,10 +60295,10 @@ msgstr ""
msgid "WorkItem|Link items together to show that they're related or that one is blocking others."
msgstr ""
-msgid "WorkItem|Linked Items"
+msgid "WorkItem|Linked item removed"
msgstr ""
-msgid "WorkItem|Linked item removed"
+msgid "WorkItem|Linked items"
msgstr ""
msgid "WorkItem|Magenta"
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_container_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_container_spec.js
index 4b593c9c1ec..f83508ec87f 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_container_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_container_spec.js
@@ -49,7 +49,6 @@ describe('MrWidgetPipelineContainer', () => {
retargeted: false,
targetProjectId: 1,
iid: 1,
- detatchedPipeline: 'DETATCHED',
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_spec.js
index f1abf07166e..ba56df17256 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_spec.js
@@ -1,10 +1,12 @@
-import { nextTick } from 'vue';
+import Vue, { nextTick } from 'vue';
import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { trimText } from 'helpers/text_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import Api from '~/api';
import { createAlert } from '~/alert';
@@ -13,13 +15,17 @@ import MRWidgetPipelineComponent from '~/vue_merge_request_widget/components/mr_
import LegacyPipelineMiniGraph from '~/ci/pipeline_mini_graph/legacy_pipeline_mini_graph/legacy_pipeline_mini_graph.vue';
import { SUCCESS } from '~/vue_merge_request_widget/constants';
import { localeDateFormat } from '~/lib/utils/datetime/locale_dateformat';
+import mergeRequestEventTypeQuery from '~/vue_merge_request_widget/queries/merge_request_event_type.query.graphql';
import mockData from '../mock_data';
jest.mock('~/alert');
jest.mock('~/api');
+Vue.use(VueApollo);
+
describe('MRWidgetPipeline', () => {
let wrapper;
+ let mergeRequestEventTypeQueryMock;
const defaultProps = {
pipeline: mockData.pipeline,
@@ -29,6 +35,7 @@ describe('MRWidgetPipeline', () => {
ciTroubleshootingDocsPath: 'ci-help',
targetProjectId: 1,
iid: 1,
+ targetProjectFullPath: 'gitlab-org/gitlab',
};
const ciErrorMessage =
@@ -56,16 +63,25 @@ describe('MRWidgetPipeline', () => {
const mockArtifactsRequest = () => new MockAdapter(axios).onGet().reply(HTTP_STATUS_OK, []);
const createWrapper = (props = {}, mountFn = shallowMount) => {
+ const apolloProvider = createMockApollo([
+ [mergeRequestEventTypeQuery, mergeRequestEventTypeQueryMock],
+ ]);
+
wrapper = extendedWrapper(
mountFn(MRWidgetPipelineComponent, {
propsData: {
...defaultProps,
...props,
},
+ apolloProvider,
}),
);
};
+ afterEach(() => {
+ mergeRequestEventTypeQueryMock = null;
+ });
+
it('should render CI error if there is a pipeline, but no status', () => {
createWrapper({ ciStatus: null }, mount);
expect(findCIErrorMessage().text()).toBe(ciErrorMessage);
@@ -327,11 +343,24 @@ describe('MRWidgetPipeline', () => {
describe('when merge request is retargeted', () => {
describe('when last pipeline is detatched', () => {
- beforeEach(() => {
+ beforeEach(async () => {
+ mergeRequestEventTypeQueryMock = jest.fn().mockResolvedValue({
+ data: {
+ project: {
+ id: 1,
+ mergeRequest: {
+ id: 1,
+ pipelines: { nodes: [{ id: 1, mergeRequestEventType: 'DETACHED' }] },
+ },
+ },
+ },
+ });
+
createWrapper({
- detatchedPipeline: 'DETACHED',
retargeted: true,
});
+
+ await waitForPromises();
});
it('renders branch changed message', () => {
@@ -410,11 +439,24 @@ describe('MRWidgetPipeline', () => {
});
describe('when last pipeline is a branch pipeline', () => {
- beforeEach(() => {
+ beforeEach(async () => {
+ mergeRequestEventTypeQueryMock = jest.fn().mockResolvedValue({
+ data: {
+ project: {
+ id: 1,
+ mergeRequest: {
+ id: 1,
+ pipelines: { nodes: [{ id: 1, mergeRequestEventType: null }] },
+ },
+ },
+ },
+ });
+
createWrapper({
- detatchedPipeline: null,
retargeted: true,
});
+
+ await waitForPromises();
});
it('renders branch changed message', () => {
diff --git a/spec/frontend/vue_merge_request_widget/mock_data.js b/spec/frontend/vue_merge_request_widget/mock_data.js
index fd205d76c06..2b573686a2d 100644
--- a/spec/frontend/vue_merge_request_widget/mock_data.js
+++ b/spec/frontend/vue_merge_request_widget/mock_data.js
@@ -453,7 +453,6 @@ export const mockStore = {
targetProjectId: 1,
iid: 1,
retargeted: false,
- detatchedPipeline: 'DETATCHED',
};
export const mockMergePipeline = {
diff --git a/spec/lib/gitlab/import/import_user_creator_spec.rb b/spec/lib/gitlab/import/import_user_creator_spec.rb
index b6f3e807a98..2589f8b4348 100644
--- a/spec/lib/gitlab/import/import_user_creator_spec.rb
+++ b/spec/lib/gitlab/import/import_user_creator_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Import::ImportUserCreator, feature_category: :importers do
- let(:group) { create(:group) }
+ let(:group) { create(:group, organization: create(:organization)) }
subject(:service) { described_class.new(portable: group) }
@@ -12,6 +12,7 @@ RSpec.describe Gitlab::Import::ImportUserCreator, feature_category: :importers d
expect(user.user_type).to eq('import_user')
expect(group.reload.import_user).to eq(user)
+ expect(user.namespace.organization).to eq(group.organization)
end
context 'when import user already exists' do
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index 184ca6131b4..b82b3bbf634 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -83,7 +83,7 @@ RSpec.describe 'Query.runner(id)', :freeze_time, feature_category: :fleet_visibi
active: runner.active,
paused: !runner.active,
status: runner.status.to_s.upcase,
- job_execution_status: runner.builds.executing.any? ? 'RUNNING' : 'IDLE',
+ job_execution_status: runner.builds.executing.any? ? 'ACTIVE' : 'IDLE',
maximum_timeout: runner.maximum_timeout,
access_level: runner.access_level.to_s.upcase,
run_untagged: runner.run_untagged,
@@ -122,7 +122,7 @@ RSpec.describe 'Query.runner(id)', :freeze_time, feature_category: :fleet_visibi
architecture_name: runner_manager.architecture,
platform_name: runner_manager.platform,
status: runner_manager.status.to_s.upcase,
- job_execution_status: runner_manager.builds.executing.any? ? 'RUNNING' : 'IDLE'
+ job_execution_status: runner_manager.builds.executing.any? ? 'ACTIVE' : 'IDLE'
)
end,
"pageInfo" => anything
diff --git a/spec/services/work_items/widgets/labels_service/create_service_spec.rb b/spec/services/work_items/widgets/labels_service/create_service_spec.rb
index 76d546c94e7..75a5ed97ad2 100644
--- a/spec/services/work_items/widgets/labels_service/create_service_spec.rb
+++ b/spec/services/work_items/widgets/labels_service/create_service_spec.rb
@@ -35,22 +35,5 @@ RSpec.describe WorkItems::Widgets::LabelsService::CreateService, feature_categor
end
end
end
-
- context 'when widget does not exist in new type' do
- let(:params) { {} }
-
- before do
- allow(service).to receive(:new_type_excludes_widget?).and_return(true)
- end
-
- it "sets label params as empty" do
- expect(service.prepare_create_params(params: params)).to include(
- {
- add_label_ids: [],
- label_ids: []
- }
- )
- end
- end
end
end
diff --git a/spec/workers/ci/click_house/finished_pipelines_sync_cron_worker_spec.rb b/spec/workers/ci/click_house/finished_pipelines_sync_cron_worker_spec.rb
new file mode 100644
index 00000000000..b3083d9daf2
--- /dev/null
+++ b/spec/workers/ci/click_house/finished_pipelines_sync_cron_worker_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::ClickHouse::FinishedPipelinesSyncCronWorker, :click_house, :freeze_time, feature_category: :fleet_visibility do
+ let(:worker) { described_class.new }
+ let(:args) { [3] }
+
+ subject(:perform) { worker.perform(*args) }
+
+ it 'invokes 3 workers' do
+ expect(Ci::ClickHouse::FinishedPipelinesSyncWorker).to receive(:perform_async).with(0, 3).once
+ expect(Ci::ClickHouse::FinishedPipelinesSyncWorker).to receive(:perform_async).with(1, 3).once
+ expect(Ci::ClickHouse::FinishedPipelinesSyncWorker).to receive(:perform_async).with(2, 3).once
+
+ perform
+ end
+
+ context 'when arguments are not specified' do
+ let(:args) { [] }
+
+ it 'invokes 1 worker with specified arguments' do
+ expect(Ci::ClickHouse::FinishedPipelinesSyncWorker).to receive(:perform_async).with(0, 1)
+
+ perform
+ end
+ end
+
+ context 'when clickhouse database is not available' do
+ before do
+ allow(Gitlab::ClickHouse).to receive(:configured?).and_return(false)
+ end
+
+ it 'does nothing' do
+ expect(Ci::ClickHouse::FinishedPipelinesSyncWorker).not_to receive(:perform_async)
+
+ perform
+ end
+ end
+end
diff --git a/spec/workers/ci/click_house/finished_pipelines_sync_worker_spec.rb b/spec/workers/ci/click_house/finished_pipelines_sync_worker_spec.rb
new file mode 100644
index 00000000000..6be35ae1d75
--- /dev/null
+++ b/spec/workers/ci/click_house/finished_pipelines_sync_worker_spec.rb
@@ -0,0 +1,132 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::ClickHouse::FinishedPipelinesSyncWorker, :click_house, :freeze_time, feature_category: :fleet_visibility do
+ let(:worker) { described_class.new }
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:pipeline1) do
+ create(:ci_pipeline, :success, project: project, ref: 'master', source: :push,
+ committed_at: 2.hours.before(1.month.ago), started_at: 1.hour.before(1.month.ago), finished_at: 1.month.ago,
+ duration: 60 * 60)
+ end
+
+ let_it_be(:pipeline2) do
+ create(:ci_pipeline, :pending, project: project, ref: 'main', source: :schedule)
+ end
+
+ subject(:perform) { worker.perform }
+
+ before do
+ create_sync_events pipeline1
+ end
+
+ specify do
+ expect(worker.class.click_house_worker_attrs).to match(
+ a_hash_including(migration_lock_ttl: ClickHouse::MigrationSupport::ExclusiveLock::DEFAULT_CLICKHOUSE_WORKER_TTL)
+ )
+ end
+
+ include_examples 'an idempotent worker' do
+ it 'calls CiFinishedPipelinesSyncService and returns its response payload' do
+ expect(worker).to receive(:log_extra_metadata_on_done)
+ .with(:result, {
+ reached_end_of_table: true, records_inserted: 1,
+ worker_index: 0, total_workers: 1
+ })
+
+ params = { worker_index: 0, total_workers: 1 }
+ expect_next_instance_of(::Ci::ClickHouse::DataIngestion::FinishedPipelinesSyncService, params) do |service|
+ expect(service).to receive(:execute).and_call_original
+ end
+
+ expect(ClickHouse::Client).to receive(:insert_csv).once.and_call_original
+
+ expect { perform }.to change { ci_finished_pipelines_row_count }.by(::Ci::Pipeline.finished.count)
+ end
+
+ context 'when an error is reported from service' do
+ before do
+ allow(Gitlab::ClickHouse).to receive(:configured?).and_return(false)
+ end
+
+ it 'skips execution' do
+ expect(worker).to receive(:log_extra_metadata_on_done)
+ .with(:result, { message: 'Disabled: ClickHouse database is not configured.', reason: :db_not_configured })
+
+ perform
+ end
+ end
+ end
+
+ context 'when exclusive lease error happens' do
+ context 'when the exclusive lease is already locked for the worker' do
+ it 'does nothing' do
+ expect_next_instance_of(::Ci::ClickHouse::DataIngestion::FinishedPipelinesSyncService) do |service|
+ expect(service).to receive(:in_lock).and_raise(Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError)
+ end
+
+ expect { perform }.not_to change { ci_finished_pipelines_row_count }
+
+ expect(perform).to eq({
+ message: 'Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError',
+ reason: :skipped
+ })
+ end
+ end
+ end
+
+ context 'with 2 workers' do
+ using RSpec::Parameterized::TableSyntax
+
+ subject(:perform) { worker.perform(worker_index, 2) }
+
+ where(:worker_index) { [0, 1] }
+
+ with_them do
+ let(:params) { { worker_index: worker_index, total_workers: 2 } }
+
+ it 'processes record if it falls on specified partition' do
+ # select the records that fall in the specified partition
+ partition_count = ::Ci::ClickHouse::DataIngestion::FinishedPipelinesSyncService::PIPELINE_ID_PARTITIONS
+ modulus_arel = Arel.sql("(pipeline_id % #{partition_count})")
+ lower_bound = (worker_index * partition_count / params[:total_workers]).to_i
+ upper_bound = ((worker_index + 1) * partition_count / params[:total_workers]).to_i
+ pipeline_ids =
+ Ci::FinishedPipelineChSyncEvent
+ .where(modulus_arel.gteq(lower_bound))
+ .where(modulus_arel.lt(upper_bound))
+ .map(&:pipeline_id)
+
+ expect(worker).to receive(:log_extra_metadata_on_done)
+ .with(:result, { reached_end_of_table: true, records_inserted: pipeline_ids.count }.merge(params))
+
+ expect_next_instance_of(::Ci::ClickHouse::DataIngestion::FinishedPipelinesSyncService, params) do |service|
+ expect(service).to receive(:execute).and_call_original
+ end
+
+ if pipeline_ids.any?
+ expect(ClickHouse::Client).to receive(:insert_csv).once.and_call_original
+ else
+ expect(ClickHouse::Client).not_to receive(:insert_csv)
+ end
+
+ perform
+ end
+ end
+ end
+
+ def create_sync_events(*pipelines)
+ pipelines.each do |pipeline|
+ Ci::FinishedPipelineChSyncEvent.new(
+ pipeline_id: pipeline.id, pipeline_finished_at: pipeline.finished_at,
+ project_namespace_id: pipeline.project.project_namespace_id
+ ).save!
+ end
+ end
+
+ def ci_finished_pipelines_row_count
+ ClickHouse::Client.select('SELECT COUNT(*) AS count FROM ci_finished_pipelines FINAL', :main).first['count']
+ end
+end
diff --git a/spec/workers/concerns/click_house_worker_spec.rb b/spec/workers/concerns/click_house_worker_spec.rb
index cb8bf9c7578..38aa2ea8afe 100644
--- a/spec/workers/concerns/click_house_worker_spec.rb
+++ b/spec/workers/concerns/click_house_worker_spec.rb
@@ -85,4 +85,8 @@ RSpec.describe ClickHouseWorker, feature_category: :database do
expect(worker.get_pause_control).to eq(:click_house_migration)
expect(another_worker.get_pause_control).to be_nil
end
+
+ it 'marks the worker as having external dependencies' do
+ expect(worker.worker_has_external_dependencies?).to be_truthy
+ end
end