feat(ses): add SES light novel pipeline; extract shared lib/wiki.py

New pipeline at pipelines/ses/ publishes mikkeli/ses-light-novel to
the SES: MediaWiki namespace. All pages get Category:SES and
Category:SES:<type> (type from frontmatter, fallback to parent dir name).

Shared functions (connect_wiki, markdown_to_wikitext, strip_first_h1,
AUTO_BANNER_PREFIX) extracted from songs/publish.py into lib/wiki.py;
songs refactored to import from there.

Also adds publish-ses.yml workflow stub and updates Dockerfile and
root README.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 16:23:26 +09:00
parent 6144d1f399
commit dfe1f9c631
10 changed files with 491 additions and 41 deletions
+84
View File
@@ -0,0 +1,84 @@
# 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](SCHEMA.md)
- Cross-cutting setup (wiki URL, bot identity, runner, Gitea secrets/variables): [repo root README](../../README.md)
## v1 scope
- YAML frontmatter parsing per [SCHEMA.md](SCHEMA.md)
- `SES:` title prefix — all pages live in the SES MediaWiki namespace
- Pandoc-based markdown → wikitext body rendering
- First-line h1 stripping
- Auto-generated banner (`{{Auto-generated|source=...|commit=<sha>}}`)
- Category injection: `[[Category:SES]]` + `[[Category:SES:<type>]]` on every page; type from `type:` field, falling back to the file's parent directory name
- Additional categories via optional `categories` list
- MediaWiki bot API write with idempotency — no-op if wiki content matches generated output
- Files without `wiki.publish: true` silently 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:
- `push` to `master` (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.