From 54e7e91339b33680596aab5f9c4562abef9a3a61 Mon Sep 17 00:00:00 2001 From: Joe Tretter Date: Thu, 23 Apr 2026 23:01:54 -0500 Subject: [PATCH] Auto-commit after successful build: update app gameplay/UI, improve build setup Changed files:\napp/build.gradle.kts app/src/main/java/com/kawomi/githugandroid/GameModels.kt app/src/main/java/com/kawomi/githugandroid/GitRuntime.kt --- app/build.gradle.kts | 4 +- .../com/kawomi/githugandroid/GameModels.kt | 23 ++++--- .../com/kawomi/githugandroid/GitRuntime.kt | 67 ++++++++++++------- 3 files changed, 57 insertions(+), 37 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 631108f..670941e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "com.kawomi.githugandroid" minSdk = 26 targetSdk = 34 - versionCode = 56 - versionName = "0.1.55" + versionCode = 59 + versionName = "0.1.58" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/main/java/com/kawomi/githugandroid/GameModels.kt b/app/src/main/java/com/kawomi/githugandroid/GameModels.kt index 6632a3b..3b0411f 100644 --- a/app/src/main/java/com/kawomi/githugandroid/GameModels.kt +++ b/app/src/main/java/com/kawomi/githugandroid/GameModels.kt @@ -25,6 +25,7 @@ data class RepoState( val commits: List = emptyList(), val headBranch: String = "master", val branches: Map = emptyMap(), + val currentDir: String = ".", ) data class Level( @@ -75,6 +76,7 @@ val RepoStateSaver = listSaver( state.files.flatMap { listOf(it.name, it.content, it.staged.toString(), it.tracked.toString()) }, state.commits.flatMap { listOf(it.id, it.message) }, state.branches.flatMap { listOf(it.key, it.value.toString()) }, + state.currentDir, ) }, restore = { saved -> @@ -97,7 +99,8 @@ val RepoStateSaver = listSaver( commits = commitParts.chunked(2).map { CommitNode(it[0] as String, it[1] as String) }, - branches = branchParts.chunked(2).associate { (it[0] as String) to (it[1] as String).toInt() } + branches = branchParts.chunked(2).associate { (it[0] as String) to (it[1] as String).toInt() }, + currentDir = saved[5] as String, ) } ) @@ -105,19 +108,17 @@ val RepoStateSaver = listSaver( object GitSandboxEngine { fun commandReferenceLines(): List = listOf( "Available sandbox commands:", - " git init", - " git status", - " git add ", - " git add .", - " git commit -m \"message\"", - " git commit -am \"message\"", - " git log", - " git branch ", - " git checkout ", + " git ", " ls", " touch ", " help", - " git help", + " pwd ", + " cat ", + " touch ", + " mkdir ", + " cd ", + " rm ", + " echo ", ) fun execute(repo: RepoState, command: String): Pair> { diff --git a/app/src/main/java/com/kawomi/githugandroid/GitRuntime.kt b/app/src/main/java/com/kawomi/githugandroid/GitRuntime.kt index 81b219d..85ad575 100644 --- a/app/src/main/java/com/kawomi/githugandroid/GitRuntime.kt +++ b/app/src/main/java/com/kawomi/githugandroid/GitRuntime.kt @@ -62,16 +62,20 @@ class GitRepositoryRuntime(private val context: Context) { prepareLevel(level) } - val tokens = GitSandboxEngine.tokenizeCommand(command) - if (tokens.isEmpty()) return inspectSandbox(level) to emptyList() + val sandboxRoot = sandbox.canonicalFile + val workingDir = File(sandboxRoot, currentRepo.currentDir).canonicalFile + .takeIf { it.path.startsWith(sandboxRoot.path) } ?: sandboxRoot - val output = when (tokens.first()) { - "git" -> runGit(nativeGit, sandbox, tokens.drop(1)).outputLines - "help", "?" -> commandReferenceLines() - else -> executeHelperCommand(sandbox, tokens) + val tokens = GitSandboxEngine.tokenizeCommand(command) + if (tokens.isEmpty()) return inspectSandbox(level).copy(currentDir = currentRepo.currentDir) to emptyList() + + val result = when (tokens.first()) { + "git" -> currentRepo to runGit(nativeGit, workingDir, tokens.drop(1)).outputLines + "help", "?" -> currentRepo to commandReferenceLines() + else -> executeHelperCommand(sandboxRoot, workingDir, currentRepo, tokens) } - return inspectSandbox(level) to output + return inspectSandbox(level).copy(currentDir = result.first.currentDir) to result.second } fun commandReferenceLines(): List { @@ -84,49 +88,64 @@ class GitRepositoryRuntime(private val context: Context) { } } - private fun executeHelperCommand(workingDir: File, tokens: List): List { + private fun executeHelperCommand(sandboxRoot: File, workingDir: File, currentRepo: RepoState, tokens: List): Pair> { return when (tokens.first()) { - "ls" -> workingDir.listFiles() + "ls" -> currentRepo to workingDir.listFiles() ?.filterNot { it.name == ".git" } ?.sortedBy { it.name } ?.map { it.name } .orEmpty() - "pwd" -> listOf("/sandbox/${workingDir.name}") + "pwd" -> currentRepo to listOf( + "/sandbox" + if (workingDir == sandboxRoot) "" else "/${workingDir.relativeTo(sandboxRoot).path}" + ) "cat" -> { - val target = tokens.getOrNull(1) ?: return listOf("usage: cat ") + val target = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: cat ") val file = File(workingDir, target) - if (!file.exists() || file.isDirectory) listOf("cat: $target: No such file") - else file.readLines().ifEmpty { listOf("") } + if (!file.exists() || file.isDirectory) currentRepo to listOf("cat: $target: No such file") + else currentRepo to file.readLines().ifEmpty { listOf("") } } "touch" -> { - val target = tokens.getOrNull(1) ?: return listOf("usage: touch ") + val target = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: touch ") val file = File(workingDir, target) if (file.exists()) { - listOf("$target already exists") + currentRepo to listOf("$target already exists") } else { file.parentFile?.mkdirs() file.writeText("") - emptyList() + currentRepo to emptyList() } } "mkdir" -> { - val target = tokens.getOrNull(1) ?: return listOf("usage: mkdir ") + val target = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: mkdir ") val dir = File(workingDir, target) - if (dir.exists()) listOf("mkdir: $target: File exists") else { + if (dir.exists()) currentRepo to listOf("mkdir: $target: File exists") else { dir.mkdirs() - emptyList() + currentRepo to emptyList() + } + } + "cd" -> { + val target = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: cd ") + val dir = File(workingDir, target).canonicalFile + when { + !dir.exists() -> currentRepo to listOf("cd: $target: does not exist") + !dir.isDirectory -> currentRepo to listOf("cd: $target: Not a directory") + !dir.path.startsWith(sandboxRoot.path) -> currentRepo to listOf("cd: $target: Permission denied") + else -> { + val relativeDir = sandboxRoot.toPath().relativize(dir.toPath()).toString().ifEmpty { "." } + currentRepo.copy(currentDir = relativeDir) to emptyList() + } } } "rm" -> { - val target = tokens.getOrNull(1) ?: return listOf("usage: rm ") + val target = tokens.getOrNull(1) ?: return currentRepo to listOf("usage: rm ") val file = File(workingDir, target) - if (!file.exists()) listOf("rm: $target: No such file or directory") else { + if (!file.exists()) currentRepo to listOf("rm: $target: No such file or directory") else { file.deleteRecursively() - emptyList() + currentRepo to emptyList() } } - "echo" -> listOf(tokens.drop(1).joinToString(" ")) - else -> listOf("Command not supported in prototype runtime. Try a git command or helper command.") + "echo" -> currentRepo to listOf(tokens.drop(1).joinToString(" ")) + else -> currentRepo to listOf("Command not supported in prototype runtime. Try a git command or helper command.") } }