Skip to content

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

Open
Alan-TheGentleman wants to merge 13 commits into
mainfrom
feat/276-termux-support-resolved
Open

feat(system): add native Termux Android support#765
Alan-TheGentleman wants to merge 13 commits into
mainfrom
feat/276-termux-support-resolved

Conversation

@Alan-TheGentleman

@Alan-TheGentleman Alan-TheGentleman commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Linked Issue

Closes #276

PR Type

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

Summary

Supersedes #277 because the contributor fork does not allow maintainer edits. This preserves the Termux support work from #277 and adds maintainer conflict/test hygiene fixes after #257 landed.

  • Detects Android/Termux through GOOS=android as the canonical platform boundary.
  • Adds Termux-aware path resolution, rootless install command handling, and PATH persistence.
  • Uses source compilation with PIE flags for Android installs/upgrades where glibc release binaries are incompatible.

Changes

Area Change
internal/system/* Android platform detection, Termux path resolver, PATH persistence, and tests.
internal/installcmd/* Rootless Termux install command handling and GGA temp path resolution.
internal/components/engram/* Android source install via versioned go install with PIE flags.
internal/update/upgrade/* Android PIE flags for Go upgrades, resolved against Windows installer upgrade changes.
scripts/install.sh Android/Termux detection, mandatory Go install, binary/brew guards, PIE flags.
e2e/docker-test.sh Docker preflight for platforms without Docker support.
docs/platforms.md, openspec/... Termux platform documentation and SDD artifacts.

Maintainer fixes added after #277

  • Resolved conflicts with feat(upgrade): add installer method for Windows self‑replace #257 Windows installer self-replace changes.
  • Fixed shellcheck SC2059 in e2e/docker-test.sh.
  • Removed duplicate go install log line in scripts/install.sh.
  • Cleaned a PowerShell escaping comment typo.
  • Fixed Android gentle-ai self-upgrade routing so it uses versioned go install with PIE flags instead of looking for non-existent Android release assets.
  • Cleaned archived OpenSpec artifacts (completed task checklist and English-only technical wording).

Test Plan

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

Contributor Checklist

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

Snakeblack and others added 12 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.
- 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
…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
…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
- 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
Integra main actual, preserva la detección Android como frontera canónica y documenta size:exception para el PR existente.
Copilot AI review requested due to automatic review settings June 5, 2026 20:00
@Alan-TheGentleman Alan-TheGentleman added the type:feature New feature label Jun 5, 2026

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

This PR adds first-class Android/Termux support across detection, installation, path resolution, and upgrade flows, enabling gentle-ai (and related components like engram/GGA) to run natively in Termux where standard Linux assumptions (glibc binaries, /usr/* paths, sudo, etc.) do not hold.

Changes:

  • Introduces GOOS=android as the canonical boundary for Termux support and marks Android as a supported OS in system detection/guards.
  • Adds Termux-aware path resolution and PATH persistence behavior (shell rc file updates) plus corresponding unit tests.
  • Forces source-based installs/upgrades with Android PIE flags (-ldflags=-extldflags=-pie) in the installer script and Go upgrade/component logic; adds docs/spec artifacts.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
scripts/install.sh Detect Android/Termux; force go install with PIE flags; guard against binary downloads on Android.
e2e/docker-test.sh Adds early failure when Docker isn’t available (e.g., Termux), clarifying E2E requirements.
docs/platforms.md Documents Android (Termux) as supported via source compilation + Termux-specific notes.
internal/system/detect.go Adds android support and maps it to a Termux-like profile (LinuxDistroTermux, apt).
internal/system/detect_test.go Tests Android support and ensures Termux isn’t inferred via Linux os-release.
internal/system/guard.go Updates supported-OS error message to include Android.
internal/system/guard_test.go Updates assertions for the Android-inclusive guard error message.
internal/system/resolver.go Adds PathResolver abstraction and Termux prefix resolver keyed on profile.OS == "android".
internal/system/resolver_test.go Adds coverage for default vs Termux prefix mapping behavior and boundary cases.
internal/system/path.go Adds Termux PATH persistence to ~/.bashrc / ~/.zshrc and testable pathGOOS.
internal/system/path_test.go Adds Termux PATH persistence tests and GOOS-boundary test for isTermux().
internal/installcmd/resolver.go Centralizes rootless/sudo handling; makes GGA temp dir Termux-prefix-aware.
internal/installcmd/resolver_test.go Adds tests for Termux “no sudo” behavior and Termux-prefixed GGA temp dir.
internal/components/engram/download.go Uses source go install @vX.Y.Z with PIE flags on Android; adds install-dir selection logic.
internal/components/engram/download_test.go Tests Android source install command/flags and managed install destination behavior.
internal/update/upgrade/strategy.go Passes platform profile into go-install upgrade path and injects PIE flags for Android.
internal/update/upgrade/strategy_test.go Adds test ensuring Android go-install upgrade includes PIE flags.
internal/update/upgrade/executor_test.go Adjusts GGA managed-path tests to use canonical gga path helpers.
openspec/specs/termux-support/spec.md Adds a spec defining Termux detection, prefix path resolution, and PATH persistence requirements.
openspec/changes/archive/2026-04-11-termux-compatibility/tasks.md Adds archived task breakdown for the Termux compatibility effort.
openspec/changes/archive/2026-04-11-termux-compatibility/proposal.md Adds archived proposal describing scope, approach, and risks for Termux support.
openspec/changes/archive/2026-04-11-termux-compatibility/exploration.md Adds archived exploration notes for Termux gaps and recommended approach.
openspec/changes/archive/2026-04-11-termux-compatibility/design.md Adds archived design describing the resolver abstraction and testing strategy.

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

Comment on lines +42 to +53
if path == "/usr" || strings.HasPrefix(path, "/usr/") {
return filepath.Join(r.Prefix, strings.TrimPrefix(path, "/usr"))
}
if path == "/bin" || strings.HasPrefix(path, "/bin/") {
return filepath.Join(r.Prefix, "bin", strings.TrimPrefix(path, "/bin"))
}
if path == "/etc" || strings.HasPrefix(path, "/etc/") {
return filepath.Join(r.Prefix, "etc", strings.TrimPrefix(path, "/etc"))
}
if path == "/tmp" || strings.HasPrefix(path, "/tmp/") {
return filepath.Join(r.Prefix, "tmp", strings.TrimPrefix(path, "/tmp"))
}
Comment thread internal/system/path.go
Comment on lines 69 to 71
// escapePowerShellString escapes a string for safe use inside a PowerShell
// single-quoted string literal by replacing each ' with '' (PowerShell's escape
// single-quoted string literal by replacing each ' with (PowerShell's escape
// sequence for a literal single quote within single-quoted strings).
@Alan-TheGentleman Alan-TheGentleman force-pushed the feat/276-termux-support-resolved branch from e9d3c5c to 4c9fe72 Compare June 5, 2026 20:05
Copilot AI review requested due to automatic review settings June 5, 2026 20:08
@Alan-TheGentleman Alan-TheGentleman force-pushed the feat/276-termux-support-resolved branch from 4c9fe72 to 5fea865 Compare June 5, 2026 20:08
@Alan-TheGentleman Alan-TheGentleman force-pushed the feat/276-termux-support-resolved branch from 5fea865 to 14aaf19 Compare June 5, 2026 20:09

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 23 out of 23 changed files in this pull request and generated 2 comments.

Comment on lines +42 to +53
if path == "/usr" || strings.HasPrefix(path, "/usr/") {
return filepath.Join(r.Prefix, strings.TrimPrefix(path, "/usr"))
}
if path == "/bin" || strings.HasPrefix(path, "/bin/") {
return filepath.Join(r.Prefix, "bin", strings.TrimPrefix(path, "/bin"))
}
if path == "/etc" || strings.HasPrefix(path, "/etc/") {
return filepath.Join(r.Prefix, "etc", strings.TrimPrefix(path, "/etc"))
}
if path == "/tmp" || strings.HasPrefix(path, "/tmp/") {
return filepath.Join(r.Prefix, "tmp", strings.TrimPrefix(path, "/tmp"))
}
Comment on lines +166 to +169
// Priority 2: GOPATH/bin
if gopath := engramGetenv("GOPATH"); gopath != "" {
return filepath.Join(gopath, "bin"), nil, nil
}
Copilot AI review requested due to automatic review settings June 5, 2026 20:11
@Alan-TheGentleman Alan-TheGentleman force-pushed the feat/276-termux-support-resolved branch from 14aaf19 to cdda85c Compare June 5, 2026 20:11

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 25 out of 25 changed files in this pull request and generated 3 comments.

Comment on lines +42 to +53
if path == "/usr" || strings.HasPrefix(path, "/usr/") {
return filepath.Join(r.Prefix, strings.TrimPrefix(path, "/usr"))
}
if path == "/bin" || strings.HasPrefix(path, "/bin/") {
return filepath.Join(r.Prefix, "bin", strings.TrimPrefix(path, "/bin"))
}
if path == "/etc" || strings.HasPrefix(path, "/etc/") {
return filepath.Join(r.Prefix, "etc", strings.TrimPrefix(path, "/etc"))
}
if path == "/tmp" || strings.HasPrefix(path, "/tmp/") {
return filepath.Join(r.Prefix, "tmp", strings.TrimPrefix(path, "/tmp"))
}
Comment on lines +166 to +169
// Priority 2: GOPATH/bin
if gopath := engramGetenv("GOPATH"); gopath != "" {
return filepath.Join(gopath, "bin"), nil, nil
}
Comment on lines +90 to +101
home := t.TempDir()
oldHome := os.Getenv("HOME")
oldTermuxVersion := os.Getenv("TERMUX_VERSION")
oldShell := os.Getenv("SHELL")
oldGOOS := pathGOOS

t.Cleanup(func() {
os.Setenv("HOME", oldHome)
os.Setenv("TERMUX_VERSION", oldTermuxVersion)
os.Setenv("SHELL", oldShell)
pathGOOS = oldGOOS
})
@Alan-TheGentleman

Alan-TheGentleman commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Good direction, and I see the justification in the body: this supersedes #277 because maintainer edits were not available, and the PR also carries conflict/test hygiene after #257.

That said, it still trips the cognitive-load gate at ~1.1k lines. Please either split this into reviewable slices, ideally platform detection/path handling, install/update behavior, then docs/e2e, or add an explicit size:exception justification if the slices are genuinely inseparable.

Once that is resolved, this remains the Termux candidate to review.

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