Skip to content

Conversation

@ZeBidule
Copy link

@ZeBidule ZeBidule commented Feb 6, 2026

Fixes reconciliation loops in PullRequest and CommitStatus controllers that occur when status fields in Kubernetes resources are out of sync with the actual state in SCM providers.

Problem

Issue 1: PullRequest Reconciliation Loop

  • When a merge request is successfully merged in GitLab/GitHub/Forgejo, the status.state field is sometimes not updated in the K8S resource
  • Controller repeatedly attempts to merge an already-merged PR/MR
  • Results in 405 (GitLab) or similar errors from SCM APIs
  • Triggers unnecessary pipeline executions on each retry

Example error:

Reconciliation failed: failed to merge pull request: PUT https://gitlab_hostname/api/v4/projects/2673/merge_requests/1003/merge: 405 {message: 405 Method Not Allowed}

Issue 2: CommitStatus Reconciliation Loop

  • Controller attempts to set commit status even when already set
  • GitLab's state machine rejects duplicate status transitions
  • Results in 400 errors

Example error:

Commit status reconciliation failed: failed to set CommitStatus state: failed to create status: POST https://gitlab_hostname/api/v4/projects/2673/statuses/...: 400 {message: Cannot transition status via :enqueue from :pending}

Solution

1. PullRequest Providers (GitLab, GitHub, Forgejo)

  • Check actual PR/MR state from SCM before attempting merge/close operations
  • If already merged/closed, log and return success (no-op) instead of making redundant API calls
  • Prevents reconciliation errors and unnecessary pipeline triggers

2. CommitStatus Controller

  • Compare status.sha and status.phase with spec values before making API calls
  • Skip redundant status updates when already correctly set
  • Prevents GitLab state transition errors

Impact

✅ Eliminates reconciliation loops caused by stale status fields
✅ Reduces unnecessary SCM API calls
✅ Prevents pipeline spam from repeated reconciliation attempts
✅ Improves resilience to transient status update failures

Testing

  • Code compiles successfully
  • Changes tested against the reported scenario (GitOps Promoter v0.21.1 + GitLab v18.8.2)

Related

This fix addresses the issue reported in the community discussion about promotion blocking due to out-of-sync status fields.

@ZeBidule ZeBidule force-pushed the fix/controller-status-reconciliation-loops branch from 7344437 to 91a7f95 Compare February 6, 2026 14:22
@codecov-commenter
Copy link

codecov-commenter commented Feb 6, 2026

Codecov Report

❌ Patch coverage is 4.00000% with 72 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.25%. Comparing base (ed43256) to head (5035e44).

Files with missing lines Patch % Lines
internal/scms/gitlab/pullrequest.go 0.00% 44 Missing ⚠️
internal/scms/github/pullrequest.go 0.00% 23 Missing ⚠️
internal/scms/forgejo/pullrequest.go 0.00% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1008      +/-   ##
==========================================
- Coverage   50.92%   50.25%   -0.68%     
==========================================
  Files          51       51              
  Lines        5492     5562      +70     
==========================================
- Hits         2797     2795       -2     
- Misses       2399     2471      +72     
  Partials      296      296              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@codecov-commenter
Copy link

Bundle Report

Bundle size has no change ✅

@ZeBidule ZeBidule force-pushed the fix/controller-status-reconciliation-loops branch 2 times, most recently from 3d37272 to bef951f Compare February 10, 2026 10:55
Fixes reconciliation loops in PullRequest and CommitStatus controllers

See commit body for full details

Signed-off-by: ZeBidule <[email protected]>
@ZeBidule ZeBidule force-pushed the fix/controller-status-reconciliation-loops branch from bef951f to 5035e44 Compare February 10, 2026 11:08
Comment on lines +104 to +109
// Check if the status is already set correctly to avoid redundant API calls
// This prevents GitLab state transition errors when status is already set
if cs.Status.Sha == cs.Spec.Sha && cs.Status.Phase == cs.Spec.Phase {
logger.Info("Commit status already set correctly, skipping API call", "sha", cs.Spec.Sha, "phase", cs.Spec.Phase)
return ctrl.Result{}, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change would make it so that other changes (like the description changing) wouldn't be written to the SCM.

I think instead of making the change in commitstatus_controller (i.e. for all providers), we should make it on a per-provider basis. This github-specific fix is a good example: #1002

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants