Skip to content
Merged
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
155 changes: 119 additions & 36 deletions .github/workflows/build_docs.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
# This builds and deploys ISLP docs
# This builds the ISLP docs into a folder of the gh-pages branch, one for
# each version: <version>/ for a release (its tag's docs and labs, with its
# package from PyPI), dev/ for the branch the workflow is run on. The site's
# root redirects to the landing version (the latest release, unless set with
# -f landing=X.Y.Z), versions.html lists them, and a path without a version
# (e.g. /ISLP/labs.html) goes to the landing version's page.
#
# gh workflow run build_docs.yml # dev/, from main
# gh workflow run build_docs.yml -f version=0.6.0 # 0.6.0/, after its PyPI upload

name: Build docs

# Controls when the workflow will run
on:
workflow_dispatch:
inputs: null

# A workflow run is made up of one or more jobs that can run
# sequentially or in parallel
inputs:
version:
description: 'A release (e.g. 0.6.0), built from its tag and its PyPI package; empty for dev/, from source'
required: false
default: ''
landing:
description: 'The version the site root (and a page without a version) goes to, kept in landing.txt; empty: as before (the latest release, if never set)'
required: false
default: ''

jobs: # This workflow contains a single
# job called "build"
# one push to gh-pages at a time
concurrency:
group: gh-pages
cancel-in-progress: false

jobs:

build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
env:
VERSION: ${{ inputs.version }}

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
# a release's docs (and the labs its submodule pins) are those of its tag
- uses: actions/checkout@v7
with:
ref: ${{ inputs.version && format('v{0}', inputs.version) || github.ref }}

- name: Install uv
uses: astral-sh/setup-uv@v10.3.0
Expand All @@ -30,14 +48,21 @@ jobs: # This workflow contains a single
**/pyproject.toml
docs/requirements.txt

# a virtual environment, on the PATH of the later steps (no R: no page
# or lab runs it)
# a virtual environment, on the PATH of the later steps; a release's
# package from PyPI. docs/requirements.txt but rpy2 (an old tag's lists
# it): no page or lab runs R
- name: Install dependencies
run: |
uv venv --python 3.12
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
uv pip install -r docs/requirements.txt
uv pip install .
grep -v '^rpy2' docs/requirements.txt > docs-requirements.txt
uv pip install -r docs-requirements.txt
if [ -n "$VERSION" ]; then
uv pip install "ISLP==$VERSION"
else
uv pip install .
fi
uv pip list | grep -i -E '^(islp|glmstar|modelmatrix) '

# Checkout labs
- name: Checkout version of labs
Expand All @@ -55,37 +80,95 @@ jobs: # This workflow contains a single
cd docs
make html

# Store the output
- name: Upload docs
uses: actions/upload-artifact@v7
with:
name: ISLP_docs
path: docs/build/html
retention-days: 5
if-no-files-found: error

deploy:
runs-on: ubuntu-latest
needs: build
# (a run on another branch only builds, to try it)
if: github.ref == 'refs/heads/main'

# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
# dev/ only from main (dev from another branch only builds, to try it);
# a release's docs are its tag's, whichever branch the workflow is run from
if: github.ref == 'refs/heads/main' || inputs.version != ''
permissions:
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source
contents: write # to push to gh-pages
env:
FOLDER: ${{ inputs.version || 'dev' }}
LANDING: ${{ inputs.landing }}

environment:
name: github-pages
url: ${{steps.deployment.outputs.page_url}}

steps:
- uses: actions/download-artifact@v8
with:
name: ISLP_docs
path: .
- uses: actions/configure-pages@v6
- uses: actions/upload-pages-artifact@v5
with:
path: .
- id: deployment
uses: actions/deploy-pages@v5
- uses: actions/download-artifact@v8
with:
name: ISLP_docs
path: built

# the folder replaced, then the root redirect, 404.html and versions.html
# remade from the folders there
- name: Push to gh-pages
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
SITE: ${{ github.event.repository.name }}
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com'
git init -q pages && cd pages
git remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git"
if git fetch -q --depth 1 origin gh-pages; then
git checkout -q -b gh-pages FETCH_HEAD
else
git checkout -q --orphan gh-pages
fi
rm -rf "$FOLDER"
cp -r ../built "$FOLDER"
touch .nojekyll # (serve the folders starting with _ too)
python3 - <<'EOF'
import json, os, re
site = os.environ['SITE']
releases = sorted((d for d in os.listdir('.') if re.fullmatch(r'\d+\.\d+\.\d+', d)),
key=lambda v: tuple(map(int, v.split('.'))), reverse=True)
folders = releases + (['dev'] if os.path.isdir('dev') else [])
latest = releases[0] if releases else 'dev'
# the landing version: given, or kept from before in landing.txt, or the latest
if os.environ.get('LANDING'):
open('landing.txt', 'w').write(os.environ['LANDING'] + '\n')
landing = open('landing.txt').read().strip() if os.path.exists('landing.txt') else latest
if landing not in folders:
raise SystemExit(f'the landing version {landing} has no docs here: {folders}')
json.dump({'latest': latest, 'landing': landing, 'versions': folders},
open('versions.json', 'w'), indent=1)
open('index.html', 'w').write(
f'<!doctype html><meta charset="utf-8"><title>{site} docs</title>'
f'<meta http-equiv="refresh" content="0; url={landing}/">'
f'<p>Redirecting to <a href="{landing}/">the {site} {landing} docs</a>.'
f' Other versions: <a href="versions.html">versions.html</a>.</p>')
# a page without a version, e.g. /ISLP/labs.html (the links from
# before the versions): the landing version's
open('404.html', 'w').write(
f'<!doctype html><meta charset="utf-8"><title>{site} docs</title>'
f'<script>(function() {{'
f'var base = "/{site}/", versions = {json.dumps(folders)};'
f'var path = location.pathname;'
f'if (path.indexOf(base) !== 0) return;'
f'var rest = path.slice(base.length), first = rest.split("/")[0];'
f'if (versions.indexOf(first) !== -1) return;'
f'location.replace(base + "{landing}/" + rest + location.search + location.hash);'
f'}})();</script>'
f'<p>Not found. The <a href="/{site}/{landing}/">{site} {landing} docs</a>,'
f' or <a href="/{site}/versions.html">other versions</a>.</p>')
items = ''.join(f'<li><a href="{v}/">{v}</a>'
f'{" (latest release)" if v == latest else ""}'
f'{" (the default)" if v == landing and v != latest else ""}'
f'{" (main)" if v == "dev" else ""}</li>' for v in folders)
open('versions.html', 'w').write(
f'<!doctype html><meta charset="utf-8"><title>{site} docs: versions</title>'
f'<h1>{site} documentation</h1><ul>{items}</ul>')
EOF
cat versions.json
git add -A
git commit -q -m "Docs for $FOLDER (${GITHUB_SHA::7})"
git push -q origin gh-pages
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ ISLP now depends on (`modelmatrix>=0.3`).

### Documentation

- **Docs for each version**: the docs workflow builds a release's docs
(from its tag, with its labs, and the package from PyPI) into
`<version>/` of the `gh-pages` branch, and main's into `dev/`. The site's
root redirects to the landing version (`-f landing=X.Y.Z`, kept in
`landing.txt`; the latest release if never set), `versions.html` lists
them, and a page without a version (e.g. `/ISLP/labs.html`) goes to the
landing version's.
- **The docs build with uv, without conda or R**: the docs workflow
installs with uv, and `docs/make_notebooks.py` runs a lab in a uv
virtual environment (Python 3.12 by default) instead of a conda
Expand Down
Loading