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
2 changes: 1 addition & 1 deletion .agents/shared
54 changes: 43 additions & 11 deletions .github/workflows/check-links.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
- 'docs/**'
- 'site/**'
- 'lychee.toml'
- '.lycheeignore'
- '.github/workflows/check-links.yml'
workflow_dispatch:

Expand Down Expand Up @@ -44,6 +45,11 @@ jobs:
# Detect the Hugo site root (`docs/` or `site/`) by looking for a Hugo
# config file. Hugo config may live directly in the site root or in a
# `config/` or `config/_default/` subdirectory (both layouts are valid).
# A repo may also keep its only Hugo config in the `_preview/` sub-tree
# (e.g. `docs/_preview/hugo.toml`), so the same paths count there too.
# Hugo finds such a config only when it runs in `$dir/_preview`, so it
# needs `$dir/_preview/package-lock.json`: the `$dir` fallback below
# is for site-root configs only.
# Outputs `present=true|false` and `work_dir` (the directory where
# `npm ci` / `hugo` commands should run — either `$dir/_preview` for
# repos that use a separate preview sub-tree, or `$dir` for repos whose
Expand All @@ -57,19 +63,22 @@ jobs:
for dir in docs site; do
for cfg in hugo.toml hugo.yaml \
config/hugo.toml config/hugo.yaml \
config/_default/hugo.toml config/_default/hugo.yaml; do
config/_default/hugo.toml config/_default/hugo.yaml \
_preview/hugo.toml _preview/hugo.yaml \
_preview/config/hugo.toml _preview/config/hugo.yaml \
_preview/config/_default/hugo.toml _preview/config/_default/hugo.yaml; do
if [ -f "$dir/$cfg" ]; then
if [ -f "$dir/_preview/package-lock.json" ]; then
echo "work_dir=$dir/_preview" >> "$GITHUB_OUTPUT"
echo "present=true" >> "$GITHUB_OUTPUT"
echo "::notice::Hugo site found under $dir/ (work_dir: $dir/_preview)"
elif [ -f "$dir/package-lock.json" ]; then
elif [ -f "$dir/package-lock.json" ] && [[ $cfg != _preview/* ]]; then
echo "work_dir=$dir" >> "$GITHUB_OUTPUT"
echo "present=true" >> "$GITHUB_OUTPUT"
echo "::notice::Hugo site found under $dir/ (work_dir: $dir)"
else
echo "present=false" >> "$GITHUB_OUTPUT"
echo "::notice::Hugo config found in $dir/ but no package-lock.json found — skipping link check."
echo "::notice::Found $dir/$cfg but no package-lock.json to build it with — skipping link check."
fi
exit 0
fi
Expand Down Expand Up @@ -110,13 +119,13 @@ jobs:
if: steps.docs.outputs.present == 'true'
run: hugo env | grep 'github.com/sass/dart-sass'

# `actions/setup-node@v4` ships with built-in npm caching that hashes
# `actions/setup-node` ships with built-in npm caching that hashes
# the lockfile and restores `~/.npm`. We use that instead of a
# standalone `actions/cache@v4` block so there is only one source of
# standalone `actions/cache` block so there is only one source of
# truth for the cache key (no drift between two layers).
- name: Setup Node
if: steps.docs.outputs.present == 'true'
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version: '26'
cache: 'npm'
Expand All @@ -128,7 +137,7 @@ jobs:
# invalidates the cache deterministically regardless of site root.
- name: Cache Hugo Modules
if: steps.docs.outputs.present == 'true'
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: /tmp/hugo_cache
key: ${{ runner.os }}-hugomod-${{ hashFiles('docs/**/go.sum', 'site/**/go.sum') }}
Expand All @@ -152,7 +161,7 @@ jobs:
# `max_cache_age` expires.
- name: Cache Lychee results
if: steps.docs.outputs.present == 'true'
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: .lycheecache
key: cache-lychee-${{ runner.os }}-${{ hashFiles('lychee.toml') }}
Expand All @@ -167,7 +176,7 @@ jobs:
- name: Cache Lychee executable
if: steps.docs.outputs.present == 'true'
id: cache-lychee
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: lychee
key: ${{ runner.os }}-${{ env.LYCHEE_VERSION_TAG }}-${{ env.LYCHEE_RELEASE }}
Expand All @@ -187,7 +196,7 @@ jobs:
# string when the file is absent, so this guard runs the download iff
# neither the exact key nor any restore-key restored the binary.
- name: Download Lychee executable
uses: robinraju/release-downloader@v1.7
uses: robinraju/release-downloader@v1.13
if: steps.docs.outputs.present == 'true' && hashFiles('lychee/lychee') == ''
with:
repository: "lycheeverse/lychee"
Expand Down Expand Up @@ -239,9 +248,32 @@ jobs:
exit 1
fi

# Lychee prints broken links to this step's log only; the run page
# shows just "Process completed with exit code 2". `tee` keeps the
# report in the log and saves a copy, which the next step adds to the
# run summary. (`--output` would not do: it moves the report from the
# log to the file.) `pipefail` keeps Lychee's exit code. Without it,
# the step would take `tee`'s 0 and pass despite broken links.
- name: Check links
if: steps.docs.outputs.present == 'true'
run: |
set -o pipefail
./lychee/lychee --config lychee.toml --timeout 60 \
--base-url http://localhost:1313/ \
'${{ steps.docs.outputs.work_dir }}/public/**/*.html'
'${{ steps.docs.outputs.work_dir }}/public/**/*.html' \
| tee lychee-report.txt

# Runs after a failed `Check links` too, which is when the report
# matters most. `hashFiles` skips this step when Lychee never ran.
# `!cancelled()` needs the `${{ }}` wrapper: a bare leading `!` is
# a YAML tag.
- name: Publish link report
if: ${{ !cancelled() && hashFiles('lychee-report.txt') != '' }}
run: |
{
echo '## Link check'
echo
echo '```text'
cat lychee-report.txt
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
17 changes: 0 additions & 17 deletions .lycheeignore

This file was deleted.

3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ To verify that the source code samples embedded into the pages are up-to-date, r
./gradlew :checkSamples
```

Both tasks first check out the example submodules under `docs/_code/examples`
that are missing, at the commits this repository pins.

## Product release

To release a new version of Spine documentation, see the [SPINE_RELEASE.md](SPINE_RELEASE.md).
Expand Down
9 changes: 9 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,26 @@ task<Exec>("installDependencies") {
commandLine("./docs/_script/install-dependencies")
}

/**
* Checks out the example submodules that the code samples come from, if they are missing.
*/
task<Exec>("checkOutExamples") {
commandLine("./docs/_script/check-out-examples")
}

/**
* Embeds the code samples into pages of the site.
*/
task<Exec>("embedCode") {
dependsOn("checkOutExamples")
commandLine("./docs/_script/embed-code")
}

/**
* Verifies that the source code samples embedded into the pages are up-to-date.
*/
task<Exec>("checkSamples") {
dependsOn("checkOutExamples")
commandLine("./docs/_script/check-samples")
}

Expand Down
2 changes: 1 addition & 1 deletion config
2 changes: 1 addition & 1 deletion docs/_preview/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.27.3

require (
github.com/SpineEventEngine/site-commons v0.0.0-20261030172811-9216e48eb656 // indirect
github.com/SpineEventEngine/validation/docs v0.0.0-20260902132507-6465029c83b9 // indirect
github.com/SpineEventEngine/validation/docs v0.0.0-20261102173850-2754355aee88 // indirect
github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20800 // indirect
github.com/gohugoio/hugo-mod-jslibs-dist/popperjs/v2 v2.21100.20000 // indirect
github.com/twbs/bootstrap v5.3.8+incompatible // indirect
Expand Down
2 changes: 2 additions & 0 deletions docs/_preview/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ github.com/SpineEventEngine/validation/docs v0.0.0-20260523180634-dd65da5abb65 h
github.com/SpineEventEngine/validation/docs v0.0.0-20260523180634-dd65da5abb65/go.mod h1:4RnP1hlrfBI7ZlTsJkllaOEyluhzmz4mOBrRgbc/tts=
github.com/SpineEventEngine/validation/docs v0.0.0-20260902132507-6465029c83b9 h1:Bsgd16jNYCBdqtpEyW/CYV2yFjO7Y4nAPSlYz0zjFb0=
github.com/SpineEventEngine/validation/docs v0.0.0-20260902132507-6465029c83b9/go.mod h1:4RnP1hlrfBI7ZlTsJkllaOEyluhzmz4mOBrRgbc/tts=
github.com/SpineEventEngine/validation/docs v0.0.0-20261102173850-2754355aee88 h1:Cl2OrO7wwgnH/tfRFMkHxph+5lz0CWzXfk/jtAKcp/Y=
github.com/SpineEventEngine/validation/docs v0.0.0-20261102173850-2754355aee88/go.mod h1:4RnP1hlrfBI7ZlTsJkllaOEyluhzmz4mOBrRgbc/tts=
github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20400 h1:L6+F22i76xmeWWwrtijAhUbf3BiRLmpO5j34bgl1ggU=
github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20400/go.mod h1:uekq1D4ebeXgduLj8VIZy8TgfTjrLdSl6nPtVczso78=
github.com/gohugoio/hugo-mod-bootstrap-scss/v5 v5.20300.20800 h1:j5myhhzYwIHTr5ctK96Elfgp5uRROvrlTzYwwe1nF8o=
Expand Down
27 changes: 27 additions & 0 deletions docs/_script/check-out-examples
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env bash

# Checks out the example submodules under `docs/_code/examples` that are not
# checked out yet, at the commits this repository pins.
#
# The embedded code samples come from these examples, but a fresh clone or
# worktree starts without them. Examples that are already checked out stay as
# they are, so this never rewinds one that `embed-code` moved to a newer commit.

set -e

git submodule status -- docs/_code/examples | while read -r status path _; do
# `git submodule status` marks a submodule that is not initialized with `-`.
case "$status" in
-*) ;;
*) continue ;;
esac

# `settings.gradle.kts` includes some examples as builds, and Gradle writes its
# `.gradle` directory even into an empty one. Git does not clone into a
# directory that is not empty, so remove `.gradle` when it is all there is.
if [ "$(ls -A "$path")" = ".gradle" ]; then
rm -rf "${path:?}/.gradle"
fi

git submodule update --init -- "$path"
done
2 changes: 1 addition & 1 deletion docs/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ module github.com/SpineEventEngine/documentation/docs

go 1.22.0

require github.com/SpineEventEngine/validation/docs v0.0.0-20260902132507-6465029c83b9 // indirect
require github.com/SpineEventEngine/validation/docs v0.0.0-20261102173850-2754355aee88 // indirect
2 changes: 2 additions & 0 deletions docs/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ github.com/SpineEventEngine/validation/docs v0.0.0-20260523180634-dd65da5abb65 h
github.com/SpineEventEngine/validation/docs v0.0.0-20260523180634-dd65da5abb65/go.mod h1:4RnP1hlrfBI7ZlTsJkllaOEyluhzmz4mOBrRgbc/tts=
github.com/SpineEventEngine/validation/docs v0.0.0-20260902132507-6465029c83b9 h1:Bsgd16jNYCBdqtpEyW/CYV2yFjO7Y4nAPSlYz0zjFb0=
github.com/SpineEventEngine/validation/docs v0.0.0-20260902132507-6465029c83b9/go.mod h1:4RnP1hlrfBI7ZlTsJkllaOEyluhzmz4mOBrRgbc/tts=
github.com/SpineEventEngine/validation/docs v0.0.0-20261102173850-2754355aee88 h1:Cl2OrO7wwgnH/tfRFMkHxph+5lz0CWzXfk/jtAKcp/Y=
github.com/SpineEventEngine/validation/docs v0.0.0-20261102173850-2754355aee88/go.mod h1:4RnP1hlrfBI7ZlTsJkllaOEyluhzmz4mOBrRgbc/tts=
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* and limitations under the License.
*/

rootProject.name = "spine-docs"
rootProject.name = "documentation"

includeBuild("./docs/_code/samples")

Expand Down
Loading