From 28b9dbd634fbdd3e7ff7278a657e4353cc2bad97 Mon Sep 17 00:00:00 2001 From: ambujsingh Date: Mon, 18 May 2026 15:00:54 +0530 Subject: [PATCH] Docs(sphinx): configure GitHub Pages added DOCS_BASE_URL-based GitHub Pages URL configuration include version flyout CSS and JS assets in Sphinx output --- .github/workflows/deploy_docs.yml | 211 +++++++++++++++++++++ docs/sphinx/_gh_pages/index.html | 12 ++ docs/sphinx/_static/css/version_flyout.css | 125 ++++++++++++ docs/sphinx/_static/js/version_flyout.js | 89 +++++++++ docs/sphinx/conf.py | 17 +- 5 files changed, 453 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/deploy_docs.yml create mode 100644 docs/sphinx/_gh_pages/index.html create mode 100644 docs/sphinx/_static/css/version_flyout.css create mode 100644 docs/sphinx/_static/js/version_flyout.js diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml new file mode 100644 index 000000000..6dd11c614 --- /dev/null +++ b/.github/workflows/deploy_docs.yml @@ -0,0 +1,211 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# Workflow to build and deploy Sphinx documentation to GitHub Pages with versioning +name: Deploy Documentation to GitHub Pages + +on: + push: + branches: [main] + tags: ['v*'] + pull_request: + branches: [main] + paths: + - 'docs/**' + - '.github/workflows/deploy_docs.yml' + workflow_dispatch: + +permissions: + contents: write # needed for uploading release assets + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +env: + ANDROID_HOME: "" + ANDROID_SDK_ROOT: "" + +jobs: + build-docs: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + + - name: Set up Bazel + uses: bazel-contrib/setup-bazel@0.14.0 + with: + bazelisk-cache: true + disk-cache: ${{ github.workflow }} + repository-cache: true + + - name: Determine version + id: version + run: | + if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then + VERSION="${GITHUB_REF#refs/tags/}" + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "is_tag=true" >> "$GITHUB_OUTPUT" + echo "should_deploy=true" >> "$GITHUB_OUTPUT" + elif [[ "${GITHUB_REF}" == refs/heads/main ]]; then + echo "version=latest" >> "$GITHUB_OUTPUT" + echo "is_tag=false" >> "$GITHUB_OUTPUT" + echo "should_deploy=true" >> "$GITHUB_OUTPUT" + else + # Feature branch or PR - deploy to preview/ + BRANCH_NAME="${GITHUB_REF#refs/heads/}" + BRANCH_NAME="${BRANCH_NAME//\//-}" + echo "version=preview/${BRANCH_NAME}" >> "$GITHUB_OUTPUT" + echo "is_tag=false" >> "$GITHUB_OUTPUT" + echo "should_deploy=true" >> "$GITHUB_OUTPUT" + fi + + - name: Build Sphinx documentation + env: + DOCS_BASE_URL: "https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}" + DOCS_VERSION: ${{ steps.version.outputs.version }} + run: | + bazel build //docs/sphinx:sphinx_doc + + - name: Prepare documentation output + run: | + mkdir -p docs_output + cp -r bazel-bin/docs/sphinx/sphinx_doc/html/* docs_output/ || \ + cp -r bazel-out/k8-fastbuild/bin/docs/sphinx/sphinx_doc/html/* docs_output/ || true + # Fix read-only permissions from Bazel output + chmod -R u+w docs_output/ + # Prevent Jekyll from ignoring _static directories + touch docs_output/.nojekyll + # Inject version flyout CSS and JS into all HTML files + # Use shared path so all versions use the same files + REPO_NAME="${{ github.event.repository.name }}" + CSS_TAG="" + JS_TAG="" + find docs_output -name '*.html' -exec sed -i \ + "s||${CSS_TAG}\n|" {} + + find docs_output -name '*.html' -exec sed -i \ + "s||${JS_TAG}\n|" {} + + + - name: Verify build output + run: | + if [ ! -f docs_output/index.html ]; then + echo "::error::Documentation build failed - no index.html found" + exit 1 + fi + echo "Documentation built successfully" + echo "Files generated:" + find docs_output -type f | head -20 + + - name: Upload docs to release + if: startsWith(github.ref, 'refs/tags/v') + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ steps.version.outputs.version }}" + tar czf "docs-${VERSION}.tar.gz" -C docs_output . + gh release create "${VERSION}" --draft=false --notes "Release ${VERSION}" 2>/dev/null || true + gh release upload "${VERSION}" "docs-${VERSION}.tar.gz" --clobber + + - name: Assemble publish tree + if: github.event_name != 'pull_request' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ steps.version.outputs.version }}" + IS_TAG="${{ steps.version.outputs.is_tag }}" + REPO_URL="https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}" + + mkdir -p publish + + # Place current build + mkdir -p "publish/${VERSION}" + cp -a docs_output/. "publish/${VERSION}/" + + # Download previously released versions + for tag in $(gh api "repos/${{ github.repository }}/releases" --paginate --jq '.[].tag_name' | grep '^v'); do + if [ "${tag}" = "${VERSION}" ]; then + continue + fi + if gh release download "${tag}" --pattern "docs-${tag}.tar.gz" --dir /tmp 2>/dev/null; then + mkdir -p "publish/${tag}" + tar xzf "/tmp/docs-${tag}.tar.gz" -C "publish/${tag}" + rm -f "/tmp/docs-${tag}.tar.gz" + echo "Restored ${tag} from release artifact" + else + echo "::warning::No docs artifact found for release ${tag}" + fi + done + + # stable = newest tagged version that has docs + if [[ "${IS_TAG}" == "true" ]]; then + rm -rf publish/stable + cp -a docs_output/. publish/stable/ + else + NEWEST_TAG=$(find publish -maxdepth 1 -mindepth 1 -type d -name "v*" -printf '%f\n' | sort -rV | head -1) + if [ -n "${NEWEST_TAG}" ]; then + rm -rf publish/stable + cp -a "publish/${NEWEST_TAG}/." publish/stable/ + fi + fi + + # Shared assets + mkdir -p publish/_shared/css publish/_shared/js + cp docs/sphinx/_static/css/version_flyout.css publish/_shared/css/ + cp docs/sphinx/_static/js/version_flyout.js publish/_shared/js/ + + # Root index and Jekyll bypass + cp docs/sphinx/_gh_pages/index.html publish/index.html + touch publish/.nojekyll + + # Generate switcher.json from actual directory contents + echo "[" > publish/switcher.json + echo " {\"name\": \"latest\", \"version\": \"latest\", \"url\": \"${REPO_URL}/latest/\"}," >> publish/switcher.json + + if [ -d "publish/stable" ]; then + echo " {\"name\": \"stable\", \"version\": \"stable\", \"url\": \"${REPO_URL}/stable/\", \"preferred\": true}," >> publish/switcher.json + fi + + for dir in $(find publish -maxdepth 1 -mindepth 1 -type d -name "v*" | sort -rV); do + ver=$(basename "$dir") + echo " {\"name\": \"${ver}\", \"version\": \"${ver}\", \"url\": \"${REPO_URL}/${ver}/\"}," >> publish/switcher.json + done + + sed -i '$ s/,$//' publish/switcher.json + echo "]" >> publish/switcher.json + + - name: Upload Pages artifact + if: github.event_name != 'pull_request' + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1 + with: + path: publish + + - name: Print preview URL + if: github.event_name != 'pull_request' + run: | + REPO_URL="https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}" + echo "::notice::Documentation deployed to: ${REPO_URL}/${{ steps.version.outputs.version }}/" + + deploy-pages: + needs: build-docs + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/docs/sphinx/_gh_pages/index.html b/docs/sphinx/_gh_pages/index.html new file mode 100644 index 000000000..95695270a --- /dev/null +++ b/docs/sphinx/_gh_pages/index.html @@ -0,0 +1,12 @@ + + + + + Redirecting to latest documentation... + + + + +

