zensical-pages¶
Builds a Zensical microsite and
deploys it to GitLab Pages. This very site is built by this component —
see its self-include in this repo's root .gitlab-ci.yml.
Two jobs:
zensical-build— runs thezensicalCLI (pre-installed in thedocs-toolsimage in its own venv — no runtime install, no per-repo lockfile), builds the site, and moves the producedsite/to$CI_PROJECT_DIR/public(Zensical's CLI has no--site-dirflag and readszensical.tomlfrom the working directory). Runs everywhere via an explicitwhen: on_successrule — a rule-less job is skipped on merge-request pipelines.pages— consumes the build artifact, deploys ondeploy_branchonly.
Change-detected by default — both jobs run only when a matching file
changes, so a code-only MR doesn't rebuild the docs site. Build and deploy
share the same changes filter, so they skip or run together (a deploy
never runs without its build artifact).
Jobs¶
| Job | What it runs |
|---|---|
zensical-build |
Removes [project] exclude_docs matches from the docs tree, installs the estate's structured-data main.html into the site's custom_dir (adding the key when the site has none), runs zensical build --clean (CLI baked into the image), writes site/llms.txt, moves site/ → public/. |
pages |
Publishes the public/ artifact via GitLab Pages (pages: true). |
Inputs¶
| Input | Type | Default | Description |
|---|---|---|---|
image_version |
string | "v0.1.1" |
docs-tools image tag (provides the baked zensical toolchain). |
stage |
string | pages |
GitLab CI stage for both jobs. |
working_directory |
string | "." |
Directory the build runs from. Must contain zensical.toml. |
docs_path |
string | "docs" |
Path to the Zensical docs source, relative to working_directory. Echoed in logs; Zensical reads zensical.toml for the canonical path. |
deploy_branch |
string | "main" |
Branch whose pipelines deploy Pages. Other branches still build, for visibility. |
changes |
array | ["docs/**/*", "zensical.toml", ".gitlab-ci.yml"] |
Change-detection paths shared by both jobs. Set ["**/*"] to always run. |
exclude_docs¶
Zensical accepts MkDocs' exclude_docs key and builds the matching files
anyway (checked at 0.0.55, the image's version, and 0.0.62, the latest release,
on 2026-09-16), which is how four estate sites came to publish their page
scaffold. The component honours the key itself, removing the matches from the
docs tree before the build so neither the site, the sitemap nor the search
index carries them. One pattern per line: a bare name matches at any depth
(_template.md), a slash anchors it to the docs root (drafts/*.md), a
trailing slash names a directory. Negation and anchored patterns are not
supported. A pattern that matches nothing is logged and ignored. See
spec 0090.
Derived configuration¶
Two zensical.toml values are filled in by the build when the site leaves them
unset, from what the checkout already knows (spec 0092): [project] edit_uri
becomes -/edit/$CI_DEFAULT_BRANCH/<docs path from the repo root>/ for a
gitlab.com repo_url, so "edit this page" links stop going to edit/master/;
and [project.extra] language is read off go.mod, Cargo.toml, a *.tf file
or pyproject.toml for the structured data's programmingLanguage. Each insert
is re-parsed before it is trusted and skipped with a log line if it would not
parse. A site that sets either keeps its own value.
Structured data¶
Every built page carries one JSON-LD graph: WebSite, SoftwareSourceCode and a
single Person (the author, with sameAs to the profiles listed in
[[project.extra.social]]), plus a TechArticle on every page but the home. All
of it is derived from zensical.toml and the page's front matter (title,
description, date, tags, authors); nothing is typed into the template.
programmingLanguage comes from [project.extra] language when set, or from a
.go., .rust. or .iac. host. The component achieves this by writing a
main.html that fills the theme's extrahead block into the site's
custom_dir: into the directory the site declares (its own main.html, if it
has one, is left alone and wins), or into .zensical-overrides with the key
added to zensical.toml in the job workspace and the result re-parsed before it
is trusted. The package's own template cannot be used: /opt/zensical is
root-owned and the job runs as ci. Canonical copy:
scripts/zensical-structured-data/main.html, byte-diffed by the self-test. See
spec 0091.
The llms.txt map¶
Every site built by this component carries /llms.txt: an H1, the site
description as a blockquote, then every page under its section as
- [title](url): description, closing with the repository's releases, the
estate changelog filtered to the project, the estate map at
phpboyscout.uk/llms.txt, and the sitemap. Assistants and answer engines fetch
one page at a time, and this is the file they look for at the root to learn
what the other pages are.
It is derived, not authored. Sections follow [project] nav when the site
declares one (nested groups flatten into their top-level entry; pages the nav
omits land under More), and the Diátaxis directory order otherwise
(getting-started, tutorials, how-to, reference, explanation,
about, then anything else, then development). A page's line uses its
front-matter title and description, falling back to its first heading.
Directories and files starting with _ are skipped, so _template/
scaffolding never appears. site_url is required; the build fails without it
rather than writing relative links an assistant cannot follow.
The engine is embedded in the template and mirrored at
scripts/zensical-llms-txt/llms.py, where its unit tests live; the self-test
byte-diffs the two. Standard library only, because the docs-tools image
carries nothing else.
Usage¶
include:
- component: gitlab.com/phpboyscout/cicd/[email protected]
See also¶
hugo-pages— the sibling component for Hugo sites.- Explanation: static sites