diff --git a/bin/ci/ci_run_n_monitor.py b/bin/ci/ci_run_n_monitor.py index a131b383089..e2c07969f7f 100755 --- a/bin/ci/ci_run_n_monitor.py +++ b/bin/ci/ci_run_n_monitor.py @@ -13,6 +13,7 @@ and show the job(s) logs. """ import argparse +import os import re import sys import time @@ -37,7 +38,7 @@ from gitlab_common import ( read_token, wait_for_pipeline, ) -from gitlab_gql import GitlabGQL, create_job_needs_dag, filter_dag, print_dag +from gitlab_gql import GitlabGQL, create_job_needs_dag, filter_dag, print_dag, print_formatted_list if TYPE_CHECKING: from gitlab_gql import Dag @@ -494,16 +495,14 @@ def print_detected_jobs( target_jobs: Iterable[str], ) -> None: def print_job_set(color: str, kind: str, job_set: Iterable[str]): - print( - color + f"Running {len(job_set)} {kind} jobs: ", - "\n\t", - ", ".join(sorted(job_set)), - Fore.RESET, - "\n", - ) + job_list = list(job_set) + print(color + f"Running {len(job_list)} {kind} jobs:") + print_formatted_list(job_list, indentation=8) + print(Style.RESET_ALL) - print(Fore.YELLOW + "Detected target job and its dependencies:", "\n") - print_dag(target_dep_dag) + print(Fore.YELLOW + "Detected target job and its dependencies:") + print_dag(target_dep_dag, indentation=8) + print(Style.RESET_ALL) print_job_set(Fore.MAGENTA, "dependency", dependency_jobs) print_job_set(Fore.BLUE, "target", target_jobs) diff --git a/bin/ci/gitlab_gql.py b/bin/ci/gitlab_gql.py index 144e3c31d7b..25fc7ba7873 100755 --- a/bin/ci/gitlab_gql.py +++ b/bin/ci/gitlab_gql.py @@ -3,12 +3,14 @@ import logging import re +import sys import traceback from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser, Namespace from collections import OrderedDict from copy import deepcopy from dataclasses import dataclass, field from itertools import accumulate +from os import get_terminal_size from pathlib import Path from subprocess import check_output from textwrap import dedent @@ -21,6 +23,8 @@ from gql import Client, gql from gql.transport.requests import RequestsHTTPTransport from graphql import DocumentNode +DEFAULT_TERMINAL_SIZE: int = 80 # columns + class DagNode(TypedDict): needs: set[str] @@ -340,9 +344,40 @@ def filter_dag( return filtered_jobs -def print_dag(dag: Dag) -> None: +def print_dag(dag: Dag, indentation: int = 0) -> None: for job, data in sorted(dag.items()): - print(f"{job}:\n\t{' '.join(data['needs'])}\n") + print(f"{' '*indentation}{job}:") + print_formatted_list(list(data['needs']), indentation=indentation+8) + + +def print_formatted_list(elements: list[str], indentation: int = 0) -> None: + """ + When a list of elements is going to be printed, if it is longer than one line, reformat it to be multiple + lines with a 'ls' command style. + :param elements: list of elements to be printed + :param indentation: number of spaces to be injected in front of each line + """ + if len(elements) == 0: + return + elements.sort() + try: + h_size = get_terminal_size().columns if sys.stdin.isatty() else DEFAULT_TERMINAL_SIZE + except OSError: + h_size = DEFAULT_TERMINAL_SIZE + if indentation + sum(len(element) for element in elements) + (len(elements)*2) < h_size: # fits in one line + print(f"{' '*indentation}{', '.join([element for element in elements])}") + return + column_separator_size = 2 + column_width: int = len(max(elements, key=len)) + column_separator_size + n_columns: int = (h_size - indentation) // column_width + step = (len(elements) // n_columns) + 1 + rows = [elements[i::step] for i in range(step)] + for line in rows: + print(' '*indentation, end='') + for column in range(len(line)): + if line[column] is not None: + print(f"{line[column]:<{column_width}}", end='') + print() def fetch_merged_yaml(gl_gql: GitlabGQL, params) -> dict[str, Any]: