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:
@@ -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
@@ -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`.
|
|
||||||
|
|||||||
@@ -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;">⚠️ This page is automatically published from source. Manual edits will be overwritten on the next pipeline run. • Source: [{{{source}}} view on Gitea] • 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;">⚠️ This page is automatically published from source. Manual edits will be overwritten on the next pipeline run. • Source: [{{{source}}} view on Gitea] • 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>
|
||||||
|
|||||||
Reference in New Issue
Block a user