Redirecting to latest documentation...

+ + diff --git a/docs/sphinx/_static/css/version_flyout.css b/docs/sphinx/_static/css/version_flyout.css new file mode 100644 index 000000000..25cbb3df0 --- /dev/null +++ b/docs/sphinx/_static/css/version_flyout.css @@ -0,0 +1,125 @@ +/* RTD-style version flyout panel */ +.version-flyout { + position: fixed; + bottom: 0; + right: 20px; + z-index: 9999; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + font-size: 14px; +} + +.version-flyout__toggle { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 16px; + background: #1f1f2e; + color: #fff; + border: none; + border-radius: 6px 6px 0 0; + cursor: pointer; + font-size: 14px; + font-weight: 500; +} + +.version-flyout__toggle:hover { + background: #2a2a3d; +} + +.version-flyout__toggle .flyout-icon { + font-size: 16px; +} + +.version-flyout__toggle .flyout-current { + color: #27ae60; + font-weight: bold; +} + +.version-flyout__toggle .flyout-arrow { + margin-left: auto; + transition: transform 0.2s; +} + +.version-flyout__toggle.active .flyout-arrow { + transform: rotate(180deg); +} + +.version-flyout__panel { + display: none; + background: #1f1f2e; + color: #ccc; + padding: 16px; + border-radius: 6px 6px 0 0; + min-width: 260px; + box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.3); +} + +.version-flyout__panel.open { + display: block; +} + +.version-flyout__panel h4 { + color: #fff; + margin: 0 0 8px 0; + font-size: 13px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.version-flyout__panel .flyout-section { + margin-bottom: 12px; +} + +.version-flyout__panel .flyout-versions { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.version-flyout__panel .flyout-versions a { + color: #55b4d4; + text-decoration: none; + padding: 2px 8px; + border-radius: 3px; + font-size: 13px; +} + +.version-flyout__panel .flyout-versions a:hover { + background: rgba(255, 255, 255, 0.1); + color: #7dd3fc; +} + +.version-flyout__panel .flyout-versions a.active { + color: #27ae60; + font-weight: bold; +} + +.version-flyout__panel .flyout-links { + border-top: 1px solid #333; + padding-top: 10px; + margin-top: 10px; +} + +.version-flyout__panel .flyout-links a { + display: inline-block; + color: #55b4d4; + text-decoration: none; + margin-right: 12px; + font-size: 13px; +} + +.version-flyout__panel .flyout-links a:hover { + color: #7dd3fc; +} + +.version-flyout__footer { + font-size: 11px; + color: #888; + margin-top: 10px; + text-align: center; +} + +.version-flyout__footer a { + color: #55b4d4; + text-decoration: none; +} diff --git a/docs/sphinx/_static/js/version_flyout.js b/docs/sphinx/_static/js/version_flyout.js new file mode 100644 index 000000000..6af9bf5b9 --- /dev/null +++ b/docs/sphinx/_static/js/version_flyout.js @@ -0,0 +1,89 @@ +/** + * RTD-style version flyout for GitHub Pages. + * Reads versions from switcher.json and renders a floating panel. + */ +(function () { + "use strict"; + + // Determine base URL and current version from the URL path + var pathParts = window.location.pathname.split("/").filter(Boolean); + // Expect: /communication//... + var repoName = pathParts[0] || ""; + var currentVersion = pathParts[1] || "latest"; + // Handle preview/ pattern + if (currentVersion === "preview" && pathParts[2]) { + currentVersion = "preview/" + pathParts[2]; + } + + var baseUrl = + window.location.origin + "/" + repoName; + var switcherUrl = baseUrl + "/switcher.json"; + + function createFlyout(versions) { + var container = document.createElement("div"); + container.className = "version-flyout"; + + // Build version links + var versionLinks = versions + .map(function (v) { + var activeClass = v.version === currentVersion ? ' class="active"' : ""; + return '" + v.name + ""; + }) + .join("\n"); + + container.innerHTML = + '
' + + '
' + + "

Versions

" + + '
' + + versionLinks + + "
" + + "
" + + ' " + + ' " + + "
" + + '"; + + document.body.appendChild(container); + + // Toggle behavior + var toggle = document.getElementById("flyout-toggle"); + var panel = document.getElementById("flyout-panel"); + toggle.addEventListener("click", function () { + panel.classList.toggle("open"); + toggle.classList.toggle("active"); + }); + } + + function getGitHubRepo() { + // Try to extract from meta or fallback + var metaRepo = document.querySelector('meta[name="github-repo"]'); + if (metaRepo) return metaRepo.getAttribute("content"); + // Fallback based on GitHub Pages domain + var host = window.location.hostname; + var user = host.split(".")[0]; + return user + "/" + repoName; + } + + // Fetch switcher.json and build the flyout + fetch(switcherUrl) + .then(function (response) { + if (!response.ok) throw new Error("switcher.json not found"); + return response.json(); + }) + .then(function (versions) { + createFlyout(versions); + }) + .catch(function (err) { + console.warn("Version flyout: Could not load switcher.json", err); + }); +})(); diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py index 04be6b9b0..0808c1bbc 100644 --- a/docs/sphinx/conf.py +++ b/docs/sphinx/conf.py @@ -28,6 +28,12 @@ author = 'Eclipse Foundation' release = '1.0.0' +# GitHub Pages base URL (update org/repo as needed) +GITHUB_PAGES_URL = os.environ.get( + 'DOCS_BASE_URL', + 'https://eclipse-score.github.io/communication' +) + # -- General configuration --- extensions = [ 'sphinx.ext.autodoc', # Auto-generate documentation from docstrings @@ -81,7 +87,7 @@ 'navbar_align': 'left', 'navbar_start': ['navbar-logo'], 'navbar_center': ['navbar-nav'], - 'navbar_end': ['navbar-icon-links', 'theme-switcher'], + 'navbar_end': ['version-switcher', 'navbar-icon-links', 'theme-switcher'], # Search configuration 'search_bar_text': 'Search documentation...', @@ -106,6 +112,12 @@ 'icon': 'fab fa-github', } ], + + # Version switcher configuration + 'switcher': { + 'json_url': f'{GITHUB_PAGES_URL}/switcher.json', + 'version_match': os.environ.get('DOCS_VERSION', 'latest'), + }, } # Add custom styling @@ -113,6 +125,9 @@ html_css_files = [ 'css/default_custom.css', ] +html_js_files = [] +# Note: version_flyout.css and version_flyout.js are injected by the +# deploy workflow via _shared/ paths so they load once across all versions. # -- Breathe configuration -- # Doxygen XML output path (provided by sphinx_docs_library)