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

# Every documentation version is a separate Hugo build, published under
# https://go-sciio.github.io/docs/<version>/ on the gh-pages branch, where
# <version> is the newest minor version among the libraries the pages describe
# (params.libraries in hugo.yaml: v0.1.2 -> 0.1), or "main" while none of
# them 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-sciio.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-sciio/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 the newest minor version among the libraries
# params.libraries in hugo.yaml names ("main" until one is released), and
# it is the last segment of baseURL: the version selector reads it there.
- name: Version
id: version
run: |
set -euo pipefail
libs=$(hugo config --format json | jq -r '.params.libraries | to_entries[] | "\(.key) \(.value)"')
test -n "${libs}" || { echo "::error::params.libraries in hugo.yaml is empty"; exit 1; }
minors=""
while read -r name tag; do
if [ "${tag}" = main ]; then continue; fi
minor=$(printf '%s\n' "${tag}" | sed -nE 's/^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/\1.\2/p')
if [ -z "${minor}" ]; then
echo "::error::params.libraries.${name} in hugo.yaml is \"${tag}\", not vMAJOR.MINOR.PATCH or main"
exit 1
fi
minors=$(printf '%s\n%s\n' "${minors}" "${minor}")
done <<< "${libs}"
version=$(printf '%s\n' "${minors}" | sed '/^$/d' | sort -t. -k1,1nr -k2,2nr | sed -n 1p)
version=${version:-main}
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "libraries: $(echo ${libs}); 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

# 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-sciio.github.io/docs*) continue ;;
# This repository's own files: on a pull request that adds
# them, they are not on main yet.
*github.com/go-sciio/docs/*) continue ;;
esac
# github.com answers curl's blob pages with 503 most of the time,
# while the raw host answers 200 for a file and 404 for a missing
# one. A blob link claims "this file exists at this ref", so ask
# the raw host (as the landing's links.yml and go-ndarray/docs#30).
probe=$u
case "$u" in
https://github.com/*/*/blob/*)
probe=https://raw.githubusercontent.com/$(printf '%s' "${u#https://github.com/}" | sed -E 's#^([^/]+/[^/]+)/blob/#\1/#; s/[?#].*$//') ;;
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 "$probe" 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-sciio/brand.git
79 changes: 78 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,79 @@
# docs
The go-sciio documentation site

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

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

go-sciio is **planned** (wave 2 of the [go-scikit roadmap](https://github.com/go-scikit/roadmap),
[PLAN.md](https://github.com/go-scikit/roadmap/blob/main/PLAN.md) and
[STATUS.md](https://github.com/go-scikit/roadmap/blob/main/STATUS.md)): no library
exists yet. The site says what the organisation will cover and what each format
will be validated against, with one page per planned package: `h5`, `mat`,
`netcdf`, `fits`, `npy`, `mtx`, `wav` and `flac`.

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 (pinned to v0.16.0),
built the same way as the [go-bignum documentation](https://github.com/go-bignum/docs),
with go-sciio's branding.

## Versions

`params.libraries` in `hugo.yaml` names the release of each library the pages
describe, or `main` while it has no tag. A version of the documentation is
**the newest minor version among the tagged ones**, or `main` while none is
tagged. No package is released, so the site is version `main`:

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

`https://go-sciio.github.io/docs/` redirects to `latest/`. Each push to `main`
replaces the directory of the version it describes, and nothing else. When a
package is released, its entry in `params.libraries` becomes its tag, and the
old version's pages stay published beside the new one.

## Layout

| Path | What |
| --- | --- |
| `content/en/` | the pages; `_index.md` is the home, one directory per planned package, `weight` orders the sidebar |
| `i18n/en.yaml` | the site's strings, beside the theme's |
| `hugo.yaml` | `baseURL`, the library versions described, the navbar (version › theme › search › GitHub), the brand mounts |
| `layouts/_partials/custom/version-select.html` | the version selector (reads `versions.json`) |
| `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 slate as Hextra's primary colour, the table headers |
| `themes/hextra` | submodule: [tannevaled/hextra](https://github.com/tannevaled/hextra), pinned to v0.16.0 |
| `branding` | submodule: [go-sciio/brand](https://github.com/go-sciio/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-sciio/docs.git
cd docs
mkdir -p data && sh themes/hextra/scripts/page-history.sh > data/pagehistory.json
hugo server --baseURL http://localhost:1313/docs/main/
```

then open <http://localhost:1313/docs/main/>. Hugo 0.146 or later; no Python,
no Node.

## 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 |
| `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.
36 changes: 36 additions & 0 deletions assets/css/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* go-sciio branding on Hextra (github.com/go-sciio/brand).
*
* The mark's gradient, #8FA3BF -> #3E516B (branding/svg/color/go-sciio.svg).
* Hextra derives its whole primary scale (links, active sidebar entry,
* buttons) from these three variables.
*
* Light theme: #3E516B = hsl(215 27% 33%), the gradient's dark end
* (8.10:1 on white).
* Dark theme: #8FA3BF = hsl(215 27% 65%), its light end (7.51:1 on the dark
* background). */
:root {
--primary-hue: 215deg;
--primary-saturation: 27%;
--primary-lightness: 33%;
}
.dark {
--primary-hue: 215deg;
--primary-saturation: 27%;
--primary-lightness: 65%;
}

/* Tables: their headers stand out in the brand colour and their text is a
* size smaller than the prose. In the dark theme the header keeps the dark
* end: white on #8FA3BF cannot be read. */
.content table { font-size: 0.875em; }
.content table th { background: #3E516B; color: #fff; }

/* The version selector's list hangs from its button's right edge, so that it
* stays on screen at the end of the navbar. */
.fs-version-select .hextra-nav-menu-items { left: auto !important; right: 0; transform: none !important; }

/* The outdated-version banner (layouts/_partials/custom/content-begin.html). */
.nd-outdated { margin-top: 1rem; padding: 0.75rem 1rem; border-radius: 0.5rem;
border: 1px solid #f59e0b; background: #fffbeb; color: #78350f; }
.dark .nd-outdated { background: #451a03; color: #fde68a; border-color: #b45309; }
.nd-outdated a { text-decoration: underline; font-weight: 600; }
1 change: 1 addition & 0 deletions branding
Submodule branding added at 4fc55c
Loading
Loading