Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2e08f2c
ci: switch release workflow to release APK pipeline
wojcikiewicz17 May 10, 2026
584550b
Merge pull request #1 from wojcikiewicz17/codex/update-github-release…
wojcikiewicz17 May 10, 2026
c54f951
docs: clarify debug validation vs official signed release flow
wojcikiewicz17 May 10, 2026
d838fc7
Merge pull request #2 from wojcikiewicz17/codex/update-installation/b…
wojcikiewicz17 May 10, 2026
f4c35e1
docs: clarify debug vs official release build flows
wojcikiewicz17 May 10, 2026
60cab75
Merge pull request #3 from wojcikiewicz17/codex/update-readme.md-inst…
wojcikiewicz17 May 10, 2026
31dcb65
ci: replace hub cleanup with gh in release workflow
wojcikiewicz17 May 10, 2026
87d426c
Merge pull request #4 from wojcikiewicz17/codex/update-github_release…
wojcikiewicz17 May 10, 2026
62eeaa0
Define ABI split policy and enforce ABI artifacts in CI
wojcikiewicz17 May 10, 2026
3ac7ccb
Merge pull request #5 from wojcikiewicz17/codex/add-abi-policy-and-na…
wojcikiewicz17 May 10, 2026
61c703f
ci: add advanced hardcoded android build workflow
wojcikiewicz17 May 10, 2026
e2c5762
Merge pull request #6 from wojcikiewicz17/codex/update-github_release…
wojcikiewicz17 May 10, 2026
1285307
ci: fix advanced workflow ABI validation for non-JNI APKs
wojcikiewicz17 May 10, 2026
18d242b
Merge branch 'master' into codex/update-github_release_build.yml-for-…
wojcikiewicz17 May 10, 2026
83c7d4e
Merge pull request #7 from wojcikiewicz17/codex/update-github_release…
wojcikiewicz17 May 10, 2026
dad1f71
Add build/docs helpers and validation scripts
wojcikiewicz17 May 10, 2026
9ed2a62
Merge pull request #8 from wojcikiewicz17/codex/reduce-friction-in-te…
wojcikiewicz17 May 10, 2026
e555b1d
Add developer debugger mode guide and helper script
wojcikiewicz17 May 10, 2026
bf4e903
Merge branch 'master' into codex/reduce-friction-in-termux-api-build-…
wojcikiewicz17 May 10, 2026
f2def3a
Merge pull request #9 from wojcikiewicz17/codex/reduce-friction-in-te…
wojcikiewicz17 May 10, 2026
4b635db
Add optional low-level C/ASM native checksum utility
wojcikiewicz17 May 10, 2026
98a615d
Merge branch 'master' into codex/reduce-friction-in-termux-api-build-…
wojcikiewicz17 May 10, 2026
5054e4d
Merge pull request #10 from wojcikiewicz17/codex/reduce-friction-in-t…
wojcikiewicz17 May 10, 2026
f12e60a
Fix Java version parsing in safe Gradle launcher
wojcikiewicz17 May 10, 2026
c617e9f
Merge branch 'master' into codex/reduce-friction-in-termux-api-build-…
wojcikiewicz17 May 10, 2026
77c14a1
Merge pull request #11 from wojcikiewicz17/codex/reduce-friction-in-t…
wojcikiewicz17 May 10, 2026
b63cf30
Reduce preflight friction without ripgrep dependency
wojcikiewicz17 May 10, 2026
57f6f29
Reduce debugger helper dependency friction
wojcikiewicz17 May 10, 2026
dd7dfc6
Generate debug build report
wojcikiewicz17 May 10, 2026
3a0a59b
ci: add beta workflow for android10 arm32/arm64 installable apks
wojcikiewicz17 May 10, 2026
a723e5b
Merge pull request #12 from wojcikiewicz17/codex/create-beta.yml-for-…
wojcikiewicz17 May 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions .github/workflows/advanced_hardcoded_ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: Advanced Hardcoded CI Build

on:
workflow_dispatch:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
android-build:
runs-on: ubuntu-latest
env:
JAVA_VERSION: '17'
APK_OUT_DIR: app/build/outputs/apk
ABI32: armeabi-v7a
ABI64: arm64-v8a
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}
cache: gradle

- name: Build debug (signed) and release (unsigned)
shell: bash
run: |
set -euo pipefail
./gradlew --no-daemon clean assembleDebug assembleRelease

