Step 23 refined: init takes a YAML fragment in the config schema

Refinement decided 2026-09-01: each side gets its native format — the
wrapper keeps a bash-sourceable transport env file, Werkator takes a
YAML fragment in its own config schema via 'init --apply FILE',
deep-merged idempotently. The env-to-config mapping table disappears
entirely: the fragment IS configuration in the one schema, validated by
the existing binding, documented by the existing reference. The env
file names the fragment (WERKATOR_INIT_CONFIG), keeping one entry point
per instance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-09-01 18:32:29 +02:00
co-authored by Claude Fable 5
parent f8e8188fc6
commit 7a24ad1d7f
2 changed files with 18 additions and 31 deletions
+16 -29
View File
@@ -13,47 +13,34 @@ Smaller duplications of the same kind: the script re-implements control-token ge
Werkator becomes the executing app wherever possible; `tools/remote` shrinks to a wrapper: build artifacts locally, transport them, execute Werkator/werkdock remotely, switch services.
Parameters travel as an **env file**, not as many CLI options:
Parameters travel as **files**, not as many CLI options — and each side gets the format that is native to it (refined 2026-09-01):
```bash
tools/remote --env .env.mih34 werkator repo-init
werkator --env .env.mih34 init
```
- The **wrapper** keeps a small, bash-sourceable env file with the transport values only: `tools/remote --env .env.mih34 werkator repo-init` selects the target (default: `.env`), so several instances (`.env.mih34`, `.env.vm4006`, later a Werkbaum instance) are files, not edits.
- **Werkator** takes a **YAML fragment in its own config schema**: `werkator init --apply mih34.yml` deep-merges the fragment into the machine config, idempotently — creating sections that are missing, updating the given values, never duplicating.
No mapping table exists: the fragment says `server: {port: …}` and `builds: {default: {bwrap: …}}` directly, is validated by the existing schema binding, and is documented by the existing `docs/configuration.md`.
- The wrapper uploads the fragment alongside the artifacts and calls `werkator init --apply …` remotely — the heredocs and `sed` calls in `tools/remote` disappear.
- The env file names the fragment (`WERKATOR_INIT_CONFIG=mih34.yml`), keeping one entry point per instance.
- `tools/remote --env FILE` selects the target (default: `.env`), so several instances (`.env.mih34`, `.env.vm4006`, later a Werkbaum instance) are files, not edits.
- `werkator --env FILE` loads the same file; `init` reads the `WERKATOR_*` values from it (or from the process environment) and writes **real values** into the files it owns, instead of commented templates the script then patches.
- The wrapper uploads the env file alongside the artifacts and calls `werkator --env … init` remotely — the heredocs and `sed` calls in `tools/remote` disappear.
The removed legacy env-to-YAML conversion stays removed — there is no conversion at all anymore: the fragment already *is* configuration in the one schema, applied once at setup time; the server reads nothing but its YAML at runtime.
Not a relapse into the removed legacy env-to-YAML conversion: the env file is an *input to init at setup time*, written once into the machine config — the server never reads `WERKATOR_*` at runtime, and the YAML stays the single source of truth.
## The Files per Instance
## Env Keys and Their Targets
Consumed by `werkator init` (written into the machine config; re-runs update these managed values in place, schema-aware instead of `sed`):
| Env key | Config key |
|---|---|
| `WERKATOR_PORT` | `server.port` |
| `WERKATOR_DOMAIN` | `server.publicBaseUrl` (`https://<domain>/`) |
| `WERKATOR_MEMORY_MAX` / `WERKATOR_TASKS_MAX` | `server.systemd.memoryMax` / `tasksMax` |
| `WERKATOR_ROOTFS` (remote path) | `builds.default.bwrap.rootfs` (+ `bwrap.enabled: true`) |
| `WERKATOR_WERKDOCK` | `builds.default.bwrap.werkdock` |
Transport-only keys (`WERKATOR_REMOTE`, `WERKATOR_PATH`, `WERKATOR_LOCAL_PORT`, `WERKATOR_REPO_URL`) stay the wrapper's business; init ignores them, documented.
Secrets (`git.token`, `gitea` keys) stay out of env files on purpose — they are entered in the machine config on the host, as today.
- `.env.mih34` (wrapper): `WERKATOR_REMOTE`, `WERKATOR_PATH`, `WERKATOR_LOCAL_PORT`, `WERKATOR_REPO_URL`, `WERKATOR_ROOTFS` (the *local* archive to upload), `WERKATOR_INIT_CONFIG`.
- `mih34.yml` (init fragment): `server.*` (port, publicBaseUrl, systemd limits) and `builds.default.bwrap.*` (enabled, the *remote* rootfs path, werkdock path) — exactly the blocks the script used to append.
- Secrets (`git.token`, `gitea` keys) stay out of both files on purpose — they are entered in the machine config on the host, as today.
## The Sessions
### A — Werkator side
- Root-level picocli option `--env FILE`: loads `KEY=VALUE` lines into the command's parameter environment; unknown keys are ignored (they belong to the wrapper).
- `init` writes real values for the keys above when they are set — creating the sections when missing, updating the managed values when present, never duplicating (the duplication class dies here).
- `init --systemd` keeps generating the units; decide in the session whether the Apache `.htaccess` becomes part of the host-integration output when `WERKATOR_PORT`/`WERKATOR_DOMAIN` are set (proposal: yes, under `init --systemd`, since it is generated host integration exactly like the units).
- `init --apply FILE`: deep-merge the YAML fragment into the machine config — reusing the loader's merge, creating missing sections, updating given values, never duplicating (the duplication class dies here); a fragment that fails the schema binding or carries unknown keys is refused loudly.
- `init --systemd` keeps generating the units; decide in the session whether the Apache `.htaccess` becomes part of the host-integration output when the applied config carries `server.port` and a public domain (proposal: yes, under `init --systemd`, since it is generated host integration exactly like the units).
- New subcommand `werkator control-token`: print the token, creating it exactly like `ControlTokenService` does — the bash duplication in the wrapper dies.
- Tests per the writing-tests conventions; `docs/configuration.md` and `docs/bootstrapping.md` document the env keys.
- Tests per the writing-tests conventions; `docs/bootstrapping.md` documents `--apply` (the fragment keys need no new reference — they are ordinary `docs/configuration.md` keys).
### B — Wrapper side
- `tools/remote --env FILE` (default `.env`); the file is uploaded and every remote `werkator` call gets `--env`.
- `tools/remote --env FILE` (default `.env`); the init fragment named by `WERKATOR_INIT_CONFIG` is uploaded, and the remote init runs with `--apply`.
- `repo-init` and `instance-start` lose their heredoc/`sed` config writing; `control-token` delegates to the new subcommand.
- `check-prerequisites` uploads the werkdock binary first and runs `werkdock doctor`; `tools/werkator-build-prerequisites.sh` retires (its werkdock port is the survivor).
@@ -63,6 +50,6 @@ Secrets (`git.token`, `gitea` keys) stay out of env files on purpose — they ar
## Acceptance Criteria
- Session A: `werkator --env … init` writes and updates the managed config values idempotently; `werkator control-token` exists; full suite green.
- Session A: `werkator init --apply …` merges and re-merges a fragment idempotently; `werkator control-token` exists; full suite green.
- Session B: `tools/remote` contains no YAML heredocs and no `sed` into the machine config; the prerequisites bash script is gone.
- Session C: the mih34 re-runs change nothing on a configured host and the deployment docs show only `--env`-style calls.
+2 -2
View File
@@ -97,7 +97,7 @@ Added to correct the bwrap prototype's drift toward self-building on the webspac
Added after step 21 session D exposed that `tools/remote` re-implements configuration Werkator owns (2026-09-01):
- [ ] `23-init-owns-the-files.md` — Werkator becomes the executing app, `tools/remote` a thin wrapper: parameters travel as env files (`remote --env .env.mih34 werkator …`, `werkator --env … init`), `init` writes real values idempotently instead of templates the script patches, `werkator control-token` and `werkdock doctor` replace the bash duplications
- [ ] `23-init-owns-the-files.md` — Werkator becomes the executing app, `tools/remote` a thin wrapper: the wrapper takes a transport env file (`remote --env .env.mih34 werkator …`), init takes a YAML fragment in the real config schema (`werkator init --apply mih34.yml`, deep-merged idempotently — no mapping table, no heredocs), `werkator control-token` and `werkdock doctor` replace the bash duplications
Added for surfacing build time as a trend (2026-08-31):
@@ -114,4 +114,4 @@ Step 18 depends on nothing in code but on the watched repository having migrated
Step 19 depends on nothing; `WatcherState` and `/api/watcher` already carry everything it needs to render.
Step 20 depends on nothing; the duration is already recorded, and the trend is derived read-only from `repository.history()`.
Step 21 depends on 17; its sessions B and C grow Werkdock in the `werkdock/` subdirectory (later its own repository), and session D supersedes the self-build prototype in `tools/remote`.
Step 23 depends on 21 session D; its env-file convention (one file per instance) also feeds step 22's instance setup and should land before Werkbaum rolls out.
Step 23 depends on 21 session D; its per-instance file convention (transport env + init fragment) also feeds step 22's instance setup and should land before Werkbaum rolls out.