Build definitions with onPush/atTimes replace branch-owned schedules
ADR 0007: the YAML builds section (next to the reserved maxConcurrent key) defines named builds (jobs) with onPush/atTimes triggers, branch selectors (name globs, activeWithin age filter), and build-setting overrides applied last over the merged branch config. The implicit default build (onPush over all branches) preserves the previous behavior; the section is pinned against the worktree layer. Results record the job name; restart, retry, and startup recovery re-run by it, resolving settings from the current config. A non-default build records under the <branch>@<build> pool with its own row, retention count, and permanent latest-green link. branches.*.autoBuild stays as a deprecated alias (plain times only); the unreleased-in-practice v0.9.13 per-slot buildCommand/name syntax is removed again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
5051c7bb99
commit
0e8db18e69
@@ -2,11 +2,11 @@
|
||||
|
||||
**Status:**
|
||||
- proposed: 2026-08-28
|
||||
- accepted: -
|
||||
- accepted: 2026-08-28
|
||||
- rejected: -
|
||||
- superseded: -
|
||||
|
||||
**Decision [proposed]:** A top-level `builds` section defines named builds (jobs) with `onPush`/`atTimes` triggers and a branch selector — the branch-owned `autoBuild` schedule and the v0.9.13 per-slot `buildCommand`/`name` syntax are replaced by it.
|
||||
**Decision [accepted]:** A top-level `builds` section defines named builds (jobs) with `onPush`/`atTimes` triggers and a branch selector — the branch-owned `autoBuild` schedule and the v0.9.13 per-slot `buildCommand`/`name` syntax are replaced by it.
|
||||
`branches` stays what it is: per-branch build settings that every build inherits.
|
||||
|
||||
## Context and Problem Statement
|
||||
@@ -82,7 +82,7 @@ Semantics:
|
||||
Compatibility and migration:
|
||||
|
||||
- No `builds` section, or no `default` entry: the implicit `default` build (`onPush: true`, all branches) preserves today's behavior exactly. Defining other builds does not disable it; `builds.default.onPush: false` does.
|
||||
- `branches.<name>.autoBuild` (`enabled` + plain `times`) keeps working for a transition period, internally mapped to a scheduled build of the branch's own pool, with a deprecation warning in the log; `docs/configuration.md` documents only `builds`.
|
||||
- `branches.<name>.autoBuild` (`enabled` + plain `times`) keeps working for compatibility, internally mapped to a scheduled build of the branch's own pool, with a deprecation warning in the log; removal is not scheduled.
|
||||
- The v0.9.13 per-slot `buildCommand` and `name` are **removed** (not deprecated): released one day ago, configured nowhere.
|
||||
|
||||
#### Advantages
|
||||
@@ -112,5 +112,5 @@ Keep `autoBuild` (including the v0.9.13 slot syntax) forever next to `builds`.
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
Top-level `builds` with `onPush`/`atTimes`, as specified above.
|
||||
`branches.*.autoBuild` survives one deprecation period as a mapped alias; the v0.9.13 slot extras are reverted.
|
||||
Top-level `builds` with `onPush`/`atTimes`, as specified above; the reserved key `maxConcurrent` stays in the same section for compatibility.
|
||||
`branches.*.autoBuild` stays as a deprecated, mapped alias; the v0.9.13 slot extras are reverted.
|
||||
|
||||
+56
-41
@@ -24,12 +24,13 @@ This layer applies **only** to the build itself. A pinned set is always taken fr
|
||||
install/project config and can never be set from the worktree:
|
||||
|
||||
- secrets and server-side settings: the whole `git`, `gitea`, and `server` sections;
|
||||
- the whole `builds` section: build definitions and `maxConcurrent`;
|
||||
- the container sandbox policy: `docker.enabled` and `docker.network`.
|
||||
|
||||
This keeps a branch from disabling its own build container, changing its network mode, or
|
||||
reaching credentials. Watcher decisions that happen before a build exists — the whole
|
||||
`autoBuild` section (schedule and slot commands) and the `requirePullRequest` gate — are
|
||||
read from the repo install/project config, because there is no worktree at that point.
|
||||
reaching credentials. The whole `builds` section (build definitions and `maxConcurrent`)
|
||||
and the deprecated `autoBuild` schedules are pinned too — jobs and their triggers are
|
||||
server-side decisions made before a build worktree exists.
|
||||
|
||||
## Inspect the Effective Config
|
||||
|
||||
@@ -84,13 +85,30 @@ gitea:
|
||||
repo: my-repo # repository name
|
||||
statusContext: GitTally # label shown on Gitea commit status checks (default: GitTally)
|
||||
|
||||
# Build execution.
|
||||
# Build execution and named build definitions (jobs, see notes below).
|
||||
# "maxConcurrent" is a reserved key; every other key names a build definition.
|
||||
builds:
|
||||
# How many branches may build at the same time.
|
||||
# At most one build per branch runs regardless; each branch builds in its own
|
||||
# git worktree under .git/gittally/worktrees/, never in the primary checkout.
|
||||
# Changing this value requires a restart.
|
||||
maxConcurrent: 1
|
||||
# Implicit unless overridden: the default build runs on push over all branches
|
||||
# with the branch's regular settings — exactly the behavior without any
|
||||
# build definitions. Set onPush: false here to disable on-push builds.
|
||||
# default:
|
||||
# onPush: true
|
||||
#
|
||||
# Example of a named build definition; all keys except its name are optional:
|
||||
# pitest:
|
||||
# onPush: false # trigger: build every new commit (default: false)
|
||||
# atTimes: ["01:00"] # trigger: daily UTC times HH:MM (default: none)
|
||||
# branches: ["master", "release/*"] # selector: names or glob patterns (default: all)
|
||||
# activeWithin: 24h # selector: only branches with commits in the last 24h
|
||||
# buildCommand: ./gradlew piTestFull # overrides; unset keys fall back to the
|
||||
# cleanCommand: rm -rf build # merged branch settings (also available:
|
||||
# artifactDirs: [build/reports] # stdoutLog, stderrLog, and docker
|
||||
# # image/dockerfile/context/env)
|
||||
|
||||
# Build artifact storage and retention.
|
||||
artifacts:
|
||||
@@ -145,15 +163,11 @@ branches:
|
||||
# Build this branch only while its head commit matches a pull-request head on origin
|
||||
# (refs/pull/*/head — read via plain git, no API token needed; see notes below).
|
||||
requirePullRequest: false
|
||||
# DEPRECATED: define a build with atTimes in the builds section instead.
|
||||
# Kept for compatibility: rebuilds this branch on schedule with its regular command.
|
||||
autoBuild:
|
||||
enabled: false # whether to rebuild on schedule
|
||||
# UTC times HH:MM for scheduled builds. An entry may carry its own build
|
||||
# command, so a nightly slot can run a fuller check than the on-commit
|
||||
# builds, and a name recording its builds in a separate pool (see notes below):
|
||||
# - time: "01:00"
|
||||
# buildCommand: ./gradlew fullCheck
|
||||
# name: main@nightly
|
||||
times: ["01:00"]
|
||||
times: ["01:00"] # UTC times HH:MM for scheduled builds
|
||||
# Optional Docker build runtime; when enabled, the clean and build commands
|
||||
# run inside a container instead of natively (see notes below).
|
||||
docker:
|
||||
@@ -170,27 +184,19 @@ branches:
|
||||
# additional environment variables set inside the build container
|
||||
env: {}
|
||||
|
||||
main:
|
||||
autoBuild:
|
||||
enabled: true
|
||||
|
||||
master:
|
||||
buildCommand: ./gradlew --console=plain --no-daemon quickCheck
|
||||
autoBuild:
|
||||
enabled: true
|
||||
times:
|
||||
# the nightly rebuild runs the full check instead of the quick on-commit
|
||||
# one, recorded separately as master@nightly
|
||||
- time: "01:00"
|
||||
buildCommand: ./gradlew --console=plain --no-daemon completeCheck
|
||||
name: master@nightly
|
||||
|
||||
release:
|
||||
buildCommand: ./gradlew --console=plain --no-daemon --no-build-cache test jacocoReport
|
||||
autoBuild:
|
||||
enabled: true
|
||||
times:
|
||||
- "04:00"
|
||||
|
||||
builds:
|
||||
# the nightly rebuild runs the full check instead of the quick on-commit one,
|
||||
# recorded separately as master@pitest
|
||||
pitest:
|
||||
atTimes: ["01:00"]
|
||||
branches: ["master"]
|
||||
buildCommand: ./gradlew -PfullPitTest --console=plain --no-daemon piTestFull
|
||||
```
|
||||
|
||||
### Notes on `server.bindAddress`
|
||||
@@ -237,24 +243,33 @@ Without the `main` override, direct pushes and merges to `main` would never buil
|
||||
A plain git origin (no Gitea/GitHub) serves no `refs/pull/*/head` at all, so gated branches would never build there.
|
||||
For such origins, disable all gates globally with `watcher.pullRequestGate: false` — typically in the machine-specific `.git/gittally/.gittally.yml`, so the committed configuration keeps the gates for forge-backed environments.
|
||||
|
||||
### Notes on `branches.<name>.autoBuild.times`
|
||||
### Notes on `builds` (build definitions)
|
||||
|
||||
Each entry is either a plain `HH:MM` string or an object with `time` and optional `buildCommand` and `name`; both forms mix freely in one list.
|
||||
A slot without its own command runs the branch's regular `buildCommand`.
|
||||
The typical use is a quick check on every commit and a fuller, slower check in the nightly slot of the same branch.
|
||||
Next to the reserved execution key `maxConcurrent`, every key of the `builds` section names a build definition (a job) over the branches — ADR 0007.
|
||||
A build definition has triggers, a branch selector, and build-setting overrides.
|
||||
|
||||
A slot's command is recorded in the build result.
|
||||
Restarting such a build from the UI re-runs it with the slot's command, and the startup recovery re-enqueues an interrupted one likewise — a build is always repeated with the command it originally ran.
|
||||
Manual `gittally build <branch>` runs and watcher builds for new commits always use the regular `buildCommand`.
|
||||
Triggers: `onPush: true` builds every new commit of the selected branches; `atTimes: ["HH:MM", …]` rebuilds their heads once per day and slot (UTC).
|
||||
A definition may have both; one with neither never triggers automatically.
|
||||
|
||||
Without a `name`, a slot's builds share the branch's history, retention pool, and permanent latest-green link — on a busy branch, the regular builds can displace the nightly build and its artifacts within a day.
|
||||
A slot `name` (e.g. `master@nightly`) records the slot's builds in their own pool instead: an own row in the branches view (sorted after its branch), an own `retentionPerBranch` count, an own latest status, and an own permanent artifact link.
|
||||
The URL key is the sanitized name — `master@nightly` is served as `/branches/master_nightly/…`.
|
||||
The builds still run in the branch's worktree, one build per branch at a time, and the Gitea commit status is still reported per commit in the shared status context, so the last build of a commit wins there regardless of its name.
|
||||
Do not name a slot like an existing branch — the pools would merge.
|
||||
The name's results live as long as the underlying branch exists on origin.
|
||||
Selector: `branches` lists branch names or glob patterns (`*` matches any characters, also across `/`); empty selects all origin branches.
|
||||
`activeWithin` (e.g. `24h`) additionally keeps only branches whose origin head commit is younger than the duration — useful to run a nightly deep check over all recently active branches.
|
||||
Both parts combine as an intersection.
|
||||
The `branches.<name>.requirePullRequest` gate stays a branch property and gates all watcher-triggered builds of that branch.
|
||||
|
||||
The whole `autoBuild` section is a watcher decision made before a build worktree exists, so — unlike `buildCommand` itself — it is read from the repo install/project config and cannot be changed by the `.gittally.yml` committed on the branch being built.
|
||||
Overrides: `buildCommand`, `cleanCommand`, `artifactDirs`, `stdoutLog`/`stderrLog`, and the docker image keys (`image`, `dockerfile`, `context`, `env`).
|
||||
The effective settings of one build on one branch merge in this order: defaults → `branches.default` → `branches.<branch>` → the worktree's committed `.gittally.yml` → the build definition's overrides.
|
||||
Unset keys fall back; the definition wins last because it is the job.
|
||||
The whole `builds` section is pinned: it always comes from the repo install/project config, and the `.gittally.yml` committed on a branch can neither define jobs nor change `maxConcurrent`.
|
||||
|
||||
The implicit `default` build (`onPush: true`, all branches) preserves the behavior without any definitions; defining other builds does not disable it, `builds.default.onPush: false` does.
|
||||
The `default` build records under the plain branch name; every other build records under `<branch>@<name>` with its own row in the branches view (sorted after its branch), its own `retentionPerBranch` count, latest status, and permanent latest-green artifact link.
|
||||
The URL key is the sanitized pool name — `master@pitest` is served as `/branches/master_pitest/…`.
|
||||
The pools live as long as the underlying branch exists on origin.
|
||||
Restart, `gittally retry`, and the startup recovery re-run a build under its recorded definition, resolving the settings from the current configuration — the job definition is the source of truth, not the historical run.
|
||||
The builds still run in their branch's worktree, one build per branch at a time, and the Gitea commit status is reported per commit in the shared status context (the last build of a commit wins there).
|
||||
|
||||
`branches.<name>.autoBuild` (`enabled` + `times`) is the deprecated pre-ADR-0007 schedule, kept for compatibility: it rebuilds the branch's own pool with its regular command and logs a deprecation warning.
|
||||
`autoBuild.times` entries carrying their own `buildCommand`/`name` (a short-lived v0.9.13 syntax) are no longer supported — use a build definition.
|
||||
|
||||
### Notes on `watcher.fastForwardLocalRefs`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user