Repository navigation
Fail PR checks closed when the provider read fails - #2085
Merged
Merged
Conversation
git.fetch_pr_checks is the CI gate a workflow wires into If/else ahead of git.merge_pr. GitLab's ListChecksAsync caught every exception while reading pipelines or jobs (429, 5xx, 403, 404, a dropped connection, a status NGitLab cannot parse) and returned an empty list, which the node summarises as allPassed=true. A provider hiccup merged a merge request whose CI nobody saw. The catch also sat inside the resilience lambda, so a 5xx was never retried and nothing was typed as a ProviderApiException. Every failure now leaves through ExternalCallResilience and fails the node. An empty pipeline list is not taken at its word either: GitLab answers [] to a credential that may not read pipelines, and this read uses the connection's credential while the merge uses the actor's. [] means "no checks" only when CI is off for the project, or when the project's own pipeline list (which refuses such a credential with 403) is readable and the merge request names no head pipeline the list left out, as a fork's can be. The latest pipeline's status floors its jobs, mapped on its own terms: only success passes. A pipeline blocked on a manual job or waiting on a delayed one is pending, and a skipped one is not a success, as GitLab's merge check holds by default. Read as job statuses all three were "skipped" and opened the gate on a merge request GitLab would hold. NGitLab reports a lost connection as a WebException or HttpIOException, which the wrapper did not treat as transient, so one TCP reset failed the gate run outright. Both are retried now, like Octokit's HttpRequestException; writes already probe before they re-send. GitHub had the same shape for a 401 on check runs and for a pull request without a head commit; both fail now too.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
git.fetch_pr_checksis the CI gate that workflows wire into If/else ahead ofgit.merge_pr. GitLab'sListChecksAsyncturned every pipelines/jobs read error into an empty list: 429, 5xx, 403, 404, a dropped connection, or a status NGitLab cannot parse. The node reads an empty list asallPassed=true, so a provider hiccup merged a red merge request. Every read failure now goes throughExternalCallResilience(typedProviderApiException, 5xx retried) and fails the node. GitHub had the same problem for a 401 on check runs and for a PR without a head commit; both fail now too.GitLab answers
200 []on the MR pipelines list to a credential that may not read pipelines. That read uses the connection credential, while the merge uses the actor's. InGitLabRepositoryProvider.ConfirmNoPipeline, an empty list now means "no checks" only when one of these holds:read_pipeline), and the MR names no head pipeline the list left out, such as a fork's.The latest pipeline's status sets a minimum for its jobs, using its own mapping (
MapPipelineStatus):successpasses.manual(blocked on a manual job) andscheduled(waiting on a delayed job) are Pending.skippedandcanceledare Cancelled.Before, all three went through the job mapping to Skipped, and the gate opened on MRs that GitLab itself would hold.
ExternalCallResilience.IsTransientnow retries NGitLab's network failures (WebExceptionwith no response,HttpIOException) the same way it retries Octokit'sHttpRequestException. A single TCP reset no longer fails the gate run. NGitLab writes already go throughExecuteNonIdempotentAsync, which checks whether an earlier attempt landed before sending again.Test plan
GitLabListChecksTests:HttpIOExceptionafterMaxAttemptstries. Refused connection:WebExceptionafter the full backoff.GitHubListChecksTests: a 429 row, and a dropped-connection row assertingMaxAttemptsattempts.ExternalCallResilienceTests:WebExceptionwith no response andHttpIOExceptioncount as transient.GitLabWriteRetryTests: aLandsThenConnectionDropsrow on all six NGitLab writes, each landing exactly once.PullRequestChecksGateFlowTests, through the real engine, Postgres and a stub GitLab:IsTransientwithout either network type, GitHub swallowing 429 and drops).