ci: route Android builds through project tooling
Some checks failed
Android build / build (push) Failing after 1m48s

This commit is contained in:
Hermes
2026-09-01 17:16:48 -05:00
parent 0bf7f6b387
commit 553c512082
3 changed files with 73 additions and 132 deletions

View File

@@ -29,73 +29,6 @@ jobs:
distribution: temurin
java-version: '17'
- name: Install Android SDK command-line tools and packages
shell: bash
run: |
set -euxo pipefail
export ANDROID_SDK_ROOT="$HOME/.android/sdk"
export ANDROID_HOME="$ANDROID_SDK_ROOT"
mkdir -p "$ANDROID_SDK_ROOT/cmdline-tools"
if [ ! -x "$ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager" ]; then
tools_url="$(curl -fsSL https://developer.android.com/studio | grep -o 'https://dl.google.com/android/repository/commandlinetools-linux-[0-9][0-9]*_latest.zip' | head -n 1)"
test -n "$tools_url"
curl -fsSL -o /tmp/commandlinetools.zip "$tools_url"
rm -rf "$ANDROID_SDK_ROOT/cmdline-tools/latest" "$ANDROID_SDK_ROOT/cmdline-tools/cmdline-tools"
unzip -q /tmp/commandlinetools.zip -d "$ANDROID_SDK_ROOT/cmdline-tools"
mv "$ANDROID_SDK_ROOT/cmdline-tools/cmdline-tools" "$ANDROID_SDK_ROOT/cmdline-tools/latest"
fi
export PATH="$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools:$PATH"
set +o pipefail
yes | sdkmanager --licenses >/dev/null
set -o pipefail
sdkmanager \
"platform-tools" \
"platforms;android-36" \
"build-tools;36.0.0" \
"ndk;27.2.12479018" \
"cmake;3.22.1"
- name: Prepare optional release signing
if: github.event_name == 'release'
continue-on-error: true
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
shell: bash
run: |
set -euo pipefail
missing=()
for key in ANDROID_KEYSTORE_BASE64 ANDROID_KEYSTORE_PASSWORD ANDROID_KEY_ALIAS ANDROID_KEY_PASSWORD; do
if [ -z "${!key:-}" ]; then
missing+=("$key")
fi
done
if [ ${#missing[@]} -gt 0 ]; then
printf -v missing_csv '%s, ' "${missing[@]}"
missing_csv="${missing_csv%, }"
echo "::error::Release signing secrets missing: ${missing_csv}. Continuing with unsigned/default-signed release AAB."
exit 1
fi
mkdir -p .ci-secrets
printf '%s' "$ANDROID_KEYSTORE_BASE64" | base64 -d > .ci-secrets/release-keystore.jks
cat > keystore.properties <<EOF
storeFile=.ci-secrets/release-keystore.jks
storePassword=$ANDROID_KEYSTORE_PASSWORD
keyAlias=$ANDROID_KEY_ALIAS
keyPassword=$ANDROID_KEY_PASSWORD
EOF
- name: Make Gradle wrapper executable
run: chmod +x gradlew
- name: Write Android SDK location
shell: bash
run: |
set -euxo pipefail
printf 'sdk.dir=%s/.android/sdk\n' "$HOME" > local.properties
- name: Install Rust toolchain
shell: bash
run: |
@@ -109,47 +42,17 @@ jobs:
rustc --version
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
- name: Compile bundled native Git binaries
- name: Run project build tooling for debug APK
if: github.event_name != 'release'
shell: bash
env:
CI_VERSION_CODE: ${{ github.run_number }}
CI_ARTIFACT_TIMESTAMP: ${{ github.run_id }}
run: |
set -euxo pipefail
export PATH="$HOME/.cargo/bin:$PATH"
export ANDROID_SDK_ROOT="$HOME/.android/sdk"
export ANDROID_HOME="$ANDROID_SDK_ROOT"
chmod +x ./AndroidProjectTooling.sh
bash ./AndroidProjectTooling.sh --compile-android-git
- name: Prepare CI build metadata
id: build_meta
shell: bash
run: |
set -euxo pipefail
timestamp_utc="$(date -u +%Y%m%d-%H%M%S)"
version_code="$(date -u +%s)"
project_name="$(basename "$GITHUB_REPOSITORY")"
python3 - "$version_code" <<'PY'
from pathlib import Path
import re, sys
path = Path('app/build.gradle.kts')
text = path.read_text(encoding='utf-8')
new_text, count = re.subn(r'(\bversionCode\s*=\s*)\d+', rf'\g<1>{sys.argv[1]}', text, count=1)
if count != 1:
raise SystemExit('versionCode not found in app/build.gradle.kts')
path.write_text(new_text, encoding='utf-8')
PY
echo "Using CI versionCode: $version_code"
echo "project_name=$project_name" >> "$GITHUB_OUTPUT"
echo "timestamp_utc=$timestamp_utc" >> "$GITHUB_OUTPUT"
echo "version_code=$version_code" >> "$GITHUB_OUTPUT"
- name: Build debug APK
if: github.event_name != 'release'
shell: bash
run: |
set -euxo pipefail
export ANDROID_SDK_ROOT="$HOME/.android/sdk"
export ANDROID_HOME="$ANDROID_SDK_ROOT"
./gradlew --no-daemon clean assembleDebug
bash ./AndroidProjectTooling.sh --build --no-commit
- name: Verify debug APK bundles native Git
if: github.event_name != 'release'
@@ -175,14 +78,21 @@ jobs:
raise SystemExit(f'Missing native Git libraries in {apk}: {missing}')
PY
- name: Build release AAB
- name: Run project build tooling for release AAB
if: github.event_name == 'release'
shell: bash
env:
CI_VERSION_CODE: ${{ github.run_number }}
CI_ARTIFACT_TIMESTAMP: ${{ github.run_id }}
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
set -euxo pipefail
export ANDROID_SDK_ROOT="$HOME/.android/sdk"
export ANDROID_HOME="$ANDROID_SDK_ROOT"
./gradlew --no-daemon bundleRelease
export PATH="$HOME/.cargo/bin:$PATH"
chmod +x ./AndroidProjectTooling.sh
bash ./AndroidProjectTooling.sh --build-release-aab --no-commit
- name: Verify release AAB bundles native Git
if: github.event_name == 'release'
@@ -208,18 +118,6 @@ jobs:
raise SystemExit(f'Missing native Git libraries in {aab}: {missing}')
PY
- name: Rename release AAB for upload
if: github.event_name == 'release'
id: release_aab
shell: bash
run: |
set -euxo pipefail
aab="$(find app/build/outputs/bundle/release -name '*.aab' | head -n 1)"
renamed="app/build/outputs/bundle/release/${{ steps.build_meta.outputs.project_name }}-${{ steps.build_meta.outputs.timestamp_utc }}-release.aab"
mv -f "$aab" "$renamed"
echo "Renamed release AAB to $(basename "$renamed")"
echo "path=$renamed" >> "$GITHUB_OUTPUT"
- name: Upload debug APK
if: github.event_name != 'release'
uses: actions/upload-artifact@v3
@@ -242,7 +140,7 @@ jobs:
exit 1
fi
release_id="$(python3 -c 'import json, os; print(json.load(open(os.environ["GITHUB_EVENT_PATH"], encoding="utf-8"))["release"]["id"])')"
aab="${{ steps.release_aab.outputs.path }}"
aab="$(find app/build/outputs/bundle/release -name '*.aab' | head -n 1)"
curl --fail-with-body \
--request POST \
--header "Authorization: token ${token}" \

View File

@@ -38,6 +38,9 @@ ANDROID_TOOLBIN="$ANDROID_NDK_DIR/toolchains/llvm/prebuilt/linux-x86_64/bin"
STATE_TTL_SECONDS=86400
HIDE_EMULATOR_WINDOW="false"
QUIET_LOG_DIR="$PROJECT_DIR/build/reports/android-project-tooling"
NO_COMMIT="false"
CI_VERSION_CODE="${CI_VERSION_CODE:-}"
CI_ARTIFACT_TIMESTAMP="${CI_ARTIFACT_TIMESTAMP:-}"
log() {
printf '\n[%s] %s\n' "tooling" "$1"
@@ -299,6 +302,7 @@ import re
path = Path(r'''$gradle_file''')
text = path.read_text()
ci_version_code = r'''$CI_VERSION_CODE'''.strip()
version_code_match = re.search(r'versionCode\s*=\s*(\d+)', text)
version_name_match = re.search(r'versionName\s*=\s*"(\d+)\.(\d+)\.(\d+)"', text)
@@ -309,14 +313,15 @@ if not version_code_match or not version_name_match:
current_code = int(version_code_match.group(1))
major, minor, patch = map(int, version_name_match.groups())
new_code = current_code + 1
new_code = int(ci_version_code) if ci_version_code else current_code + 1
new_name = f'{major}.{minor}.{patch + 1}'
text = re.sub(r'versionCode\s*=\s*\d+', f'versionCode = {new_code}', text, count=1)
if not ci_version_code:
text = re.sub(r'versionName\s*=\s*"\d+\.\d+\.\d+"', f'versionName = "{new_name}"', text, count=1)
path.write_text(text)
print(f'Updated versionCode to {new_code} and versionName to {new_name}')
print(f'Updated versionCode to {new_code}' + (f' and versionName to {new_name}' if not ci_version_code else ''))
PY
}
@@ -352,6 +357,34 @@ upload_build_artifact_if_possible() {
bash "$UPLOAD_SCRIPT" "$artifact_path"
}
prepare_optional_release_signing() {
local required=(ANDROID_KEYSTORE_BASE64 ANDROID_KEYSTORE_PASSWORD ANDROID_KEY_ALIAS ANDROID_KEY_PASSWORD)
local missing=()
local key
for key in "${required[@]}"; do
if [ -z "${!key:-}" ]; then
missing+=("$key")
fi
done
if [ ${#missing[@]} -eq ${#required[@]} ]; then
return
fi
if [ ${#missing[@]} -gt 0 ]; then
printf 'Release signing secrets missing: %s\n' "${missing[*]}" >&2
exit 1
fi
mkdir -p "$PROJECT_DIR/.ci-secrets"
printf '%s' "$ANDROID_KEYSTORE_BASE64" | base64 -d > "$PROJECT_DIR/.ci-secrets/release-keystore.jks"
cat > "$PROJECT_DIR/keystore.properties" <<EOF_INNER
storeFile=.ci-secrets/release-keystore.jks
storePassword=$ANDROID_KEYSTORE_PASSWORD
keyAlias=$ANDROID_KEY_ALIAS
keyPassword=$ANDROID_KEY_PASSWORD
EOF_INNER
}
setup_env() {
setup_java_env
export ANDROID_HOME="$SDK_DIR"
@@ -810,7 +843,11 @@ maybe_run_operation() {
artifact_target="$PROJECT_DIR/app/build/outputs/apk/debug/githug-android-debug-v${version_code}.apk"
;;
--build-release-aab)
if [ -n "$CI_ARTIFACT_TIMESTAMP" ]; then
artifact_target="$PROJECT_DIR/app/build/outputs/bundle/release/Githug-Android-${CI_ARTIFACT_TIMESTAMP}-release.aab"
else
artifact_target="$PROJECT_DIR/app/build/outputs/bundle/release/githug-android-release-v${version_code}.aab"
fi
;;
esac
fi
@@ -818,6 +855,9 @@ maybe_run_operation() {
build_host_git
export GITHUG_TEST_GIT_BINARY="$HOST_GIT_BINARY"
fi
if [ "$mode" = "--build-release-aab" ]; then
prepare_optional_release_signing
fi
if [ "$should_compile_android_git" = "true" ]; then
build_android_git
fi
@@ -845,7 +885,7 @@ maybe_run_operation() {
log "Expected build output not found for rename: ${artifact_source#$PROJECT_DIR/}"
fi
if [ "$should_auto_commit" = "true" ]; then
if [ "$should_auto_commit" = "true" ] && [ "$NO_COMMIT" != "true" ]; then
auto_commit_if_needed
fi
@@ -893,6 +933,9 @@ validate_args() {
case "$1" in
--quiet)
;;
--no-commit)
NO_COMMIT="true"
;;
--hide-emulator-window)
if [ "$mode" != "--test-emulator" ]; then
echo "--hide-emulator-window can only be used with --test-emulator" >&2

View File

@@ -21,7 +21,7 @@ The UI supports a terminal-centered workflow with optional inspection panes:
## 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.
Development tasks should be run through the project tooling script. Local developers and Gitea Actions both use this same script so the compile path stays aligned.
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.
@@ -50,8 +50,8 @@ Available commands:
| `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 --build` | Build the debug APK. Add `--no-commit` to skip the post-build commit. | `app/build/outputs/apk/debug/githug-android-debug-v<versionCode>.apk` |
| `bash ./AndroidProjectTooling.sh --build-release-aab` | Build the release Android App Bundle. Add `--no-commit` to skip the post-build commit. | `app/build/outputs/bundle/release/githug-android-release-v<versionCode>.aab` or `Githug-Android-<timestamp>-release.aab` when `CI_ARTIFACT_TIMESTAMP` is set |
| `bash ./AndroidProjectTooling.sh --compile-git` | Compile Git for the development host and all Android target ABIs. | Host and Android `libgit.so` binaries |
Add `--quiet` before any tooling command when running through an AI or other log-sensitive automation:
@@ -120,13 +120,13 @@ If all required components were verified successfully, the script will skip SDK
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`
- increments `versionCode` by 1 for local builds, or honors `CI_VERSION_CODE` when provided
- increments the patch component of `versionName` for local builds
- 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`
- renames the generated artifact to a `githug-android-*` filename that includes the post-bump `versionCode`, or to `Githug-Android-<timestamp>-release.aab` when `CI_ARTIFACT_TIMESTAMP` is set for CI release builds
- 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
- attempts to create a git commit after a successful build unless `--no-commit` is passed
The build commands currently run these Gradle tasks: