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"
|
applicationId = "solutions.tretter.githugandroid"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 95
|
versionCode = 96
|
||||||
versionName = "0.1.94"
|
versionName = "0.1.95"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|||||||
@@ -43,10 +43,7 @@ class GitRepositoryRuntime(private val context: Context) {
|
|||||||
runGit(nativeGit, sandbox, listOf("checkout", "-B", desired.headBranch))
|
runGit(nativeGit, sandbox, listOf("checkout", "-B", desired.headBranch))
|
||||||
}
|
}
|
||||||
|
|
||||||
val stageTargets = desired.files.filter { it.staged || it.tracked }.map { it.name }
|
materializeNativeGitState(nativeGit, sandbox, desired)
|
||||||
if (stageTargets.isNotEmpty()) {
|
|
||||||
runGit(nativeGit, sandbox, listOf("add") + stageTargets)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return inspectSandbox(level)
|
return inspectSandbox(level)
|
||||||
@@ -146,17 +143,85 @@ class GitRepositoryRuntime(private val context: Context) {
|
|||||||
currentRepo to emptyList()
|
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.")
|
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 {
|
private fun inspectSandbox(level: Level): RepoState {
|
||||||
val sandbox = sandboxDir(level)
|
val sandbox = sandboxDir(level)
|
||||||
val nativeGit = nativeGitBinary()
|
val nativeGit = nativeGitBinary()
|
||||||
val filesOnDisk = sandbox.listFiles()
|
val filesOnDisk = sandbox.walkTopDown()
|
||||||
?.filter { it.isFile && it.name != ".git" }
|
.filter { it.isFile && !it.relativeTo(sandbox).path.startsWith(".git/") }
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
|
.toList()
|
||||||
|
|
||||||
if (nativeGit == null || !File(sandbox, ".git").exists()) {
|
if (nativeGit == null || !File(sandbox, ".git").exists()) {
|
||||||
return RepoState(
|
return RepoState(
|
||||||
@@ -208,9 +273,10 @@ class GitRepositoryRuntime(private val context: Context) {
|
|||||||
return RepoState(
|
return RepoState(
|
||||||
initialized = true,
|
initialized = true,
|
||||||
files = filesOnDisk.map { file ->
|
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(
|
GitFile(
|
||||||
name = file.name,
|
name = relativePath,
|
||||||
content = file.readText(),
|
content = file.readText(),
|
||||||
staged = staged,
|
staged = staged,
|
||||||
tracked = tracked,
|
tracked = tracked,
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ class LevelSolutionsTest {
|
|||||||
fun knownSolutionsCompleteEveryLevel() {
|
fun knownSolutionsCompleteEveryLevel() {
|
||||||
val evidence = StringBuilder()
|
val evidence = StringBuilder()
|
||||||
evidence.appendLine("GitHug Android level solution evidence")
|
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("Levels: ${allGithugLevels().size}")
|
||||||
evidence.appendLine()
|
evidence.appendLine()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user