Create at most 1 pipeline when in the context of merge requests
Some areas of the problem, possible solutions and tradeoffs are documented in this [opportunity canvas lite](https://gitlab.com/gitlab-org/ci-cd/testing-group/-/issues/60). ## Problem Statement Currently, when pushing to a branch with an open merge request, GitLab creates **two pipelines** - both a branch pipeline and a merge request pipeline. This creates several problems: 1. **Confusion about which pipeline is the source of truth** for merge request status 2. **Compliance enforcement gaps** - security policies can't reliably target "merge request context" pipelines 3. **Resource waste** from running duplicate jobs 4. **Split-brain scenarios** where mergeability checks may come from different pipelines As highlighted in recent customer feedback, this particularly impacts Pipeline Security Policies where compliance teams cannot consistently enforce security scans across all merge requests due to the unpredictable pipeline source. ## Current Behavior * `git push` to a branch with open MR → Creates both branch pipeline AND merge request pipeline * Projects must carefully configure rules to avoid job duplication * Policy enforcement becomes inconsistent across projects using different pipeline strategies ## Proposed Solution ### Phase 1: New Opt-in setting 1. **New project setting**: "Prevent duplicate pipelines in merge requests" (**not** enabled by default for new projects) 2. **When enabled**: `git push` to branch with open MR creates **only** merge request pipeline 3. **Backward compatibility**: Existing projects keep current behavior until they opt-in ### Phase 2: Opinionated default for new projects 1. Ensure the setting is read- and writable via REST and GraphQL API. 2. Update documentation to reflect the new setting. 3. Change the default for the "Prevent duplicate pipelines in merge requests" setting to **enabled** for _newly created projects_. ### Phase 3: Migration Path * Provide clear migration guidance for projects currently relying on branch pipelines * Update documentation and templates to reflect new default behavior * Eventually deprecate the old workflow in future major version ## Research Questions Before implementation, we need to understand: 1. **Why do customers choose branch pipelines over merge request pipelines today?** * Performance differences? * Feature limitations in MR pipelines? * Configuration complexity? 2. **What are the edge cases where branch pipelines are genuinely needed alongside MR pipelines?** 3. **How does this impact existing integrations and downstream pipeline triggers?** ## Success Criteria * [ ] Single pipeline created per `git push` when MR is open * [ ] Security policies can reliably target "merge request context" or at least the configurable context * [ ] Clear migration path for existing projects * [ ] No regression in pipeline functionality * [ ] Improved resource utilization ## Implementation A new opt-in project setting (`prevent_duplicate_mr_pipelines`). When enabled, a `git push` to a branch associated with a merge request skips creating the branch pipeline. ### Design Decisions | Decision | Choice | Rationale | |----------|--------|-----------| | Suppression point | `BaseHooksService#create_pipeline?` | Earliest, cheapest point. No CI config parsing, no Sidekiq job. | | Pipeline sources affected | `push` only | Manual/API/scheduled/triggered pipelines unaffected. This method is only called in the push hook path. | | MR existence check | Reuse existing `params[:merge_request_branches]` | Already computed by `PushedBranchesService` and plumbed through `ProcessRefChangesService` → `BranchPushService` → `BranchHooksService`. No additional DB queries. | | Default | `false` (opt-in) | Backward compatible. Existing projects keep current behavior. | ### Call Chain ``` git push → PostReceive → Git::ProcessRefChangesService#execute → Git::BranchPushService.new(params: { merge_request_branches: ...}) #execute_related_hooks → Git::BranchHooksService.new(project, user, params) → Git::BaseHooksService#create_pipeline → create_pipeline? ← NEW CHECK HERE: skip_pipeline_for_merge_request? → Ci::CreatePipelineService#execute_async(:push) ← skipped when suppressed ``` --- _This issue is part of the broader effort to eliminate unexpected duplicate pipelines and provide a more predictable CI/CD experience._
epic