`tools/remote repo-add` installed Werkator's own `builds.deploy` (restartsInstance, unsandboxed) into werkcrappit on mih09. Its `main@deploy` ran on the host with werkcrappit's build command and would have restarted the instance on every green main. - Registry entries get `selfDeploys`; with a registry, `restartsInstance` is refused in every repository whose entry lacks it (repository not served, logged). At most one entry may say it. - `init --apply` repeats; fragments are merged in order. - `tools/remote`: `WERKATOR_SELF_DEPLOY_CONFIG` for the watched repository alone; `repo-add` refuses a shared fragment with `restartsInstance`; a failed fragment upload now aborts. - v1.26.0 **Before merging:** add `selfDeploys: true` to the `werkator` entry of `~/.werkator.yml` on mih09, or the self-deployed v1.26.0 will not serve `werkator`. PR-doc: `docs/prs/2026-10-01-PR#87-self-deploy-only-from-its-repository.md` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: mhoennig <michael@hoennig.de> Reviewed-on: #87
5.5 KiB
WARNING: This document describes only the change applied in this PR. It may already be outdated once the next PR is merged. Historic PR-documentation is not maintained along with new PRs — treat it as a snapshot, not as current documentation.
The Problem
tools/remote repo-add runs werkator init --apply with the instance fragment named by WERKATOR_INIT_CONFIG.
Since PR#61, the fragment of mih09 also carries Werkator's own builds.deploy: afterSuccessOf: default, branches: [main], restartsInstance: true, werkdock.enabled: false.
On 2026-10-01, repo-add installed this block into the newly registered repository werkcrappit.
Its main@deploy then ran on the host, outside the sandbox, with werkcrappit's inherited buildCommand (./gradlew build).
It failed only because the host has no JDK 21.
With a JDK, every green main build of werkcrappit would have restarted the instance.
Werkator accepted the block, because nothing in a repository's own files can say whether the instance runs from that repository. The fragment mixed two things: the sandbox policy that every repository gets, and the deployment that belongs to one repository.
Non-Goals
- No detection of the repository the instance runs from; the operator says it in the instance file.
- No change to an instance without a registry: it serves one repository, and that one may restart it.
- No change to the pinning of
restartsInstanceagainst branch layers; it already holds.
The Scenarios
Feature: only the repository the instance deploys itself from may restart it
Scenario#87.01: Several fragments are merged in order
So that the self-deployment can travel in its own fragment, apart from the sandbox policy.
- Given two fragments, the second one overriding a key of the first and adding a build
- When
init --applygets both - Then the applied layer holds their merge, the later fragment winning
- And a header names both files
Verified by
- ConfigLoaderTest — "several fragments are merged in order"
- InitCommandTest — the existing
--applytests with the option as a list
Scenario#87.02: restartsInstance needs selfDeploys in the registry
So that a deployment block copied into a foreign repository cannot restart the instance.
- Given a registry with
werkatormarkedselfDeploys: trueandwerkcrappitunmarked, both with arestartsInstancebuild - When their configurations are loaded
- Then
werkatorloads with the restarting build - And
werkcrappitis refused, naming the build, the instance file andselfDeploys: true - And a directory outside the registry is refused too, and so is a branch layer on a refused configuration
Verified by
- ConfigLoaderTest — "only the registry entry marked selfDeploys may restart the instance"
Scenario#87.03: A refused repository is not served
So that the error shows at start, and the other repositories keep building.
- Given an unmarked registry entry whose repository sets
restartsInstance - When the registry opens
- Then that repository is skipped with an error, and the others are served
Verified by
- RepoRegistryTest — "a repository that would restart the instance without being marked selfDeploys is not served"
Scenario#87.04: One repository at most deploys the instance
So that "the repository the instance runs from" names one repository.
- Given two registry entries with
selfDeploys: true - When the registry opens
- Then the start aborts, naming both entries
Verified by
- RepoRegistryTest — "two entries marked selfDeploys abort the start"
The Solution
RepositoryEntry gets selfDeploys.
ConfigLoader.loadPrimary refuses any restartsInstance: true in builds or the legacy branches when a registry exists and the repository's entry does not say selfDeploys: true.
The paths are compared after resolving symlinks, so ~/werkator and its real path match.
The check sits in loadPrimary, so every load — the registry at start, the watcher per poll, the branch layer at build time — stands on it.
RepositoryEntry.directory now resolves an entry's path for the registry and the check alike.
init --apply repeats, and ConfigLoader.applyInstanceFragments merges the fragments in order.
tools/remote gets WERKATOR_SELF_DEPLOY_CONFIG: repo-init and instance-start apply it on top of WERKATOR_INIT_CONFIG, repo-add never does.
repo-add dies when WERKATOR_INIT_CONFIG sets restartsInstance: true, before it installs anything.
A failed upload inside a command substitution now ends the script; set -e does not reach into one.
Rollout on mih09: add selfDeploys: true to the werkator entry of ~/.werkator.yml before the new version starts.
An older Werkator ignores the key, so the order is safe; the other way round, the new version would not serve werkator and could not deploy itself again.
Then split .env.mih09.yml locally into the sandbox policy and .env.mih09.self-deploy.yml.
Additional Changes
docs/configuration.md,docs/deployment.md,docs/llms.md,AGENTS.mdand the architecture skill describeselfDeploys, the repeated--applyand the second fragment.- Version 1.26.0 with its release note.