ci/bin: Do not forget to add early-stage dependencies

In Mesa MR pipelines, we have the sanity job. This job is not an
explicit need for any one job, because only jobs with `- needs: []`
ignores previous jobs. If no `needs` is specified, the job will wait
until all the jobs of earlier stages are finished before starting.

See additional details section at:
https://docs.gitlab.com/ee/ci/yaml/index.html#stage-post

Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/10032
Signed-off-by: Guilherme Gallo <guilherme.gallo@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/25858>
This commit is contained in:
Guilherme Gallo
2023-10-24 00:13:01 -03:00
committed by Marge Bot
parent c7b67d8619
commit 4e4743ec0a
2 changed files with 29 additions and 5 deletions

View File

@@ -3,9 +3,10 @@
import re
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace
from collections import defaultdict
from collections import OrderedDict, defaultdict
from copy import deepcopy
from dataclasses import dataclass, field
from itertools import accumulate
from os import getenv
from pathlib import Path
from typing import Any, Iterable, Optional, Pattern, Union
@@ -85,7 +86,6 @@ class GitlabGQL:
def create_job_needs_dag(
gl_gql: GitlabGQL, params
) -> tuple[Dag, dict[str, dict[str, Any]]]:
result = gl_gql.query("pipeline_details.gql", params)
incomplete_dag = defaultdict(set)
jobs = {}
@@ -93,28 +93,49 @@ def create_job_needs_dag(
if not pipeline:
raise RuntimeError(f"Could not find any pipelines for {params}")
# Record the stage sequence to post process deps that are not based on needs
# field, for example: sanity job
stage_sequence: OrderedDict[str, set[str]] = OrderedDict()
for stage in pipeline["stages"]["nodes"]:
stage_jobs: set[str] = set()
for stage_job in stage["groups"]["nodes"]:
for job in stage_job["jobs"]["nodes"]:
stage_jobs.add(job["name"])
needs = job.pop("needs")["nodes"]
jobs[job["name"]] = job
incomplete_dag[job["name"]] = {node["name"] for node in needs}
# ensure that all needed nodes its in the graph
[incomplete_dag[node["name"]] for node in needs]
stage_sequence[stage["name"]] = stage_jobs
pre_processed_dag: Dag = {}
final_dag: Dag = {}
for job, needs in incomplete_dag.items():
final_needs: set = deepcopy(needs)
# Pre-process jobs that are not based on needs field
# e.g. sanity job in mesa MR pipelines
if not final_needs:
for stage_index, stage_jobs in enumerate(stage_sequence.values()):
if job in stage_jobs:
break
for prev_stage, prev_stage_jobs in list(stage_sequence.items())[:stage_index]:
final_needs |= prev_stage_jobs
pre_processed_dag[job] = final_needs
for job, needs in pre_processed_dag.items():
final_needs: set = deepcopy(needs)
# Post process jobs that are based on needs field
partial = True
while partial:
next_depth = {n for dn in final_needs for n in incomplete_dag[dn]}
next_depth = {n for dn in final_needs for n in pre_processed_dag[dn]}
partial = not final_needs.issuperset(next_depth)
final_needs = final_needs.union(next_depth)
final_dag[job] = final_needs
return final_dag, jobs
return final_dag, jobs_metadata
def filter_dag(dag: Dag, regex: Pattern) -> Dag:

View File

@@ -7,13 +7,16 @@ query getPipelineDetails($projectPath: ID!, $iid: ID!) {
complete
stages {
nodes {
name,
name
groups {
nodes {
jobs {
nodes {
id
name
stage {
name
}
needs {
nodes {
id