Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions .github/workflows/main-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: Deploy Lakeflow Framework Documentation to GitHub Pages
on:
push:
branches: ["main"]
tags: ["v*.*.*"]

permissions:
contents: read
Expand All @@ -20,8 +21,11 @@ jobs:
group: databricks-solutions-protected-runner-group
labels: linux-ubuntu-latest
steps:
- name: Checkout
- name: Checkout (full history + tags)
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0 # required by sphinx-multiversion to walk all refs
fetch-tags: true

- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
Expand All @@ -38,10 +42,34 @@ jobs:
python -m pip install --upgrade pip
pip install --require-hashes --no-deps -r requirements-docs.lock

- name: Build HTML
- name: Select versions to build
# Writes SMV_TAG_WHITELIST to $GITHUB_ENV so the build step picks it up.
run: python docs/select_versions.py

- name: Build versioned HTML
# sphinx-multiversion writes one subdirectory per version under the output dir.
# The SMV_TAG_WHITELIST env var (set above) controls which tags are built.
run: |
sphinx-multiversion docs docs/build/html

- name: Create root redirect to main (dev) docs
# Visitors who land at the bare GitHub Pages URL are forwarded to the
# "main" branch build. Adjust the meta-refresh target if you later
# publish a dedicated "stable" alias.
run: |
cd docs
make html
cat > docs/build/html/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Lakeflow Framework Docs</title>
<meta http-equiv="refresh" content="0; url=main/">
</head>
<body>
<p>Redirecting to <a href="main/">latest docs</a>…</p>
</body>
</html>
EOF

- name: Setup Pages
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
Expand Down
23 changes: 23 additions & 0 deletions docs/_templates/versions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{# Version switcher injected into the RTD sidebar by sphinx-multiversion.
Rendered for every page; hidden via CSS when there is only one version. #}
{%- if current_version %}
<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="versions">
<span class="rst-current-version" data-toggle="rst-current-version">
<span class="fa fa-book"> </span>
{{ current_version.name }}
<span class="fa fa-caret-down"></span>
</span>
<div class="rst-other-versions">
<dl>
<dt>Versions</dt>
{%- for item in versions %}
{%- if item.url %}
<dd><a href="{{ item.url }}">{{ item.name }}</a></dd>
{%- else %}
<dd>{{ item.name }}</dd>
{%- endif %}
{%- endfor %}
</dl>
</div>
</div>
{%- endif %}
53 changes: 46 additions & 7 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,17 @@
project = 'Lakeflow Framework'
copyright = '2025, Databricks'
author = 'Erik Seefeld, Haille Woldegebriel, Amin Movahed'
release = '0.4.0'

# Read version from the VERSION file at the repo root so conf.py never
# needs a manual update when a release is cut.
_here = os.path.dirname(os.path.abspath(__file__))
_version_file = os.path.join(_here, '..', 'VERSION')
try:
release = open(_version_file).read().strip().lstrip('v')
except FileNotFoundError:
release = 'dev'

version = '.'.join(release.split('.')[:2]) # e.g. "0.15" from "0.15.3"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand All @@ -23,12 +33,34 @@
'sphinx_design',
'myst_parser',
'sphinx_tabs.tabs',
'custom_markdown_builder'
'custom_markdown_builder',
'sphinx_multiversion',
]

templates_path = ['source/_templates']
templates_path = ['_templates', 'source/_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']

# -- sphinx-multiversion -----------------------------------------------------
# Tags to build: set via the SMV_TAG_WHITELIST env var by the CI helper
# script (docs/select_versions.py). Falls back to matching all semver tags
# so local builds still work without running the helper first.
smv_tag_whitelist = os.environ.get('SMV_TAG_WHITELIST', r'^v\d+\.\d+\.\d+$')

# Always build docs from main as the "dev" version.
smv_branch_whitelist = r'^main$'

# Only consider the upstream remote.
smv_remote_whitelist = r'^origin$'

# Pattern that identifies a ref as a "released" (non-prerelease) version.
smv_released_pattern = r'^v\d+\.\d+\.\d+$'

# The version shown when visitors arrive without an explicit version in the URL.
smv_latest_version = 'main'

# Rebuild all versions together rather than just the latest.
smv_rebuild_tags = True

# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
import sphinx_rtd_theme
Expand All @@ -39,10 +71,17 @@
}

html_theme = "sphinx_rtd_theme"
# html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# html_theme_options = {
# 'collapse_navigation': True
# }

# Inject the version switcher into the sidebar.
html_sidebars = {
'**': [
'versions.html',
'globaltoc.html',
'relations.html',
'sourcelink.html',
'searchbox.html',
],
}

html_static_path = ['source/_static']
html_css_files = [
Expand Down
102 changes: 102 additions & 0 deletions docs/select_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/env python3
"""Select which git tags to include in the versioned docs build.

Strategy
--------
* Latest patch of each *major* version — always included so that users on any
major series always have an up-to-date docs URL.
* The 5 most recent *minor* series (regardless of major) — keeps the switcher
short while still covering recent history.

The union of those two sets is written as a ``smv_tag_whitelist`` regex to
``$GITHUB_ENV`` (so the next workflow step can read it) and also printed to
stdout for local debugging.

Usage
-----
python docs/select_versions.py

The regex is then picked up by ``conf.py`` via::

smv_tag_whitelist = os.environ.get('SMV_TAG_WHITELIST', r'^v\\d+\\.\\d+\\.\\d+$')
"""
from __future__ import annotations

import os
import re
import subprocess
import sys

TAG_RE = re.compile(r"^v?(\d+)\.(\d+)\.(\d+)$")


def _get_tags() -> list[str]:
result = subprocess.run(
["git", "tag", "--list", "v*"],
capture_output=True,
text=True,
check=True,
)
return [t.strip() for t in result.stdout.splitlines() if t.strip()]


def _parse(tag: str) -> tuple[int, int, int, str] | None:
m = TAG_RE.match(tag)
return (int(m.group(1)), int(m.group(2)), int(m.group(3)), tag) if m else None


def select_versions(tags: list[str]) -> list[str]:
parsed = [p for t in tags if (p := _parse(t))]
if not parsed:
return []

# Best (highest) patch within each (major, minor) series.
best_per_minor: dict[tuple[int, int], tuple[int, str]] = {}
for major, minor, patch, tag in parsed:
key = (major, minor)
if key not in best_per_minor or patch > best_per_minor[key][0]:
best_per_minor[key] = (patch, tag)

# Latest minor series per major — ensures every major has representation.
best_per_major: dict[int, tuple[int, str]] = {}
for (major, minor), (patch, tag) in best_per_minor.items():
if major not in best_per_major or minor > best_per_major[major][0]:
best_per_major[major] = (minor, tag)

always_include = {tag for _, tag in best_per_major.values()}

# Last 5 distinct minor series overall (sorted newest-first).
sorted_minor_keys = sorted(best_per_minor.keys(), reverse=True)
last_5 = {best_per_minor[k][1] for k in sorted_minor_keys[:5]}

return sorted(always_include | last_5)


def tags_to_regex(tags: list[str]) -> str:
if not tags:
return r"^$"
escaped = [re.escape(t) for t in tags]
return r"^(" + "|".join(escaped) + r")$"


def main() -> None:
tags = _get_tags()
selected = select_versions(tags)
pattern = tags_to_regex(selected)

print(f"All tags found : {tags}", file=sys.stderr)
print(f"Selected tags : {selected}", file=sys.stderr)
print(f"SMV_TAG_WHITELIST={pattern}", file=sys.stderr)

github_env = os.environ.get("GITHUB_ENV")
if github_env:
with open(github_env, "a") as fh:
fh.write(f"SMV_TAG_WHITELIST={pattern}\n")
print(f"Written to $GITHUB_ENV ({github_env})", file=sys.stderr)

# Print pattern to stdout so callers can capture it if needed.
print(pattern)


if __name__ == "__main__":
main()
3 changes: 2 additions & 1 deletion requirements-docs.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
myst-parser==5.0.0
nbsphinx==0.9.8
sphinx==9.0.4
gitsphinx-autoapi==3.8.0
sphinx-autoapi==3.8.0
sphinx-markdown-builder==0.6.10
sphinx-copybutton==0.5.2
sphinx_rtd_theme==3.1.0
sphinx-design==0.7.0
sphinxcontrib-spelling==8.0.2
docutils==0.22.0
sphinx-tabs==3.5.0
sphinx-multiversion==0.2.4