Separate queue wait from build duration

BuildResult gains runningSince, set when a build leaves the queue; the
recorded duration now measures pure build time from that point, so build
runtimes can be tracked without queue wait. A build cancelled while still
queued records neither. The UI shows the live wait time in italics while
pending and switches to the real build time once the build runs; the Gitea
"after mm:ss" descriptions now also report pure build time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-10 15:55:36 +02:00
co-authored by Claude Fable 5
parent a03f85bc17
commit 56aa306481
12 changed files with 112 additions and 17 deletions
@@ -120,6 +120,7 @@ class BuildExecutor(
return
}
build.running = true
build.runningBuild.runningSince = Instant.now()
transition(build, BuildStatus.RUNNING, duration = null)
val preparedWorkspace =
workspaces.prepare(
@@ -140,7 +141,8 @@ class BuildExecutor(
log.error("build of branch {} crashed", build.runningBuild.branch, e)
appendToLiveLog(build, "\nbuild crashed: ${e.message}\n")
} finally {
val duration = Duration.between(build.runningBuild.startedAt, Instant.now())
// pure build time, without the queue wait; null when the build never started executing
val duration = build.runningBuild.runningSince?.let { Duration.between(it, Instant.now()) }
val result = transition(build, finalStatus, duration)
try {
artifactStore.persist(result, build.runningBuild.stagingDir, workspace)
@@ -277,12 +279,17 @@ class BuildExecutor(
val runningBuild = build.runningBuild
val updated =
repository.updateByArtifactKey(runningBuild.artifactKey) {
it.copy(status = status, duration = duration ?: it.duration)
it.copy(
status = status,
runningSince = runningBuild.runningSince ?: it.runningSince,
duration = duration ?: it.duration,
)
} ?: BuildResult(
branch = runningBuild.branch,
commit = runningBuild.commit,
status = status,
startedAt = runningBuild.startedAt,
runningSince = runningBuild.runningSince,
duration = duration,
artifactKey = runningBuild.artifactKey,
).also { repository.append(it) }
@@ -7,7 +7,11 @@ data class BuildResult(
val branch: String,
val commit: String,
val status: BuildStatus,
/** When the build was accepted (enqueued); the time until [runningSince] is queue wait. */
val startedAt: Instant,
/** When the build actually started executing; null while queued or when cancelled in the queue. */
val runningSince: Instant? = null,
/** Pure build execution time (from [runningSince]), without the queue wait. */
val duration: Duration? = null,
val artifactKey: String,
)
@@ -13,7 +13,11 @@ data class RunningBuild(
val stagingDir: Path,
/** Combined stdout+stderr log, written live while the build runs. */
val liveLogFile: Path,
)
) {
/** Set by the executor when the build leaves the queue and starts executing. */
@Volatile
var runningSince: Instant? = null
}
/** Published via Spring's `ApplicationEventPublisher` on every persisted status transition. */
data class BuildStatusChangedEvent(