Files
werkator/src/main/resources/templates/releases.html
T
16543f038b the sandbox config section is werkdock, not bwrap (#19)
`bwrap` named the mechanism one layer below the tool that actually runs it: since
v1.0.0 Werkator does not invoke bwrap at all, it shells out to the werkdock CLI —
which made `bwrap.werkdock` a key naming its own executor.

The section is `werkdock` now and that key is `werkdock.binary`; BwrapConfig,
BwrapOverrides and BwrapBuildRunner follow the name. A file still writing `bwrap`
is read as before and warned about once per file, in `renameLegacySandbox` on the
raw map of every layer before merging — so nothing downstream knows two names, and
the old name is not a way around the pinning either. Renaming rather than refusing,
because the section lives in the machine configuration of every webspace instance,
which no repository tracks; the hard refusal belongs to the release that sets
ConfigVersions.FORMAT_BROKE_IN, where a file declaring no version can be caught
by name at all.

WERKATOR_SANDBOX in tools/remote follows, and still accepts `bwrap`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: mhoennig <michael@hoennig.de>
Reviewed-on: #19
2026-09-03 20:41:18 +02:00

384 lines
26 KiB
HTML

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head th:replace="~{fragments :: head(${pageTitle})}"></head>
<body>
<main>
<h1 th:replace="~{fragments :: header(${pageTitle})}"></h1>
<div th:replace="~{fragments :: nav(${view})}"></div>
<div class="panel release-notes">
<h2>v1.2.0 <span class="muted">— 2026-09-03</span></h2>
<ul>
<li>The build sandbox for hosts without Docker is configured as <code>werkdock</code> now,
not <code>bwrap</code> (PR#19), and its <code>bwrap.werkdock</code> key — which named its
own executor — is <code>werkdock.binary</code>. The old section is still read, with a
warning naming the file, so no installation has to be changed before its next
configuration edit. <code>bwrap</code> named the mechanism one layer below the tool that
actually runs it: builds have been executed by the werkdock CLI since v1.0.0.</li>
</ul>
<h2>v1.1.2 <span class="muted">— 2026-09-03</span></h2>
<ul>
<li>On a Hostsharing Managed Webspace, an <code>instance-update</code> restart no longer looks
dead while nothing is listening on the port for a moment: the generated
<code>.htaccess</code> (PR#17) now maps a refused connection to a static
"Werkator is restarting — please retry in a few minutes" page instead of Apache's default
error page. Generated only where a <code>server.publicBaseUrl</code> is configured, next to
the existing <code>.htaccess</code>; nothing changes for a Docker-host deployment.</li>
</ul>
<h2>v1.1.1 <span class="muted">— 2026-09-03</span></h2>
<ul>
<li>The system page's disk metric is now quota-aware (PR#16): it shows the tightest of the
user quota, the group quota, and the volume itself, instead of always the volume. On a
Hostsharing Managed Webspace that is usually a group quota, far tighter than the shared
host disk — the info line names the binding source and its hard limit, and the
warn/critical highlighting now fires against the real budget. A host without a binding
quota renders exactly as before.</li>
</ul>
<h2>v1.1.0 <span class="muted">— 2026-09-03</span></h2>
<ul>
<li>One instance can now serve several repositories (ADR 0009): a <code>~/.werkator.yml</code>
lists them, and each is worked on through its own <code>RepoContext</code> — checkout,
results, artifact store, and worktrees never share state across repositories. The watcher
polls every registered repository in its own guard, and the executor's
<code>maxConcurrent</code> still applies per <code>(repository, branch)</code>, not just
per branch.</li>
<li>Server routes carry the repository as <code>/repos/&lt;name&gt;/…</code> and
<code>/api/repos/&lt;name&gt;/…</code>; the unscoped routes keep meaning the served
repository, so a single-repository installation notices nothing. <code>build</code>,
<code>retry</code>, and <code>status</code> take a <code>--repo</code> option to pick a
registered repository instead of always acting on the current directory.</li>
<li>The system page's resource metrics are sampled and reported per repository instead of
once for the whole instance.</li>
<li>The page title carries a drop-down next to the heading in place of the plain repository
name once more than one repository is registered — picking a different one switches
straight to the same view (<em>Branches</em>, <em>History</em>, …) on it. A single-repository
installation still shows the plain name, unchanged.</li>
</ul>
<h2>v1.0.1 <span class="muted">— 2026-08-31</span></h2>
<ul>
<li>The restart button on the <em>Branches</em> view builds the branch's current head
instead of repeating the commit its last build ran on, and says so: it reads
"Build current head" there. A row on that page is a branch, not a past run — and
repeating an overtaken commit can be worse than useless, because a build gate
comparing the built commit against origin can never pass on it. The row keeps its
build definition, and a branch that is gone from origin is refused by name instead
of quietly falling back to the old commit.</li>
<li><em>Latest</em> and <em>History</em> are unchanged: a row there is a recorded run,
and restarting it means that commit.</li>
</ul>
<h2>v1.0.0 <span class="muted">— 2026-08-31</span></h2>
<ul>
<li>GitTally is now <strong>Werkator</strong>. The old name already belongs to another
product in the git space, so the rename is a precaution and nothing more: what the
build system does, how it is configured, and how it reports to Gitea are unchanged.
In prose it is Werkator, everywhere a machine reads the name it is
<code>werkator</code>. The version marks the new name rather than a claim about
maturity: the release after <code>0.9.21</code> is <code>1.0.0</code>, because a
product that changes its name is better off counting from one under it.</li>
<li>The rename reaches the file names an installation depends on. The committed
configuration is <code>.werkator.yml</code>, the machine-specific one
<code>.git/werkator/.werkator.yml</code>, and all state — build results, artifacts,
worktrees, the control token — lives under <code>.git/werkator/</code>.</li>
<li>The default Gitea check context is <code>werkator</code>. Where the old name is
pinned in a branch protection rule, the rule has to be updated with it, or a pull
request waits forever for a check nobody posts any more. Statuses already written
keep their old context, so a commit built before and after the change shows
both.</li>
</ul>
<h2>v0.9.21 <span class="muted">— 2026-08-30</span></h2>
<ul>
<li>An origin the watcher cannot reach is now visible in the web UI: a banner above the
table says that the list below is not updating, since when, and why. Until now the
page kept showing its last known state, calm and plausible, while GitTally had not
been able to fetch for an hour — the failure lived in the log alone. The banner is
deliberately separate from the live indicator: that one says whether your browser
reaches the server, this one whether the server reaches origin.</li>
<li>A lasting fetch failure is logged when its message changes instead of on every poll
cycle, and the recovery is logged once. One wrong token used to write several hundred
identical warnings an hour. An invalid <code>atTimes</code> slot is likewise reported
once per slot.</li>
</ul>
<h2>v0.9.20 <span class="muted">— 2026-08-29</span></h2>
<ul>
<li>A build definition is now split in two: a <code>trigger</code> block says when the
build runs and for which branches (<code>onPush</code>, <code>atTimes</code>,
<code>branches</code>, <code>activeWithin</code>), everything beside it says what
the build does. Only the second half is inherited from <code>builds.default</code>,
and making that structural means a selector added later cannot become inheritable
by accident. A definition still writing those keys flat is refused by name — a
trigger nobody reads any more is a build that silently stops running.</li>
<li>A branch pattern prefixed with <code>!</code> excludes instead of selecting, and an
exclusion wins whatever the order: <code>branches: ["*", "!master"]</code> is every
branch but master. That lets one branch have a build of its own without being built
by the default one as well — previously the only way to avoid the double build was
to give up the second definition's push trigger.</li>
<li>A build may report under its own Gitea check with <code>statusContext</code>; empty
keeps the repository-wide <code>gitea.statusContext</code>. Two builds of one commit
used to overwrite each other's result there, so a second build over the same branch
— a quick check next to a long one — was not readable in Gitea.</li>
<li><strong>Fixed:</strong> a branch whose builds all belong to named definitions showed
an empty row in the branches view, reading as "never built" right next to its actual
builds.</li>
</ul>
<h2>v0.9.19 <span class="muted">— 2026-08-29</span></h2>
<ul>
<li>A build definition now describes its build completely: <code>requirePullRequest</code>
and the whole <code>docker</code> section, the sandbox policy included, live in
<code>builds.&lt;name&gt;</code>. <code>builds.default</code> is the base every other
definition inherits its settings from — never its trigger, because
<code>onPush</code>, <code>atTimes</code>, <code>branches</code>, and
<code>activeWithin</code> say when and where that one build runs.</li>
<li>The per-branch <code>branches</code> section is thereby superseded and will be
removed. It is still read, but only while nothing defines a build at all: as soon as
one real definition exists — a leftover <code>builds.maxConcurrent</code> is not one
<code>branches</code> is ignored completely and a warning names it. Either or,
never both: two half-answers to what a build runs would pull against each other.</li>
<li><code>requirePullRequest</code>, <code>docker.enabled</code>, and
<code>docker.network</code> stay pinned to the host, wherever a branch writes them.
Since the inheritance is applied after all configuration layers are merged, a build
a branch invents and the host has never heard of still inherits the host's
<code>builds.default</code> — it cannot reach a native build by defining a new
job.</li>
<li><strong>Fixed:</strong> the archived artifact directories came from the plain branch
settings, so a build definition adding its own <code>artifactDirs</code> — a
release job storing <code>build/libs</code>, say — never had them stored.</li>
<li><strong>Fixed:</strong> the build definitions of a branch were cached by its head
commit alone, so an edited machine or project configuration only took effect once
that branch moved. On a quiet branch, a new scheduled job never started at all.</li>
</ul>
<h2>v0.9.18 <span class="muted">— 2026-08-29</span></h2>
<ul>
<li>A configuration file can declare which GitTally it is written for, so a version
that renames or drops a key says so instead of silently ignoring what it no longer
understands:
<code>gitTally: { version: { since: "0.9.18", below: "2.0" } }</code>.
<code>since</code> is enforced — both against a GitTally that is too old and
against a file written before the version in which the configuration format last
changed incompatibly, which GitTally knows about itself. <code>below</code> is a
release marker and only warns, so an unmaintained value can never stop a build.</li>
<li>The reach follows the file: the machine and project configuration abort the start
naming the file and the way back, an incompatible configuration committed on a
branch fails only that branch's builds. A file that declares nothing keeps working,
and <code>gittally init</code> writes the current version into the config it
generates.</li>
</ul>
<h2>v0.9.17 <span class="muted">— 2026-08-29</span></h2>
<ul>
<li><strong>Fixed:</strong> a page left in the background — a phone tab switched away
from for an hour — kept showing the state it was left in, with the durations of
running builds counting up client-side although the builds had long finished.
A returning page now fetches the current state immediately, whether the browser
reports the return as a visibility change, a focus, or a restore from its cache.</li>
</ul>
<h2>v0.9.16 <span class="muted">— 2026-08-29</span></h2>
<ul>
<li>A scheduled build can run hourly: <code>atTimes: ["??:05"]</code> stands for that
minute of every hour. Each hour is its own slot, so it triggers once per hour —
mixing it with fixed <code>HH:MM</code> times works, the latest due slot wins.
Only the hour may be a wildcard; anything else is skipped with a warning.</li>
<li><strong>Fixed:</strong> the artifact page showed the build command of the plain
branch configuration — for a build of a named definition, or on a branch whose
committed <code>.gittally.yml</code> overrides the command, that was a command the
build never ran. It now shows what this build actually runs, resolved from the
branch configuration committed at the build's own commit plus its definition.</li>
</ul>
<h2>v0.9.15 <span class="muted">— 2026-08-29</span></h2>
<ul>
<li>The <code>.gittally.yml</code> committed on a branch now takes precedence for the
<code>builds</code> section as well — a branch can define its own build definitions
and override those from the project config. The watcher reads each origin branch's
committed configuration, so a new build definition takes effect by committing it on
a branch, without touching any other branch's builds. A branch's definitions apply
to that branch alone.</li>
<li>Pinned to the server side are only the keys that do not describe the branch's build:
secrets (<code>git</code>), the host and repository sections (<code>server</code>,
<code>gitea</code>, <code>executor</code>, <code>watcher</code>), the container sandbox
policy (<code>docker.enabled</code>, <code>docker.network</code>), and the
<code>requirePullRequest</code> gate.</li>
<li><strong>Changed:</strong> the build concurrency limit moved from
<code>builds.maxConcurrent</code> to <code>executor.maxConcurrent</code> (default 1,
no compatibility alias) — the <code>builds</code> section now holds build definitions
only. A leftover <code>builds.maxConcurrent</code> key is ignored with a warning
instead of failing the configuration, so an installation keeps running until its
committed config can be updated.</li>
</ul>
<h2>v0.9.14 <span class="muted">— 2026-08-28</span></h2>
<ul>
<li>Named build definitions (ADR 0007): the <code>builds</code> section of
<code>.gittally.yml</code> now defines jobs with <code>onPush</code>/<code>atTimes</code>
triggers, branch selectors (name globs, <code>activeWithin</code> age filter), and
overrides of the branch settings — e.g. a nightly <code>pitest</code> build running a
fuller check than the quick on-push builds. A named build records under its own
<code>&lt;branch&gt;@&lt;build&gt;</code> pool with its own branches-view row, retention
count, and permanent latest-green artifact link.</li>
<li>Restart, <code>gittally retry</code>, and the startup recovery re-run a build under its
recorded definition, resolving the settings from the current configuration.</li>
<li><strong>Changed:</strong> the v0.9.13 per-slot <code>buildCommand</code>/<code>name</code>
syntax in <code>autoBuild.times</code> is gone again — use a build definition instead.
<code>branches.*.autoBuild</code> with plain times keeps working but is deprecated.</li>
</ul>
<h2>v0.9.13 <span class="muted">— 2026-08-28</span></h2>
<ul>
<li>A scheduled auto-build slot (<code>autoBuild.times</code> entry) can carry its own
<code>buildCommand</code>, so a nightly slot runs a fuller check than the quick
on-commit builds of the same branch. Restart, retry, and the startup recovery repeat
a build with the command it originally ran.</li>
<li>A slot can also carry a <code>name</code> (e.g. <code>master@nightly</code>): its
builds then get their own row in the branches view, their own history and retention
pool, and their own permanent latest-green artifact link
(<code>/branches/master_nightly/…</code>) — the branch's regular builds no longer
displace the nightly build and its artifacts.</li>
</ul>
<h2>v0.9.12 <span class="muted">— 2026-08-26</span></h2>
<ul>
<li>A build whose branch is deleted from origin mid-build (the usual fate of a merged
branch) no longer vanishes from the UI and history while it is still queued or
running — previously the queue looked stuck although a build was executing.</li>
<li>Triggering a build that is already queued or running for the same commit no longer
stacks a duplicate — an impatient double-click on <em>restart</em> now hits the
existing build. Re-running a finished build is unaffected.</li>
</ul>
<h2>v0.9.11 <span class="muted">— 2026-08-14</span></h2>
<ul>
<li>At the end of each poll cycle, the local branch refs of the watched repository are
fast-forwarded to their state on origin, so build steps that compare a branch with its
origin counterpart no longer fail once origin moved on. Only fast-forwards are applied —
a branch that diverged from origin or is ahead of it stays untouched. Switched off with
<code>watcher.fastForwardLocalRefs: false</code>.</li>
<li>On the artifact index of a failed build, the logs that actually contain the build tool's
failure line carry a <span class="status status-failed">failed</span> badge — with stdout
and stderr stored separately, that points straight at the log worth opening. Logs of a
green build are not scanned.</li>
</ul>
<h2>v0.9.10 <span class="muted">— 2026-08-11</span></h2>
<ul>
<li>The control token is no longer embedded in the pages — reading them is unauthenticated,
so anyone could have picked it out of the HTML. Viewing stays public; the first click on
a restart/cancel/delete button asks for the token once (it is in
<code>.git/gittally/control-token</code> on the host) and keeps it in the browser.</li>
</ul>
<h2>v0.9.9 <span class="muted">— 2026-08-11</span></h2>
<ul>
<li><strong>Changed default:</strong> <code>server.bindAddress</code> is now <code>127.0.0.1</code>
instead of <code>0.0.0.0</code>, because neither the UI nor the API authenticates read access.
Existing <code>.gittally.yml</code> files keep whatever they set; with the managed nginx
container, <code>0.0.0.0</code> has to be set explicitly.</li>
<li>The control token is accepted in the <code>X-GitTally-Token</code> header only — the
<code>token</code> query parameter is gone, as URLs end up in access logs and browser history.</li>
<li><code>config:print</code> masks <code>git.token</code>; <code>--show-secrets</code> prints it.</li>
<li>Files holding secrets (the Gitea token written by <code>init</code>, the control token) are
created with mode <code>0600</code> right away instead of being <code>chmod</code>-ed afterwards,
the control token is compared in constant time, and git calls pass <code>--</code> before
branch names.</li>
<li>On narrow screens the page title and the repository name are stacked instead of wrapping.</li>
</ul>
<h2>v0.9.8 <span class="muted">— 2026-08-10</span></h2>
<ul>
<li>The artifact index also links report pages of directories without an <code>index.html</code>.
A directory holding a single page is linked as a directory, so Gradle's <code>--profile</code>
report keeps a stable URL although its file name carries the build timestamp.</li>
<li>The permanent <code>🔗</code> link now appears on the build it resolves to — the branch's
latest green build — instead of on every build of that branch, and on all build tables.</li>
<li>The <em>Current</em> tab gave way to a <code>📡</code> link in the artifacts column,
shown while a build runs.</li>
</ul>
<h2>v0.9.7 <span class="muted">— 2026-08-10</span></h2>
<ul>
<li><code>init --systemd</code> also installs a nightly Docker cleanup timer
(<code>gittally-docker-prune.timer</code>, 02:00 host time): stopped containers and unused
images are pruned before the auto builds — like the legacy host, but the per-repository
Gradle cache volumes survive.</li>
</ul>
<h2>v0.9.6 <span class="muted">— 2026-08-10</span></h2>
<ul>
<li>This release-notes page, linked from the version in the footer.</li>
<li>Mobile: the live indicator collapses to a colored state dot so it no longer squeezes the menu.</li>
<li>The system page highlights critical utilization: the current value of CPU, RAM, and disk used
turns <span class="metric-warn">orange from 80%</span> and
<span class="metric-crit">red from 90%</span> of the respective total.</li>
</ul>
<h2>v0.9.5 <span class="muted">— 2026-08-10</span></h2>
<ul>
<li>Builds killed by a server shutdown (e.g. a deployment restart) are recorded as
<span class="status status-interrupted">interrupted</span> instead of
<span class="status status-failed">failed</span>, publish a <em>pending</em> Gitea status,
and are re-enqueued when the server starts again.</li>
</ul>
<h2>v0.9.4 <span class="muted">— 2026-08-10</span></h2>
<ul>
<li>The legacy page names (<code>/index.html</code>, <code>/branches.html</code>, …) answer with
permanent redirects to the new routes, so pre-rewrite bookmarks and the redirect from the
old host keep working.</li>
</ul>
<h2>v0.9.3 <span class="muted">— 2026-08-10</span></h2>
<ul>
<li>The artifact page shows a <span class="status status-failed">n failed</span> badge behind
links to test reports that contain failures.</li>
</ul>
<h2>v0.9.2 <span class="muted">— 2026-08-10</span></h2>
<ul>
<li>Cancelling a build also terminates its auxiliary phases (Docker image build, Gradle cache
volume preparation), so the next queued build starts immediately.</li>
</ul>
<h2>v0.9.1 <span class="muted">— 2026-08-10</span></h2>
<ul>
<li>Durations no longer flicker while live-updating.</li>
<li>While a build is running or pending, the artifact link shows an hourglass (logs only,
so far) instead of the report icon.</li>
<li>Pending builds show their queue wait time in italics; running and finished builds show
the pure build time without the wait.</li>
</ul>
<h2>v0.9.0 <span class="muted">— 2026-08-10</span></h2>
<p>Port of the legacy <em>gitTally</em> bash script to Kotlin/Spring Boot — the first production
deployment of the rewrite.</p>
<ul>
<li>Dual-mode application: CLI (<code>init</code>, <code>status</code>, <code>build</code>,
<code>retry</code>, <code>config:print</code>) and HTTP server with this web UI and a JSON API.</li>
<li>Declarative YAML configuration: committed <code>.gittally.yml</code> plus machine-specific
overrides and secrets under <code>.git/gittally/</code>.</li>
<li>Builds run detached in git worktrees — the primary checkout is never used for builds.</li>
<li>Docker build containers with automatic image rebuilds and a per-repository Gradle cache volume;
works under rootless Docker daemons; git metadata is available read-only inside the container.</li>
<li>Branch watcher with auto-builds (nightly slots), a pull-request gate, and pruning of results,
artifacts, and stale worktrees.</li>
<li>Commit statuses reported to Gitea; artifact store with per-branch retention and permanent
latest-green links; system metrics page.</li>
<li>Managed nginx/TLS container (Let's Encrypt) for hosts without a reverse proxy;
<code>init --systemd</code> generates the service unit.</li>
<li>Self-contained runtime bundle (jlink-trimmed JRE + jar) for hosts without a Java runtime.</li>
</ul>
</div>
</main>
<footer th:replace="~{fragments :: footer}"></footer>
<script src="/werkator.js"></script>
</body>
</html>