Songs always carries a # Song title line so stripping makes sense there, but SES body structure is freeform and may use h1 deliberately. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pipelines/ses
Publishes SES light novel pages from mikkeli/ses-light-novel to https://wiki.novoyuuparosk.org under the SES: namespace.
- Source-file contract (frontmatter and body conventions): SCHEMA.md
- Cross-cutting setup (wiki URL, bot identity, runner, Gitea secrets/variables): repo root README
v1 scope
- YAML frontmatter parsing per SCHEMA.md
SES:title prefix — all pages live in the SES MediaWiki namespace- Pandoc-based markdown → wikitext body rendering
- Auto-generated banner (
{{Auto-generated|source=...|commit=<sha>}}) - Category injection:
[[Category:SES]]+[[Category:SES:<type>]]on every page; type fromtype:field, falling back to the file's parent directory name - Additional categories via optional
categorieslist - MediaWiki bot API write with idempotency — no-op if wiki content matches generated output
- Files without
wiki.publish: truesilently skipped - Pages with existing non-auto-generated content skipped (logged to stderr)
Not in scope (intentional):
- Sibling/cross-page link resolution — write wikilinks directly in the markdown body
- Companion file uploads
Source repo and branch convention
Source repo: mikkeli/ses-light-novel. The pipeline triggers on push to master.
No branch convention enforced — work directly on master or use whatever branch workflow suits.
Excluded paths (won't trigger the pipeline):
README.md,.gitignore,.claude/**
Invocation
The pipeline runs as a self-contained Gitea Actions workflow in mikkeli/ses-light-novel (.gitea/workflows/publish.yml). It clones this repo at runtime to get the renderer.
Triggers:
pushtomaster(path-filtered as above)workflow_dispatch— manual trigger from the Gitea Actions UI
Environment (secrets and variables)
| Name | Kind | Source | Purpose |
|---|---|---|---|
WIKI_API_URL |
variable | Gitea (cross-cutting) | MediaWiki action API endpoint |
WIKI_BASE_URL |
variable | Gitea (cross-cutting) | Wiki base URL |
WIKI_BOT_USER |
secret | Gitea (cross-cutting) | Bot login |
WIKI_BOT_PASSWORD |
secret | Gitea (cross-cutting) | BotPasswords value |
URL_TO_GITEA |
variable | Gitea (cross-cutting) | Gitea instance base URL for cloning |
FAPAT |
secret | Gitea (cross-cutting) | Full-Access PAT for cloning private repos |
SOURCE_REF |
env (set in workflow) | github.sha |
Pushed commit SHA, used in banner and edit summary |
GITEA_REPO_URL |
env (set in workflow) | composed from vars | Source-file URL base for the banner link |
Dependencies
Job container: novoyuuparosk-wiki-runner:latest (pre-built, stored in the local Docker daemon on the runner host). Bakes in Python 3.12, Pandoc, and all pipeline Python packages — no install steps at job runtime.
See the Dockerfile at the repo root and requirements.txt in this directory. Rebuild the image after changes to either.
Shared code lives in lib/wiki.py (repo root).
One-off wiki setup
Template:Auto-generated— must exist on the wiki (shared with songs pipeline; already created).
Decisions log (ses pipeline)
| Decision | Value | Date |
|---|---|---|
| Namespace | All pages prefixed SES: — auto-applied by renderer, not written in source title: |
2026-06-10 |
| Type resolution | type: frontmatter field; falls back to immediate parent directory name |
2026-06-10 |
| Categories | Category:SES + Category:SES:<type> on every page |
2026-06-10 |
| Sibling links | Not implemented — write [[SES:Page title]] wikilinks directly in the body |
2026-06-10 |
| WIP exclusion | None — wiki.publish: false is the only gate |
2026-06-10 |
| Publish mode | Always --all |
2026-06-10 |
| Idempotency | Commit SHA in banner, not timestamp | 2026-06-10 |
| Manual-page protection | Bot skips pages without {{Auto-generated banner |
2026-06-10 |
Status
v1 in development.