Use latestPipeline for commit list CI status badge

What does this MR do and why?

On a project's commits list, each commit shows a small CI status badge, a green check or a red X. That badge could show the wrong status.

The badge read the single newest pipeline of any kind, using the GraphQL pipelines(first: 1) field. That field includes pipelines that are not the commit's real CI run, such as security policy scans (security_orchestration_policy). If one of those scans ran after the real CI pipeline and passed, the badge showed a green check even though the commit's actual CI pipeline had failed. Users saw a passing badge on a commit that really failed.

This MR switches the badge to the Commit.latestPipeline GraphQL field. That field only considers real CI pipelines, using the ci_sources scope, and ignores scans and other non-CI pipelines. This makes the commits-list badge agree with the commit page, the commit status API field, and everywhere else that already ignores these non-CI pipelines.

Before: badge could show a passing security scan instead of a failed CI pipeline. After: badge always reflects the commit's actual CI pipeline.

latestPipeline takes a ref argument, so the badge still shows the pipeline for the branch you are viewing. A commit can have separate pipelines on different branches, and this keeps that per-branch behavior, added earlier in commit c416feed.

Bug report: #579690

Release sequencing (ships in 19.4)

  • 19.2: latestPipeline field added (!243957 (merged)).
  • 19.3: ref argument added to latestPipeline (!245748 (merged)).
  • 19.4: this MR, which is the frontend change that uses ref.

This ships one release after the argument because of rolling upgrades. GitLab Self-Managed and Dedicated can run older and newer backend nodes at the same time during an upgrade. Shipping in 19.4 means every backend node during a 19.3-to-19.4 upgrade already has the ref argument, so the query works no matter which node answers it.

Why no @gl_introduced directive

@gl_introduced can hide a whole field from older backends, but it cannot hide a single argument. The new part here is the ref argument, not the field itself, so neither directive version is safe.

Version 19.2.0 would not hide the field on a 19.2 node, since that node lacks the ref argument. The query would fail there with an unknown-argument error. Version 19.3.0 would hide the field on a 19.2 node, but then the $pipelineRef variable is no longer used anywhere. GraphQL rejects the whole commits query for having an unused variable, so the entire list would fail to load, not just the badge.

Because of this, the fix relies on release sequencing, shipping in 19.4, instead of the directive.

Verifying against production data

Run this single query in the GraphQL explorer (or with POST /api/graphql). It returns both the new field (new: latestPipeline, what the badge now reads) and the old one (old: pipelines(first: 1)) for the same commits in one request, so the two are always directly comparable, with no drift between separate runs:

query compareBadgeSource($projectPath: ID!, $ref: String!, $pipelineRef: String, $first: Int) {
  project(fullPath: $projectPath) {
    id
    repository {
      commits(ref: $ref, first: $first) {
        nodes {
          sha
          new: latestPipeline(ref: $pipelineRef) {
            id
            detailedStatus { icon text }
          }
          old: pipelines(first: 1, ref: $pipelineRef) {
            nodes {
              id
              detailedStatus { icon text }
            }
          }
        }
      }
    }
  }
}

Variables:

{
  "projectPath": "gitlab-org/gitlab",
  "ref": "master",
  "pipelineRef": "master",
  "first": 100
}

How to read the result

  • For almost every commit, new matches old (same pipeline id and status), and both are null or empty for commits with no pipeline of their own. This is expected. The two fields only differ when a commit's newest pipeline is a dangling source, such as security_orchestration_policy. For every other commit, the ci_sources scope changes nothing.
  • A commit where new.id does not equal old.nodes[0].id, or where new shows a CI status while old shows a green dangling scan, is a live instance of the bug in #579690. The badge previously showed the dangling pipeline and now shows the real CI status.
  • The bug is intermittent, so a small window may show no difference. Widen the search with first: 100 and page through with pageInfo and endCursor, or check a commit known to have a security policy scan with repository { commit(ref: "<sha>") { new: latestPipeline { ... } old: pipelines(first: 1) { nodes { ... } } } }.

MR acceptance checklist

  • Frontend unit spec updated (commit_list_item_badges_spec.js), passing.
  • Regression feature spec added (spec/features/commits_spec.rb): a newer dangling-source pipeline does not determine the commit badge status.
  • Milestone: 19.4.
Edited by Stan Hu

Merge request reports

Loading
Loading