Skip to content

Build OpenCode Android #48

Build OpenCode Android

Build OpenCode Android #48

Workflow file for this run

name: Build OpenCode Android
on:
workflow_dispatch:
inputs:
opencode_version:
description: 'OpenCode version'
required: false
default: '1.3.13'
abis:
description: 'ABIs to build (comma separated)'
required: false
default: 'armeabi-v7a'
push:
tags:
- 'v*'
env:
BUN_VERSION: "1.2.13"
OPENCODE_VERSION: "1.3.13"
ANDROID_API: "24"
ANDROID_NDK_VERSION: "28.1.13356709"
WEBKIT_COMMIT: "017930ebf915121f8f593bef61cbbca82d78132d"
ICU_VERSION: "75.1"
ZIG_VERSION: "0.16.0"
TINYCC_COMMIT: "b91835d8701523e895a2b447713630069514e291"
JOBS: "2"
jobs:
build-binary:
name: Build opencode (${{ matrix.abi }})
runs-on: ubuntu-24.04
timeout-minutes: 360
strategy:
fail-fast: false
matrix:
abi: ["armeabi-v7a"]
outputs:
opencode_version: ${{ env.OPENCODE_VERSION }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set ABI
run: |
echo "TARGET_ABI=${{ matrix.abi }}" >> $GITHUB_ENV
SLUG="${{ matrix.abi }}"
SLUG="${SLUG//-/_}"
echo "ABI_SLUG=${SLUG}" >> $GITHUB_ENV
- name: Override OpenCode version
if: github.event.inputs.opencode_version
run: echo "OPENCODE_VERSION=${{ github.event.inputs.opencode_version }}" >> $GITHUB_ENV
- name: Restore main dependency cache
id: cache-restore
uses: actions/cache/restore@v4
with:
path: |
build-${{ env.ABI_SLUG }}/deps-android/prefix
build-${{ env.ABI_SLUG }}/webkit-android
build-${{ env.ABI_SLUG }}/opentui-lib
build-${{ env.ABI_SLUG }}/dist
key: opencode-main-${{ env.ABI_SLUG }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
opencode-main-${{ env.ABI_SLUG }}-
opencode-v2-${{ env.ABI_SLUG }}-
- name: Restore Bun build cache
id: cache-restore-bun
uses: actions/cache/restore@v4
with:
path: |
build-${{ env.ABI_SLUG }}/bun-build
build-${{ env.ABI_SLUG }}/bun-src/vendor
key: opencode-bun-${{ env.ABI_SLUG }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
opencode-bun-${{ env.ABI_SLUG }}-
- name: Install system packages
run: |
sudo apt-get update
sudo apt-get install -y \
ninja-build python3 ruby perl \
git curl wget unzip xz-utils zip file \
build-essential pkg-config \
libxml2-dev libxslt1-dev \
golang-go \
bison flex gperf \
autoconf automake libtool
- name: Install CMake 3.28+
run: |
if cmake --version 2>/dev/null | grep -q "3\.[2-9][0-9]"; then
echo "CMake ok"; cmake --version
else
CMAKE_VER="3.28.6"
cd /tmp
wget -q "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-linux-x86_64.tar.gz" -O cmake.tar.gz
sudo tar xzf cmake.tar.gz -C /opt/
sudo ln -sf "/opt/cmake-${CMAKE_VER}-linux-x86_64/bin/cmake" /usr/local/bin/cmake
fi
- name: Install Android NDK r28b
run: |
NDK_DIR="/opt/android-ndk"
if [ ! -d "$NDK_DIR/toolchains/llvm/prebuilt/linux-x86_64" ]; then
cd /tmp
wget -q "https://dl.google.com/android/repository/android-ndk-r28b-linux.zip" -O ndk.zip
sudo mkdir -p /opt
sudo unzip -q ndk.zip -d /opt/
sudo mv /opt/android-ndk-r28b "$NDK_DIR"
rm ndk.zip
fi
echo "ANDROID_NDK_HOME=$NDK_DIR" >> $GITHUB_ENV
echo "$NDK_DIR/toolchains/llvm/prebuilt/linux-x86_64/bin" >> $GITHUB_PATH
- name: Install Rust + Android targets
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"
rustup target add aarch64-linux-android
rustup target add armv7-linux-androideabi
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install host Bun
run: |
# Pin to the same version as the Android bun (BUN_VERSION): bun.sh/install
# otherwise pulls the latest 1.3.x, whose standalone format embeds the
# module graph in an ELF .bun section (no EOF trailer), breaking
# build-opencode-android.ts's append-at-EOF module graph extraction.
curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}"
export PATH="$HOME/.bun/bin:$PATH"
echo "$HOME/.bun/bin" >> $GITHUB_PATH
bun --version
- name: Install host Zig
run: |
cd /tmp
wget -q "https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-linux-${ZIG_VERSION}.tar.xz" -O zig.tar.xz
tar xf zig.tar.xz
# Zig resolves its install dir from the binary's realpath and needs its
# lib/ tree next to it. Copy the whole tree to /opt/zig, symlink bin.
sudo mkdir -p /opt/zig
sudo cp -r "zig-x86_64-linux-${ZIG_VERSION}/." /opt/zig/
sudo ln -sf /opt/zig/zig /usr/local/bin/zig
zig version
- name: Apply patches (bun, webkit, opentui)
run: |
chmod +x scripts/*.sh
source scripts/env.sh
./scripts/apply-patches.sh
- name: Build ICU
run: |
source scripts/env.sh
./scripts/build-icu.sh
- name: Build WebKit/JSC
run: |
source scripts/env.sh
./scripts/build-webkit.sh
- name: Build TinyCC
run: |
source scripts/env.sh
./scripts/build-tinycc.sh
- name: Build OpenTUI
run: |
source scripts/env.sh
./scripts/build-opentui.sh
- name: Build Bun
run: |
source scripts/env.sh
./scripts/build-bun.sh
- name: Build OpenCode standalone binary
run: |
source scripts/env.sh
./scripts/build-opencode.sh
- name: Package + upload
run: |
source scripts/env.sh
# The bundle (bun + opencode) becomes libopencode.so for jniLibs
if [ -f "$DIST_DIR/opencode" ]; then
ABI_SLUG="${{ matrix.abi }}"
mkdir -p dist/jniLibs/${ABI_SLUG}
cp "$DIST_DIR/opencode" "dist/jniLibs/${ABI_SLUG}/libopencode.so"
else
echo "ERROR: dist/opencode missing"; exit 1
fi
shell: bash
- name: Upload binary artifact
uses: actions/upload-artifact@v4
with:
name: opencode-android-${{ matrix.abi }}
path: dist/jniLibs
- name: Save main dependency cache (rolling)
if: always()
uses: actions/cache/save@v4
with:
path: |
build-${{ env.ABI_SLUG }}/deps-android/prefix
build-${{ env.ABI_SLUG }}/webkit-android
build-${{ env.ABI_SLUG }}/opentui-lib
build-${{ env.ABI_SLUG }}/dist
key: opencode-main-${{ env.ABI_SLUG }}-${{ github.run_id }}-${{ github.run_attempt }}
- name: Save Bun build cache (rolling)
if: always()
uses: actions/cache/save@v4
with:
path: |
build-${{ env.ABI_SLUG }}/bun-build
build-${{ env.ABI_SLUG }}/bun-src/vendor
key: opencode-bun-${{ env.ABI_SLUG }}-${{ github.run_id }}-${{ github.run_attempt }}
build-apk:
name: Build APK (arm64-v8a + armeabi-v7a)
needs: build-binary
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download binaries
uses: actions/download-artifact@v4
with:
path: /tmp/libs
merge-multiple: true
- name: Copy jniLibs into APK source set
run: |
mkdir -p android/app/src/main/jniLibs
# Artifacts may merge as <abi>/libopencode.so or flat libopencode.so;
# map each found binary into its ABI-specific jniLibs dir.
find /tmp/libs -name 'libopencode.so' -print0 | while IFS= read -r -d '' f; do
rel="${f#/tmp/libs/}"
case "$rel" in
arm64-v8a*) abi="arm64-v8a" ;;
armeabi-v7a*) abi="armeabi-v7a" ;;
*) abi="$(basename "$(dirname "$f")")" ;;
esac
mkdir -p "android/app/src/main/jniLibs/$abi"
cp "$f" "android/app/src/main/jniLibs/$abi/libopencode.so"
echo "placed: $abi/libopencode.so"
done
find android/app/src/main/jniLibs -type f -exec ls -la {} \;
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: Build APK
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: "8.9"
- name: Gradle assemble
working-directory: android
run: |
# System gradle from setup-gradle above (wrapper .bat is Windows-only; .sh not committed).
gradle --no-daemon assembleDebug
- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: opencode-apk
path: android/app/build/outputs/apk/debug/*.apk