summaryrefslogtreecommitdiff
path: root/util/dependency_updater/tools/repo.py
blob: 1e6363c1cd66a98fd196a195c3813452974720f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# Copyright (C) 2020 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
import re
from enum import IntEnum
from urllib.parse import unquote

import yaml

from .namespace import Namespace
from .proposal import Proposal


class PROGRESS(IntEnum):
    ERROR = 0
    UNSPECIFIED = 1
    READY = 2
    WAIT_DEPENDENCY = 3
    WAIT_INCONSISTENT = 4
    IN_PROGRESS = 5
    RETRY = 6
    DONE = 7
    DONE_NO_UPDATE = 8
    DONE_FAILED_NON_BLOCKING = 9
    DONE_FAILED_BLOCKING = 10
    DONE_FAILED_DEPENDENCY = 11
    IGNORE_IS_META = 12


class Repo(Namespace):
    """Base information about a repository/submodule"""
    id: str = ""  # Fully qualified id such as qt/qtbase or qt/tqtc-qtbase
    prefix: str = ""  # Bare prefix such as qt/ or qt/tqtc-
    name: str = ""  # Bare name such as qtbase
    original_ref: str = ""  # Ref to associate with this repo. This value should never be changed.
    branch: str = ""  # Branch where dependencies.yaml was found. May differ from the specified branch.
    deps_yaml: yaml = dict()
    dep_list: list[str]
    proposal: Proposal = Proposal()
    to_stage: list[str]
    progress: PROGRESS = PROGRESS.UNSPECIFIED
    failed_dependencies: list[str]
    stage_count: int = 0
    retry_count: int = 0
    is_supermodule: bool = False  # Bypasses dependency calculation
    # Does not stop the round from continuing unless a blocking module depends on it.
    is_non_blocking: bool = False

    def __init__(self, id: str, prefix: str,
                 proposal: Proposal = None,
                 to_stage: list[str] = None, **kwargs):
        super().__init__(**kwargs)
        self.to_stage = list()
        self.dep_list = list()
        self.failed_dependencies = list()
        self.id = unquote(id)
        self.prefix = prefix
        self.name = id.removeprefix(prefix)
        self.proposal = proposal or Proposal()
        if to_stage is not None:
            self.to_stage = to_stage
        if proposal and proposal.change_id not in self.to_stage:
            self.to_stage.append(proposal.change_id)

    def __str__(self):
        return (f"Repo(id='{self.id}', name='{self.name}',"
                f" ref='{self.original_ref}', branch='{self.branch}',"
                f" progress={self.progress},"
                f" stage_count={self.stage_count},"
                f" retry_count={self.retry_count},"
                f" proposal={str(self.proposal)})")

    def __repr__(self):
        return self.id

    def __eq__(self, other: [str, 'Repo']):
        if type(other) == str:
            r = r'((?:.*/){1,}(?:(?!(.*-){2,})|(?:[^-]*-)))'
            re_other_prefix = re.findall(r, other)
            if len(re_other_prefix):
                other_prefix: str = re_other_prefix.pop()[0]
                # Strip relative prefixes from dependency.yaml inputs
                if other_prefix.startswith('../'):
                    return other.removeprefix(other_prefix) == self.name
                else:
                    return other == self.id
            else:
                return other == self.name
        return self.id == other.id

    def merge(self, other: "Repo"):
        if self.progress >= PROGRESS.DONE:
            # Anything marked as done should only ever be updated
            # with specific intention, not blindly merged.
            return
        for prop, val in vars(other).items():
            if val:
                self.__setattr__(prop, val)