Release Notes
mhoennig/werkator
static
v1.0.1 — 2026-08-31
- The restart button on the Branches 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.
- Latest and History are unchanged: a row there is a recorded run, and restarting it means that commit.
v1.0.0 — 2026-08-31
- GitTally is now Werkator. 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
werkator. The version marks the new name rather than a claim about maturity: the release after0.9.21is1.0.0, because a product that changes its name is better off counting from one under it. - The rename reaches the file names an installation depends on. The committed
configuration is
.werkator.yml, the machine-specific one.git/werkator/.werkator.yml, and all state — build results, artifacts, worktrees, the control token — lives under.git/werkator/. - The default Gitea check context is
werkator. 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.
v0.9.21 — 2026-08-30
- 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.
- 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
atTimesslot is likewise reported once per slot.
v0.9.20 — 2026-08-29
- A build definition is now split in two: a
triggerblock says when the build runs and for which branches (onPush,atTimes,branches,activeWithin), everything beside it says what the build does. Only the second half is inherited frombuilds.default, 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. - A branch pattern prefixed with
!excludes instead of selecting, and an exclusion wins whatever the order:branches: ["*", "!master"]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. - A build may report under its own Gitea check with
statusContext; empty keeps the repository-widegitea.statusContext. 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. - Fixed: 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.
v0.9.19 — 2026-08-29
- A build definition now describes its build completely:
requirePullRequestand the wholedockersection, the sandbox policy included, live inbuilds.<name>.builds.defaultis the base every other definition inherits its settings from — never its trigger, becauseonPush,atTimes,branches, andactiveWithinsay when and where that one build runs. - The per-branch
branchessection 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 leftoverbuilds.maxConcurrentis not one —branchesis ignored completely and a warning names it. Either or, never both: two half-answers to what a build runs would pull against each other. requirePullRequest,docker.enabled, anddocker.networkstay 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'sbuilds.default— it cannot reach a native build by defining a new job.- Fixed: the archived artifact directories came from the plain branch
settings, so a build definition adding its own
artifactDirs— a release job storingbuild/libs, say — never had them stored. - Fixed: 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.
v0.9.18 — 2026-08-29
- 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:
gitTally: { version: { since: "0.9.18", below: "2.0" } }.sinceis 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.belowis a release marker and only warns, so an unmaintained value can never stop a build. - 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
gittally initwrites the current version into the config it generates.
v0.9.17 — 2026-08-29
- Fixed: 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.
v0.9.16 — 2026-08-29
- A scheduled build can run hourly:
atTimes: ["??:05"]stands for that minute of every hour. Each hour is its own slot, so it triggers once per hour — mixing it with fixedHH:MMtimes works, the latest due slot wins. Only the hour may be a wildcard; anything else is skipped with a warning. - Fixed: 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
.gittally.ymloverrides 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.
v0.9.15 — 2026-08-29
- The
.gittally.ymlcommitted on a branch now takes precedence for thebuildssection 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. - Pinned to the server side are only the keys that do not describe the branch's build:
secrets (
git), the host and repository sections (server,gitea,executor,watcher), the container sandbox policy (docker.enabled,docker.network), and therequirePullRequestgate. - Changed: the build concurrency limit moved from
builds.maxConcurrenttoexecutor.maxConcurrent(default 1, no compatibility alias) — thebuildssection now holds build definitions only. A leftoverbuilds.maxConcurrentkey is ignored with a warning instead of failing the configuration, so an installation keeps running until its committed config can be updated.
v0.9.14 — 2026-08-28
- Named build definitions (ADR 0007): the
buildssection of.gittally.ymlnow defines jobs withonPush/atTimestriggers, branch selectors (name globs,activeWithinage filter), and overrides of the branch settings — e.g. a nightlypitestbuild running a fuller check than the quick on-push builds. A named build records under its own<branch>@<build>pool with its own branches-view row, retention count, and permanent latest-green artifact link. - Restart,
gittally retry, and the startup recovery re-run a build under its recorded definition, resolving the settings from the current configuration. - Changed: the v0.9.13 per-slot
buildCommand/namesyntax inautoBuild.timesis gone again — use a build definition instead.branches.*.autoBuildwith plain times keeps working but is deprecated.
v0.9.13 — 2026-08-28
- A scheduled auto-build slot (
autoBuild.timesentry) can carry its ownbuildCommand, 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. - A slot can also carry a
name(e.g.master@nightly): 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 (/branches/master_nightly/…) — the branch's regular builds no longer displace the nightly build and its artifacts.
v0.9.12 — 2026-08-26
- 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.
- Triggering a build that is already queued or running for the same commit no longer stacks a duplicate — an impatient double-click on restart now hits the existing build. Re-running a finished build is unaffected.
v0.9.11 — 2026-08-14
- 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
watcher.fastForwardLocalRefs: false. - On the artifact index of a failed build, the logs that actually contain the build tool's failure line carry a failed badge — with stdout and stderr stored separately, that points straight at the log worth opening. Logs of a green build are not scanned.
v0.9.10 — 2026-08-11
- 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
.git/gittally/control-tokenon the host) and keeps it in the browser.
v0.9.9 — 2026-08-11
- Changed default:
server.bindAddressis now127.0.0.1instead of0.0.0.0, because neither the UI nor the API authenticates read access. Existing.gittally.ymlfiles keep whatever they set; with the managed nginx container,0.0.0.0has to be set explicitly. - The control token is accepted in the
X-GitTally-Tokenheader only — thetokenquery parameter is gone, as URLs end up in access logs and browser history. config:printmasksgit.token;--show-secretsprints it.- Files holding secrets (the Gitea token written by
init, the control token) are created with mode0600right away instead of beingchmod-ed afterwards, the control token is compared in constant time, and git calls pass--before branch names. - On narrow screens the page title and the repository name are stacked instead of wrapping.
v0.9.8 — 2026-08-10
- The artifact index also links report pages of directories without an
index.html. A directory holding a single page is linked as a directory, so Gradle's--profilereport keeps a stable URL although its file name carries the build timestamp. - The permanent
🔗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. - The Current tab gave way to a
📡link in the artifacts column, shown while a build runs.
v0.9.7 — 2026-08-10
init --systemdalso installs a nightly Docker cleanup timer (gittally-docker-prune.timer, 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.
v0.9.6 — 2026-08-10
- This release-notes page, linked from the version in the footer.
- Mobile: the live indicator collapses to a colored state dot so it no longer squeezes the menu.
- The system page highlights critical utilization: the current value of CPU, RAM, and disk used turns orange from 80% and red from 90% of the respective total.
v0.9.5 — 2026-08-10
- Builds killed by a server shutdown (e.g. a deployment restart) are recorded as interrupted instead of failed, publish a pending Gitea status, and are re-enqueued when the server starts again.
v0.9.4 — 2026-08-10
- The legacy page names (
/index.html,/branches.html, …) answer with permanent redirects to the new routes, so pre-rewrite bookmarks and the redirect from the old host keep working.
v0.9.3 — 2026-08-10
- The artifact page shows a n failed badge behind links to test reports that contain failures.
v0.9.2 — 2026-08-10
- Cancelling a build also terminates its auxiliary phases (Docker image build, Gradle cache volume preparation), so the next queued build starts immediately.
v0.9.1 — 2026-08-10
- Durations no longer flicker while live-updating.
- While a build is running or pending, the artifact link shows an hourglass (logs only, so far) instead of the report icon.
- Pending builds show their queue wait time in italics; running and finished builds show the pure build time without the wait.
v0.9.0 — 2026-08-10
Port of the legacy gitTally bash script to Kotlin/Spring Boot — the first production deployment of the rewrite.
- Dual-mode application: CLI (
init,status,build,retry,config:print) and HTTP server with this web UI and a JSON API. - Declarative YAML configuration: committed
.gittally.ymlplus machine-specific overrides and secrets under.git/gittally/. - Builds run detached in git worktrees — the primary checkout is never used for builds.
- 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.
- Branch watcher with auto-builds (nightly slots), a pull-request gate, and pruning of results, artifacts, and stale worktrees.
- Commit statuses reported to Gitea; artifact store with per-branch retention and permanent latest-green links; system metrics page.
- Managed nginx/TLS container (Let's Encrypt) for hosts without a reverse proxy;
init --systemdgenerates the service unit. - Self-contained runtime bundle (jlink-trimmed JRE + jar) for hosts without a Java runtime.