File: conf_helpers.py

package info (click to toggle)
rsyslog 8.2512.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 30,464 kB
  • sloc: ansic: 123,809; sh: 42,109; makefile: 5,962; javascript: 1,842; python: 1,222; lex: 607; yacc: 193; perl: 162; sql: 103; tcl: 9; ruby: 2
file content (159 lines) | stat: -rw-r--r-- 5,619 bytes parent folder | download
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# Helper functions for main conf.py Sphinx build configuration

import datetime
import re
import shutil
import subprocess

# Resolve git executable to avoid relying on PATH (Bandit B607)
#
# Note: Building docs from a release/dist tarball should NOT require git.
# If git is unavailable or the working directory is not a git repository,
# we degrade gracefully and return placeholder values so Sphinx can build.
GIT_EXE = shutil.which("git")


def _run_git(args):
    """Run a git command and return decoded stdout, or None on failure.

    We explicitly handle environments where git is missing or the current
    directory is not a git repository (e.g., dist tarball builds).
    """
    if GIT_EXE is None:
        return None
    try:
        output_bytes = subprocess.check_output([GIT_EXE] + args)
        return output_bytes.decode("utf-8").strip()
    except (subprocess.CalledProcessError, FileNotFoundError):
        return None

def get_current_branch():
    """Return the current branch we are on or the branch that the detached head
    is pointed to"""

    current_branch = _run_git(['rev-parse', '--abbrev-ref', 'HEAD'])
    if not current_branch:
        return 'unknown'

    if current_branch == 'HEAD':
        # This means we are operating in a detached head state, will need to
        # parse out the branch that the commit is from.

        # Decode "bytes" type to UTF-8 string to avoid Python 3 error:
        # "TypeError: a bytes-like object is required, not 'str'""
        # https://docs.python.org/3/library/stdtypes.html#bytes.decode
        branches_output = _run_git(['branch'])
        if not branches_output:
            return 'unknown'
        branches = branches_output.split('\n')
        for branch in branches:

            # Git marks the current branch, or in this case the branch
            # we are currently detached from with an asterisk
            if '*' in branch:
                # Split on the remote/branch separator, grab the
                # last entry in the list and then strip off the trailing
                # parentheis
                detached_from_branch = branch.split('/')[-1].replace(')', '')

                return detached_from_branch

    else:
        # The assumption is that we are on a branch at this point. Return that.
        return current_branch


def get_current_stable_version():
    """Return the current X.Y stable version number from the latest git tag"""

    def get_latest_tag():
        """"Helper function: Return the latest git tag"""

        git_tag_output = _run_git(['tag', '--list', 'v*'])
        if not git_tag_output:
            return None

        git_tag_list = re.sub('[A-Za-z]', '', git_tag_output).split('\n')
        git_tag_list.sort(key=lambda s: [int(u) for u in s.split('.')])

        # The latest tag is the last in the list
        git_tag_latest = git_tag_list[-1]

        return git_tag_latest

    latest_tag = get_latest_tag()
    if not latest_tag:
        # Fallback for non-git environments (e.g., release tarball builds)
        return '0.0'

    # Return 'X.Y' from 'X.Y.Z'
    return latest_tag[:-2]


def get_next_stable_version():
    """Return the next stable version"""

    current_version = get_current_stable_version()

    # Break apart 'x.y' value, increment y and then concatenate into 'x.y' again
    try:
        next_version = "{}.{}".format(
            int(current_version.split('.')[0]),
            int(current_version.split('.')[1]) + 1
        )
    except (IndexError, ValueError):
        # Conservative fallback if parsing fails
        next_version = '0.1'

    return next_version


def get_current_commit_hash():
    """Return commit hash string"""

    commit_hash = _run_git(['log', '--pretty=format:%h', 'HEAD', '-n1'])
    return commit_hash if commit_hash else 'nogit'


def get_release_string(release_type, release_string_detail, version):
    """Return a release string representing the type of build. Verbose for
    dev builds and with sparse version info for release builds"""

    if release_type == "dev":

        # Used in dev builds
        DATE = datetime.date.today()
        TODAY = DATE.strftime('%Y%m%d')

        # The detailed release string is too long for the rsyslog.com 'better'
        # theme and perhaps too long for other themes as well, so we set the
        # level to 'simple' by default (to be explicit) and
        # allow overriding by command-line if desired.
        if release_string_detail == "simple":

            # 'rsyslog' prefix is already set via 'project' variable in conf.py
            # HASH
            # 'docs' string suffix is already ...
            release_string = "{}".format(get_current_commit_hash())
        elif release_string_detail == "detailed":

            # The verbose variation of the release string. This was previously
            # the default when using the 'classic' theme, but proves to be
            # too long when viewed using alternate themes. If requested
            # via command-line override this format is used.
            release_string = "{}-{}-{}-{}".format(
                get_next_stable_version(),
                get_current_branch(),
                TODAY,
                get_current_commit_hash()
            )
        else:
            # This means that someone set a value that we do not
            # have a format defined for. Return an error string instead
            # to help make it clear what happened.
            release_string = "invalid value for release_string_detail"

    else:
        release_string = "{}".format(version)

    return release_string