Auto-commit after successful build: update app gameplay/UI, improve build setup

Changed files:\napp/build.gradle.kts
app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt
app/src/main/java/solutions/tretter/githugandroid/levels/CoreLevels.kt
app/src/main/java/solutions/tretter/githugandroid/levels/LevelCatalog.kt
This commit is contained in:
Joe Tretter
2026-04-24 15:30:23 -05:00
parent 51cdc05f40
commit f1ca54a2ad
4 changed files with 88 additions and 6 deletions

View File

@@ -11,8 +11,8 @@ android {
applicationId = "solutions.tretter.githugandroid"
minSdk = 26
targetSdk = 34
versionCode = 78
versionName = "0.1.77"
versionCode = 79
versionName = "0.1.78"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true

View File

@@ -182,6 +182,16 @@ class GitRepositoryRuntime(private val context: Context) {
val tagResult = runGit(nativeGit, sandbox, listOf("tag", "--list"))
val remoteResult = runGit(nativeGit, sandbox, listOf("remote", "-v"))
val headResult = runGit(nativeGit, sandbox, listOf("branch", "--show-current"))
val userNameResult = runGit(nativeGit, sandbox, listOf("config", "--get", "user.name"))
val userEmailResult = runGit(nativeGit, sandbox, listOf("config", "--get", "user.email"))
val config = buildMap {
userNameResult.outputLines.firstOrNull()?.takeIf { it.isNotBlank() }?.let { put("user.name", it) }
userEmailResult.outputLines.firstOrNull()?.takeIf { it.isNotBlank() }?.let { put("user.email", it) }
}
AppLog.d(
"GitRuntime",
"inspectSandbox level=${level.id} config=$config user.name.exit=${userNameResult.exitCode} user.email.exit=${userEmailResult.exitCode}",
)
val commits = if (logResult.exitCode == 0) {
logResult.outputLines.filter { it.isNotBlank() }.mapNotNull { line ->
val parts = line.split('\t', limit = 2)
@@ -210,6 +220,7 @@ class GitRepositoryRuntime(private val context: Context) {
val parts = line.trim().split(Regex("\\s+"))
if (parts.size >= 2) parts[0] to parts[1] else null
}.toMap(),
config = config,
)
}

View File

@@ -18,7 +18,21 @@ internal fun coreLevels(): List<Level> = listOf(
commandSuggestions = listOf("git config user.name GitHug", "git config user.email githug@example.com"),
setup = { RepoState(initialized = true, branches = mapOf("master" to 0)) },
validator = repoPredicate { repo ->
repo.config.containsKey("user.name") && repo.config.containsKey("user.email")
val hasUserName = repo.config.containsKey("user.name")
val observedUserName = repo.config["user.name"]
AppLog.d(
"Validation",
"Config rule key='user.name' expected=defined observed=${observedUserName ?: "<missing>"} passed=$hasUserName",
)
val hasUserEmail = repo.config.containsKey("user.email")
val observedUserEmail = repo.config["user.email"]
AppLog.d(
"Validation",
"Config rule key='user.email' expected=defined observed=${observedUserEmail ?: "<missing>"} passed=$hasUserEmail",
)
hasUserName && hasUserEmail
},
),
level(

View File

@@ -10,11 +10,68 @@ internal fun level(
commandSuggestions: List<String> = listOf("git status", "git log", "git help"),
setup: () -> RepoState,
validator: (RepoState, String) -> Boolean,
): Level = Level(id, title, description, hints, commandSuggestions, validator, setup)
): Level = Level(
id = id,
title = title,
description = description,
hints = hints,
commandSuggestions = commandSuggestions,
validator = loggingValidator(id, title, validator),
setup = setup,
)
internal fun commandAnswer(vararg answers: String): (RepoState, String) -> Boolean = { _, command ->
val normalized = command.trim()
answers.any { it.equals(normalized, ignoreCase = true) }
val matched = answers.firstOrNull { it.equals(normalized, ignoreCase = true) }
AppLog.d(
"Validation",
"commandAnswer normalized='$normalized' expected=${answers.toList()} matched=${matched != null}",
)
matched != null
}
internal fun repoPredicate(block: (RepoState) -> Boolean): (RepoState, String) -> Boolean = { repo, _ -> block(repo) }
internal fun repoPredicate(block: (RepoState) -> Boolean): (RepoState, String) -> Boolean = { repo, _ ->
block(repo)
}
private fun loggingValidator(
levelId: String,
levelTitle: String,
validator: (RepoState, String) -> Boolean,
): (RepoState, String) -> Boolean = { repo, command ->
AppLog.d(
"Validation",
buildString {
append("Evaluating level=")
append(levelId)
append(" title=")
append(levelTitle)
append(" command='")
append(command)
append("' repo=")
append(repo.validationSnapshot())
},
)
val result = validator(repo, command)
AppLog.d("Validation", "Result level=$levelId passed=$result")
result
}
private fun RepoState.validationSnapshot(): String = buildString {
append("initialized=")
append(initialized)
append(", headBranch=")
append(headBranch)
append(", branches=")
append(branches.keys.sorted())
append(", tags=")
append(tags.sorted())
append(", remotes=")
append(remotes.toSortedMap())
append(", config=")
append(config.toSortedMap())
append(", files=")
append(files.map { file -> "${file.name}(staged=${file.staged},tracked=${file.tracked})" }.sorted())
append(", commits=")
append(commits.map { it.id to it.message })
}