docs: update all docs to reflect v1 live state

- template_auto_generated.wikitext: generated_at → commit parameter
- SCHEMA.md: same banner fix; clarify that frontmatter-less files are
  silently skipped, not errors
- pipelines/songs/README.md: full rewrite — v1 live status, self-
  contained workflow pattern, correct env table, modes, one-off setup
  marked done, full decisions log
- README.md: architecture reflects actual pattern (no workflow_call),
  URL_TO_GITEA variable added, setup checklist marked complete, host
  network mode documented

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 18:39:13 +09:00
parent cf6567cd77
commit afc6a62791
4 changed files with 62 additions and 54 deletions
+23 -15
View File
@@ -6,24 +6,26 @@ CI/CD pipelines that auto-apply commits to https://wiki.novoyuuparosk.org from u
| Path | Source repo | Purpose | Status | | Path | Source repo | Purpose | Status |
|---|---|---|---| |---|---|---|---|
| [`pipelines/songs/`](pipelines/songs/) | `mikkeli/ncmr-songs` | Song lyric pages | in development (v1, no executable code yet) | | [`pipelines/songs/`](pipelines/songs/) | `mikkeli/ncmr-songs` | Song lyric pages | v1 live |
Per-pipeline READMEs cover everything specific to that pipeline (source schema, renderer, runtime, decisions). This root README covers only what's cross-cutting. Per-pipeline READMEs cover everything specific to that pipeline (source schema, renderer, runtime, decisions). This root README covers only what's cross-cutting.
## Architecture ## Architecture
Hybrid layout. The Gitea Actions trigger has to live in the source repo (Gitea only fires workflows from `.gitea/workflows/` of the pushed-to repo); the real logic lives here. Source-repo workflows are thin stubs that call reusable workflows defined here. Hybrid layout. The Gitea Actions trigger must live in the source repo (Gitea only fires workflows from `.gitea/workflows/` of the pushed-to repo); the rendering logic lives here. The source-repo workflow is self-contained but clones this repo at runtime to get the renderer.
``` ```
<source-repo>/ <source-repo>/
.gitea/workflows/<name>.yml <- thin stub, calls into this repo .gitea/workflows/<name>.yml <- workflow: installs deps, clones this repo, runs renderer
novoyuuparosk-auto-wiki/ <- this repo novoyuuparosk-auto-wiki/ <- this repo
.gitea/workflows/<pipeline>.yml <- reusable workflows (the actual logic) .gitea/workflows/<pipeline>.yml <- reusable workflow stubs (kept for reference; not actively called)
pipelines/<pipeline>/ <- per-pipeline code, schema, templates pipelines/<pipeline>/ <- per-pipeline code, schema, templates
lib/ <- shared modules (MediaWiki client, etc.) lib/ <- shared modules (MediaWiki client, etc.)
``` ```
Note: `workflow_call` across private repos was abandoned — the auto-generated run token is scoped to the triggering repo only and cannot clone a private callee. The source-repo workflow clones this repo directly using `FAPAT`.
## Wiki ## Wiki
- Base URL: https://wiki.novoyuuparosk.org - Base URL: https://wiki.novoyuuparosk.org
@@ -44,6 +46,8 @@ One `act_runner` instance serves all pipelines. Runs on a Pi 5 (Raspberry Pi OS
`act_runner` build: `linux-arm64`, from the `gitea/act_runner` Docker image. `act_runner` build: `linux-arm64`, from the `gitea/act_runner` Docker image.
Container network mode: `host` — required so job containers can reach `localhost:3005` (Gitea) and resolve mDNS hostnames.
## Gitea Actions setup (cross-cutting) ## Gitea Actions setup (cross-cutting)
Secrets and variables are scoped to user `mikkeli` (no orgs on this instance), inherited by all repos under that account. Secrets and variables are scoped to user `mikkeli` (no orgs on this instance), inherited by all repos under that account.
@@ -51,11 +55,12 @@ Secrets and variables are scoped to user `mikkeli` (no orgs on this instance), i
**Secrets:** **Secrets:**
- `WIKI_BOT_USER` = `Dubrowski@giteaAutomaton` - `WIKI_BOT_USER` = `Dubrowski@giteaAutomaton`
- `WIKI_BOT_PASSWORD` = the value from *Bot identity* above - `WIKI_BOT_PASSWORD` = the value from *Bot identity* above
- `FAPAT` = Full-Access PAT under `mikkeli`, used by source-repo shim workflows to clone this repo at workflow time - `FAPAT` = Full-Access PAT under `mikkeli`, used by source-repo workflows to clone this repo at runtime
**Variables:** **Variables:**
- `WIKI_BASE_URL` = `https://wiki.novoyuuparosk.org` - `WIKI_BASE_URL` = `https://wiki.novoyuuparosk.org`
- `WIKI_API_URL` = `https://wiki.novoyuuparosk.org/api.php` - `WIKI_API_URL` = `https://wiki.novoyuuparosk.org/api.php`
- `URL_TO_GITEA` = Gitea instance base URL (e.g. `http://localhost:3005`). Named with `URL_TO_` prefix — Gitea blocks variable names starting with `GITEA_` or `GITHUB_`.
## Branch naming ## Branch naming
@@ -66,12 +71,14 @@ Secrets and variables are scoped to user `mikkeli` (no orgs on this instance), i
| Decision | Value | Date | | Decision | Value | Date |
|---|---|---| |---|---|---|
| Architecture | Hybrid: stub in source repo, logic in this repo via reusable workflows | 2026-06-09 | | Architecture | Hybrid: source-repo workflow clones this repo at runtime for the renderer | 2026-06-09 |
| Workflow pattern | Gitea reusable workflows (`workflow_call`); requires Gitea ≥ 1.20 — confirmed 1.25+ | 2026-06-09 | | Workflow pattern | Self-contained (not `workflow_call`) — cross-repo `workflow_call` blocked by token scoping on private repos | 2026-06-09 |
| Runner execution | Docker, added as a service to the existing Gitea docker-compose | 2026-06-09 | | Runner execution | Docker, added as a service to the existing Gitea docker-compose | 2026-06-09 |
| Runner network mode | `host` — job containers need to reach Gitea on localhost | 2026-06-09 |
| Secret/runner scope | User-level on `mikkeli` (no orgs on this instance) | 2026-06-09 | | Secret/runner scope | User-level on `mikkeli` (no orgs on this instance) | 2026-06-09 |
| MediaWiki API path | `api.php` (classic action API) | 2026-06-09 | | MediaWiki API path | `api.php` (classic action API) | 2026-06-09 |
| Branch naming (this repo) | `automation/<pipeline>` for pipeline-development branches | 2026-06-09 | | Branch naming (this repo) | `automation/<pipeline>` for pipeline-development branches | 2026-06-09 |
| Variable naming | `URL_TO_GITEA` not `GITEA_URL` — Gitea blocks `GITEA_`/`GITHUB_` prefixes | 2026-06-09 |
Per-pipeline decisions live in each pipeline's README. Per-pipeline decisions live in each pipeline's README.
@@ -79,16 +86,17 @@ Per-pipeline decisions live in each pipeline's README.
Via the Gitea web UI logged in as `mikkeli`: Via the Gitea web UI logged in as `mikkeli`:
- [v] User-scoped secrets and variables set per *Gitea Actions setup* above - [x] User-scoped secrets and variables set per *Gitea Actions setup* above
- [v] `WIKI_BOT_USER` - [x] `WIKI_BOT_USER`
- [v] `WIKI_BOT_PASSWORD` - [x] `WIKI_BOT_PASSWORD`
- [v] `FAPAT` (Full-Access PAT — value not stored in this README; saved directly into the Gitea secret. Regenerate if lost.) - [x] `FAPAT` (Full-Access PAT — value not stored in this README; saved directly into the Gitea secret. Regenerate if lost.)
- [v] `WIKI_BASE_URL` - [x] `WIKI_BASE_URL`
- [v] `WIKI_API_URL` - [x] `WIKI_API_URL`
- [x] `URL_TO_GITEA`
With Pi access: With Pi access:
- [ ] Add `act_runner` service to the existing Gitea docker-compose - [x] Add `act_runner` service to the existing Gitea docker-compose
- [ ] Generate a runner registration token at `/-/admin/actions/runners` (or `/user/settings/actions/runners` if user-scoped runners are exposed), bake into the compose env, `docker compose up -d act_runner`, confirm "online" in the Gitea UI - [x] Generate a runner registration token at `/-/admin/actions/runners`, bake into the compose env, `docker compose up -d act_runner`, confirm "online" in the Gitea UI
Per-pipeline setup lives in each pipeline's README. Start with [`pipelines/songs/`](pipelines/songs/). Per-pipeline setup lives in each pipeline's README. Start with [`pipelines/songs/`](pipelines/songs/).
+31 -29
View File
@@ -12,8 +12,10 @@ Implemented:
- YAML frontmatter parsing per [SCHEMA.md](SCHEMA.md) - YAML frontmatter parsing per [SCHEMA.md](SCHEMA.md)
- Pandoc-based markdown → wikitext body rendering - Pandoc-based markdown → wikitext body rendering
- First-line h1 stripping - First-line h1 stripping
- Auto-generated banner + album category injection - Auto-generated banner (`{{Auto-generated|source=...|commit=<sha>}}`) + album category injection
- MediaWiki bot API write with idempotency (no-op edits skipped) - 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) — delete the page or add the banner to hand ownership to the bot
Deferred (schema reserves the fields; renderer doesn't yet act on them): Deferred (schema reserves the fields; renderer doesn't yet act on them):
@@ -35,41 +37,39 @@ Source repo: `mikkeli/ncmr-songs`. The pipeline triggers on push to `master`.
## Invocation ## Invocation
The pipeline is invoked via a reusable Gitea Actions workflow (`.gitea/workflows/publish-songs.yml` in this repo). The `ncmr-songs` repo holds a thin stub workflow (`.gitea/workflows/publish.yml`) that calls into this one on push to `master`. The pipeline runs as a self-contained Gitea Actions workflow in `mikkeli/ncmr-songs` (`.gitea/workflows/publish.yml`). It clones this repo at runtime to get the renderer.
### Workflow inputs and secrets Triggers:
- `push` to `master` (path-filtered as above)
- `workflow_dispatch` — manual trigger from the Gitea Actions UI; useful after renderer or template changes without needing a dummy commit
### Environment (secrets and variables)
| Name | Kind | Source | Purpose | | Name | Kind | Source | Purpose |
|---|---|---|---| |---|---|---|---|
| `WIKI_API_URL` | variable | Gitea (cross-cutting) | MediaWiki action API endpoint | | `WIKI_API_URL` | variable | Gitea (cross-cutting) | MediaWiki action API endpoint |
| `WIKI_BASE_URL` | variable | Gitea (cross-cutting) | Wiki base URL (used in the banner link) | | `WIKI_BASE_URL` | variable | Gitea (cross-cutting) | Wiki base URL |
| `WIKI_BOT_USER` | secret | Gitea (cross-cutting) | Bot login | | `WIKI_BOT_USER` | secret | Gitea (cross-cutting) | Bot login (`Dubrowski@giteaAutomaton`) |
| `WIKI_BOT_PASSWORD` | secret | Gitea (cross-cutting) | BotPasswords value | | `WIKI_BOT_PASSWORD` | secret | Gitea (cross-cutting) | BotPasswords value |
| `SOURCE_DIR` | workflow input | from the stub | Path to checked-out `ncmr-songs` working tree | | `URL_TO_GITEA` | variable | Gitea (cross-cutting) | Gitea instance base URL for cloning |
| `SOURCE_REF` | workflow input | from the stub | The pushed-to commit SHA | | `FAPAT` | secret | Gitea (cross-cutting) | Full-Access PAT for cloning private repos |
| `SOURCE_BASE_REF` | workflow input | from the stub | The SHA prior to the push (for diff-based change detection) | | `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 ## Dependencies
- Python 3.12+ - Python 3.12+ (job container: `python:3.12-slim`)
- Pandoc 2.x or newer (apt-installable in the runner container) - Pandoc (apt-installed in the job container)
- `mwclient` or `requests` for the MediaWiki API - `python-frontmatter`, `mwclient`, `PyYAML` — see `requirements.txt`
- `PyYAML` for frontmatter parsing
- `python-frontmatter` (convenience wrapper around PyYAML for markdown frontmatter)
`requirements.txt` will be added when the implementation lands.
## One-off wiki setup
Before the first run, two things must exist on the wiki:
1. **`Template:Auto-generated`** — the banner injected at the top of every auto-published page. Wikitext for this template is included in the implementation step (not yet written).
2. **The bot user has edit rights** for the namespace(s) the pipeline writes to. The default main namespace is fine; verify by attempting a manual edit via the bot account before relying on the pipeline.
## Modes ## Modes
- **Incremental** (default, triggered by push to `master`): publishes only files changed between `SOURCE_BASE_REF` and `SOURCE_REF`. The pipeline always runs `--all`: every file with `wiki.publish: true` (not under `wip/`) is checked against the current wiki content. Writes only happen when content has changed (commit SHA in the banner drives idempotency). This is appropriate for the current repo size.
- **Full** (`--all` flag, triggered by `workflow_dispatch`): re-publishes every publishable file in the source tree. Use after template or renderer changes.
## One-off wiki setup
- [x] **`Template:Auto-generated`** — exists on the wiki. Wikitext source: `template_auto_generated.wikitext` in this directory.
- [x] **Bot user in `bot` group** — grants `skipcaptcha`; verified working.
## Decisions log (songs pipeline) ## Decisions log (songs pipeline)
@@ -79,11 +79,13 @@ Before the first run, two things must exist on the wiki:
| Body rendering | Pandoc-based, with a thin Python pre/post-processor | 2026-06-09 | | Body rendering | Pandoc-based, with a thin Python pre/post-processor | 2026-06-09 |
| Excluded paths | `wip/**`, plus repo-meta files | 2026-06-09 | | Excluded paths | `wip/**`, plus repo-meta files | 2026-06-09 |
| Page template | Designed from scratch (no existing wiki pages to mirror) | 2026-06-09 | | Page template | Designed from scratch (no existing wiki pages to mirror) | 2026-06-09 |
| Album landing pages | Auto-injected `[[Category:<album>]]`; the wiki category page is user-written and not overwritten by the pipeline | 2026-06-09 | | Album landing pages | Auto-injected `[[Category:<album>]]`; the wiki category page is user-written and not overwritten | 2026-06-09 |
| Source-repo branch convention | `autowiki/<song-slug>` short-lived branches in `ncmr-songs` | 2026-06-09 | | Source-repo branch convention | `autowiki/<song-slug>` short-lived branches in `ncmr-songs` | 2026-06-09 |
| Idempotency | Commit SHA in banner (`commit=<sha>`), not timestamp — stable across identical re-runs | 2026-06-09 |
| Publish mode | Always `--all`; no diff detection — small repo, simpler than fragile git-diff gating | 2026-06-09 |
| Manual-page protection | Bot skips pages without the `{{Auto-generated` banner to avoid overwriting hand-written content | 2026-06-09 |
| `workflow_call` abandoned | Cross-repo `workflow_call` fails — run token scoped to triggering repo; cannot clone private callee | 2026-06-09 |
## Status ## Status
Schema and pipeline overview committed. No executable code yet. v1 live. Pipeline active and publishing to https://wiki.novoyuuparosk.org.
**Next**: implement renderer (Python module), workflow YAMLs (this repo + `ncmr-songs` stub), `Template:Auto-generated` wikitext, and end-to-end dry-run on `ses/pulse.md`.
+5 -7
View File
@@ -94,7 +94,7 @@ For each `.md` with `wiki.publish: true` and not under `wip/`:
3. Replace `[[PLACEHOLDER]]` markers with safe tokens. *(Deferred v1; no-op until siblings are implemented.)* 3. Replace `[[PLACEHOLDER]]` markers with safe tokens. *(Deferred v1; no-op until siblings are implemented.)*
4. Pipe the body through `pandoc -f markdown -t mediawiki`. 4. Pipe the body through `pandoc -f markdown -t mediawiki`.
5. Re-substitute sibling tokens with resolved `[[Page|Display]]` wikilinks. *(Deferred v1.)* 5. Re-substitute sibling tokens with resolved `[[Page|Display]]` wikilinks. *(Deferred v1.)*
6. Prepend the auto-generated banner: `{{Auto-generated|source=<source URL>|generated_at=<ISO timestamp>}}`. 6. Prepend the auto-generated banner: `{{Auto-generated|source=<source URL>|commit=<sha>}}`.
7. Append the album category: `[[Category:<album>]]`. 7. Append the album category: `[[Category:<album>]]`.
8. Read the current wiki page content via the MediaWiki API; if identical to the generated output, skip the write (idempotency — keeps the wiki history clean). 8. Read the current wiki page content via the MediaWiki API; if identical to the generated output, skip the write (idempotency — keeps the wiki history clean).
9. Otherwise, write the page with an edit summary referencing the source commit. 9. Otherwise, write the page with an edit summary referencing the source commit.
@@ -164,14 +164,12 @@ wiki:
## Validation errors the renderer must produce ## Validation errors the renderer must produce
The pipeline fails fast and loud on the following: Files without a `wiki.publish: true` frontmatter field are silently skipped — no error. The pipeline fails fast and loud only on files that are opted in (`wiki.publish: true`) with invalid data:
- Missing or unparseable frontmatter block - Missing required field (`title` or `album`) on a file with `wiki.publish: true`
- Missing required field (`title`, `album`, `wiki.publish`) - `wiki.publish` present but not a boolean
- `title` is empty or whitespace-only
- `wiki.publish` is not a boolean
- `release_date` (if present) does not parse as YYYY-MM-DD - `release_date` (if present) does not parse as YYYY-MM-DD
- `siblings` (if present) references a file that doesn't exist - `siblings` (if present) references a file that doesn't exist
- Two source files declare the same `title` (page-name collision) - Two publishable source files declare the same `title` (page-name collision)
Validation runs before any wiki API calls. A failed validation aborts the run with a non-zero exit and leaves the wiki untouched. Validation runs before any wiki API calls. A failed validation aborts the run with a non-zero exit and leaves the wiki untouched.
@@ -1,9 +1,9 @@
{{#if:{{{source|}}}|<div style="background:#f8f9fa;border:1px solid #a2a9b1;padding:0.4em 0.8em;margin-bottom:1em;font-size:0.85em;color:#54595d;">&#x26A0;&#xFE0F; This page is automatically published from source. Manual edits will be overwritten on the next pipeline run. &nbsp;&bull;&nbsp; Source: [{{{source}}} view on Gitea] &nbsp;&bull;&nbsp; Generated: {{{generated_at}}}</div>}} {{#if:{{{source|}}}|<div style="background:#f8f9fa;border:1px solid #a2a9b1;padding:0.4em 0.8em;margin-bottom:1em;font-size:0.85em;color:#54595d;">&#x26A0;&#xFE0F; This page is automatically published from source. Manual edits will be overwritten on the next pipeline run. &nbsp;&bull;&nbsp; Source: [{{{source}}} view on Gitea] &nbsp;&bull;&nbsp; Commit: {{{commit}}}</div>}}
<noinclude> <noinclude>
== Usage == == Usage ==
Applied automatically by the songs pipeline. Parameters: Applied automatically by the songs pipeline. Parameters:
* <code>source</code> — URL to the source file on Gitea * <code>source</code> — URL to the source file on Gitea at the publishing commit
* <code>generated_at</code> — ISO 8601 timestamp of the publishing run * <code>commit</code> — commit SHA of the source that produced this page
[[Category:Auto-generated templates]] [[Category:Auto-generated templates]]
</noinclude> </noinclude>