Skip to content

Commit fb6a41a

Browse files
authored
fix(docs): resolve main branch ref on tag-triggered CI checkouts (#122)
Versioned docs builds failed when GitHub Pages workflow ran on a tag push because the checkout is detached HEAD with origin/main but no local main.
1 parent d474150 commit fb6a41a

2 files changed

Lines changed: 59 additions & 7 deletions

File tree

docs/scripts/build_versioned_docs.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,27 @@ def _run_capture(command: list[str], *, cwd: Path | None = None) -> str:
5050
return result.stdout.strip()
5151

5252

53+
def _resolve_git_ref(repo_root: Path, ref: str) -> str:
54+
"""Return a git ref that exists in the local clone.
55+
56+
Tag-triggered CI checkouts are often detached HEAD with ``origin/main`` but
57+
no local ``main`` branch. Branch-like refs try ``ref`` then ``origin/ref``.
58+
"""
59+
candidates = [ref]
60+
if not ref.startswith("refs/") and "/" not in ref:
61+
candidates.append(f"origin/{ref}")
62+
for candidate in candidates:
63+
probe = subprocess.run(
64+
["git", "rev-parse", "--verify", f"{candidate}^{{commit}}"],
65+
cwd=repo_root,
66+
capture_output=True,
67+
text=True,
68+
)
69+
if probe.returncode == 0:
70+
return candidate
71+
return ref
72+
73+
5374
def _repo_root(docs_dir: Path) -> Path:
5475
return docs_dir.parent
5576

@@ -123,13 +144,15 @@ def _conf_ref_for(version_name: str) -> str | None:
123144

124145
def _release_date_for_ref(repo_root: Path, ref: str) -> str:
125146
# ISO-style date (YYYY-MM-DD) for deterministic display in docs headers.
126-
return _run_capture(["git", "log", "-1", "--format=%cs", ref], cwd=repo_root)
147+
resolved = _resolve_git_ref(repo_root, ref)
148+
return _run_capture(["git", "log", "-1", "--format=%cs", resolved], cwd=repo_root)
127149

128150

129151
def _version_for_ref(repo_root: Path, ref: str, fallback: str) -> str:
130152
# Resolve display version from the VERSION file in the selected ref.
153+
resolved = _resolve_git_ref(repo_root, ref)
131154
try:
132-
raw = _run_capture(["git", "show", f"{ref}:VERSION"], cwd=repo_root)
155+
raw = _run_capture(["git", "show", f"{resolved}:VERSION"], cwd=repo_root)
133156
value = raw.splitlines()[0].strip().lstrip("v")
134157
return value or fallback
135158
except Exception:
@@ -178,8 +201,9 @@ def _ensure_worktree(
178201
path = worktrees_root / name
179202
if path.exists():
180203
return path
204+
resolved = _resolve_git_ref(repo_root, ref)
181205
try:
182-
_run(["git", "worktree", "add", "--detach", str(path), ref], cwd=repo_root)
206+
_run(["git", "worktree", "add", "--detach", str(path), resolved], cwd=repo_root)
183207
except subprocess.CalledProcessError:
184208
# Recover from a previous run that deleted the dir without unregistering.
185209
subprocess.run(
@@ -189,7 +213,7 @@ def _ensure_worktree(
189213
capture_output=True,
190214
)
191215
_run(
192-
["git", "worktree", "add", "--force", "--detach", str(path), ref],
216+
["git", "worktree", "add", "--force", "--detach", str(path), resolved],
193217
cwd=repo_root,
194218
)
195219
return path
@@ -319,7 +343,8 @@ def main() -> None:
319343
worktrees_root.mkdir(parents=True, exist_ok=True)
320344

321345
tags = _selected_tags(repo_root)
322-
versions: list[dict[str, str]] = [{"name": "current", "ref": "main"}]
346+
main_ref = _resolve_git_ref(repo_root, "main")
347+
versions: list[dict[str, str]] = [{"name": "current", "ref": main_ref}]
323348
versions.extend({"name": t, "ref": t} for t in tags)
324349
if args.preview:
325350
preview_branch = _current_branch(repo_root)
@@ -415,7 +440,7 @@ def main() -> None:
415440
(output_root / "index.html").write_text(index_html, encoding="utf-8")
416441

417442
# Basic crawl artifacts for search engines.
418-
sitemap_entries: list[tuple[str, str]] = [("", _release_date_for_ref(repo_root, "main"))]
443+
sitemap_entries: list[tuple[str, str]] = [("", _release_date_for_ref(repo_root, main_ref))]
419444
for item in links:
420445
sitemap_entries.append((f"{item['name']}/index.html", item.get("release_date", "")))
421446

tests/test_select_versions.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,46 @@
22

33
from __future__ import annotations
44

5+
import subprocess
56
import sys
67
from pathlib import Path
78

89
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "docs" / "scripts"))
910

10-
from build_versioned_docs import _superset_versions
11+
from build_versioned_docs import _resolve_git_ref, _superset_versions
1112
from select_versions import MIN_MINOR_MAJOR_0, select_versions
1213

1314

1415
def _tags(*versions: str) -> list[str]:
1516
return [f"v{version}" if not version.startswith("v") else version for version in versions]
1617

1718

19+
def test_resolve_git_ref_uses_origin_main_when_local_main_missing(tmp_path):
20+
origin = tmp_path / "origin.git"
21+
work = tmp_path / "work"
22+
work.mkdir()
23+
subprocess.run(["git", "init", "--bare", str(origin)], check=True, capture_output=True)
24+
subprocess.run(["git", "init"], cwd=work, check=True, capture_output=True)
25+
subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=work, check=True)
26+
subprocess.run(["git", "config", "user.name", "Test User"], cwd=work, check=True)
27+
(work / "VERSION").write_text("0.21.0\n", encoding="utf-8")
28+
subprocess.run(["git", "add", "VERSION"], cwd=work, check=True, capture_output=True)
29+
subprocess.run(["git", "commit", "-m", "init"], cwd=work, check=True, capture_output=True)
30+
subprocess.run(["git", "branch", "-M", "main"], cwd=work, check=True, capture_output=True)
31+
subprocess.run(
32+
["git", "remote", "add", "origin", str(origin)],
33+
cwd=work,
34+
check=True,
35+
capture_output=True,
36+
)
37+
subprocess.run(["git", "push", "-u", "origin", "main"], cwd=work, check=True, capture_output=True)
38+
subprocess.run(["git", "checkout", "--detach", "HEAD"], cwd=work, check=True, capture_output=True)
39+
subprocess.run(["git", "branch", "-D", "main"], cwd=work, check=True, capture_output=True)
40+
41+
assert _resolve_git_ref(work, "main") == "origin/main"
42+
assert _resolve_git_ref(work, "v9.9.9") == "v9.9.9"
43+
44+
1845
def test_major_0_includes_minors_back_to_0_12_latest_patch_only():
1946
tags = _tags(
2047
"0.16.0",

0 commit comments

Comments
 (0)