diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7955114..cb00d02 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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 diff --git a/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt b/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt index bb1fb9c..f09be05 100644 --- a/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt +++ b/app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt @@ -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, ) } diff --git a/app/src/main/java/solutions/tretter/githugandroid/levels/CoreLevels.kt b/app/src/main/java/solutions/tretter/githugandroid/levels/CoreLevels.kt index be72c50..e32dee9 100644 --- a/app/src/main/java/solutions/tretter/githugandroid/levels/CoreLevels.kt +++ b/app/src/main/java/solutions/tretter/githugandroid/levels/CoreLevels.kt @@ -18,7 +18,21 @@ internal fun coreLevels(): List = 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 ?: ""} 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 ?: ""} passed=$hasUserEmail", + ) + + hasUserName && hasUserEmail }, ), level( diff --git a/app/src/main/java/solutions/tretter/githugandroid/levels/LevelCatalog.kt b/app/src/main/java/solutions/tretter/githugandroid/levels/LevelCatalog.kt index 2315365..82dd591 100644 --- a/app/src/main/java/solutions/tretter/githugandroid/levels/LevelCatalog.kt +++ b/app/src/main/java/solutions/tretter/githugandroid/levels/LevelCatalog.kt @@ -10,11 +10,68 @@ internal fun level( commandSuggestions: List = 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) } \ No newline at end of file +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 }) +} \ No newline at end of file