diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 18c6220..4c7d78d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -66,6 +66,21 @@ jobs:
- name: PHP CS Fixer
run: castor qa:cs --dry-run
+ docs:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+ with:
+ persist-credentials: false
+
+ - name: Setup Castor
+ uses: castor-php/setup-castor@2a495b8c91f00be6768ad8a040ba8634c797d386 # v1.1.0
+
+ - name: Build the documentation website
+ run: castor docs:build
+
zizmor:
name: GitHub Actions security analysis
runs-on: ubuntu-latest
diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml
index 41d610e..92c51d2 100644
--- a/.github/workflows/documentation.yml
+++ b/.github/workflows/documentation.yml
@@ -19,7 +19,7 @@ permissions: {}
jobs:
build:
- name: Build documentation website with MkDocs
+ name: Build documentation website with Zensical
runs-on: ubuntu-latest
permissions:
contents: read
@@ -40,13 +40,13 @@ jobs:
# Turn Pages on if it is not already, instead of failing
enablement: true
- - name: Build site with mkdocs
+ - name: Build the documentation website
run: castor docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
- path: ./tools/mkdocs/site
+ path: ./tools/doc/site
deploy:
name: Deploy to GitHub Pages
diff --git a/README.md b/README.md
index f57c5a4..0e142b4 100644
--- a/README.md
+++ b/README.md
@@ -142,11 +142,11 @@ multiple applications and services.
## Contributing
-The documentation lives in [`doc/`](./doc/) and is built with MkDocs:
+The documentation lives in [`doc/`](./doc/) and is built with [Zensical](https://zensical.org/):
```bash
castor docs:serve # http://127.0.0.1:8000, rebuilds on change
-castor docs:build # build the static site in tools/mkdocs/site
+castor docs:build # build the static site in tools/doc/site
```
Quality tooling for the plugin itself:
diff --git a/castor.php b/castor.php
index 2f7f0a9..a4c23a0 100644
--- a/castor.php
+++ b/castor.php
@@ -11,7 +11,7 @@
defined('CASTOR_USE_CHDIR') || define('CASTOR_USE_CHDIR', false);
-import(__DIR__ . '/tools/mkdocs/castor.php');
+import(__DIR__ . '/tools/doc/castor.php');
#[AsTask(description: 'Run PHPStan', namespace: 'qa', name: 'phpstan', aliases: ['phpstan'])]
function qa_phpstan(bool $generateBaseline = false): int
diff --git a/doc/changelog.md b/doc/changelog.md
index 0e4a63b..3a403a6 100644
--- a/doc/changelog.md
+++ b/doc/changelog.md
@@ -1,8 +1,9 @@
---
+render_macros: true
title: Changelog
description: What changed in each release of the Castor Docker plugin.
---
-{% include-markdown "../CHANGELOG.md" %}
+{{ markdown("CHANGELOG.md") }}
diff --git a/doc/configuration.md b/doc/configuration.md
index 18abe46..8a57add 100644
--- a/doc/configuration.md
+++ b/doc/configuration.md
@@ -25,7 +25,7 @@ function default_context(): Context
|----------|------|
| `root_domain` | Root domain used by the services exposing a UI (`redis.{root_domain}`, `mailpit.{root_domain}`, …) |
| `registry` | Registry the build cache is pushed to and pulled from |
-| `repository` | Repository `castor docker:push` labels the images with, so the registry attaches the packages to it — deduced from `GITHUB_REPOSITORY` or the `origin` remote otherwise, see [`docker:push`](tasks.md#publishing-to-ghcr-io) |
+| `repository` | Repository `castor docker:push` labels the images with, so the registry attaches the packages to it — deduced from `GITHUB_REPOSITORY` or the `origin` remote otherwise, see [`docker:push`](tasks.md#publishing-to-ghcrio) |
| `project_name` | Compose project name — wins over the `name:` of `compose.yaml`, see [running two checkouts side by side](#running-two-checkouts-side-by-side) |
| `user_id` | UID the containers run as, defaults to your own |
| `twig_dockerfile_frontend` | Overrides the pinned [twig-dockerfile frontend](going-further/custom-dockerfile.md#pinning-the-frontend) |
diff --git a/doc/index.md b/doc/index.md
index bed0f59..07af155 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -1,4 +1,5 @@
---
+render_macros: true
title: Docker environments for PHP, driven from PHP
description: A Castor plugin that generates your Docker Compose stack and the tasks to drive it — applications, databases, router and HTTPS included.
---
@@ -17,7 +18,7 @@ description: A Castor plugin that generates your Docker Compose stack and the ta
## 🚀 TL;DR
-{% include-markdown "../README.md" start="" end="" %}
+{{ markdown("README.md", start="", end="") }}
## Want more?
diff --git a/doc/tasks.md b/doc/tasks.md
index a2c7ba4..584653a 100644
--- a/doc/tasks.md
+++ b/doc/tasks.md
@@ -265,7 +265,7 @@ and its image under `latest` — `--tag` publishes it under another name. A
service listing several caches in its `cache_from` pushes to all of them, and
its image to each registry among them. The
image carries `org.opencontainers.image.source`, which is what
-[attaches the package to your repository](#publishing-to-ghcr-io).
+[attaches the package to your repository](#publishing-to-ghcrio).
`--dry-run` prints the build plan bake resolved, without running it.
diff --git a/tools/mkdocs/.gitignore b/tools/doc/.gitignore
similarity index 77%
rename from tools/mkdocs/.gitignore
rename to tools/doc/.gitignore
index 4809185..18d7a9c 100644
--- a/tools/mkdocs/.gitignore
+++ b/tools/doc/.gitignore
@@ -5,3 +5,5 @@ doc/
example/
site/
overrides/partials/jolicode-footer.html
+__pycache__/
+__pycache__/
diff --git a/tools/doc/Dockerfile b/tools/doc/Dockerfile
new file mode 100644
index 0000000..c52a768
--- /dev/null
+++ b/tools/doc/Dockerfile
@@ -0,0 +1,9 @@
+FROM python:3-slim
+
+WORKDIR /doc/
+
+RUN pip install \
+ zensical \
+ markdown-callouts
+
+CMD ["zensical", "serve"]
diff --git a/tools/mkdocs/cards-layouts/custom.yml b/tools/doc/cards-layouts/custom.yml
similarity index 97%
rename from tools/mkdocs/cards-layouts/custom.yml
rename to tools/doc/cards-layouts/custom.yml
index 9ab24c8..73da07a 100644
--- a/tools/mkdocs/cards-layouts/custom.yml
+++ b/tools/doc/cards-layouts/custom.yml
@@ -6,7 +6,7 @@ layers:
- size: { width: 385, height: 100 }
offset: { x: 50, y: 50 }
background:
- image: /mkdocs/doc/assets/logo_CASTOR_line@2x.png
+ image: /doc/doc/assets/logo_CASTOR_line@2x.png
- size: { width: 1200, height: 10 }
offset: { x: 0, y: 620 }
diff --git a/tools/mkdocs/castor.php b/tools/doc/castor.php
similarity index 74%
rename from tools/mkdocs/castor.php
rename to tools/doc/castor.php
index 0064357..dcd138f 100644
--- a/tools/mkdocs/castor.php
+++ b/tools/doc/castor.php
@@ -13,9 +13,9 @@
use function Castor\io;
use function Castor\run;
-const IMAGE_NAME = 'castor-docker-mkdocs';
+const IMAGE_NAME = 'castor-docker-doc';
-#[AsTask(description: 'Build the MkDocs Docker image', namespace: 'docs', name: 'image')]
+#[AsTask(description: 'Build the documentation Docker image', namespace: 'docs', name: 'image')]
function image(bool $force = false): void
{
if (!$force && has_image()) {
@@ -24,7 +24,7 @@ function image(bool $force = false): void
return;
}
- io()->title('Building the MkDocs Docker image');
+ io()->title('Building the documentation Docker image');
run(['docker', 'build', '-t', IMAGE_NAME, __DIR__]);
}
@@ -32,10 +32,10 @@ function image(bool $force = false): void
#[AsTask(description: 'Fetch the JoliCode theme assets used by the documentation', namespace: 'docs', name: 'assets')]
function assets(): void
{
- io()->title('Fetching external assets for the MkDocs documentation');
+ io()->title('Fetching external assets for the documentation');
http_download(
- 'https://raw.githubusercontent.com/jolicode/oss-theme/refs/heads/main/MkDocs/extra.css',
+ 'https://raw.githubusercontent.com/jolicode/oss-theme/refs/heads/main/Zensical/extra.css',
root() . '/doc/assets/stylesheets/jolicode.css',
);
http_download(
@@ -64,9 +64,9 @@ function build(): void
{
assets();
- io()->title('Building the MkDocs documentation');
+ io()->title('Building the documentation');
- do_run(['mkdocs', 'build']);
+ do_run(['zensical', 'build', '--strict']);
io()->success(\sprintf('Documentation built in %s/site.', __DIR__));
}
@@ -76,11 +76,11 @@ function serve(int $port = 8000): void
{
assets();
- io()->title('Serving the MkDocs documentation');
+ io()->title('Serving the documentation');
io()->writeln(\sprintf(' http://127.0.0.1:%d', $port));
do_run(
- ['mkdocs', 'serve', '--livereload', '--dev-addr', '0.0.0.0:8000'],
+ ['zensical', 'serve', '--dev-addr', '0.0.0.0:8000'],
ports: [\sprintf('%d:8000', $port)],
);
}
@@ -117,16 +117,15 @@ function do_run(array $command, array $ports = []): Process
$arguments = [
'docker', 'run', '--rm', '--init',
'--user', "{$userId}:{$groupId}",
- // The image installs MkDocs system-wide, but the social plugin wants a
- // writable home for its font cache.
+ // The social plugin wants a writable home for its font cache.
'--env', 'HOME=/tmp',
- '--volume', \sprintf('%s:/mkdocs:cached', __DIR__),
+ '--volume', \sprintf('%s:/doc:cached', __DIR__),
// Mounted next to the docs so pages can include them, see the
- // "include-markdown" directives in doc/.
- '--volume', \sprintf('%s:/mkdocs/doc:cached', root() . '/doc'),
- '--volume', \sprintf('%s:/mkdocs/README.md:cached', root() . '/README.md'),
- '--volume', \sprintf('%s:/mkdocs/CHANGELOG.md:cached', root() . '/CHANGELOG.md'),
- '--volume', \sprintf('%s:/mkdocs/example:cached', root() . '/example'),
+ // markdown() macros in doc/.
+ '--volume', \sprintf('%s:/doc/doc:cached', root() . '/doc'),
+ '--volume', \sprintf('%s:/doc/README.md:cached', root() . '/README.md'),
+ '--volume', \sprintf('%s:/doc/CHANGELOG.md:cached', root() . '/CHANGELOG.md'),
+ '--volume', \sprintf('%s:/doc/example:cached', root() . '/example'),
];
foreach ($ports as $port) {
diff --git a/tools/doc/main.py b/tools/doc/main.py
new file mode 100644
index 0000000..fa30c77
--- /dev/null
+++ b/tools/doc/main.py
@@ -0,0 +1,27 @@
+"""Macros used by the documentation, see https://zensical.org/docs/compatibility/mkdocs/plugins/#macros"""
+
+import os
+
+ROOT = os.path.dirname(os.path.abspath(__file__))
+
+
+def define_env(env):
+ @env.macro
+ def markdown(path, start=None, end=None):
+ """Include a Markdown file, or only the part between the start and end markers."""
+ file = os.path.join(ROOT, path)
+ if not os.path.isfile(file):
+ raise FileNotFoundError(f'The file "{path}" included in "{env.page.path}" does not exist.')
+
+ with open(file) as f:
+ content = f.read()
+
+ for marker in (start, end):
+ if marker and marker not in content:
+ raise ValueError(f'The marker "{marker}" was not found in "{path}", included in "{env.page.path}".')
+ if start:
+ content = content.split(start, 1)[1]
+ if end:
+ content = content.split(end, 1)[0]
+
+ return content
diff --git a/tools/mkdocs/overrides/404.html b/tools/doc/overrides/404.html
similarity index 100%
rename from tools/mkdocs/overrides/404.html
rename to tools/doc/overrides/404.html
diff --git a/tools/mkdocs/overrides/main.html b/tools/doc/overrides/main.html
similarity index 96%
rename from tools/mkdocs/overrides/main.html
rename to tools/doc/overrides/main.html
index 793db39..fd7428d 100644
--- a/tools/mkdocs/overrides/main.html
+++ b/tools/doc/overrides/main.html
@@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block htmltitle %}
- {% if page.is_homepage %}
+ {% if page.meta and page.meta.title and not page.url %}
{{ page.meta.title }}
{% elif page.meta and page.meta.title %}
{{ page.meta.title }} - Castor Docker plugin
diff --git a/tools/doc/overrides/partials/header.html b/tools/doc/overrides/partials/header.html
new file mode 100644
index 0000000..0510026
--- /dev/null
+++ b/tools/doc/overrides/partials/header.html
@@ -0,0 +1,68 @@
+{# Retrieved from zensical/templates/partials/header.html to override site name #}
+{% set class = "md-header" %}
+{% if "navigation.tabs.sticky" in features %}
+ {% set class = class ~ " md-header--shadow md-header--lifted" %}
+{% elif "navigation.tabs" not in features %}
+ {% set class = class ~ " md-header--shadow" %}
+{% endif %}
+
+
+ {% if "navigation.tabs.sticky" in features %}
+ {% if "navigation.tabs" in features %}
+ {% include "partials/tabs.html" %}
+ {% endif %}
+ {% endif %}
+
diff --git a/tools/doc/zensical.toml b/tools/doc/zensical.toml
new file mode 100644
index 0000000..011bbe4
--- /dev/null
+++ b/tools/doc/zensical.toml
@@ -0,0 +1,111 @@
+[project]
+site_name = "Castor Docker - Docker environments for PHP"
+site_description = "A Castor plugin that generates your Docker Compose stack and the tasks to drive it, for PHP applications and everything around them."
+site_url = "https://castor-php.github.io/docker/"
+repo_url = "https://github.com/castor-php/docker"
+edit_uri = "edit/main/doc/"
+docs_dir = "doc"
+exclude_docs = "_nav.md"
+extra_javascript = [
+ "assets/javascripts/highlight.min.js",
+]
+extra_css = [
+ "assets/stylesheets/highlight-github.min.css",
+ "assets/stylesheets/jolicode.css",
+]
+
+[project.theme]
+logo = "assets/logo_CASTOR_line.svg"
+favicon = "assets/favicon.png"
+language = "en"
+custom_dir = "overrides"
+features = [
+ "content.action.edit",
+ "content.code.copy",
+ "content.tabs.link",
+ "navigation.footer",
+ "navigation.indexes",
+ "navigation.instant",
+ "navigation.instant.prefetch",
+ "navigation.path",
+ "navigation.sections",
+ "navigation.tabs",
+ "navigation.tabs.sticky",
+ "navigation.top",
+ "navigation.tracking",
+ "search.highlight",
+ "toc.integrate",
+]
+
+[project.theme.icon]
+repo = "fontawesome/brands/github"
+
+# Palette toggle for automatic mode
+[[project.theme.palette]]
+media = "(prefers-color-scheme)"
+scheme = "default"
+primary = "white"
+accent = "amber"
+toggle.icon = "material/brightness-auto"
+toggle.name = "Switch to light mode"
+
+# Palette toggle for light mode
+[[project.theme.palette]]
+media = "(prefers-color-scheme: light)"
+scheme = "default"
+primary = "white"
+accent = "amber"
+toggle.icon = "material/brightness-7"
+toggle.name = "Switch to dark mode"
+
+# Palette toggle for dark mode
+[[project.theme.palette]]
+media = "(prefers-color-scheme: dark)"
+scheme = "slate"
+primary = "black"
+accent = "amber"
+toggle.icon = "material/brightness-4"
+toggle.name = "Switch to system preference"
+
+[project.extra]
+generator = false
+
+[[project.extra.social]]
+icon = "fontawesome/brands/github-alt"
+link = "https://github.com/castor-php"
+
+[[project.extra.social]]
+icon = "fontawesome/brands/x-twitter"
+link = "https://x.com/jolicode"
+
+[project.markdown_extensions]
+admonition = {}
+attr_list = {}
+"github-callouts" = {}
+footnotes = {}
+pymdownx.details = {}
+pymdownx.highlight.use_pygments = false
+pymdownx.inlinehilite = {}
+pymdownx.snippets = {}
+pymdownx.superfences = {}
+pymdownx.tabbed.alternate_style = true
+pymdownx.tabbed.combine_header_slug = true
+pymdownx.tabbed.slugify.object = "pymdownx.slugs.slugify"
+pymdownx.tabbed.slugify.kwds.case = "lower"
+toc.permalink = true
+
+[project.plugins.search]
+
+[project.plugins."literate-nav"]
+nav_file = "_nav.md"
+
+# The pages opt in with "render_macros: true": most of them show Twig syntax that must stay as is.
+[project.plugins.macros]
+render_by_default = false
+on_error_fail = true
+
+[project.plugins.social]
+cards_layout_dir = "cards-layouts"
+cards_layout = "custom"
+cards_layout_options.font_family = "Noto Sans SC"
+cards_layout_options.background_color = "white"
diff --git a/tools/mkdocs/Dockerfile b/tools/mkdocs/Dockerfile
deleted file mode 100644
index d2c5fb1..0000000
--- a/tools/mkdocs/Dockerfile
+++ /dev/null
@@ -1,24 +0,0 @@
-FROM python:3-slim
-
-WORKDIR /mkdocs/
-
-RUN pip install \
- mkdocs\
- mkdocs-material\
- mkdocs-material[imaging]\
- markdown-callouts\
- mkdocs-literate-nav\
- mkdocs-include-markdown-plugin
-
-RUN apt-get update \
- && apt-get install -y --no-install-recommends \
- libcairo2-dev \
- libfreetype6-dev \
- libffi-dev \
- libjpeg-dev \
- libpng-dev \
- libz-dev \
- && apt-get clean \
- && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* \
-
-CMD ["mkdocs", "serve"]
diff --git a/tools/mkdocs/mkdocs.yml b/tools/mkdocs/mkdocs.yml
deleted file mode 100644
index 0852c93..0000000
--- a/tools/mkdocs/mkdocs.yml
+++ /dev/null
@@ -1,107 +0,0 @@
-site_name: Castor Docker - Docker environments for PHP
-site_description: A Castor plugin that generates your Docker Compose stack and the tasks to drive it, for PHP applications and everything around them.
-site_url: https://castor-php.github.io/docker/
-repo_url: https://github.com/castor-php/docker
-
-edit_uri: edit/main/doc/
-docs_dir: doc
-exclude_docs: |
- _nav.md
-
-theme:
- name: material
- logo: assets/logo_CASTOR_line.svg
- favicon: assets/favicon.png
- icon:
- repo: fontawesome/brands/github
- language: en
- palette:
- # Palette toggle for automatic mode
- - media: "(prefers-color-scheme)"
- scheme: default
- primary: white
- accent: amber
- toggle:
- icon: material/brightness-auto
- name: Switch to light mode
-
- # Palette toggle for light mode
- - media: "(prefers-color-scheme: light)"
- scheme: default
- primary: white
- accent: amber
- toggle:
- icon: material/brightness-7
- name: Switch to dark mode
-
- # Palette toggle for dark mode
- - media: "(prefers-color-scheme: dark)"
- scheme: slate
- primary: black
- accent: amber
- toggle:
- icon: material/brightness-4
- name: Switch to system preference
- features:
- - content.action.edit
- - content.code.copy
- - content.tabs.link
- - navigation.footer
- - navigation.indexes
- - navigation.instant
- - navigation.instant.prefetch
- - navigation.path
- - navigation.sections
- - navigation.tabs
- - navigation.tabs.sticky
- - navigation.top
- - navigation.tracking
- - search.highlight
- - toc.integrate
- custom_dir: overrides
-
-extra:
- social:
- - icon: fontawesome/brands/github-alt
- link: https://github.com/castor-php
- - icon: fontawesome/brands/x-twitter
- link: https://x.com/jolicode
- generator: false
-
-markdown_extensions:
- - admonition
- - attr_list
- - github-callouts
- - footnotes
- - pymdownx.details
- - pymdownx.highlight:
- use_pygments: false
- - pymdownx.inlinehilite
- - pymdownx.snippets
- - pymdownx.superfences
- - pymdownx.tabbed:
- alternate_style: true
- combine_header_slug: true
- slugify: !!python/object/apply:pymdownx.slugs.slugify
- kwds:
- case: lower
- - toc:
- permalink: true
-
-plugins:
- - search
- - literate-nav:
- nav_file: _nav.md
- - include-markdown
- - social:
- cards_layout_dir: cards-layouts
- cards_layout: custom
- cards_layout_options:
- font_family: Noto Sans SC
- background_color: white
-
-extra_javascript:
- - assets/javascripts/highlight.min.js
-extra_css:
- - assets/stylesheets/highlight-github.min.css
- - assets/stylesheets/jolicode.css
diff --git a/tools/mkdocs/overrides/partials/header.html b/tools/mkdocs/overrides/partials/header.html
deleted file mode 100644
index 690f27f..0000000
--- a/tools/mkdocs/overrides/partials/header.html
+++ /dev/null
@@ -1,68 +0,0 @@
-{# Retrieved from https://github.com/squidfunk/mkdocs-material/blob/master/src/templates/partials/header.html to override site name #}
-{% set class = "md-header" %}
-{% if "navigation.tabs.sticky" in features %}
- {% set class = class ~ " md-header--shadow md-header--lifted" %}
- {% elif "navigation.tabs" not in features %}
- {% set class = class ~ " md-header--shadow" %}
-{% endif %}
-
-
- {% if "navigation.tabs.sticky" in features %}
- {% if "navigation.tabs" in features %}
- {% include "partials/tabs.html" %}
- {% endif %}
- {% endif %}
-