Faster Android builds: ccache, Gradle build cache, 64-bit only, no release lint
Most of a build was compiling the same C++ twice per APK with nothing reused between builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Y2iUsKEWDuRGjK5FPmcUP
This commit is contained in:
@@ -22,8 +22,8 @@ jobs:
|
||||
runs-on: musicshare-android
|
||||
container:
|
||||
image: musicshare-android-builder
|
||||
# Keep Gradle and npm downloads between builds.
|
||||
options: -v musicshare-gradle:/root/.gradle -v musicshare-npm:/root/.npm
|
||||
# Keep Gradle (downloads and build cache), npm, and compiled C++ (ccache) between builds.
|
||||
options: -v musicshare-gradle:/root/.gradle -v musicshare-npm:/root/.npm -v musicshare-ccache:/root/.ccache
|
||||
timeout-minutes: 150
|
||||
env:
|
||||
SERVER: ${{ github.server_url }}
|
||||
|
||||
@@ -7,24 +7,37 @@
|
||||
#
|
||||
# Both are signed with the upload key when it's configured (see plugins/withReleaseSigning.js;
|
||||
# in CI it comes from ORG_GRADLE_PROJECT_MUSICSHARE_UPLOAD_* variables). Set REQUIRE_SIGNED=1 to
|
||||
# fail rather than produce unsigned APKs. ABIS picks the CPU types (default: phones, 32 and 64 bit).
|
||||
# Regenerates apps/mobile/android each time (prebuild --clean).
|
||||
# fail rather than produce unsigned APKs. ABIS picks the CPU types (default: 64-bit phones, which is
|
||||
# nearly every phone from the last several years; ABIS=armeabi-v7a,arm64-v8a adds old 32-bit ones).
|
||||
# Regenerates apps/mobile/android each time (prebuild --clean); Gradle's build cache and ccache (when
|
||||
# installed) make the rebuild cheap anyway.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
OUT=$(realpath -m "${1:-dist}")
|
||||
VERSION=$(node -p "require('./app.json').expo.version")
|
||||
ABIS=${ABIS:-armeabi-v7a,arm64-v8a}
|
||||
ABIS=${ABIS:-arm64-v8a}
|
||||
BUILD_TOOLS=$(ls -d "$ANDROID_HOME"/build-tools/*/ | sort -V | tail -1)
|
||||
mkdir -p "$OUT"
|
||||
|
||||
say() { printf '\n==> %s\n' "$*"; }
|
||||
|
||||
# Most of a build is compiling the C++ in React Native and its libraries, which rarely changes:
|
||||
# with ccache, CMake (3.17+) reuses earlier results via these launcher variables.
|
||||
if command -v ccache > /dev/null; then
|
||||
export CMAKE_C_COMPILER_LAUNCHER=ccache CMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
export CCACHE_BASEDIR=${CCACHE_BASEDIR:-$PWD} CCACHE_COMPILERCHECK=content CCACHE_MAXSIZE=${CCACHE_MAXSIZE:-6G}
|
||||
ccache --zero-stats > /dev/null
|
||||
fi
|
||||
|
||||
build() {
|
||||
local suffix=$1 foss=$2
|
||||
say "Building musicshare-$VERSION$suffix.apk"
|
||||
MUSICSHARE_FOSS=$foss CI=1 npx expo prebuild --platform android --clean --no-install
|
||||
(cd android && ./gradlew :app:assembleRelease -PreactNativeArchitectures="$ABIS" --console=plain)
|
||||
# --build-cache reuses compiled Kotlin/Java, dex and resources from earlier builds (kept in ~/.gradle).
|
||||
# Release lint is skipped: it takes a while and TypeScript, ESLint and the tests cover what matters.
|
||||
(cd android && ./gradlew :app:assembleRelease -PreactNativeArchitectures="$ABIS" --build-cache \
|
||||
-x lintVitalAnalyzeRelease -x lintVitalReportRelease -x lintVitalRelease --console=plain)
|
||||
local apk
|
||||
apk=$(ls android/app/build/outputs/apk/release/*.apk | head -1)
|
||||
|
||||
@@ -56,3 +69,4 @@ build() {
|
||||
build "" 0
|
||||
build "-foss" 1
|
||||
(cd "$OUT" && sha256sum musicshare-"$VERSION"*.apk > SHA256SUMS && cat SHA256SUMS)
|
||||
if command -v ccache > /dev/null; then say "ccache"; ccache --show-stats | grep -iE 'hits|misses|cache size' || true; fi
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#
|
||||
# Versions match what React Native 0.86 / Expo 57 ask for
|
||||
# (node_modules/react-native/gradle/libs.versions.toml). Gradle downloads anything
|
||||
# else it needs on first use; the workflow keeps ~/.gradle in a volume.
|
||||
# else it needs on first use; the workflow keeps ~/.gradle (and ccache's ~/.ccache) in volumes.
|
||||
FROM node:22-bookworm
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends openjdk-17-jdk-headless unzip \
|
||||
&& apt-get install -y --no-install-recommends openjdk-17-jdk-headless unzip ccache \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 \
|
||||
|
||||
@@ -20,7 +20,8 @@ container:
|
||||
# Use Docker's default network instead of creating one per job: a server with many compose
|
||||
# projects runs out of address pools ("all predefined address pools have been fully subnetted").
|
||||
network: bridge
|
||||
# The volumes the workflow may mount (Gradle and npm caches), and nothing else.
|
||||
# The volumes the workflow may mount (Gradle, npm and ccache caches), and nothing else.
|
||||
valid_volumes:
|
||||
- musicshare-gradle
|
||||
- musicshare-npm
|
||||
- musicshare-ccache
|
||||
|
||||
+5
-2
@@ -20,8 +20,11 @@ The pieces:
|
||||
|
||||
## One-time setup (on the Gitea server)
|
||||
|
||||
The server needs about 8 GB of free memory during a build and about 15 GB of disk for the image and
|
||||
caches. A first build takes 30-40 minutes; later ones reuse the Gradle cache.
|
||||
The server needs about 8 GB of free memory during a build and about 20 GB of disk for the image and
|
||||
caches. A first build takes 15-40 minutes depending on the machine; later ones reuse the Gradle build
|
||||
cache and ccache's compiled C++ (volumes `musicshare-gradle` and `musicshare-ccache`) and take a few
|
||||
minutes. The APKs are for 64-bit phones (`arm64-v8a`); set `ABIS=armeabi-v7a,arm64-v8a` in the workflow's
|
||||
Build step to include old 32-bit ones.
|
||||
The caches are Docker volumes, which live wherever Docker keeps its data (usually `/var/lib/docker`).
|
||||
Check that disk has room (`df -h /var/lib/docker`): if it fills up, everything else using Docker on the
|
||||
server, including databases, stops writing.
|
||||
|
||||
Reference in New Issue
Block a user