Files
Githug-Android/README.md

207 lines
15 KiB
Markdown

# GitHug Android
Native Android adaptation of the Ruby [Githug](https://github.com/Gazler/githug) learning game, built with **Kotlin + Jetpack Compose**. The product goal is a shippable mobile implementation that preserves GitHug's CLI-first learning style while making repository state easier to inspect on a phone.
## Implementation Strategy
The app is organized around GitHug parity rather than simplified command quizzes:
- The level catalog follows the upstream Ruby Githug exercise order from `Githug::Level::LEVELS`, excluding the final `contribute` call to action.
- Each exercise lives in its own Kotlin source file under `app/src/main/java/solutions/tretter/githugandroid/levels/`.
- Each level file documents the intended repository setup, evaluation strategy, hints, command suggestions, and embedded solution scenarios.
- Validators prefer repository state and Git objects over raw command text. Direct command-answer validation is reserved for upstream answer-style levels such as identifying a hash, filename, remote URL, author, or count.
- Shared tests execute the embedded solution scenarios for every level and assert that the Android catalog still matches the upstream level order.
- The runtime prepares an isolated sandbox per level and requires a bundled native Git binary for the device ABI. If no native Git binary is available, the app shows an unavailable-build message instead of starting a playable session.
The UI supports a terminal-centered workflow with optional inspection panes:
- **CLI ONLY**: hide visualization and play with terminal-style input.
- **HYBRID**: command line with optional repository panels.
- **VISUAL**: repository panels visible by default, still backed by the same command engine.
## Development Workflow
Development tasks should be run through the project tooling script. It provisions the local toolchain, keeps paths project-relative, and runs Gradle with the project configuration expected by this repository.
While implementing changes, keep `commit-summary.txt` updated as you go. This file is intentionally ignored by git and excluded from automatic commits so it can be edited manually too; the build tooling uses it as the commit message when it exists and is non-empty.
## Android project tooling
This repo includes a root-level tooling script:
```bash
bash ./AndroidProjectTooling.sh
```
It is designed to be idempotent and uses project-relative paths to:
- install a project-local JDK into `./jdk`
- install Android command-line tools into `./android-sdk`
- restore `gradle/wrapper/gradle-wrapper.jar` if missing
- install required Android SDK packages
- write `local.properties`
Available commands:
| Command | Purpose | Output |
| --- | --- | --- |
| `bash ./AndroidProjectTooling.sh` | Provision or refresh the local Android/JDK toolchain only. | Toolchain under `./jdk` and `./android-sdk` |
| `bash ./AndroidProjectTooling.sh --test` | Compile host Git, set `GITHUG_TEST_GIT_BINARY`, and run JVM unit tests. | Test reports under `app/build/reports/` |
| `bash ./AndroidProjectTooling.sh --clean-test` | Compile host Git, clean Gradle outputs, set `GITHUG_TEST_GIT_BINARY`, and run JVM unit tests. | Fresh test reports under `app/build/reports/` |
| `bash ./AndroidProjectTooling.sh --test-emulator` | Install emulator packages if needed, create/start the visible project test AVD, compile Android Git, and run debug instrumentation tests. | Instrumentation reports under `app/build/reports/androidTests/` |
| `bash ./AndroidProjectTooling.sh --test-emulator --hide-emulator-window` | Run the same emulator instrumentation tests without showing the emulator window. | Instrumentation reports under `app/build/reports/androidTests/` |
| `bash ./AndroidProjectTooling.sh --build` | Build the debug APK. | `app/build/outputs/apk/debug/githug-android-debug-v<versionCode>.apk` |
| `bash ./AndroidProjectTooling.sh --build-release-aab` | Build the release Android App Bundle. | `app/build/outputs/bundle/release/githug-android-release-v<versionCode>.aab` |
| `bash ./AndroidProjectTooling.sh --compile-git` | Compile Git for the development host and all Android target ABIs. | Host and Android `libgit.so` binaries |
To run the JVM unit test suite after ensuring the local toolchain is ready:
```bash
bash ./AndroidProjectTooling.sh --test
```
To run instrumentation tests on an Android emulator:
```bash
bash ./AndroidProjectTooling.sh --test-emulator
```
The emulator window is shown by default so UI instrumentation can be followed visually. Add `--hide-emulator-window` for headless runs:
```bash
bash ./AndroidProjectTooling.sh --test-emulator --hide-emulator-window
```
Test outputs and logs are written under project-local build directories:
- JVM test HTML report: `app/build/reports/tests/testDebugUnitTest/index.html`
- JVM test XML results: `app/build/test-results/testDebugUnitTest/`
- Emulator instrumentation HTML report: `app/build/reports/androidTests/connected/debug/index.html`
- Emulator instrumentation XML/results: `app/build/outputs/androidTest-results/connected/debug/`
- Per-test emulator logcat files: `app/build/outputs/androidTest-results/connected/debug/<device-name>/logcat-*.txt`
- Emulator startup log: `build/reports/android-emulator.log`
Runtime diagnostics are emitted to Android logcat with the `GitHugAndroid` tag. To capture a focused trace from a device or emulator:
```bash
android-sdk/platform-tools/adb logcat -c
android-sdk/platform-tools/adb logcat -v time -s GitHugAndroid:D '*:S'
```
The trace includes level loading and preparation timings, native Git argv/cwd/exit/output previews, repository inspection timings, validation snapshots, and the final app decision for each submitted command. For level-resolution issues, compare the `GitRuntime`, `GitProcess`, `GitInspector`, `Validation`, and `GitHugApp` lines around the submitted command.
To build installable/debuggable artifacts:
```bash
bash ./AndroidProjectTooling.sh --build
```
To build the release AAB intended for Play Store style distribution work:
```bash
bash ./AndroidProjectTooling.sh --build-release-aab
```
The script avoids depending on the system Java version by provisioning a project-local **JDK 17**, which is required by current Android SDK command-line tools.
To avoid repeated expensive SDK verification, the script writes a small state file named:
`./.android-project-tooling.state`
If all required components were verified successfully, the script will skip SDK verification/reinstallation for the next **24 hours** unless required directories are missing.
When `--build` or `--build-release-aab` is used, the script also:
- increments `versionCode` by 1
- increments the patch component of `versionName`, for example `0.1.0` to `0.1.1`
- compiles the generated Android `libgit.so` binaries when they are missing or stale
- bundles full Git manpage source files from Git's `Documentation/` directory into app assets
- renames the generated artifact to a `githug-android-*` filename that includes the post-bump `versionCode`
- uploads the renamed APK/AAB with local `./upload2DL.sh` when that script exists
- attempts to create a git commit after a successful build if there are source changes
The build commands currently run these Gradle tasks:
- `--build`: `assembleDebug`
- `--build-release-aab`: `bundleRelease`
## Git Binary Compilation
Native Git compilation is integrated into the root tooling script:
```bash
bash ./AndroidProjectTooling.sh --compile-git
```
Options:
| Command | Purpose | Output |
| --- | --- | --- |
| `bash ./AndroidProjectTooling.sh --test` | Ensure the host Git binary is current, then run tests with it. | `build/host-git/libgit.so` and test reports |
| `bash ./AndroidProjectTooling.sh --clean-test` | Ensure the host Git binary is current, clean Gradle outputs, then run tests with it. | `build/host-git/libgit.so` and fresh test reports |
| `bash ./AndroidProjectTooling.sh --test-emulator` | Ensure Android ABI Git binaries are current, then run instrumentation tests on the visible project AVD. | Android `libgit.so` binaries and instrumentation reports |
| `bash ./AndroidProjectTooling.sh --compile-git` | Ensure host Git and Android ABI Git binaries are current. | Host and Android outputs |
Android ABI outputs are generated files and are ignored by git:
- `app/src/main/jniLibs/arm64-v8a/libgit.so`
- `app/src/main/jniLibs/armeabi-v7a/libgit.so`
- `app/src/main/jniLibs/x86/libgit.so`
- `app/src/main/jniLibs/x86_64/libgit.so`
The `--test` tooling command automatically builds host Git only when the Git source checkout has changed, then exports `GITHUG_TEST_GIT_BINARY=build/host-git/libgit.so`. This keeps JVM tests on the same `GitRepositoryRuntime` path as the app, including the packaged-runtime helper resolution behavior.
Git build outputs are stamped with a Git source fingerprint. Re-running `--test` or `--compile-git` reuses existing `libgit.so` binaries when the checked-out Git sources and target build flavor are unchanged.
Git manpage assets are also refreshed from the checked-out Git source whenever Git is compiled or an app artifact is built.
Git source patches can live under `patches/git/` and are applied by `AndroidProjectTooling.sh` after the Git source checkout is cloned or reused. These patches are part of the source fingerprint, so changing a patch forces the host and Android Git binaries to rebuild. The current runtime does not require a Git source patch: it resolves Git's existing exported `init_git` and `cmd_main` symbols through JNI and calls them with Git-style `argc`/`argv`.
## Runtime Architecture
The command engine has one app-facing runtime:
- **Native Git path**: the packaged Git binary for the device ABI is loaded by the JNI bridge and every `git ...` command invokes Git's existing `init_git(argv)` and `cmd_main(argc, argv)` path in a real per-level repository sandbox in app-private storage.
The runtime exposes a `RepoState` surface to validators. In addition to files, commits, branches, tags, remotes, and config, the model tracks learning-relevant effects such as stashes, fetched remote refs, pushed branches/tags, submodules, and repository maintenance actions.
Helper shell-like commands (`ls`, `pwd`, `cat`, `sh <script>`, `./<script>`, `touch`, `mkdir`, `rm`, `echo`, `cd`) remain implemented in Kotlin so the mobile terminal behaves consistently across devices. Git command behavior, option parsing, and command dispatch are not modeled in Kotlin; they are delegated to Git's own entry path.
Source files should stay comfortably reviewable. Treat files approaching roughly 700 lines as refactor candidates, and prefer extracting cohesive runtime helpers, command handlers, or focused test classes over letting orchestration classes absorb unrelated responsibilities.
## Known Level Differences From Upstream
The Android port keeps the upstream GitHug exercise order through `submodule`. The final upstream `contribute` entry is intentionally excluded because it is a call to action for the original repository rather than a Git exercise. Some included fixtures assume desktop tools, network access, Ruby, Perl/Python helpers, or direct filesystem behavior that should not be required in a mobile learning sandbox. Differences must be documented here when they are intentional.
| Level | Upstream behavior | Android behavior | Why it differs |
| --- | --- | --- | --- |
| `bisect` | Copies the upstream Ruby fixture. The learner tests each checked-out commit with `ruby prog.rb 5` or `make test`, then answers the abbreviated hash `18ed2ac`. | Creates a native Git history with `balance.txt` and `test-balance.sh`. The learner can run `./test-balance.sh` or `sh test-balance.sh`, mark `HEAD` bad and `known-good` good, then use `git bisect run ./test-balance.sh`. The level accepts only the abbreviated hash of the last good commit before the break. | Android does not bundle Ruby or `make`. The replacement still demonstrates the real `git bisect` workflow: identify known good/bad endpoints, run a test at each checked-out commit, and use the discovered first bad commit to identify the last good commit. |
| `clone` / `clone_to_folder` | Clones `https://github.com/Gazler/cloneme` and checks the cloned repository content. | Accepts the intended clone command and models the resulting folder. | The app must remain playable offline and avoid relying on GitHub network access from a phone. |
| `pull`, `fetch`, `push`, `push_branch`, `push_tags` | Use remote-style workflows from upstream fixtures. | Use local synthetic remotes created inside the sandbox and validate fetched/pushed refs through `RepoState`. | This preserves Git behavior without external network dependencies. |
| `stage_lines` | Requires partial hunk staging: one feature line staged and another left unstaged. | Currently validates that `feature.rb` is staged. | Android does not yet expose enough index-vs-working-tree hunk detail in `RepoState` to validate partial staging precisely. |
| `rebase_onto`, `merge_squash` | Upstream validates detailed object graph, merge-parent, or squash-content details. | Android validates the relevant user-facing action or resulting state, but with less object-level detail in some cases. | The current `RepoState` projection does not expose every low-level Git object fact. These should be tightened when the state surface grows. |
| `repack` | Upstream validates object database packing details. | Android now validates that Git produced a pack file under `.git/objects/pack`. | Equivalent user-facing repository outcome. |
| `submodule` | Uses `git submodule add` against the upstream GitHub repository. | Uses an offline local source repository and validates tracked `.gitmodules` metadata for `githug-include-me`. | Android must remain playable offline, and the packaged Git build currently lacks the `git submodule` porcelain. |
| `conflict` | Copies the upstream conflicting poem fixture and validates that the merge commit has two parents, conflict markers are removed, and the correct poem line remains. | Recreates the conflicting poem history natively and validates the latest commit has two parents, no conflict markers remain, and the correct `Sat on a wall` line is present. | Equivalent. |
## Level Authoring
When adding or changing a level:
- Keep one source file per exercise.
- Keep `allGithugLevels()` in upstream Ruby Githug order.
- Document the setup and validation intent in the level file.
- Update `LevelsCompare.md` whenever level setup, validation, hints, or accepted solution behavior changes.
- Prefer validators that inspect `RepoState` over validators that match command strings.
- Add multiple `LevelTestCase` scenarios when more than one solution path should be accepted.
- Update this README's known-differences table if the Android level intentionally differs from upstream Ruby GitHug.
- Run `bash ./AndroidProjectTooling.sh --test` before building.
## Production Focus
Current work is focused on:
- improving parity with upstream Ruby Githug setup and validation semantics
- expanding native-Git-backed behavior across complex repository workflows
- improving onboarding, accessibility, UI polish, icons, and Play Store readiness