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/test/java/solutions/tretter/githugandroid/LevelSolutionsTest.kt
This commit is contained in:
@@ -19,8 +19,8 @@ android {
|
||||
applicationId = "solutions.tretter.githugandroid"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 95
|
||||
versionName = "0.1.94"
|
||||
versionCode = 96
|
||||
versionName = "0.1.95"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
@@ -43,10 +43,7 @@ class GitRepositoryRuntime(private val context: Context) {
|
||||
runGit(nativeGit, sandbox, listOf("checkout", "-B", desired.headBranch))
|
||||
}
|
||||
|
||||
val stageTargets = desired.files.filter { it.staged || it.tracked }.map { it.name }
|
||||
if (stageTargets.isNotEmpty()) {
|
||||
runGit(nativeGit, sandbox, listOf("add") + stageTargets)
|
||||
}
|
||||
materializeNativeGitState(nativeGit, sandbox, desired)
|
||||
}
|
||||
|
||||
return inspectSandbox(level)
|
||||
@@ -146,17 +143,85 @@ class GitRepositoryRuntime(private val context: Context) {
|
||||
currentRepo to emptyList()
|
||||
}
|
||||
}
|
||||
"echo" -> currentRepo to listOf(tokens.drop(1).joinToString(" "))
|
||||
"echo" -> executeEcho(workingDir, currentRepo, tokens)
|
||||
else -> currentRepo to listOf("Command not supported in prototype runtime. Try a git command or helper command.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun materializeNativeGitState(nativeGit: File, sandbox: File, desired: RepoState) {
|
||||
desired.config.forEach { (key, value) ->
|
||||
runGit(nativeGit, sandbox, listOf("config", key, value))
|
||||
}
|
||||
|
||||
val trackedFiles = desired.files.filter { it.tracked }
|
||||
val desiredCommitCount = maxOf(
|
||||
desired.commits.size,
|
||||
desired.branches.values.maxOrNull() ?: 0,
|
||||
if (desired.tags.isNotEmpty()) 1 else 0,
|
||||
)
|
||||
if (desiredCommitCount > 0) {
|
||||
repeat(desiredCommitCount) { index ->
|
||||
filesForSetupCommit(trackedFiles, index, desiredCommitCount).forEach { file ->
|
||||
runGit(nativeGit, sandbox, listOf("add", file.name))
|
||||
}
|
||||
val message = desired.commits.getOrNull(index)?.message ?: "Setup commit ${index + 1}"
|
||||
runGit(nativeGit, sandbox, listOf("commit", "--allow-empty", "-m", message))
|
||||
}
|
||||
|
||||
desired.branches
|
||||
.keys
|
||||
.filterNot { it == desired.headBranch }
|
||||
.forEach { branch -> runGit(nativeGit, sandbox, listOf("branch", branch)) }
|
||||
if (desired.branches.containsKey(desired.headBranch)) {
|
||||
runGit(nativeGit, sandbox, listOf("checkout", desired.headBranch))
|
||||
}
|
||||
}
|
||||
|
||||
desired.tags.forEach { tag -> runGit(nativeGit, sandbox, listOf("tag", tag)) }
|
||||
desired.remotes.forEach { (name, url) -> runGit(nativeGit, sandbox, listOf("remote", "add", name, url)) }
|
||||
|
||||
val stagedTargets = desired.files.filter { it.staged }.map { it.name }
|
||||
if (stagedTargets.isNotEmpty()) {
|
||||
runGit(nativeGit, sandbox, listOf("add") + stagedTargets)
|
||||
}
|
||||
}
|
||||
|
||||
private fun filesForSetupCommit(files: List<GitFile>, index: Int, commitCount: Int): List<GitFile> {
|
||||
if (files.isEmpty()) return emptyList()
|
||||
if (commitCount <= 1) return files
|
||||
if (index == 0) return files.take(1)
|
||||
if (index == commitCount - 1) return files.drop(index)
|
||||
return files.getOrNull(index)?.let { listOf(it) }.orEmpty()
|
||||
}
|
||||
|
||||
private fun executeEcho(workingDir: File, currentRepo: RepoState, tokens: List<String>): Pair<RepoState, List<String>> {
|
||||
val redirectIndex = tokens.indexOfFirst { it == ">" || it == ">>" }
|
||||
if (redirectIndex == -1 || redirectIndex == tokens.lastIndex) {
|
||||
return currentRepo to listOf(tokens.drop(1).joinToString(" "))
|
||||
}
|
||||
|
||||
val file = File(workingDir, tokens[redirectIndex + 1])
|
||||
file.parentFile?.mkdirs()
|
||||
val content = tokens.subList(1, redirectIndex).joinToString(" ")
|
||||
if (tokens[redirectIndex] == ">>") {
|
||||
if (file.exists() && file.length() > 0L) {
|
||||
file.appendText("\n$content")
|
||||
} else {
|
||||
file.writeText(content)
|
||||
}
|
||||
} else {
|
||||
file.writeText(content)
|
||||
}
|
||||
return currentRepo to emptyList()
|
||||
}
|
||||
|
||||
private fun inspectSandbox(level: Level): RepoState {
|
||||
val sandbox = sandboxDir(level)
|
||||
val nativeGit = nativeGitBinary()
|
||||
val filesOnDisk = sandbox.listFiles()
|
||||
?.filter { it.isFile && it.name != ".git" }
|
||||
val filesOnDisk = sandbox.walkTopDown()
|
||||
.filter { it.isFile && !it.relativeTo(sandbox).path.startsWith(".git/") }
|
||||
.orEmpty()
|
||||
.toList()
|
||||
|
||||
if (nativeGit == null || !File(sandbox, ".git").exists()) {
|
||||
return RepoState(
|
||||
@@ -208,9 +273,10 @@ class GitRepositoryRuntime(private val context: Context) {
|
||||
return RepoState(
|
||||
initialized = true,
|
||||
files = filesOnDisk.map { file ->
|
||||
val (staged, tracked) = statusMap[file.name] ?: (false to true)
|
||||
val relativePath = file.relativeTo(sandbox).path
|
||||
val (staged, tracked) = statusMap[relativePath] ?: (false to true)
|
||||
GitFile(
|
||||
name = file.name,
|
||||
name = relativePath,
|
||||
content = file.readText(),
|
||||
staged = staged,
|
||||
tracked = tracked,
|
||||
|
||||
@@ -25,7 +25,8 @@ class LevelSolutionsTest {
|
||||
fun knownSolutionsCompleteEveryLevel() {
|
||||
val evidence = StringBuilder()
|
||||
evidence.appendLine("GitHug Android level solution evidence")
|
||||
evidence.appendLine("Engine: GitSandboxEngine")
|
||||
evidence.appendLine("Engine: GitSandboxEngine fallback")
|
||||
evidence.appendLine("Scope: validates fallback command handling and level validators; native runtime setup is covered by app runtime tests/builds.")
|
||||
evidence.appendLine("Levels: ${allGithugLevels().size}")
|
||||
evidence.appendLine()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user