Skip to content

feat(system): add native Termux (Android) support#277

Closed
Snakeblack wants to merge 15 commits into
Gentleman-Programming:mainfrom
Snakeblack:feat/termux-support
Closed

feat(system): add native Termux (Android) support#277
Snakeblack wants to merge 15 commits into
Gentleman-Programming:mainfrom
Snakeblack:feat/termux-support

Conversation

@Snakeblack

@Snakeblack Snakeblack commented Apr 11, 2026

Copy link
Copy Markdown

Linked Issue

Closes #276

PR Type

  • type:feature - New feature (non-breaking change that adds functionality)

Summary

Adds native Android/Termux support to gentle-ai:

  • Detects Android/Termux as a first-class platform profile through GOOS=android.
  • Resolves Termux paths through a dedicated resolver, including $PREFIX aware paths.
  • Keeps Android installs and upgrades on source compilation with PIE flags, avoiding incompatible glibc release binaries.
  • Preserves current main hardening for checksum verification, Windows/Scoop docs, Go availability detection, and pinned GGA upgrade behavior.

Changes

Area Files Change
Platform detection internal/system/detect.go, tests Adds Android as a supported OS and maps it to the Termux profile.
Path resolution internal/system/resolver.go, internal/system/path.go, tests Adds prefix-aware path resolution and Termux shell PATH persistence.
Install commands internal/installcmd/resolver.go, tests Centralizes sudo/rootless behavior and keeps Android commands sudo-free.
Engram install internal/components/engram/download.go, tests Uses versioned go install with PIE flags on Android while preserving checksum verification for binary downloads.
Upgrade strategy internal/update/upgrade/strategy.go, tests Applies PIE flags for Android Go installs and preserves current main upgrade hardening.
Installer script scripts/install.sh Forces Android/Termux to use go install; blocks brew/binary methods on Android.
Docs/specs docs/platforms.md, openspec/... Documents Android/Termux support and the review path.

Key Technical Decisions

  1. Android/Termux is detected through GOOS=android, not through Linux ID=termux or TERMUX_VERSION in Go code. This avoids a half-detected linux + termux state.
  2. Release binaries remain for linux, darwin, and windows. Android uses source compilation because Linux glibc binaries are incompatible with Android Bionic libc.
  3. Android builds and upgrades use -ldflags=-extldflags=-pie, required by Android executable loading rules.
  4. Rootless Termux flows do not use sudo.
  5. The path resolver keeps platform-specific path mapping isolated from install and update logic.

Review workload note

This PR exceeds the current 400-line review budget, but it predates the chained-PR policy and keeps one cohesive platform-support unit together: Android/Termux detection, prefix-aware paths, install behavior, upgrade behavior, tests, and docs.

Splitting it now would add review churn and risk losing the existing validation and context. Treating this as a maintainer-approved size:exception is the lower-risk path for this already-open PR.

