From ed00c0c58efa756e361c09b294040b50f3bd9fdf Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Sun, 22 Mar 2026 07:44:34 -0400 Subject: [PATCH 1/3] feat: generate index.html from template with styling - Add HTML template with light/dark mode, version labels, and repo link - Add generate_index.py script using string.Template - Replace inline HTML generation in docs-release workflow Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/scripts/generate_index.py | 57 ++++++++++++++++++++++++++++++ .github/templates/index.html | 33 +++++++++++++++++ .github/workflows/docs-release.yml | 21 +++-------- 3 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 .github/scripts/generate_index.py create mode 100644 .github/templates/index.html diff --git a/.github/scripts/generate_index.py b/.github/scripts/generate_index.py new file mode 100644 index 0000000..7780d38 --- /dev/null +++ b/.github/scripts/generate_index.py @@ -0,0 +1,57 @@ +'''Generate index.html listing all doc versions on gh-pages.''' +import re +import sys +from pathlib import Path +from string import Template + + +def find_versions() -> list[str]: + '''Find version directories (e.g., 0.2.1, 1.0.0) sorted descending.''' + dirs = [ + d.name for d in Path('.').iterdir() + if d.is_dir() and re.match(r'\d', d.name) + ] + dirs.sort(key=lambda v: [int(x) for x in v.split('.')], reverse=True) + return dirs + + +def find_latest_version(versions: list[str]) -> str | None: + '''Return the version that /latest/ points to, if any.''' + if not versions or not Path('latest').is_dir(): + return None + return versions[0] + + +def build_version_list(versions: list[str], latest_version: str | None) -> str: + '''Build HTML list items for all versions.''' + lines: list[str] = [] + if latest_version: + lines.append(f'
  • latest ({latest_version})
  • ') + if Path('dev').is_dir(): + lines.append('
  • dev
  • ') + for v in versions: + lines.append(f'
  • {v}
  • ') + return '\n'.join(lines) + + +def main() -> None: + repo_name = sys.argv[1] + repo_url = sys.argv[2] + + template_path = Path(__file__).parent / '../templates/index.html' + template = Template(template_path.read_text()) + + versions = find_versions() + latest_version = find_latest_version(versions) + version_list = build_version_list(versions, latest_version) + + html = template.substitute( + repo_name=repo_name, + repo_url=repo_url, + version_list=version_list, + ) + + Path('index.html').write_text(html) + + +main() diff --git a/.github/templates/index.html b/.github/templates/index.html new file mode 100644 index 0000000..ed997e3 --- /dev/null +++ b/.github/templates/index.html @@ -0,0 +1,33 @@ + + + + + $repo_name + + + +

    $repo_name documentation

    + + + + diff --git a/.github/workflows/docs-release.yml b/.github/workflows/docs-release.yml index ec9f3d5..b22576b 100644 --- a/.github/workflows/docs-release.yml +++ b/.github/workflows/docs-release.yml @@ -44,24 +44,11 @@ jobs: - name: Update latest and index.html run: | - TARGET="${{ steps.version.outputs.version }}" rm -rf latest cp -r "${{ steps.build.outputs.site-dir }}" latest - # Generate root index.html - REPO_NAME="${{ github.event.repository.name }}" - { - echo '' - echo "${REPO_NAME}" - echo "

    ${REPO_NAME} documentation

    ' - } > index.html + python .github/scripts/generate_index.py \ + "${{ github.event.repository.name }}" \ + "${{ github.event.repository.html_url }}" git add latest index.html - git diff --cached --quiet || git commit -m "Update latest and index.html for v${TARGET}" + git diff --cached --quiet || git commit -m "Update latest and index.html for v${{ steps.version.outputs.version }}" git push origin gh-pages From d90551c41e6f877c50da272834468a8ff0202348 Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Sun, 22 Mar 2026 07:51:16 -0400 Subject: [PATCH 2/3] refactor: extract update-latest composite action Move generate_index.py and index.html template into .github/actions/update-latest/ alongside the new action.yml. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/actions/update-latest/action.yml | 30 +++++++++++++++++++ .../update-latest}/generate_index.py | 2 +- .../update-latest}/index.html | 0 .github/workflows/docs-release.yml | 16 ++++------ 4 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 .github/actions/update-latest/action.yml rename .github/{scripts => actions/update-latest}/generate_index.py (95%) rename .github/{templates => actions/update-latest}/index.html (100%) diff --git a/.github/actions/update-latest/action.yml b/.github/actions/update-latest/action.yml new file mode 100644 index 0000000..ad38cd0 --- /dev/null +++ b/.github/actions/update-latest/action.yml @@ -0,0 +1,30 @@ +name: Update latest and index.html +description: Copy built site to latest/, regenerate index.html, commit and push + +inputs: + site-dir: + description: Path to the built site + required: true + repo-name: + description: Repository name for the index page title + required: true + repo-url: + description: Repository URL for the GitHub link + required: true + version: + description: Version string for the commit message + required: true + +runs: + using: composite + steps: + - shell: bash + run: | + rm -rf latest + cp -r "${{ inputs.site-dir }}" latest + python "${{ github.action_path }}/generate_index.py" \ + "${{ inputs.repo-name }}" \ + "${{ inputs.repo-url }}" + git add latest index.html + git diff --cached --quiet || git commit -m "Update latest and index.html for v${{ inputs.version }}" + git push origin gh-pages diff --git a/.github/scripts/generate_index.py b/.github/actions/update-latest/generate_index.py similarity index 95% rename from .github/scripts/generate_index.py rename to .github/actions/update-latest/generate_index.py index 7780d38..7bca076 100644 --- a/.github/scripts/generate_index.py +++ b/.github/actions/update-latest/generate_index.py @@ -38,7 +38,7 @@ def main() -> None: repo_name = sys.argv[1] repo_url = sys.argv[2] - template_path = Path(__file__).parent / '../templates/index.html' + template_path = Path(__file__).parent / 'index.html' template = Template(template_path.read_text()) versions = find_versions() diff --git a/.github/templates/index.html b/.github/actions/update-latest/index.html similarity index 100% rename from .github/templates/index.html rename to .github/actions/update-latest/index.html diff --git a/.github/workflows/docs-release.yml b/.github/workflows/docs-release.yml index b22576b..628148a 100644 --- a/.github/workflows/docs-release.yml +++ b/.github/workflows/docs-release.yml @@ -42,13 +42,9 @@ jobs: target: ${{ steps.version.outputs.version }} commit-message: Deploy docs for v${{ steps.version.outputs.version }} - - name: Update latest and index.html - run: | - rm -rf latest - cp -r "${{ steps.build.outputs.site-dir }}" latest - python .github/scripts/generate_index.py \ - "${{ github.event.repository.name }}" \ - "${{ github.event.repository.html_url }}" - git add latest index.html - git diff --cached --quiet || git commit -m "Update latest and index.html for v${{ steps.version.outputs.version }}" - git push origin gh-pages + - uses: ./.github/actions/update-latest + with: + site-dir: ${{ steps.build.outputs.site-dir }} + repo-name: ${{ github.event.repository.name }} + repo-url: ${{ github.event.repository.html_url }} + version: ${{ steps.version.outputs.version }} From f7a6cae6431a7e0f460fa977031633cb3e3d59bc Mon Sep 17 00:00:00 2001 From: Tai Sakuma Date: Sun, 22 Mar 2026 08:11:29 -0400 Subject: [PATCH 3/3] refactor: extract update-index action and move git config to workflows - Rename update-latest to update-index, focused on index.html only - Move latest/ copy to an inline step in docs-release.yml - Add update-index step to docs-dev.yml - Move git config from deploy-to-gh-pages action to each workflow Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/actions/deploy-to-gh-pages/action.yml | 2 -- .github/actions/update-index/action.yml | 22 ++++++++++++++ .../generate_index.py | 0 .../index.html | 0 .github/actions/update-latest/action.yml | 30 ------------------- .github/workflows/docs-dev.yml | 10 +++++++ .github/workflows/docs-pr-cleanup.yml | 5 +++- .github/workflows/docs-pr-preview.yml | 5 ++++ .github/workflows/docs-release.yml | 16 ++++++++-- 9 files changed, 54 insertions(+), 36 deletions(-) create mode 100644 .github/actions/update-index/action.yml rename .github/actions/{update-latest => update-index}/generate_index.py (100%) rename .github/actions/{update-latest => update-index}/index.html (100%) delete mode 100644 .github/actions/update-latest/action.yml diff --git a/.github/actions/deploy-to-gh-pages/action.yml b/.github/actions/deploy-to-gh-pages/action.yml index 8c5f2d8..7c4b59d 100644 --- a/.github/actions/deploy-to-gh-pages/action.yml +++ b/.github/actions/deploy-to-gh-pages/action.yml @@ -23,8 +23,6 @@ runs: - id: deploy shell: bash run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" TARGET="${{ inputs.target }}" git fetch origin gh-pages 2>/dev/null || true if git rev-parse --verify origin/gh-pages >/dev/null 2>&1; then diff --git a/.github/actions/update-index/action.yml b/.github/actions/update-index/action.yml new file mode 100644 index 0000000..133ae26 --- /dev/null +++ b/.github/actions/update-index/action.yml @@ -0,0 +1,22 @@ +name: Update index.html +description: Regenerate index.html, commit and push + +inputs: + repo-name: + description: Repository name for the index page title + required: true + repo-url: + description: Repository URL for the GitHub link + required: true + +runs: + using: composite + steps: + - shell: bash + run: | + python "${{ github.action_path }}/generate_index.py" \ + "${{ inputs.repo-name }}" \ + "${{ inputs.repo-url }}" + git add index.html + git diff --cached --quiet || git commit -m "Update index.html" + git push origin gh-pages diff --git a/.github/actions/update-latest/generate_index.py b/.github/actions/update-index/generate_index.py similarity index 100% rename from .github/actions/update-latest/generate_index.py rename to .github/actions/update-index/generate_index.py diff --git a/.github/actions/update-latest/index.html b/.github/actions/update-index/index.html similarity index 100% rename from .github/actions/update-latest/index.html rename to .github/actions/update-index/index.html diff --git a/.github/actions/update-latest/action.yml b/.github/actions/update-latest/action.yml deleted file mode 100644 index ad38cd0..0000000 --- a/.github/actions/update-latest/action.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Update latest and index.html -description: Copy built site to latest/, regenerate index.html, commit and push - -inputs: - site-dir: - description: Path to the built site - required: true - repo-name: - description: Repository name for the index page title - required: true - repo-url: - description: Repository URL for the GitHub link - required: true - version: - description: Version string for the commit message - required: true - -runs: - using: composite - steps: - - shell: bash - run: | - rm -rf latest - cp -r "${{ inputs.site-dir }}" latest - python "${{ github.action_path }}/generate_index.py" \ - "${{ inputs.repo-name }}" \ - "${{ inputs.repo-url }}" - git add latest index.html - git diff --cached --quiet || git commit -m "Update latest and index.html for v${{ inputs.version }}" - git push origin gh-pages diff --git a/.github/workflows/docs-dev.yml b/.github/workflows/docs-dev.yml index 5e187fe..1db2f23 100644 --- a/.github/workflows/docs-dev.yml +++ b/.github/workflows/docs-dev.yml @@ -25,8 +25,18 @@ jobs: with: subdir: dev + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + - uses: ./.github/actions/deploy-to-gh-pages with: site-dir: ${{ steps.build.outputs.site-dir }} target: dev commit-message: Deploy dev docs + + - uses: ./.github/actions/update-index + with: + repo-name: ${{ github.event.repository.name }} + repo-url: ${{ github.event.repository.html_url }} diff --git a/.github/workflows/docs-pr-cleanup.yml b/.github/workflows/docs-pr-cleanup.yml index 100ed54..5a84df3 100644 --- a/.github/workflows/docs-pr-cleanup.yml +++ b/.github/workflows/docs-pr-cleanup.yml @@ -17,10 +17,13 @@ jobs: steps: - uses: actions/checkout@v6 - - name: Remove PR preview + - name: Configure git run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Remove PR preview + run: | git fetch origin gh-pages 2>/dev/null || true if ! git rev-parse --verify origin/gh-pages >/dev/null 2>&1; then echo "gh-pages branch does not exist, nothing to clean up" diff --git a/.github/workflows/docs-pr-preview.yml b/.github/workflows/docs-pr-preview.yml index 52fb6ad..3a27187 100644 --- a/.github/workflows/docs-pr-preview.yml +++ b/.github/workflows/docs-pr-preview.yml @@ -61,6 +61,11 @@ jobs: name: docs-preview path: ${{ env.SITE_DIR }} + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + # Reference the action from @main so deploy logic always comes from the # trusted main branch. - uses: TaiSakuma/improved-octo-fortnight/.github/actions/deploy-to-gh-pages@main diff --git a/.github/workflows/docs-release.yml b/.github/workflows/docs-release.yml index 628148a..f343c5e 100644 --- a/.github/workflows/docs-release.yml +++ b/.github/workflows/docs-release.yml @@ -36,15 +36,25 @@ jobs: with: subdir: ${{ steps.version.outputs.version }} + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + - uses: ./.github/actions/deploy-to-gh-pages with: site-dir: ${{ steps.build.outputs.site-dir }} target: ${{ steps.version.outputs.version }} commit-message: Deploy docs for v${{ steps.version.outputs.version }} - - uses: ./.github/actions/update-latest + - name: Update latest + run: | + rm -rf latest + cp -r "${{ steps.build.outputs.site-dir }}" latest + git add latest + git commit -m "Update latest" + + - uses: ./.github/actions/update-index with: - site-dir: ${{ steps.build.outputs.site-dir }} repo-name: ${{ github.event.repository.name }} repo-url: ${{ github.event.repository.html_url }} - version: ${{ steps.version.outputs.version }}