feat(server): Routen, Seiten und Artefakte tragen das Repository — /repos/<name>/… (Sitzung D)
Zweite Hälfte von Sitzung D (docs/plan/22-multi-repo.md, PR #13): Der Server bediente bisher genau ein Repository der Registry. Jede Route — API, Seiten, Artefakt-Dateien — arbeitete auf `registry.current()`; ein zweites registriertes Repository wurde gebaut und gepollt, war aber unsichtbar und unerreichbar. Jeder Controller löst sein Repository jetzt je Anfrage auf, statt das bediente als Bohne zu halten; jede Route ist zweimal gemappt. Die unscoped Form ist kein Übergangs-Alias, sondern dauerhaft die Art zu sagen „das bediente Repository" — Lesezeichen und die nach Gitea geposteten Links kennen kein Segment. Entschieden gegen die Repo-Spalte: Die Seiten bleiben je Repository, die Navigation bekommt einen Umschalter. Die Aktionen einer Zeile brauchen das Repository ohnehin, Branches kommen von einem origin und Artefakte aus einem Store — und bei dem einen Repository, das die meisten Installationen haben, wäre eine Spalte nur Rauschen. Das Link-Präfix folgt der ZAHL der bedienten Repositories, nicht dem Weg, über den eine Seite erreicht wurde: mit einem behält die Installation ihre bisherigen URLs (Abnahmekriterium der Sitzung), mit mehreren benennt jeder Link sein Repository. werkator.js liest das Präfix einmal aus einem `werkator-repo-base`-Meta. `BranchPermalinks.permanentUrl` bekommt es ebenfalls — der permanente Schlüssel ist ein Hash des Build-Namens allein, zwei Repositories mit `main` teilten sich sonst eine permanente URL. Fünf neue Tests, Gegenprobe per Mutation gezogen (Präfix fest auf leer → genau der Mehr-Repo-Test fällt). 498 Tests grün, ktlint sauber. PR-Dokument docs/prs/2026-09-03-PR#13-…, Plan, Architektur-Skill und AGENTS.md nachgezogen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4304dd7c4b
commit
c82f2a965c
@@ -37,7 +37,7 @@ All production code lives under `de.hoennig.werkator`, with sub-packages `comman
|
||||
- `exitProcess` is called only from `main()` — never inside `CliRunner.run()`; this keeps the Spring context alive during tests.
|
||||
- Nothing is scheduled during CLI runs or tests: the watcher poll loop and metrics sampling start only via an explicit `start()` in the `server` profile.
|
||||
- Builds run detached in worktrees under `.git/werkator/worktrees/<branchKey>`; the primary checkout is never used for builds; never assume a single running build.
|
||||
- Everything repository-scoped (results, artifacts, worktrees, git and config access) goes through a `RepoContext`, never through an implicit current directory: the executor serializes per (context, branch) under one global `maxConcurrent`, the watcher polls every context in its own guard. `RepoRegistry` opens one context per entry of the instance configuration `~/.werkator.yml` (ADR 0009), or the current directory without one; the instance-level keys (`server`, `executor`, `watcher.pollInterval`) and the `defaults` block are folded into every repository's effective config by `ConfigLoader` itself, so no consumer reads the home file directly.
|
||||
- Everything repository-scoped (results, artifacts, worktrees, git and config access) goes through a `RepoContext`, never through an implicit current directory: the executor serializes per (context, branch) under one global `maxConcurrent`, the watcher polls every context in its own guard. `RepoRegistry` opens one context per entry of the instance configuration `~/.werkator.yml` (ADR 0009), or the current directory without one; the instance-level keys (`server`, `executor`, `watcher.pollInterval`) and the `defaults` block are folded into every repository's effective config by `ConfigLoader` itself, so no consumer reads the home file directly. Server routes carry the repository as `/repos/<name>/…` and `/api/repos/<name>/…`, with the unscoped form permanently meaning the served repository; the pages stay per repository and the navigation switches between them.
|
||||
- When config keys change, three places must stay in sync: the `WerkatorConfig` data classes, the `InitCommand` templates, and `docs/configuration.md`.
|
||||
- Every config file may declare `werkator.version.since`/`below` (the Werkator it is written for, never a format version — no API is involved). `since` is enforced in both directions, using `ConfigVersions.FORMAT_BROKE_IN` for "file predates a breaking change"; `below` only warns. A violation aborts the start for the machine and project config, but fails only that branch's builds for a branch config.
|
||||
- A branch describes its own CI: its committed `.werkator.yml` is the branch layer (`ConfigLoader.loadWithBranchLayer`, used by the watcher per origin branch and by `loadForWorktree` at build time) and takes precedence over `.git`/project — including the whole `builds` section, so a new configuration can be tried out on a branch without affecting other branches. Only the pinned set is stripped from that layer: secrets (`git`), host/repository sections (`server`, `gitea`, `executor`, `watcher`), the docker (`docker.enabled`, `docker.network`) and bubblewrap (`bwrap.enabled`, `bwrap.rootfs`, `bwrap.werkdock`) sandbox policies, and the trust gate (`requirePullRequest`). A branch must never reach credentials, disable its container or sandbox, change its network, substitute a foreign rootfs, raise global concurrency, or bypass its own pull-request gate; a branch's definitions apply to that branch alone.
|
||||
|
||||
Reference in New Issue
Block a user