- name: Validate APK architecture compatibility
shell: bash
run: |
set -euo pipefail

validate_apk_arch() {
local apk_path="$1"
local label="$2"

test -n "$apk_path"
if unzip -l "$apk_path" | grep -qE 'lib/[^/]+/'; then
unzip -l "$apk_path" | grep -q "lib/${ABI32}/"
unzip -l "$apk_path" | grep -q "lib/${ABI64}/"
echo "$label contains JNI libs for both ${ABI32} and ${ABI64}."
else
echo "$label has no JNI libs; APK is architecture-agnostic at package level."
fi
}

DEBUG_APK="$(find "$APK_OUT_DIR/debug" -maxdepth 1 -type f -name 'termux-api-app_*.apk' | head -n1)"
RELEASE_APK="$(find "$APK_OUT_DIR/release" -maxdepth 1 -type f -name 'termux-api-app_*.apk' | head -n1)"

validate_apk_arch "$DEBUG_APK" "Debug APK"
validate_apk_arch "$RELEASE_APK" "Release APK"

- name: Generate internal validation keystore
shell: bash
run: |
set -euo pipefail
keytool -genkeypair -v \
-keystore ci-validation.jks \
-storepass changeit \
-keypass changeit \
-alias ci-validation \
-keyalg RSA \
-keysize 2048 \
-validity 3650 \
-dname "CN=CI Validation, OU=CI, O=Termux, L=NA, ST=NA, C=US"

