Resolve dismissal MRs by source branch for merged-results pipelines

Problem

Issue #560563 (closed) (Scenario 1, "Gate 3"): when all vulnerabilities are dismissed, the merge request approval policy can stay blocked until the pipeline is re-run, specifically for merge requests using merge trains or merged results pipelines.

This is a separate, follow-up fix to !240608 (closed). !240608 (closed) addresses the dismissal-unblock evaluation paths (UUID-cascade and the UpdateApprovalsService artifact-expiry early-return). This MR fixes the trigger / MR-resolution gap that those changes do not cover.

Root cause

A dismissal triggers SyncFindingsToApprovalRulesService with the scan (branch) pipeline. update_required_approvals_for_scan_finding resolves affected MRs via pipeline.opened_merge_requests_with_head_sha, which matches MRs whose latest diff head_commit_sha is in the pipeline's [sha, source_sha].

When the MR's current head pipeline is a merged results / merge train pipeline, its SHA differs from the dismissal's branch scan pipeline SHA, so the head-sha match does not return the MR. UpdateApprovalsService is therefore never invoked for that MR, the policy is not re-evaluated, and the MR stays blocked. A manual pipeline re-run sidesteps this by creating a head pipeline whose SHA matches.

Change

SyncFindingsToApprovalRulesService now additionally resolves opened merge requests by source branch (for branch pipelines only) and evaluates each against its own current head pipeline (diff_head_pipeline), deduplicated against the MRs already handled by the head-sha match.

  • Guarded with pipeline.merge_request? only (merge request pipelines already resolve to their single MR via the head-sha match).
  • Intentionally not guarded with pipeline.latest?: for merge trains / merged results, the scan pipeline's SHA is the internal merge ref commit rather than the source branch tip, so latest? returns false for exactly the pipelines this branch needs to catch. Using it would make the fix a no-op. Resolution stays safe because each MR is synced via its own diff_head_pipeline (not this scan pipeline) and SyncMergeRequestApprovalsWorker is deduplicated.
  • Reuses the existing diff_head_pipeline + start_merge_request_worker_policy_sync pattern, so policy sync-state tracking stays correct.

Testing

  • New spec: a branch pipeline whose SHA is not the MR diff head SHA resolves the MR by source branch and syncs via its head pipeline.
  • New spec: a merge request pipeline does not double-resolve by source branch.
  • Updated the existing 'when pipeline is not for diff_head_sha' example to reflect the new behavior.
  • New specs use isolated projects so they cannot pollute the shared let_it_be records used by other examples.

Notes for reviewers

  • Performance: this adds one by_source_branch query per branch-pipeline completion and may enqueue SyncMergeRequestApprovalsWorker for additional same-source-branch MRs. The worker is deduplicated, and resolution is project-scoped + branch-scoped. Please confirm this is acceptable on busy projects.
  • Forks: like the existing target-branch resolution, project.merge_requests.by_source_branch resolves MRs in the project; cross-fork source branches are out of scope here (same limitation as the pre-existing code).
Edited by Alan (Maciej) Paruszewski

Merge request reports

Loading