Edit

IABSD.fr/xenocara/lib/mesa/bin/ci/gitlab_common.py

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2025-06-05 11:23:11
    Hash : 67d6f117
    Message : Import Mesa 25.0.7

  • lib/mesa/bin/ci/gitlab_common.py
  • #!/usr/bin/env python3
    # Copyright © 2020 - 2022 Collabora Ltd.
    # Authors:
    #   Tomeu Vizoso <tomeu.vizoso@collabora.com>
    #   David Heidelberg <david.heidelberg@collabora.com>
    #   Guilherme Gallo <guilherme.gallo@collabora.com>
    #
    # SPDX-License-Identifier: MIT
    '''Shared functions between the scripts.'''
    
    import logging
    import os
    import re
    import time
    from functools import cache
    from pathlib import Path
    
    GITLAB_URL = "https://gitlab.freedesktop.org"
    TOKEN_DIR = Path(os.getenv("XDG_CONFIG_HOME") or Path.home() / ".config")
    
    # Known GitLab token prefixes: https://docs.gitlab.com/ee/security/token_overview.html#token-prefixes
    TOKEN_PREFIXES: dict[str, str] = {
        "Personal access token": "glpat-",
        "OAuth Application Secret": "gloas-",
        "Deploy token": "gldt-",
        "Runner authentication token": "glrt-",
        "CI/CD Job token": "glcbt-",
        "Trigger token": "glptt-",
        "Feed token": "glft-",
        "Incoming mail token": "glimt-",
        "GitLab Agent for Kubernetes token": "glagent-",
        "SCIM Tokens": "glsoat-",
    }
    
    
    @cache
    def print_once(*args, **kwargs):
        """Print without spamming the output"""
        print(*args, **kwargs)
    
    
    def pretty_duration(seconds):
        """Pretty print duration"""
        hours, rem = divmod(seconds, 3600)
        minutes, seconds = divmod(rem, 60)
        if hours:
            return f"{hours:0.0f}h{minutes:02.0f}m{seconds:02.0f}s"
        if minutes:
            return f"{minutes:0.0f}m{seconds:02.0f}s"
        return f"{seconds:0.0f}s"
    
    
    def get_gitlab_pipeline_from_url(gl, pipeline_url) -> tuple:
        """
        Extract the project and pipeline object from the url string
        :param gl: Gitlab object
        :param pipeline_url: string with a url to a pipeline
        :return: ProjectPipeline, Project objects
        """
        pattern = rf"^{re.escape(GITLAB_URL)}/(.*)/-/pipelines/([0-9]+)$"
        match = re.match(pattern, pipeline_url)
        if not match:
            raise AssertionError(f"url {pipeline_url} doesn't follow the pattern {pattern}")
        namespace_with_project, pipeline_id = match.groups()
        cur_project = gl.projects.get(namespace_with_project)
        pipe = cur_project.pipelines.get(pipeline_id)
        return pipe, cur_project
    
    
    def get_gitlab_project(glab, name: str):
        """Finds a specified gitlab project for given user"""
        if "/" in name:
            project_path = name
        else:
            glab.auth()
            username = glab.user.username
            project_path = f"{username}/{name}"
        return glab.projects.get(project_path)
    
    
    def get_token_from_default_dir() -> str:
        """
        Retrieves the GitLab token from the default directory.
    
        Returns:
            str: The path to the GitLab token file.
    
        Raises:
            FileNotFoundError: If the token file is not found.
        """
        token_file = TOKEN_DIR / "gitlab-token"
        try:
            return str(token_file.resolve())
        except FileNotFoundError as ex:
            print(
                f"Could not find {token_file}, please provide a token file as an argument"
            )
            raise ex
    
    
    def validate_gitlab_token(token: str) -> bool:
        # Match against recognised token prefixes
        token_suffix = None
        for token_type, token_prefix in TOKEN_PREFIXES.items():
            if token.startswith(token_prefix):
                logging.info(f"Found probable token type: {token_type}")
                token_suffix = token[len(token_prefix):]
                break
    
        if not token_suffix:
            return False
    
        # Basic validation of the token suffix based on:
        # https://gitlab.com/gitlab-org/gitlab/-/blob/master/gems/gitlab-secret_detection/lib/gitleaks.toml
        if not re.match(r"(\w+-)?[0-9a-zA-Z_\-]{20,64}", token_suffix):
            return False
    
        return True
    
    
    def get_token_from_arg(token_arg: str | Path | None) -> str | None:
        if not token_arg:
            logging.info("No token provided.")
            return None
    
        token_path = Path(token_arg)
        if token_path.is_file():
            return read_token_from_file(token_path)
    
        return handle_direct_token(token_path, token_arg)
    
    
    def read_token_from_file(token_path: Path) -> str:
        token = token_path.read_text().strip()
        logging.info(f"Token read from file: {token_path}")
        return token
    
    
    def handle_direct_token(token_path: Path, token_arg: str | Path) -> str | None:
        if token_path == Path(get_token_from_default_dir()):
            logging.warning(
                f"The default token file {token_path} was not found. "
                "Please provide a token file or a token directly via --token arg."
            )
            return None
        logging.info("Token provided directly as an argument.")
        return str(token_arg)
    
    
    def read_token(token_arg: str | Path | None) -> str | None:
        token = get_token_from_arg(token_arg)
        if token and not validate_gitlab_token(token):
            logging.warning("The provided token is either an old token or does not seem to "
                            "be a valid token.")
            logging.warning("Newer tokens are the ones created from a Gitlab 14.5+ instance.")
            logging.warning("See https://about.gitlab.com/releases/2021/11/22/"
                            "gitlab-14-5-released/"
                            "#new-gitlab-access-token-prefix-and-detection")
        return token
    
    
    def wait_for_pipeline(projects, sha: str, timeout=None):
        """await until pipeline appears in Gitlab"""
        project_names = [project.path_with_namespace for project in projects]
        print(f"⏲ for the pipeline to appear in {project_names}..", end="")
        start_time = time.time()
        while True:
            for project in projects:
                pipelines = project.pipelines.list(sha=sha)
                if pipelines:
                    print("", flush=True)
                    return (pipelines[0], project)
            print("", end=".", flush=True)
            if timeout and time.time() - start_time > timeout:
                print(" not found", flush=True)
                return (None, None)
            time.sleep(1)