File: doc-make-git-optional-for-Sphinx-builds-dist-tarballs.patch

package info (click to toggle)
rsyslog 8.2510.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 32,100 kB
  • sloc: ansic: 121,163; sh: 41,318; makefile: 4,822; javascript: 1,842; python: 1,140; lex: 607; yacc: 193; perl: 162; sql: 103; tcl: 9; ruby: 2
file content (156 lines) | stat: -rw-r--r-- 6,274 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
From: Andre Lorbach <alorbach@adiscon.com>
Date: Mon, 20 Oct 2025 12:43:04 +0000
Subject: doc: make git optional for Sphinx builds (dist tarballs)

Building documentation from the dist tarball failed with a RuntimeError
("git executable not found") thrown during Sphinx startup. The helper
module hard-required git even when no .git repo is present.

This change replaces the hard requirement with a safe wrapper that
returns placeholders when git or a repository is unavailable. This lets
Sphinx complete when building from release/dist sources, while keeping
dev builds unchanged.

Details:
- conf_helpers.py: introduce _run_git(); return 'unknown'/'nogit' for
  branch/commit and '0.0' for tag-derived versions when git is absent.
- Harden next-version parsing for non-git environments.
- conf.py already guards dev-only dynamic strings behind .git presence.

Fixes: https://github.com/rsyslog/rsyslog/issues/6253

Co-authored-by: alorbach <alorbach@adiscon.com>
(cherry picked from commit 18c8eed4858c4fb5f04018dd8b12148822129069)
---
 doc/source/conf_helpers.py | 62 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 43 insertions(+), 19 deletions(-)

diff --git a/doc/source/conf_helpers.py b/doc/source/conf_helpers.py
index 70af780..a6aa4e7 100644
--- a/doc/source/conf_helpers.py
+++ b/doc/source/conf_helpers.py
@@ -6,17 +6,34 @@ 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")
-if GIT_EXE is None:
-    raise RuntimeError("git executable not found")
+
+
+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 = subprocess.check_output(
-        [GIT_EXE, 'rev-parse', '--abbrev-ref', "HEAD"]
-        ).decode("utf-8").strip()
+    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
@@ -25,7 +42,10 @@ def get_current_branch():
         # 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 = subprocess.check_output([GIT_EXE, 'branch']).decode('utf-8').split('\n')
+        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
@@ -49,9 +69,9 @@ def get_current_stable_version():
     def get_latest_tag():
         """"Helper function: Return the latest git tag"""
 
-        git_tag_output = subprocess.check_output(
-            [GIT_EXE, 'tag', '--list', "v*"]
-            ).decode("utf-8").strip()
+        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('.')])
@@ -62,6 +82,9 @@ def get_current_stable_version():
         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]
@@ -73,10 +96,14 @@ def get_next_stable_version():
     current_version = get_current_stable_version()
 
     # Break apart 'x.y' value, increment y and then concatenate into 'x.y' again
-    next_version = "{}.{}".format(
-        int(current_version[:1]),
-        int(current_version[-2:]) + 1
+    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
 
@@ -84,9 +111,8 @@ def get_next_stable_version():
 def get_current_commit_hash():
     """Return commit hash string"""
 
-    commit_hash = subprocess.check_output([GIT_EXE, 'log', '--pretty=format:%h', 'HEAD', '-n1']).decode("utf-8")
-
-    return commit_hash
+    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):
@@ -108,9 +134,7 @@ def get_release_string(release_type, release_string_detail, version):
             # 'rsyslog' prefix is already set via 'project' variable in conf.py
             # HASH
             # 'docs' string suffix is already ...
-            release_string = "{}".format(
-                get_current_commit_hash()
-                )
+            release_string = "{}".format(get_current_commit_hash())
         elif release_string_detail == "detailed":
 
             # The verbose variation of the release string. This was previously
@@ -122,7 +146,7 @@ def get_release_string(release_type, release_string_detail, 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