To keep review focused, please review in this order:

  1. internal/system/* platform detection and path resolution
  2. internal/installcmd/* sudo/rootless install behavior
  3. internal/components/engram/* Android source install with PIE
  4. internal/update/upgrade/* upgrade strategy
  5. docs/OpenSpec updates

Test Plan

  • go test ./internal/system ./internal/installcmd ./internal/components/engram ./internal/update/upgrade
  • git diff --check
  • Manual validation on a physical Android device running Termux: detection, install, upgrade, PATH persistence, and PIE source builds.
  • PR branch is mergeable against current main after conflict resolution.
  • Full CI validates broad repository checks on GitHub runners.

Contributor Checklist

  • Linked an approved issue (Closes #276)
  • Added exactly one type:* label (type:feature)
  • Relevant unit tests pass
  • Documentation updated
  • Conventional commit format used
  • No Co-Authored-By trailers

Snakeblack and others added 4 commits April 11, 2026 03:50
- Add 'android' to supported operating systems in system detection.
- Implement source compilation for 'engram' via 'go install' on Android to ensure compatibility with Bionic libc.
- Fix path resolution by mapping '/tmp' to '$PREFIX/tmp' in TermuxResolver.
- Update engram installation directory to use home-relative paths on Android.
- Improve OS support error messages to include Android.
Copilot AI review requested due to automatic review settings April 11, 2026 02:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds native Android/Termux support across system detection, path resolution, installation, and upgrade flows so gentle-ai can run end-to-end in Termux without sudo and with Android PIE-compatible builds.

Changes:

  • Add Android (GOOS=android) as a supported OS and map it to a Termux platform profile (apt, supported).
  • Introduce a PathResolver abstraction with a Termux-aware implementation for $PREFIX-based path mapping, and use it in install flows (e.g., /tmp).
  • Add Termux-specific PATH persistence and Android PIE go install upgrade behavior; expand unit tests and platform docs/specs.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
internal/system/detect.go Adds android support and introduces termux distro mapping.
internal/system/detect_test.go Adds coverage for Android support and Termux distro detection.
internal/system/guard.go Updates supported OS error message to include Android.
internal/system/guard_test.go Updates assertions for the supported OS error message.
internal/system/resolver.go New PathResolver abstraction and Termux $PREFIX resolver.
internal/system/resolver_test.go Adds unit tests for default/Termux resolvers and resolver factory.
internal/system/path.go Adds Termux PATH persistence (.bashrc/.zshrc) and test hook for GOOS.
internal/system/path_test.go Adds Termux PATH persistence test coverage.
internal/installcmd/resolver.go Suppresses sudo in Termux and uses resolver-mapped /tmp for GGA installs.
internal/components/engram/download.go Adds Android install-via-go install path and Android install dir handling.
internal/update/upgrade/strategy.go Injects Android PIE -ldflags for go install upgrades.
internal/update/upgrade/strategy_test.go Adds PIE-flag verification test and adjusts command stubbing.
docs/platforms.md Documents Android/Termux support and platform notes.
openspec/specs/termux-support/spec.md Adds feature specification for Termux support requirements.
openspec/changes/archive/2026-04-11-termux-compatibility/exploration.md SDD exploration artifact for Termux support.
openspec/changes/archive/2026-04-11-termux-compatibility/proposal.md SDD proposal artifact for Termux support.
openspec/changes/archive/2026-04-11-termux-compatibility/design.md SDD design artifact (PathResolver + persistence approach).
openspec/changes/archive/2026-04-11-termux-compatibility/tasks.md SDD task breakdown and test plan for Termux support.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/system/resolver.go Outdated
Comment thread internal/system/resolver_test.go Outdated
Comment thread internal/system/path.go Outdated
Comment thread internal/system/path.go Outdated
Comment thread internal/system/path_test.go Outdated
Comment thread internal/update/upgrade/strategy.go Outdated
Comment thread internal/update/upgrade/strategy_test.go Outdated
Comment thread internal/components/engram/download.go Outdated
Comment thread internal/components/engram/download.go Outdated
Comment thread docs/platforms.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/system/resolver.go
Comment thread e2e/docker-test.sh
Comment thread docs/platforms.md Outdated
Comment thread internal/components/engram/download.go Outdated
Comment thread internal/components/engram/download.go
Comment thread openspec/specs/termux-support/spec.md Outdated
- Fix AddToUserPath docstring to reflect Termux PATH persistence
- Handle os.ReadFile errors explicitly in persistPathTermux (ignore only NotExist)
- Add HOME/fallback chain in engramInstallDir for Android edge case
- Clarify engramAssetURL comment: android→linux mapping is URL-only, not runtime
- Replace manual os.Setenv/defer with t.Setenv in resolver_test
- Remove leftover TDD 'RED:' markers from committed tests
- Fix strategy.go comment about PlatformProfile.OS on Termux
- Correct docs/platforms.md: Android uses source compilation, not release binaries
- Fix spec scenario: detection uses GOOS=android, not TERMUX_VERSION

@Alan-TheGentleman Alan-TheGentleman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: feat(system): add native Termux (Android) support

Solid contribution — the architecture is clean (PathResolver interface, clean detection branching, PIE awareness), the test coverage is good, and the SDD artifacts provide excellent context. Two issues need attention before merge:

Must Fix

1. Inconsistent Termux detection between GOOS=android and GOOS=linux + ID=termux

The case "android" branch in resolvePlatformProfile correctly sets LinuxDistro=termux, but the PR also adds Termux detection in the "linux" branch via detectLinuxDistro. If someone runs on GOOS=linux with ID=termux in os-release (which Termux does report), the profile gets OS=linux + LinuxDistro=termux. But downstream checks in download.go (profile.OS == "android") and engramInstallDir won't trigger, creating a half-detected state.

Recommendation: Remove the Termux case from detectLinuxDistro and the LinuxDistroTermux entry in the linux distro switch — GOOS=android handles the full detection. Or, update all downstream checks to also match on LinuxDistro == "termux".

2. Dead code in engramAssetURL (android→linux mapping)

DownloadLatestBinary returns early for OS=android via installViaGo, so the goos == "android" mapping in engramAssetURL is unreachable. If defensive, add a comment. If not needed, remove it.

Suggestions

  • persistPathTermux: the \n prefix creates a blank first line when writing to a new RC file
  • installViaGo: uses os.Getenv directly instead of the testable osGetenv pattern used elsewhere
  • Sudo suppression in ResolveDependencyInstall: the prepend logic is duplicated 3 times — consider a small helper
  • echo okgo version in unrelated tests: worth mentioning in the PR description as an incidental fix

Great work on the PathResolver abstraction and the thorough test coverage. Looking forward to the updated version!

…eanup

- Remove Termux from Linux distro detection (detectLinuxDistro, resolvePlatformProfile)
  GOOS=android is now the exclusive Termux detection path, eliminating the
  half-detected state where OS=linux + LinuxDistro=termux bypassed downstream
  Android-specific logic (installViaGo, engramInstallDir, PIE flags)

- Remove dead android→linux mapping in engramAssetURL
  DownloadLatestBinary returns early via installViaGo for OS=android,
  making the goos rewrite unreachable

- Fix leading blank line in persistPathTermux for new rc files
  exportCmd no longer starts with \n when writing to an empty file

- Extract withSudo helper in ResolveDependencyInstall
  Replaces 3 duplicate if-sudo-prepend blocks with a single function

- Update tests to match: distro matrix expects unknown for ID=termux,
  PIE test uses OS=android instead of OS=linux
Copilot AI review requested due to automatic review settings April 12, 2026 13:08

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/system/resolver.go Outdated
Comment thread internal/system/resolver.go Outdated
Comment thread internal/system/path.go
Comment thread internal/components/engram/download.go
Copilot AI review requested due to automatic review settings April 12, 2026 13:32
…uards

- Set GORELEASER_OS='' for android (no release assets exist)
- Add fatal guard in detect_install_method when OS=android and Go is missing
- Add fatal guard when FORCE_METHOD=binary on Android (glibc incompatible)
- Add safety net in install_binary for empty GORELEASER_OS (defense in depth)
- Clean up contradictory priority comments into single coherent block
@Snakeblack Snakeblack force-pushed the feat/termux-support branch from 49c17f2 to deff64b Compare April 12, 2026 13:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/install.sh
Comment thread internal/update/upgrade/strategy.go Outdated
Comment thread internal/components/engram/download.go
- Tighten TermuxResolver prefix matching to exact directory boundaries
  (/usr/ not /usrbin, /etc/ not /etcetera) with boundary-safe checks
- Add shell-unsafe character guard in persistPathTermux to prevent
  rc file corruption from backticks, quotes, dollar signs, or newlines
- Document engramInstallDir android branch as defensive (currently
  unused since installViaGo writes to GOBIN)
- Include full args in goInstallUpgrade error message for easier
  debugging of PIE-related failures on Termux
- Add engramInstallDir android test case and resolver boundary tests
@Snakeblack

Copy link
Copy Markdown
Author

Hey Alan, thanks for the thorough review — every point was valid. Here's what we addressed:


Must Fix 1 — Inconsistent Termux detection (GOOS=android vs GOOS=linux + ID=termux)

Resolved in 06413cd. We chose your first recommendation: remove Termux from the Linux detection path entirely.

  • Removed LinuxDistroTermux from the case "linux" distro switch in resolvePlatformProfile
  • Removed if id == LinuxDistroTermux from detectLinuxDistro
  • GOOS=android is now the exclusive Termux detection path — no half-detected state possible
  • Added an explicit comment in detectLinuxDistro documenting this decision
  • Updated tests: distro matrix now expects unknown for ID=termux, PIE test uses OS:"android"

Must Fix 2 — Dead code in engramAssetURL (android→linux mapping)

Resolved in 06413cd. Removed the unreachable if goos == "android" { goos = "linux" } block. Added a comment explaining that Android never reaches this function since DownloadLatestBinary returns early via installViaGo().


Suggestions

persistPathTermux\n prefix on new rc files

Fixed in 06413cd. The exportCmd now conditionally omits the leading \n when writing to an empty file.

Sudo suppression duplication in ResolveDependencyInstall

Fixed in 06413cd. Extracted a withSudo() helper that prepends "sudo" unless the profile indicates Termux. Replaces the 3 duplicate if-prepend blocks.

installViaGoos.Getenv vs testable osGetenv

Not applied. This is a single call in a function that's already tested via exec.Command mocking. Adding osGetenv indirection here felt like over-engineering for one call site. Happy to revisit if you feel strongly about it.

echo okgo version in unrelated tests

Pre-existing — this was an incidental fix in an earlier commit to make tests pass on environments where echo isn't available as a standalone binary (Windows). Noted for PR description clarity.


Additional hardening (scripts + Copilot review)

After your review, we also:

  • scripts/install.sh (deff64b): Set GORELEASER_OS="" for Android (no release assets exist), added 4 layers of guards to make binary download on Android impossible (auto-detect fatal, force-method fatal, empty GORELEASER_OS fatal, install_binary guard). Preserved your original priority comments — only appended the Android exception note.

  • TermuxResolver prefix matching (145091f): Tightened from HasPrefix("/usr")path == "/usr" || HasPrefix("/usr/") to prevent false matches on paths like /usrbin or /etcetera. Added boundary edge-case tests.

  • Shell injection guard (145091f): Added character validation in persistPathTermux rejecting backticks, quotes, $, and newlines before writing to rc files.

  • Error message improvement (145091f): goInstallUpgrade error now includes full args (including PIE flags) for easier Termux debugging.

  • engramInstallDir android branch (145091f): Documented as defensive/future-proofing in docstring, added test case.


All changes have been validated on a physical Android device running Termux — detection, go install with PIE flags, path resolution, and the install script flow. Tests pass on both Windows and Linux (the remaining failures in CI are pre-existing Windows-specific issues unrelated to this PR).

Let me know if anything else needs adjustment!

Copilot AI review requested due to automatic review settings April 30, 2026 22:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/install.sh Outdated
@@ -216,8 +248,14 @@ install_go() {

local go_package="github.com/${GITHUB_OWNER,,}/${GITHUB_REPO}/cmd/${BINARY_NAME}@latest"

Copilot AI Apr 30, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go_package uses Bash 4+ lowercase expansion (${GITHUB_OWNER,,}), which fails on macOS’s default Bash 3.2 when users run --method go (or any environment using older Bash). Consider avoiding ,, (e.g., hardcode the lowercase owner, or lower-case via tr) so the installer works with the shebang’s bash on macOS.

Suggested change
local go_package="github.com/${GITHUB_OWNER,,}/${GITHUB_REPO}/cmd/${BINARY_NAME}@latest"
local github_owner_lower
github_owner_lower="$(printf '%s' "$GITHUB_OWNER" | tr '[:upper:]' '[:lower:]')"
local go_package="github.com/${github_owner_lower}/${GITHUB_REPO}/cmd/${BINARY_NAME}@latest"

Copilot uses AI. Check for mistakes.

@Snakeblack Snakeblack left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hice los cambios que me propusiste hace unos días, pero no se integro el feature a main

@Alan-TheGentleman Alan-TheGentleman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Termux support is a real product expansion, not just a small platform patch.

Please move this through a design issue first or split it into smaller approved slices: platform detection, path resolution, install behavior, upgrade behavior, and docs. The current PR is too broad to review safely as one unit.

@Alan-TheGentleman

Copy link
Copy Markdown
Contributor

Heads-up: this PR is now showing a merge conflict against main. We shipped v1.31.0 (and a number of follow-up fixes) over the past two days — several files this PR touches were also modified upstream.

Could you rebase against current main and resolve the conflicts?

git fetch upstream main
git rebase upstream/main
# resolve any conflicts
git push --force-with-lease

Once it's green again I can review for merge. Sorry for the drift — it's the cost of the cleanup velocity. Thanks for sticking with it.

Integra main actual, preserva la detección Android como frontera canónica y documenta size:exception para el PR existente.
@Snakeblack

Copy link
Copy Markdown
Author

Hey Alan, thanks for the heads-up.

The branch is updated against current main, the merge conflicts are resolved, and the PR is now showing as mergeable. I also rebuilt the PR description with clean Markdown and added the explicit size:exception rationale, since this PR predates the current 400-line/chained-PR policy and the Termux work is still one cohesive platform-support unit.

Validated with:

  • go test ./internal/system ./internal/installcmd ./internal/components/engram ./internal/update/upgrade
  • git diff --check
  • PR mergeability check: MERGEABLE

Could you take another look when you have a chance?

@Alan-TheGentleman

Copy link
Copy Markdown
Contributor

Thanks for the rebase and for addressing the earlier review points. I did another full pass.

The core Termux direction looks good, but main moved again after #257 and this PR is now conflicting. Because maintainerCanModify is disabled on the fork, I cannot push the fix directly here. I prepared a maintainer validation branch/PR with the conflict resolution and the review fixes here: #765.

Important fix included in that branch: Android gentle-ai self-upgrade now routes to versioned go install with PIE flags instead of trying to download a non-existent Android release asset. It also keeps standard Linux self-upgrade on release binaries, even when Go is installed.

Validation run on the resolved branch:

  • bash -n scripts/install.sh e2e/docker-test.sh
  • shellcheck scripts/install.sh e2e/docker-test.sh
  • go test ./internal/update ./internal/update/upgrade ./internal/system ./internal/installcmd ./internal/components/engram
  • go test ./...

All passed locally. The remaining policy issue is size: this is now ~1.1k changed lines, so we either need the maintainer-approved size:exception path or a split. Given this PR predates the current 400-line policy and the feature is a cohesive platform-support unit, I think size:exception is reasonable if maintainers agree.

@Snakeblack

Copy link
Copy Markdown
Author

Hi Alan, thanks for preparing #765 and calling out the upgrade edge case.

I updated #277 with your validation branch resolution, merged current main, and pushed one extra fix on top: scripts/install.sh now uses the latest release tag as a versioned go install target instead of @latest, while preserving Android PIE flags.

Validation:

  • bash -n scripts/install.sh e2e/docker-test.sh (run from an LF checkout)
  • git diff --check origin/main...HEAD
  • go test ./internal/system ./internal/installcmd ./internal/components/engram ./internal/update/upgrade
  • Fresh focused review of the installer fix: approved, no blocking issues

PR #277 is now MERGEABLE at 2de32b5. I also tried to add the size:exception label, but the fork account does not have permission to apply upstream labels. The PR body still documents the size-exception rationale.

@Alan-TheGentleman

Copy link
Copy Markdown
Contributor

Closing this in favor of #765.\n\nThe Termux work still matters, but #765 supersedes this branch with maintainer conflict cleanup and broader install/update/test coverage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(system): add native Termux (Android) support

3 participants