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
252 changes: 252 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
name: docs

# Every documentation version is a separate Hugo build, published under
# https://go-plot.github.io/docs/<version>/ on the gh-pages branch, where
# <version> is the minor version of go-plot/plot the pages describe
# (params.plot.version in hugo.yaml: v0.1.2 -> 0.1), or "main" while no
# release is tagged. main is that version's source: each push to main replaces /docs/<version>/, and
# /docs/latest/ follows the newest. Pull requests build and check, and publish
# nothing.

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

# The build needs nothing beyond the source. Write access to the repository
# is granted to the deploy job alone, below, so a pull request build cannot
# push to gh-pages.
permissions:
contents: read

env:
HUGO_VERSION: "0.167.0"
# sha256 of hugo_${HUGO_VERSION}_linux-amd64.tar.gz, from the release's checksums.txt.
HUGO_SHA256: "4d84519b9f619e6d4c3fb45a50157abeabeb724f859c60605f44c23def6e1169"
DOCS_URL: "https://go-plot.github.io/docs"

jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v7
with:
# themes/hextra (the tannevaled/hextra fork, pinned to a release tag)
# and branding (go-plot/brand) are submodules.
submodules: recursive
# The whole history: each page's creation and modification come from git.
fetch-depth: 0

- name: Install Hugo ${{ env.HUGO_VERSION }}
run: |
curl -fsSL -o /tmp/hugo.tar.gz \
"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/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
echo "${HUGO_SHA256} /tmp/hugo.tar.gz" | sha256sum -c -
sudo tar -xzf /tmp/hugo.tar.gz -C /usr/local/bin hugo
hugo version

# The version label is plot's minor version, read from hugo.yaml
# ("main" until the first release), and it is the last segment of
# baseURL: the version selector reads it there.
- name: Version
id: version
run: |
plot=$(hugo config --format json | jq -r '.params.plot.version')
if [ "${plot}" = main ]; then
version=main
else
version=$(printf '%s\n' "${plot}" | sed -nE 's/^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/\1.\2/p')
fi
if [ -z "${version}" ]; then
echo "::error::params.plot.version in hugo.yaml is \"${plot}\", not vMAJOR.MINOR.PATCH or main"
exit 1
fi
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "plot ${plot}: documentation version ${version}"

# Each page's creation (first commit, followed across renames) and its
# author, for the page history at its foot (params.page.history).
- name: Page history
run: |
mkdir -p data
sh themes/hextra/scripts/page-history.sh > data/pagehistory.json

# --panicOnWarning: a deprecated setting or a template warning fails the
# build rather than scrolling by. A broken {{< relref >}} is an error.
- name: Build
env:
HUGO_ENVIRONMENT: production
run: >-
hugo --gc --minify --panicOnWarning --printPathWarnings
--baseURL "${DOCS_URL}/${{ steps.version.outputs.version }}/"

# Internal links and anchors, offline, with the site laid out under
# /docs/<version>/ as it is served.
- name: Lay out the site for the link check
run: |
mkdir -p _check/docs
cp -R public "_check/docs/${{ steps.version.outputs.version }}"

- name: Check links
uses: lycheeverse/lychee-action@v2
with:
args: >-
--offline --no-progress --include-fragments
--index-files index.html
--root-dir ${{ github.workspace }}/_check
'_check/docs/**/*.html'
fail: true

- uses: actions/upload-artifact@v7
with:
name: site
path: public
retention-days: 7

# Every figure on the site is drawn by go-plot from the code the page shows
# (figures/<name>.go, read by the plotfig shortcode). This job draws them
# again and fails if any image differs from the committed one, so a figure
# cannot outlive the code or the go-plot version that drew it. It also
# fails if figures/go.mod requires another go-plot than the release the
# pages say they describe (params.plot.version in hugo.yaml).
figures:
name: every figure is what its code draws
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: figures/go.mod
cache-dependency-path: figures/go.sum
- name: figures/go.mod requires the documented release
run: |
set -euo pipefail
documented=$(sed -nE 's/^ version: "(v[0-9.]+|main)"$/\1/p' hugo.yaml)
required=$(cd figures && go list -m -f '{{.Version}}' github.com/go-plot/plot)
echo "hugo.yaml documents ${documented}; figures/go.mod requires ${required}"
if [ "${documented}" != main ] && [ "${documented}" != "${required}" ]; then
echo "::error::the pages describe go-plot ${documented}, the figures are drawn by ${required}"
exit 1
fi
- name: Draw every figure
working-directory: figures
run: go run .
- name: The committed images are the ones drawn
run: |
git add -N static/images/figures
if ! git diff --exit-code --stat -- static/images/figures; then
echo "::error::a figure differs from what its code draws: run 'cd figures && go run .' and commit the images"
exit 1
fi