- name: Sign release APK for internal validation
shell: bash
run: |
set -euo pipefail
RELEASE_APK="$(find "$APK_OUT_DIR/release" -maxdepth 1 -type f -name 'termux-api-app_*.apk' | head -n1)"
SIGNED_APK="${RELEASE_APK%.apk}-ci-signed.apk"
"$ANDROID_HOME/build-tools/$(ls "$ANDROID_HOME/build-tools" | sort -V | tail -n1)/apksigner" sign \
--ks ci-validation.jks \
--ks-pass pass:changeit \
--key-pass pass:changeit \
--ks-key-alias ci-validation \
--out "$SIGNED_APK" \
"$RELEASE_APK"
"$ANDROID_HOME/build-tools/$(ls "$ANDROID_HOME/build-tools" | sort -V | tail -n1)/apksigner" verify "$SIGNED_APK"

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: advanced-hardcoded-apks
path: |
app/build/outputs/apk/debug/*.apk
app/build/outputs/apk/release/*.apk
ci-validation.jks
100 changes: 100 additions & 0 deletions .github/workflows/beta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Beta APK (Android 10 arm32/arm64)

on:
workflow_dispatch:
inputs:
app_version_name:
description: "SemVer versionName override (ex: 0.53.0-beta.1)"
required: false
default: "0.53.0-beta.1"

jobs:
build-beta:
runs-on: ubuntu-latest
env:
JAVA_VERSION: "17"
ABI32: armeabi-v7a
ABI64: arm64-v8a
APK_DEBUG_DIR: app/build/outputs/apk/debug
APK_RELEASE_DIR: app/build/outputs/apk/release
TERMUX_API_APP__BUILD__APP_VERSION_NAME: ${{ github.event.inputs.app_version_name }}
TERMUX_API_APP__BUILD__APK_VERSION_TAG: beta-${{ github.run_number }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}
cache: gradle

- name: Build debug and release APK splits
shell: bash
run: |
set -euo pipefail
./gradlew --no-daemon clean assembleDebug assembleRelease

- name: Validate ABI split outputs
shell: bash
run: |
set -euo pipefail

ARM32_DEBUG_APK="$(find "$APK_DEBUG_DIR" -maxdepth 1 -type f -name "*-${ABI32}.apk" | head -n1)"
ARM64_DEBUG_APK="$(find "$APK_DEBUG_DIR" -maxdepth 1 -type f -name "*-${ABI64}.apk" | head -n1)"
ARM32_RELEASE_APK="$(find "$APK_RELEASE_DIR" -maxdepth 1 -type f -name "*-${ABI32}.apk" | head -n1)"
ARM64_RELEASE_APK="$(find "$APK_RELEASE_DIR" -maxdepth 1 -type f -name "*-${ABI64}.apk" | head -n1)"

test -n "$ARM32_DEBUG_APK"
test -n "$ARM64_DEBUG_APK"
test -n "$ARM32_RELEASE_APK"
test -n "$ARM64_RELEASE_APK"

echo "arm32 debug: $ARM32_DEBUG_APK"
echo "arm64 debug: $ARM64_DEBUG_APK"
echo "arm32 release: $ARM32_RELEASE_APK"
echo "arm64 release: $ARM64_RELEASE_APK"

- name: Generate internal beta signing key
shell: bash
run: |
set -euo pipefail
keytool -genkeypair -v \
-keystore beta-validation.jks \
-storepass changeit \
-keypass changeit \
-alias beta-validation \
-keyalg RSA \
-keysize 2048 \
-validity 3650 \
-dname "CN=Beta Validation, OU=CI, O=Termux, L=NA, ST=NA, C=US"

- name: Sign release splits for beta validation
shell: bash
run: |
set -euo pipefail
APKSIGNER="$ANDROID_HOME/build-tools/$(ls "$ANDROID_HOME/build-tools" | sort -V | tail -n1)/apksigner"
for abi in "$ABI32" "$ABI64"; do
UNSIGNED_APK="$(find "$APK_RELEASE_DIR" -maxdepth 1 -type f -name "*-${abi}.apk" | head -n1)"
SIGNED_APK="${UNSIGNED_APK%.apk}-beta-signed.apk"
"$APKSIGNER" sign \
--ks beta-validation.jks \
--ks-pass pass:changeit \
--key-pass pass:changeit \
--ks-key-alias beta-validation \
--out "$SIGNED_APK" \
"$UNSIGNED_APK"
"$APKSIGNER" verify --verbose "$SIGNED_APK"
done

- name: Upload beta artifacts
uses: actions/upload-artifact@v4
with:
name: beta-android10-arm32-arm64-apks
path: |
app/build/outputs/apk/debug/*.apk
app/build/outputs/apk/release/*-${{ env.ABI32 }}.apk
app/build/outputs/apk/release/*-${{ env.ABI64 }}.apk
app/build/outputs/apk/release/*-beta-signed.apk
beta-validation.jks
20 changes: 13 additions & 7 deletions .github/workflows/github_action_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
APK_DIR_PATH="./app/build/outputs/apk/debug"
APK_VERSION_TAG="$RELEASE_VERSION_NAME.github.debug" # Note the ".", GITHUB_SHA will already have "+" before it
APK_BASENAME_PREFIX="termux-api-app_$APK_VERSION_TAG"
EXPECTED_ABIS=("armeabi-v7a" "arm64-v8a")

# Used by upload step later
echo "APK_DIR_PATH=$APK_DIR_PATH" >> $GITHUB_ENV
Expand All @@ -53,14 +54,18 @@ jobs:
exit_on_error "Build failed for '$RELEASE_VERSION_NAME' release with '$APK_VERSION_TAG' tag."
fi

echo "Validating APK file"
if ! test -f "$APK_DIR_PATH/${APK_BASENAME_PREFIX}.apk"; then
files_found="$(ls "$APK_DIR_PATH")"
exit_on_error "Failed to find built APK file at '$APK_DIR_PATH/${APK_BASENAME_PREFIX}.apk'. Files found: "$'\n'"$files_found"
fi
echo "Validating ABI APK files"
for abi in "${EXPECTED_ABIS[@]}"; do
apk_path="$APK_DIR_PATH/${APK_BASENAME_PREFIX}-${abi}.apk"
if ! test -f "$apk_path"; then
files_found="$(ls "$APK_DIR_PATH")"
exit_on_error "Failed to find built ABI APK file at '$apk_path'. Files found: "$'
'"$files_found"
fi
done

echo "Generating checksums-sha256.txt file"
if ! (cd "$APK_DIR_PATH"; sha256sum "${APK_BASENAME_PREFIX}.apk" > checksums-sha256.txt); then
if ! (cd "$APK_DIR_PATH"; sha256sum "${APK_BASENAME_PREFIX}-"*.apk > checksums-sha256.txt); then
exit_on_error "Generate checksums-sha256.txt file failed for '$RELEASE_VERSION_NAME' release."
fi
echo "checksums-sha256.txt:"$'\n```\n'"$(cat "$APK_DIR_PATH/checksums-sha256.txt")"$'\n```'
Expand All @@ -70,6 +75,7 @@ jobs:
with:
name: ${{ env.APK_BASENAME_PREFIX }}
path: |
${{ env.APK_DIR_PATH }}/${{ env.APK_BASENAME_PREFIX }}.apk
${{ env.APK_DIR_PATH }}/${{ env.APK_BASENAME_PREFIX }}-armeabi-v7a.apk
${{ env.APK_DIR_PATH }}/${{ env.APK_BASENAME_PREFIX }}-arm64-v8a.apk
${{ env.APK_DIR_PATH }}/checksums-sha256.txt
${{ env.APK_DIR_PATH }}/output-metadata.json
37 changes: 24 additions & 13 deletions .github/workflows/github_release_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,53 @@ jobs:
exit_on_error() {
echo "$1"
echo "Deleting '$RELEASE_VERSION_NAME' release and '$GITHUB_REF' tag"
hub release delete "$RELEASE_VERSION_NAME"
git push --delete origin "$GITHUB_REF"
gh release delete "$RELEASE_VERSION_NAME" --yes
gh api --method DELETE "repos/${GITHUB_REPOSITORY}/git/${GITHUB_REF}"
exit 1
}


echo "Checking GitHub CLI availability"
if ! gh --version >/dev/null 2>&1; then
exit_on_error "GitHub CLI (gh) is required but not available in runner PATH."
fi

echo "Setting vars"
RELEASE_VERSION_NAME="${GITHUB_REF/refs\/tags\//}"
if ! printf "%s" "${RELEASE_VERSION_NAME/v/}" | grep -qP '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'; then
exit_on_error "The release version '${RELEASE_VERSION_NAME/v/}' is not a valid version as per semantic version '2.0.0' spec in the format 'major.minor.patch(-prerelease)(+buildmetadata)'. https://semver.org/spec/v2.0.0.html."
fi

APK_DIR_PATH="./app/build/outputs/apk/debug"
APK_VERSION_TAG="$RELEASE_VERSION_NAME+github.debug"
APK_DIR_PATH="./app/build/outputs/apk/release"
APK_VERSION_TAG="${RELEASE_VERSION_NAME#v}"
APK_BASENAME_PREFIX="termux-api-app_$APK_VERSION_TAG"
EXPECTED_ABIS=("armeabi-v7a" "arm64-v8a")

echo "Building APK file for '$RELEASE_VERSION_NAME' release with '$APK_VERSION_TAG' tag"
export TERMUX_API_APP__BUILD__APK_VERSION_TAG="$APK_VERSION_TAG" # Used by app/build.gradle
if ! ./gradlew assembleDebug; then
if ! ./gradlew assembleRelease; then
exit_on_error "Build failed for '$RELEASE_VERSION_NAME' release with '$APK_VERSION_TAG' tag."
fi

echo "Validating APK file"
if ! test -f "$APK_DIR_PATH/${APK_BASENAME_PREFIX}.apk"; then
files_found="$(ls "$APK_DIR_PATH")"
exit_on_error "Failed to find built APK file at '$APK_DIR_PATH/${APK_BASENAME_PREFIX}.apk'. Files found: "$'\n'"$files_found"
fi

echo "Validating ABI APK files"
for abi in "${EXPECTED_ABIS[@]}"; do
apk_path="$APK_DIR_PATH/${APK_BASENAME_PREFIX}-${abi}.apk"
if ! test -f "$apk_path"; then
files_found="$(ls "$APK_DIR_PATH")"
exit_on_error "Failed to find built ABI APK file at '$apk_path'. Files found: "$'
'"$files_found"
fi
done
echo "Generating checksums-sha256.txt file"
if ! (cd "$APK_DIR_PATH"; sha256sum "${APK_BASENAME_PREFIX}.apk" > checksums-sha256.txt); then
if ! (cd "$APK_DIR_PATH"; sha256sum "${APK_BASENAME_PREFIX}-"*.apk > checksums-sha256.txt); then
exit_on_error "Generate checksums-sha256.txt file failed for '$RELEASE_VERSION_NAME' release."
fi
echo "checksums-sha256.txt:"$'\n```\n'"$(cat "$APK_DIR_PATH/checksums-sha256.txt")"$'\n```'

echo "Uploading files to release"
if ! gh release upload "$RELEASE_VERSION_NAME" \
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}.apk" \
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}-armeabi-v7a.apk" \
"$APK_DIR_PATH/${APK_BASENAME_PREFIX}-arm64-v8a.apk" \
"$APK_DIR_PATH/checksums-sha256.txt" \
; then
exit_on_error "Upload files to release failed for '$RELEASE_VERSION_NAME' release."
Expand Down
59 changes: 55 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,66 @@ Latest version is `v0.53.0`.

Termux:API application can be obtained from [F-Droid](https://f-droid.org/en/packages/com.termux.api/).

Additionally we provide per-commit debug builds for those who want to try
out the latest features or test their pull request. This build can be obtained
from one of the workflow runs listed on [Github Actions](https://github.com/termux/termux-api/actions/workflows/github_action_build.yml?query=branch%3Amaster+event%3Apush)
page.
Additionally we provide per-commit **debug validation builds** for those who want to try
out the latest features or test their pull request. These artifacts come from
[`github_action_build.yml`](.github/workflows/github_action_build.yml) workflow runs on [GitHub Actions](https://github.com/termux/termux-api/actions/workflows/github_action_build.yml?query=branch%3Amaster+event%3Apush).

These debug workflow APKs are for validation only and are **not** an official
release substitute for production permission scenarios.

Signature keys of all offered builds are different. Before you switch the
installation source, you will have to uninstall the Termux application and
all currently installed plugins. Check https://github.com/termux/termux-app#Installation for more info.

## Build and release flows

Use the correct build path depending on your goal:

- **Validation build (debug / per-commit):**
- Command: `./gradlew assembleDebug`
- Purpose: local verification, CI checks, pull request validation.
- CI workflow: [`github_action_build.yml`](.github/workflows/github_action_build.yml) (builds and uploads debug APK artifacts).
- Output: per-ABI debug APK artifacts (`armeabi-v7a`, `arm64-v8a`) from `app/build/outputs/apk/debug`.
- Important: published debug APK from workflow artifacts is **not** a substitute for the official release APK, especially for production permissions/use-cases.

- **Official distribution build (signed release):**
- Command: `./gradlew assembleRelease`
- Purpose: official distributable release artifacts only.
- CI workflow: [`github_release_build.yml`](.github/workflows/github_release_build.yml) (builds per-ABI release APKs, checksums, uploads to GitHub Release).
- Requirement: release signing credentials must be provided securely via CI secrets. Do not commit keys, keystore files, or plaintext credentials to the repository.
- Rule: official release must be produced only from the `assembleRelease` path with secure signing configured in CI.


## Official ABI support matrix

Termux:API artifacts are produced only for the ABIs below:

| ABI | Arch | Validation (debug) | Official release |
| --- | --- | --- | --- |
| `armeabi-v7a` | ARM32 | ✅ | ✅ |
| `arm64-v8a` | ARM64 | ✅ | ✅ |

Build configuration uses ABI split packaging (`splits { abi { ... } }`) and emits one APK per ABI.
Artifact naming includes ABI suffix:

- Debug CI: `termux-api-app_<version>.github.debug-<abi>.apk`
- Release CI: `termux-api-app_<version>-<abi>.apk`


## Quick start docs and scripts

- Build guide: [`docs/BUILD.md`](docs/BUILD.md)
- Permissions matrix: [`docs/PERMISSIONS.md`](docs/PERMISSIONS.md)
- API methods index: [`docs/API_METHODS.md`](docs/API_METHODS.md)
- Troubleshooting: [`docs/TROUBLESHOOTING.md`](docs/TROUBLESHOOTING.md)
- Environment checks: [`scripts/doctor.sh`](scripts/doctor.sh)
- Debug build helper: [`scripts/build-debug.sh`](scripts/build-debug.sh)
- Safe Gradle launcher (Java preflight): [`scripts/gradlew-safe.sh`](scripts/gradlew-safe.sh)
- Developer debugger guide: [`docs/DEVELOPER_DEBUGGER.md`](docs/DEVELOPER_DEBUGGER.md)
- Developer debugger helper: [`scripts/enable-debugger-mode.sh`](scripts/enable-debugger-mode.sh)
- Native lowlevel (C/ASM) notes: [`docs/NATIVE_LOWLEVEL.md`](docs/NATIVE_LOWLEVEL.md)
- Build native helper: [`scripts/build-native-tools.sh`](scripts/build-native-tools.sh)

## License

Released under the [GPLv3 license](http://www.gnu.org/licenses/gpl-3.0.en.html).
Expand Down
Loading