Step 22 C: --repo selects a registered repository in build, retry, and status

The RepoOption mixin resolves the name through the registry; without it a
command means the current directory when served, else the first entry. An
unknown name is a usage error naming the registered repositories.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-09-02 09:03:07 +02:00
co-authored by Claude Fable 5.1
parent a5027acfaf
commit b66d26a03a
7 changed files with 109 additions and 12 deletions
@@ -3,9 +3,11 @@ package de.hoennig.werkator.commands
import de.hoennig.werkator.build.BuildStatus
import de.hoennig.werkator.git.GitService
import de.hoennig.werkator.repo.RepoContext
import de.hoennig.werkator.repo.RepoRegistry
import org.springframework.stereotype.Component
import picocli.CommandLine.Command
import picocli.CommandLine.ExitCode
import picocli.CommandLine.Mixin
import picocli.CommandLine.Parameters
import java.nio.file.Path
import java.util.concurrent.Callable
@@ -24,9 +26,11 @@ import java.util.concurrent.Callable
class BuildCommand(
private val gitService: GitService,
private val consoleBuildRunner: ConsoleBuildRunner,
/** The repository to build: the current working directory (a repo selector comes with the registry). */
var repo: RepoContext,
private val registry: RepoRegistry,
) : Callable<Int> {
@Mixin
var repoOption = RepoOption()
@Parameters(
index = "0",
arity = "0..1",
@@ -35,6 +39,8 @@ class BuildCommand(
)
var branchFragment: String? = null
private lateinit var repo: RepoContext
private val workingDir: Path
get() = repo.workingDir
@@ -42,6 +48,7 @@ class BuildCommand(
val branch: String
val commit: String
try {
repo = repoOption.select(registry)
fetchBestEffort()
branch = resolveBranch() ?: return ExitCode.USAGE
commit = commitToBuild(branch) ?: return ExitCode.USAGE
@@ -0,0 +1,28 @@
package de.hoennig.werkator.commands
import de.hoennig.werkator.repo.RepoContext
import de.hoennig.werkator.repo.RepoRegistry
import picocli.CommandLine.Option
/**
* The `--repo` selector of the repository-scoped commands (ADR 0009): names an entry
* of the instance registry. Without it a command means the current working directory
* when that is served, otherwise the first registered repository — so inside a
* repository every command behaves exactly as it did with one.
*/
class RepoOption {
@Option(
names = ["--repo"],
paramLabel = "<name>",
description = ["registered repository to act on (default: the current directory)"],
)
var name: String? = null
fun select(registry: RepoRegistry): RepoContext {
val wanted = name?.trim()?.takeIf { it.isNotEmpty() } ?: return registry.current()
return registry.byName(wanted)
?: throw IllegalArgumentException(
"no repository named '$wanted' is registered (registered: ${registry.all().joinToString(", ") { it.name }})",
)
}
}
@@ -4,9 +4,11 @@ import de.hoennig.werkator.build.BuildResult
import de.hoennig.werkator.build.BuildStatus
import de.hoennig.werkator.git.GitService
import de.hoennig.werkator.repo.RepoContext
import de.hoennig.werkator.repo.RepoRegistry
import org.springframework.stereotype.Component
import picocli.CommandLine.Command
import picocli.CommandLine.ExitCode
import picocli.CommandLine.Mixin
import java.nio.file.Path
import java.util.concurrent.Callable
@@ -25,15 +27,20 @@ import java.util.concurrent.Callable
class RetryCommand(
private val gitService: GitService,
private val consoleBuildRunner: ConsoleBuildRunner,
/** The repository to retry in: the current working directory (a repo selector comes with the registry). */
var repo: RepoContext,
private val registry: RepoRegistry,
) : Callable<Int> {
@Mixin
var repoOption = RepoOption()
private lateinit var repo: RepoContext
private val workingDir: Path
get() = repo.workingDir
override fun call(): Int {
val failed: List<BuildResult>
try {
repo = repoOption.select(registry)
fetchBestEffort()
failed = repo.results.latestPerName().filter { it.status == BuildStatus.FAILED }
} catch (e: Exception) {
@@ -1,11 +1,12 @@
package de.hoennig.werkator.commands
import de.hoennig.werkator.build.BuildResult
import de.hoennig.werkator.build.BuildResultRepository
import de.hoennig.werkator.repo.RepoRegistry
import de.hoennig.werkator.server.UiFormats
import org.springframework.stereotype.Component
import picocli.CommandLine.Command
import picocli.CommandLine.ExitCode
import picocli.CommandLine.Mixin
import picocli.CommandLine.Option
import java.util.concurrent.Callable
@@ -20,12 +21,22 @@ import java.util.concurrent.Callable
mixinStandardHelpOptions = true,
)
class StatusCommand(
private val repository: BuildResultRepository,
private val registry: RepoRegistry,
) : Callable<Int> {
@Option(names = ["--history"], description = ["Print all recorded builds, not only the latest per branch"])
var history: Boolean = false
@Mixin
var repoOption = RepoOption()
override fun call(): Int {
val repository =
try {
repoOption.select(registry).results
} catch (e: IllegalArgumentException) {
System.err.println("error: ${e.message}")
return ExitCode.USAGE
}
val results = if (history) repository.history() else repository.latestPerName()
if (results.isEmpty()) {
println("(no builds recorded)")