implemented 06-watcher.md: non-blocking poll cycle enqueueing changed/new/auto-build branches via the async executor, startup recovery, retention/worktree pruning, JSON auto-build slot state, watcher.pollInterval config, and watcher health state

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Michael Hoennig
2026-07-07 10:28:28 +02:00
co-authored by Claude Fable 5
parent 3c9eeda5da
commit a1db450bdb
16 changed files with 905 additions and 6 deletions
@@ -2,18 +2,25 @@ package de.hoennig.gittally.config
import java.time.Duration
/** Parses durations in the `watcher.newBranchMaxAge` format: `5d` (days) or `12h` (hours). */
/**
* Parses durations in the format used by `watcher.newBranchMaxAge` and `watcher.pollInterval`:
* `5d` (days), `12h` (hours), `10m` (minutes), or `30s` (seconds).
*/
object DurationParser {
private val pattern = Regex("""(\d+)([dh])""")
private val pattern = Regex("""(\d+)([dhms])""")
fun parse(value: String): Duration {
val match =
pattern.matchEntire(value.trim())
?: throw IllegalArgumentException("invalid duration '$value': expected <amount>d or <amount>h, e.g. 5d or 12h")
?: throw IllegalArgumentException(
"invalid duration '$value': expected <amount>d, <amount>h, <amount>m, or <amount>s, e.g. 5d or 10s",
)
val (amount, unit) = match.destructured
return when (unit) {
"d" -> Duration.ofDays(amount.toLong())
else -> Duration.ofHours(amount.toLong())
"h" -> Duration.ofHours(amount.toLong())
"m" -> Duration.ofMinutes(amount.toLong())
else -> Duration.ofSeconds(amount.toLong())
}
}
}
@@ -41,6 +41,8 @@ data class ArtifactsConfig(
)
data class WatcherConfig(
/** Delay between poll cycles, e.g. `10s` or `1m`. */
val pollInterval: String = "10s",
val newBranchMaxAge: String = "5d",
)