# The offline check above proves the site's own links and anchors. An
# https:// URL is text to it: this job fetches each one the pages name.
links:
name: every external link resolves
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Check
run: |
set -uo pipefail
# The character class matters: an extractor stopping only at ) and "
# carries a closing > or < into the request.
urls=$(grep -rhoE 'https?://[^)">< [:space:]]+' content/ README.md | sed -E 's/[.,`]+$//' | sort -u)
test -n "$urls" || { echo "::error::no links found; are the pages still there?"; exit 1; }
echo "checking $(printf '%s\n' "$urls" | wc -l) distinct URLs"
fail=0
while read -r u; do
case "$u" in
# Renders whatever it is asked for; proves nothing.
*img.shields.io*) continue ;;
# Documented placeholders, not hosts.
*example.com*|*example.org*|*example.net*|*localhost*|*127.0.0.1*|*.invalid*) continue ;;
# This site's own pages: they exist only once this workflow
# has deployed them, and the offline check above already
# proves every link inside the site.
*go-plot.github.io/docs*) continue ;;
# This repository's own files (figures/): on a pull request
# that adds them, they are not on main yet.
*github.com/go-plot/docs/*) continue ;;
esac
# Three attempts: one is a flake detector rather than a link
# checker, and a lane that fails at random gets ignored.
code=000
for _ in 1 2 3; do
code=$(curl -sS -o /dev/null -w '%{http_code}' -L --max-time 25 "$u" 2>/dev/null) || true
code=${code:-000}
case "$code" in 2*|3*) break ;; esac
sleep 3
done
case "$code" in
2*|3*) printf ' ok %s %s\n' "$code" "$u" ;;
*) printf ' FAIL %s %s\n' "$code" "$u"; fail=1 ;;
esac
done <<< "$urls"
test "$fail" -eq 0 || { echo "::error::at least one link does not resolve"; exit 1; }

deploy:
# Only main publishes. A pull request stops after build above: it has no
# business pushing to gh-pages.
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
needs: build
# Never two deploys at once: both would push gh-pages and the second would
# be rejected. cancel-in-progress stays false -- killing a publish half way
# through is worse than making it wait.
concurrency:
group: gh-pages-deploy
cancel-in-progress: false
runs-on: ubuntu-latest
permissions:
contents: write
steps:
# The publishing script, from the commit being published.
- uses: actions/checkout@v7
with:
path: source

# The published site. Only the version being built is replaced; every
# other version stays as it is. The first deploy finds no gh-pages
# branch and starts it empty (orphan): nothing else ever creates it.
- uses: actions/checkout@v7
with:
path: gh-pages
fetch-depth: 0

- name: Switch to gh-pages
working-directory: gh-pages
run: |
if git ls-remote --exit-code --heads origin gh-pages >/dev/null; then
git fetch -q origin gh-pages
git checkout -q -B gh-pages FETCH_HEAD
else
echo "no gh-pages branch yet: starting one"
git checkout -q --orphan gh-pages
git rm -rqf .
git clean -fdxq
fi

- uses: actions/download-artifact@v8
with:
name: site
path: site

- name: Publish ${{ needs.build.outputs.version }}
run: source/scripts/publish-version.sh "${{ needs.build.outputs.version }}" site gh-pages

- name: Push gh-pages
working-directory: gh-pages
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -A
if git diff --cached --quiet; then
echo "nothing changed"
exit 0
fi
git commit -q -m "Deployed ${GITHUB_SHA::7} to ${{ needs.build.outputs.version }} with Hugo ${HUGO_VERSION}"
git push origin HEAD:gh-pages
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/public/
/resources/
/_check/
.hugo_build.lock
.DS_Store

# Written by themes/hextra/scripts/page-history.sh before each build (CI and local).
/data/pagehistory.json
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "themes/hextra"]
path = themes/hextra
url = https://github.com/tannevaled/hextra.git
[submodule "branding"]
path = branding
url = https://github.com/go-plot/brand.git
3 changes: 2 additions & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
BSD 3-Clause License

Copyright (c) 2026, go-plot
Copyright (c) 2026, the go-plot/docs authors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Expand Down
92 changes: 91 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,92 @@
# docs
Documentation for go-plot (Hugo + Hextra), served at go-plot.github.io/docs/

[![docs](https://github.com/go-plot/docs/actions/workflows/docs.yml/badge.svg)](https://github.com/go-plot/docs/actions/workflows/docs.yml)
[![License](https://img.shields.io/badge/license-BSD--3--Clause-1f77b4?style=flat-square)](LICENSE)

**The go-plot documentation site**, published at
<https://go-plot.github.io/docs/>.

A [Hugo](https://gohugo.io/) site with the [tannevaled/hextra](https://github.com/tannevaled/hextra)
fork of the [Hextra](https://github.com/imfing/hextra) theme, built the same way
as the [go-ndarray documentation](https://github.com/go-ndarray/docs), with
go-plot's branding. It documents
[`go-plot/plot`](https://github.com/go-plot/plot).

## Versions

A version of the documentation is **the minor version of
[`go-plot/plot`](https://github.com/go-plot/plot) it describes**, or `main`
while no release is tagged:

| Source | Published under |
| --- | --- |
| branch `main`, with `params.plot.version` in `hugo.yaml` | `https://go-plot.github.io/docs/<MAJOR.MINOR>/`, or `/docs/main/` while it says `main` |
| the newest release (else `main`) | also `https://go-plot.github.io/docs/latest/` |

`https://go-plot.github.io/docs/` redirects to `latest/`. Each push to `main`
replaces the directory of the version it describes, and nothing else. The
pages describe **v0.1.0**, published under `0.1/`.

To document a new release, set `params.plot.version` to its tag, require the
same version in `figures/go.mod`, redraw the figures, and update the pages.

## Figures

Every figure on the site is drawn by go-plot. `figures/` is a Go module, one
file per figure; `go run .` there writes `static/images/figures/<name>.svg`.
The `plotfig` shortcode (`layouts/_shortcodes/plotfig.html`) shows an image
and then **reads the file that drew it**, so the code on the page is the code
that ran. The `figures` job of the CI draws them all again and fails if an
image differs from the committed one, or if `figures/go.mod` requires another
go-plot than the version the pages describe.

```sh
cd figures && go run .
```

## Layout

| Path | What |
| --- | --- |
| `content/en/` | the pages; `_index.md` is the home, `weight` orders the sidebar |
| `i18n/en.yaml` | the site's strings, beside the theme's |
| `hugo.yaml` | `baseURL`, the go-plot version described, the navbar (version › theme › search › GitHub), the brand mounts |
| `figures/` | the Go module that draws every figure, one file each |
| `layouts/_shortcodes/plotfig.html` | a figure and the code that drew it |
| `static/images/figures/` | the figures, as `figures/` writes them |
| `layouts/_partials/custom/version-select.html` | the version selector (reads `versions.json`), from go-ndarray/docs |
| `layouts/_partials/custom/content-begin.html` | the outdated-version banner (reads `versions.json`) |
| `layouts/_partials/favicons.html` | the brand's favicons |
| `assets/css/custom.css` | the brand blue as Hextra's primary colour, the table headers, a white card under figures |
| `themes/hextra` | submodule: [tannevaled/hextra](https://github.com/tannevaled/hextra), pinned to a release tag |
| `branding` | submodule: [go-plot/brand](https://github.com/go-plot/brand) (the mark, its PNGs and ICO) |
| `scripts/publish-version.sh` | puts one build into a `gh-pages` checkout and rewrites `versions.json`, `latest` and the root redirect |

## Working locally

```sh
git clone --recurse-submodules https://github.com/go-plot/docs.git
cd docs
mkdir -p data && sh themes/hextra/scripts/page-history.sh > data/pagehistory.json
hugo server --baseURL http://localhost:1313/docs/0.1/
```

then open <http://localhost:1313/docs/0.1/>. Hugo 0.146 or later; no Python,
no Node. Go 1.27.2 or later only to redraw the figures.

## Publication

`.github/workflows/docs.yml`:

| Job | When | What |
| --- | --- | --- |
| `build` | pull requests, `main` | builds with `--baseURL …/docs/<version>/`, checks internal links and anchors (lychee, offline), uploads the site |
| `links` | pull requests, `main` | fetches every external URL the pages name |
| `figures` | pull requests, `main` | draws every figure again and compares it with the committed image |
| `deploy` | `main` | `scripts/publish-version.sh` into `gh-pages` (started empty on the first deploy), then a commit and a push |

GitHub Pages serves the `gh-pages` branch.

## Licence

BSD-3-Clause.
Loading
Loading