Files
werkator/src/main/kotlin/de/hoennig/gittally/server/ApiDtos.kt
T
mhoennigandClaude Fable 5 8aee3190ea Let auto-build slots run their own build command under their own name
A branches.<name>.autoBuild.times entry is now either a plain HH:MM
string or an object with time, its own buildCommand, and a name, so a
nightly slot can run a fuller check than the on-commit builds. The
watcher passes the slot's command and name to the executor, persisted in
the build result — UI restarts, gittally retry, and the startup recovery
repeat a build with the command and name it originally ran under.

A named slot (e.g. master@nightly) gets its own pool: repository
grouping, retention count, branches-view row (sorted after its branch),
latest status, and permanent latest-green artifact link are keyed by the
build name, while origin lookups, gone-branch pruning, worktrees, and
Gitea links/statuses stay keyed by the real branch. Without a name,
slot builds share the branch's pool as before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 19:11:35 +02:00

134 lines
4.5 KiB
Kotlin

package de.hoennig.gittally.server
import de.hoennig.gittally.build.BuildResult
import de.hoennig.gittally.build.BuildStatus
import java.time.Instant
/** JSON statuses are lowercase like the legacy TSV/HTML statuses. */
val BuildStatus.jsonName: String
get() = name.lowercase()
data class BuildResultDto(
/** The git branch that was built; the UI links this to Gitea. */
val branch: String,
/** The build name the result is recorded under; the UI displays and restarts by this (= [branch] unless a named auto-build slot). */
val name: String = branch,
val commit: String,
val status: String,
val startedAt: Instant,
/** When the build left the queue; the UI derives the live build time from this, the wait time from [startedAt]. */
val runningSince: Instant? = null,
val durationSeconds: Long?,
val artifactKey: String,
/** The permanent branch URL, set only on the build it resolves to — the name's latest green build. */
val latestGreenUrl: String? = null,
) {
companion object {
fun from(
result: BuildResult,
isLatestGreen: Boolean = false,
) = BuildResultDto(
branch = result.branch,
name = result.name,
commit = result.commit,
status = result.status.jsonName,
startedAt = result.startedAt,
runningSince = result.runningSince,
durationSeconds = result.duration?.seconds,
artifactKey = result.artifactKey,
latestGreenUrl = if (isLatestGreen) BranchPermalinks.permanentUrl(result.name) else null,
)
}
}
/**
* One entry of `GET /api/branches`, like a legacy branches-view row: an origin
* branch with its latest build, or an `unknown` placeholder when never built.
* [latestGreenUrl] is the permanent artifact URL of the branch's latest green
* build; set only when this row's build is that green build, so the link appears
* where it resolves to.
*/
data class BranchDto(
/** The git branch; the UI links this to Gitea. */
val branch: String,
/** The row's build name; = [branch], except for the extra rows of named auto-build slots. */
val name: String = branch,
val commit: String,
val status: String,
val startedAt: Instant?,
val runningSince: Instant? = null,
val durationSeconds: Long?,
val artifactKey: String,
val latestGreenUrl: String? = null,
) {
companion object {
fun from(
branch: String,
headCommit: String,
latest: BuildResult?,
isLatestGreen: Boolean = false,
name: String = branch,
) = if (latest == null) {
BranchDto(
branch = branch,
name = name,
commit = headCommit,
status = CommitStatusDto.UNKNOWN_STATUS,
startedAt = null,
durationSeconds = null,
artifactKey = "",
)
} else {
BranchDto(
branch = branch,
name = name,
commit = latest.commit,
status = latest.status.jsonName,
startedAt = latest.startedAt,
runningSince = latest.runningSince,
durationSeconds = latest.duration?.seconds,
artifactKey = latest.artifactKey,
latestGreenUrl = if (isLatestGreen) BranchPermalinks.permanentUrl(name) else null,
)
}
}
}
/** One entry of `GET /api/builds/current`; the log grows while the build runs. */
data class CurrentBuildDto(
val branch: String,
/** The build name; = [branch] unless a named auto-build slot triggered this build. */
val name: String = branch,
val commit: String,
val artifactKey: String,
val status: String,
val startedAt: Instant,
val runningSince: Instant? = null,
val logSize: Long,
)
/** Incremental live-log chunk; poll again with `offset = nextOffset`. */
data class LogTailDto(
val artifactKey: String,
val offset: Long,
val nextOffset: Long,
val content: String,
)
/**
* Effective status of a commit: the Gitea status when available, otherwise the
* local repository status, otherwise `unknown`. A Gitea failure is an explicit
* [giteaError] value — the request answers HTTP 200 and never hangs.
*/
data class CommitStatusDto(
val commit: String,
val status: String,
val localStatus: String?,
val giteaStatus: String?,
val giteaError: String?,
) {
companion object {
const val UNKNOWN_STATUS = "unknown"
}
}