diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-19 23:18:09 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-19 23:18:09 +0000 |
commit | 6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde (patch) | |
tree | dc4d20fe6064752c0bd323187252c77e0a89144b /app/services/ci | |
parent | 9868dae7fc0655bd7ce4a6887d4e6d487690eeed (diff) | |
download | gitlab-ce-6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde.tar.gz |
Add latest changes from gitlab-org/gitlab@15-4-stable-eev15.4.0-rc42
Diffstat (limited to 'app/services/ci')
24 files changed, 292 insertions, 33 deletions
diff --git a/app/services/ci/after_requeue_job_service.rb b/app/services/ci/after_requeue_job_service.rb index 1ae4639751b..634c547a623 100644 --- a/app/services/ci/after_requeue_job_service.rb +++ b/app/services/ci/after_requeue_job_service.rb @@ -21,9 +21,16 @@ module Ci @processable.pipeline.reset_source_bridge!(current_user) end + # rubocop: disable CodeReuse/ActiveRecord def dependent_jobs + return legacy_dependent_jobs unless ::Feature.enabled?(:ci_requeue_with_dag_object_hierarchy, project) + ordered_by_dag( - stage_dependent_jobs.or(needs_dependent_jobs).ordered_by_stage + ::Ci::Processable + .from_union(needs_dependent_jobs, stage_dependent_jobs) + .skipped + .ordered_by_stage + .preload(:needs) ) end @@ -34,22 +41,37 @@ module Ci end def stage_dependent_jobs - skipped_jobs.after_stage(@processable.stage_idx) + @processable.pipeline.processables.after_stage(@processable.stage_idx) end def needs_dependent_jobs - skipped_jobs.scheduling_type_dag.with_needs([@processable.name]) + ::Gitlab::Ci::ProcessableObjectHierarchy.new( + ::Ci::Processable.where(id: @processable.id) + ).descendants end - def skipped_jobs - @skipped_jobs ||= @processable.pipeline.processables.skipped + def legacy_skipped_jobs + @legacy_skipped_jobs ||= @processable.pipeline.processables.skipped + end + + def legacy_dependent_jobs + ordered_by_dag( + legacy_stage_dependent_jobs.or(legacy_needs_dependent_jobs).ordered_by_stage.preload(:needs) + ) + end + + def legacy_stage_dependent_jobs + legacy_skipped_jobs.after_stage(@processable.stage_idx) + end + + def legacy_needs_dependent_jobs + legacy_skipped_jobs.scheduling_type_dag.with_needs([@processable.name]) end - # rubocop: disable CodeReuse/ActiveRecord def ordered_by_dag(jobs) sorted_job_names = sort_jobs(jobs).each_with_index.to_h - jobs.preload(:needs).group_by(&:stage_idx).flat_map do |_, stage_jobs| + jobs.group_by(&:stage_idx).flat_map do |_, stage_jobs| stage_jobs.sort_by { |job| sorted_job_names.fetch(job.name) } end end diff --git a/app/services/ci/archive_trace_service.rb b/app/services/ci/archive_trace_service.rb index 9705a236d98..566346a4b09 100644 --- a/app/services/ci/archive_trace_service.rb +++ b/app/services/ci/archive_trace_service.rb @@ -27,7 +27,7 @@ module Ci job.trace.archive! job.remove_pending_state! - if Feature.enabled?(:datadog_integration_logs_collection, job.project) && job.job_artifacts_trace.present? + if job.job_artifacts_trace.present? job.project.execute_integrations(Gitlab::DataBuilder::ArchiveTrace.build(job), :archive_trace_hooks) end diff --git a/app/services/ci/build_erase_service.rb b/app/services/ci/build_erase_service.rb new file mode 100644 index 00000000000..8a468e094eb --- /dev/null +++ b/app/services/ci/build_erase_service.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Ci + class BuildEraseService + include BaseServiceUtility + + def initialize(build, current_user) + @build = build + @current_user = current_user + end + + def execute + unless build.erasable? + return ServiceResponse.error(message: _('Build cannot be erased'), http_status: :unprocessable_entity) + end + + if build.project.refreshing_build_artifacts_size? + Gitlab::ProjectStatsRefreshConflictsLogger.warn_artifact_deletion_during_stats_refresh( + method: 'Ci::BuildEraseService#execute', + project_id: build.project_id + ) + end + + destroy_artifacts + erase_trace! + update_erased! + + ServiceResponse.success(payload: build) + end + + private + + attr_reader :build, :current_user + + def destroy_artifacts + # fix_expire_at is false because in this case we want to explicitly delete the job artifacts + # this flag is a workaround that will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/355833 + Ci::JobArtifacts::DestroyBatchService.new(build.job_artifacts, fix_expire_at: false).execute + end + + def erase_trace! + build.trace.erase! + end + + def update_erased! + build.update(erased_by: current_user, erased_at: Time.current, artifacts_expire_at: nil) + end + end +end diff --git a/app/services/ci/build_report_result_service.rb b/app/services/ci/build_report_result_service.rb index f9146b3677a..20a31322919 100644 --- a/app/services/ci/build_report_result_service.rb +++ b/app/services/ci/build_report_result_service.rb @@ -22,7 +22,8 @@ module Ci private def generate_test_suite_report(build) - build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) + test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) + test_report.get_suite(build.test_suite_name) end def tests_params(test_suite) diff --git a/app/services/ci/compare_reports_base_service.rb b/app/services/ci/compare_reports_base_service.rb index 9aba3a50ec1..ee687706b53 100644 --- a/app/services/ci/compare_reports_base_service.rb +++ b/app/services/ci/compare_reports_base_service.rb @@ -8,6 +8,8 @@ module Ci # issue: https://gitlab.com/gitlab-org/gitlab/issues/34224 class CompareReportsBaseService < ::BaseService def execute(base_pipeline, head_pipeline) + return parsing_payload(base_pipeline, head_pipeline) if base_pipeline&.running? + base_report = get_report(base_pipeline) head_report = get_report(head_pipeline) comparer = build_comparer(base_report, head_report) @@ -33,6 +35,13 @@ module Ci protected + def parsing_payload(base_pipeline, head_pipeline) + { + status: :parsing, + key: key(base_pipeline, head_pipeline) + } + end + def build_comparer(base_report, head_report) comparer_class.new(base_report, head_report) end diff --git a/app/services/ci/create_downstream_pipeline_service.rb b/app/services/ci/create_downstream_pipeline_service.rb index b38b3b93353..25cc9045052 100644 --- a/app/services/ci/create_downstream_pipeline_service.rb +++ b/app/services/ci/create_downstream_pipeline_service.rb @@ -11,6 +11,7 @@ module Ci DuplicateDownstreamPipelineError = Class.new(StandardError) MAX_NESTED_CHILDREN = 2 + MAX_HIERARCHY_SIZE = 1000 def execute(bridge) @bridge = bridge @@ -86,6 +87,11 @@ module Ci return false end + if Feature.enabled?(:ci_limit_complete_hierarchy_size) && pipeline_tree_too_large? + @bridge.drop!(:reached_max_pipeline_hierarchy_size) + return false + end + unless can_create_downstream_pipeline?(target_ref) @bridge.drop!(:insufficient_bridge_permissions) return false @@ -137,10 +143,17 @@ module Ci return false unless @bridge.triggers_child_pipeline? # only applies to parent-child pipelines not multi-project - ancestors_of_new_child = @bridge.pipeline.self_and_ancestors + ancestors_of_new_child = @bridge.pipeline.self_and_project_ancestors ancestors_of_new_child.count > MAX_NESTED_CHILDREN end + def pipeline_tree_too_large? + return false unless @bridge.triggers_downstream_pipeline? + + # Applies to the entire pipeline tree across all projects + @bridge.pipeline.complete_hierarchy_count >= MAX_HIERARCHY_SIZE + end + def config_checksum(pipeline) [pipeline.project_id, pipeline.ref, pipeline.source].hash end diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index 02f25a82307..af175b8da1c 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -23,6 +23,7 @@ module Ci Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs, Gitlab::Ci::Pipeline::Chain::SeedBlock, Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules, + Gitlab::Ci::Pipeline::Chain::AssignPartition, Gitlab::Ci::Pipeline::Chain::Seed, Gitlab::Ci::Pipeline::Chain::Limit::Size, Gitlab::Ci::Pipeline::Chain::Limit::Deployments, diff --git a/app/services/ci/delete_objects_service.rb b/app/services/ci/delete_objects_service.rb index bac99abadc9..7a93d0e9665 100644 --- a/app/services/ci/delete_objects_service.rb +++ b/app/services/ci/delete_objects_service.rb @@ -27,9 +27,7 @@ module Ci # `find_by_sql` performs a write in this case and we need to wrap it in # a transaction to stick to the primary database. Ci::DeletedObject.transaction do - Ci::DeletedObject.find_by_sql([ - next_batch_sql, new_pick_up_at: RETRY_IN.from_now - ]) + Ci::DeletedObject.find_by_sql([next_batch_sql, new_pick_up_at: RETRY_IN.from_now]) end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/services/ci/expire_pipeline_cache_service.rb b/app/services/ci/expire_pipeline_cache_service.rb index bf2355c447a..15597eb7209 100644 --- a/app/services/ci/expire_pipeline_cache_service.rb +++ b/app/services/ci/expire_pipeline_cache_service.rb @@ -86,7 +86,7 @@ module Ci etag_paths << path end - pipeline.all_pipelines_in_hierarchy.includes(project: [:route, { namespace: :route }]).each do |relative_pipeline| # rubocop: disable CodeReuse/ActiveRecord + pipeline.upstream_and_all_downstreams.includes(project: [:route, { namespace: :route }]).each do |relative_pipeline| # rubocop: disable CodeReuse/ActiveRecord etag_paths << project_pipeline_path(relative_pipeline.project, relative_pipeline) etag_paths << graphql_pipeline_path(relative_pipeline) etag_paths << graphql_pipeline_sha_path(relative_pipeline.sha) diff --git a/app/services/ci/generate_coverage_reports_service.rb b/app/services/ci/generate_coverage_reports_service.rb index 81f26e84ef8..8beecb79fd9 100644 --- a/app/services/ci/generate_coverage_reports_service.rb +++ b/app/services/ci/generate_coverage_reports_service.rb @@ -43,7 +43,7 @@ module Ci end def last_update_timestamp(pipeline_hierarchy) - pipeline_hierarchy&.self_and_descendants&.maximum(:updated_at) + pipeline_hierarchy&.self_and_project_descendants&.maximum(:updated_at) end end end diff --git a/app/services/ci/job_artifacts/create_service.rb b/app/services/ci/job_artifacts/create_service.rb index af56eb221d5..3dc097a8603 100644 --- a/app/services/ci/job_artifacts/create_service.rb +++ b/app/services/ci/job_artifacts/create_service.rb @@ -80,7 +80,7 @@ module Ci Gitlab::CurrentSettings.current_application_settings.default_artifacts_expire_in artifact_attributes = { - job_id: job.id, + job: job, project: project, expire_in: expire_in } diff --git a/app/services/ci/job_artifacts/delete_service.rb b/app/services/ci/job_artifacts/delete_service.rb new file mode 100644 index 00000000000..65cae03312e --- /dev/null +++ b/app/services/ci/job_artifacts/delete_service.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Ci + module JobArtifacts + class DeleteService + include BaseServiceUtility + + def initialize(build) + @build = build + end + + def execute + if build.project.refreshing_build_artifacts_size? + Gitlab::ProjectStatsRefreshConflictsLogger.warn_artifact_deletion_during_stats_refresh( + method: 'Ci::JobArtifacts::DeleteService#execute', + project_id: build.project_id + ) + end + + # fix_expire_at is false because in this case we want to explicitly delete the job artifacts + # this flag is a workaround that will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/355833 + Ci::JobArtifacts::DestroyBatchService.new(build.job_artifacts.erasable, fix_expire_at: false).execute + + ServiceResponse.success + end + + private + + attr_reader :build + end + end +end diff --git a/app/services/ci/job_artifacts/track_artifact_report_service.rb b/app/services/ci/job_artifacts/track_artifact_report_service.rb new file mode 100644 index 00000000000..1be1d98394f --- /dev/null +++ b/app/services/ci/job_artifacts/track_artifact_report_service.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Ci + module JobArtifacts + class TrackArtifactReportService + include Gitlab::Utils::UsageData + + REPORT_TRACKED = %i[test].freeze + + def execute(pipeline) + REPORT_TRACKED.each do |report| + if pipeline.complete_and_has_reports?(Ci::JobArtifact.of_report_type(report)) + track_usage_event(event_name(report), pipeline.user_id) + end + end + end + + def event_name(report) + "i_testing_#{report}_report_uploaded" + end + end + end +end diff --git a/app/services/ci/pipeline_artifacts/coverage_report_service.rb b/app/services/ci/pipeline_artifacts/coverage_report_service.rb index c11a8f7a0fd..99877603554 100644 --- a/app/services/ci/pipeline_artifacts/coverage_report_service.rb +++ b/app/services/ci/pipeline_artifacts/coverage_report_service.rb @@ -27,12 +27,18 @@ module Ci end def pipeline_artifact_params - { + attributes = { pipeline: pipeline, file_type: :code_coverage, file: carrierwave_file, size: carrierwave_file['tempfile'].size } + + if ::Feature.enabled?(:ci_update_unlocked_pipeline_artifacts, pipeline.project) + attributes[:locked] = pipeline.locked + end + + attributes end def carrierwave_file diff --git a/app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb b/app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb index d6865efac9f..aeb68a75f88 100644 --- a/app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb +++ b/app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb @@ -13,21 +13,31 @@ module Ci return if pipeline.has_codequality_mr_diff_report? return unless new_errors_introduced? + pipeline.pipeline_artifacts.create!(**artifact_attributes) + end + + private + + attr_reader :pipeline + + def artifact_attributes file = build_carrierwave_file! - pipeline.pipeline_artifacts.create!( + attributes = { project_id: pipeline.project_id, file_type: :code_quality_mr_diff, file_format: Ci::PipelineArtifact::REPORT_TYPES.fetch(:code_quality_mr_diff), size: file["tempfile"].size, file: file, expire_at: Ci::PipelineArtifact::EXPIRATION_DATE.from_now - ) - end + } - private + if ::Feature.enabled?(:ci_update_unlocked_pipeline_artifacts, pipeline.project) + attributes[:locked] = pipeline.locked + end - attr_reader :pipeline + attributes + end def merge_requests strong_memoize(:merge_requests) do diff --git a/app/services/ci/pipelines/add_job_service.rb b/app/services/ci/pipelines/add_job_service.rb index fc852bc3edd..dfbb37cf0dc 100644 --- a/app/services/ci/pipelines/add_job_service.rb +++ b/app/services/ci/pipelines/add_job_service.rb @@ -39,11 +39,13 @@ module Ci job.pipeline = pipeline job.project = pipeline.project job.ref = pipeline.ref + job.partition_id = pipeline.partition_id # update metadata since it might have been lazily initialised before this call # metadata is present on `Ci::Processable` if job.respond_to?(:metadata) && job.metadata job.metadata.project = pipeline.project + job.metadata.partition_id = pipeline.partition_id end end end diff --git a/app/services/ci/queue/pending_builds_strategy.rb b/app/services/ci/queue/pending_builds_strategy.rb index c8bdbba5e65..cfafe66d10b 100644 --- a/app/services/ci/queue/pending_builds_strategy.rb +++ b/app/services/ci/queue/pending_builds_strategy.rb @@ -19,7 +19,11 @@ module Ci def builds_for_group_runner return new_builds.none if runner.namespace_ids.empty? - new_builds.where('ci_pending_builds.namespace_traversal_ids && ARRAY[?]::int[]', runner.namespace_ids) + new_builds_relation = new_builds.where('ci_pending_builds.namespace_traversal_ids && ARRAY[?]::int[]', runner.namespace_ids) + + return order(new_builds_relation) if ::Feature.enabled?(:order_builds_for_group_runner) + + new_builds_relation end def builds_matching_tag_ids(relation, ids) diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb index b357855db12..0bd4bf8cc86 100644 --- a/app/services/ci/register_job_service.rb +++ b/app/services/ci/register_job_service.rb @@ -287,7 +287,7 @@ module Ci Gitlab::ErrorTracking.track_exception(ex, build_id: build.id, build_name: build.name, - build_stage: build.stage, + build_stage: build.stage_name, pipeline_id: build.pipeline_id, project_id: build.project_id ) diff --git a/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb b/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb index dfd97498fc8..d7078200c14 100644 --- a/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb +++ b/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb @@ -9,8 +9,10 @@ module Ci free_resources = resource_group.resources.free.count - resource_group.upcoming_processables.take(free_resources).each do |processable| - processable.enqueue_waiting_for_resource + resource_group.upcoming_processables.take(free_resources).each do |upcoming| + Gitlab::OptimisticLocking.retry_lock(upcoming, name: 'enqueue_waiting_for_resource') do |processable| + processable.enqueue_waiting_for_resource + end end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/services/ci/runners/set_runner_associated_projects_service.rb b/app/services/ci/runners/set_runner_associated_projects_service.rb new file mode 100644 index 00000000000..7930776749d --- /dev/null +++ b/app/services/ci/runners/set_runner_associated_projects_service.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module Ci + module Runners + class SetRunnerAssociatedProjectsService + # @param [Ci::Runner] runner: the project runner to assign/unassign projects from + # @param [User] current_user: the user performing the operation + # @param [Array<Integer>] project_ids: the IDs of the associated projects to assign the runner to + def initialize(runner:, current_user:, project_ids:) + @runner = runner + @current_user = current_user + @project_ids = project_ids + end + + def execute + unless current_user&.can?(:assign_runner, runner) + return ServiceResponse.error(message: 'user not allowed to assign runner', http_status: :forbidden) + end + + return ServiceResponse.success if project_ids.blank? + + set_associated_projects + end + + private + + def set_associated_projects + new_project_ids = [runner.owner_project.id] + project_ids + + response = ServiceResponse.success + runner.transaction do + # rubocop:disable CodeReuse/ActiveRecord + current_project_ids = runner.projects.ids + # rubocop:enable CodeReuse/ActiveRecord + + unless associate_new_projects(new_project_ids, current_project_ids) + response = ServiceResponse.error(message: 'failed to assign projects to runner') + raise ActiveRecord::Rollback, response.errors + end + + unless disassociate_old_projects(new_project_ids, current_project_ids) + response = ServiceResponse.error(message: 'failed to destroy runner project') + raise ActiveRecord::Rollback, response.errors + end + end + + response + end + + def associate_new_projects(new_project_ids, current_project_ids) + missing_projects = Project.id_in(new_project_ids - current_project_ids) + missing_projects.all? { |project| runner.assign_to(project, current_user) } + end + + def disassociate_old_projects(new_project_ids, current_project_ids) + projects_to_be_deleted = current_project_ids - new_project_ids + return true if projects_to_be_deleted.empty? + + Ci::RunnerProject + .destroy_by(project_id: projects_to_be_deleted) + .all?(&:destroyed?) + end + + attr_reader :runner, :current_user, :project_ids + end + end +end + +Ci::Runners::SetRunnerAssociatedProjectsService.prepend_mod diff --git a/app/services/ci/runners/update_runner_service.rb b/app/services/ci/runners/update_runner_service.rb index 6cc080f81c2..bd01f52f396 100644 --- a/app/services/ci/runners/update_runner_service.rb +++ b/app/services/ci/runners/update_runner_service.rb @@ -9,11 +9,14 @@ module Ci @runner = runner end - def update(params) + def execute(params) params[:active] = !params.delete(:paused) if params.include?(:paused) - runner.update(params).tap do |updated| - runner.tick_runner_queue if updated + if runner.update(params) + runner.tick_runner_queue + ServiceResponse.success + else + ServiceResponse.error(message: runner.errors.full_messages) end end end diff --git a/app/services/ci/stuck_builds/drop_helpers.rb b/app/services/ci/stuck_builds/drop_helpers.rb index dca50963883..f56c9aaeb55 100644 --- a/app/services/ci/stuck_builds/drop_helpers.rb +++ b/app/services/ci/stuck_builds/drop_helpers.rb @@ -48,7 +48,7 @@ module Ci Gitlab::ErrorTracking.track_exception(ex, build_id: build.id, build_name: build.name, - build_stage: build.stage, + build_stage: build.stage_name, pipeline_id: build.pipeline_id, project_id: build.project_id ) diff --git a/app/services/ci/test_failure_history_service.rb b/app/services/ci/test_failure_history_service.rb index 2214a6a2729..5a8072b2a0d 100644 --- a/app/services/ci/test_failure_history_service.rb +++ b/app/services/ci/test_failure_history_service.rb @@ -80,8 +80,8 @@ module Ci end def generate_test_suite!(build) - # Returns an instance of Gitlab::Ci::Reports::TestSuite - build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) + test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) + test_report.get_suite(build.test_suite_name) end def ci_unit_test_attrs(batch) diff --git a/app/services/ci/unlock_artifacts_service.rb b/app/services/ci/unlock_artifacts_service.rb index 30da31ba8ec..1fee31da4fc 100644 --- a/app/services/ci/unlock_artifacts_service.rb +++ b/app/services/ci/unlock_artifacts_service.rb @@ -7,9 +7,12 @@ module Ci def execute(ci_ref, before_pipeline = nil) results = { unlocked_pipelines: 0, - unlocked_job_artifacts: 0 + unlocked_job_artifacts: 0, + unlocked_pipeline_artifacts: 0 } + unlock_pipeline_artifacts_enabled = ::Feature.enabled?(:ci_update_unlocked_pipeline_artifacts, ci_ref.project) + if ::Feature.enabled?(:ci_update_unlocked_job_artifacts, ci_ref.project) loop do unlocked_pipelines = [] @@ -18,6 +21,10 @@ module Ci ::Ci::Pipeline.transaction do unlocked_pipelines = unlock_pipelines(ci_ref, before_pipeline) unlocked_job_artifacts = unlock_job_artifacts(unlocked_pipelines) + + if unlock_pipeline_artifacts_enabled + results[:unlocked_pipeline_artifacts] += unlock_pipeline_artifacts(unlocked_pipelines) + end end break if unlocked_pipelines.empty? @@ -100,6 +107,14 @@ module Ci ) end + # rubocop:disable CodeReuse/ActiveRecord + def unlock_pipeline_artifacts(pipelines) + return 0 if pipelines.empty? + + ::Ci::PipelineArtifact.where(pipeline_id: pipelines.rows.flatten).update_all(locked: :unlocked) + end + # rubocop:enable CodeReuse/ActiveRecord + def unlock_pipelines(ci_ref, before_pipeline) ::Ci::Pipeline.connection.exec_query(unlock_pipelines_query(ci_ref, before_pipeline)) end |