# pear-git CI for the cthuloid repo: builds the Android app (android/) on
# android-v<x.y.z> TAGS ONLY, and publishes a signed APK as a forge download:
# https://7rnx.net/git/releases/cthuloid/android-v<x.y.z>/dist/cthuloid-lights.apk
# See docs/DESIGN-android.md §5.
#
# ⛔ The forge runs this for EVERY pushed branch and tag and cannot filter, so each
# step checks the ref itself (GIT_REF / GIT_REF_KIND are injected) and does nothing
# unless the gate step decided to build. Every push publishes `dist/` -- on a
# non-release ref it holds only skipped.txt. (A listed-but-missing file would make
# collectArtifacts throw and leave a misleading error in the forge log.)
#
# ⛔ CI runs in a `git archive | tar -x` extraction: no .git, no node_modules, no
# PATH guarantees, `sh -c` (not bash). Host paths are absolute, as in
# favela-surface-android's ci.yaml.
#
# ⛔ THE SIGNING KEY is ~/android-toolchain/cthuloid.keystore with its password in
# cthuloid.keystore.pass -- outside the repo, never in git. Android refuses an
# update signed with any other key (uninstall, i.e. data loss, is the only way
# out), so the build FAILS if the key is missing rather than signing with another.
steps:
- name: gate
run: |
set -e
mkdir -p dist
if [ "$GIT_REF_KIND" = "tag" ] && echo "$GIT_REF" | grep -Eq '^android-v[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "release build: $GIT_REF"
else
echo "not an android-v<x.y.z> tag ($GIT_REF_KIND $GIT_REF): nothing to build"
echo "APKs are built on android-v<x.y.z> tags only; this ref ($GIT_REF_KIND $GIT_REF) built nothing." > dist/skipped.txt
touch .skip
fi
timeout: 30
- name: deps
run: |
set -e
[ -f .skip ] && exit 0
export PATH=/home/fleeky/.nvm/versions/node/v24.13.0/bin:$PATH
(cd webui && npm ci --no-audit --no-fund)
(cd android && npm ci --no-audit --no-fund)
test -e android/node_modules/cthuloid-web/lib/app.js
timeout: 900
- name: version
# The version comes from the tag, never from hand-editing a field.
run: |
set -e
[ -f .skip ] && exit 0
export PATH=/home/fleeky/.nvm/versions/node/v24.13.0/bin:$PATH
V=${GIT_REF#android-v}
(cd android && node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json'));p.version=process.argv[1];fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\n')" "$V")
grep '"version"' android/package.json
timeout: 30
- name: prebuild
run: |
set -e
[ -f .skip ] && exit 0
export PATH=/home/fleeky/.nvm/versions/node/v24.13.0/bin:$PATH
(cd android && npm run prebuild:android)
test -s android/android/app/src/main/assets/worklet.bundle
timeout: 900
- name: assemble
run: |
set -e
[ -f .skip ] && exit 0
T=/home/fleeky/android-toolchain
test -s $T/cthuloid.keystore
test -s $T/cthuloid.keystore.pass
export JAVA_HOME=$T/jdk-17.0.13+11
export ANDROID_HOME=$T/sdk
export PATH=/home/fleeky/.nvm/versions/node/v24.13.0/bin:$PATH
export CTHULOID_KEYSTORE=$T/cthuloid.keystore
CTHULOID_KEYSTORE_PASS=$(cat $T/cthuloid.keystore.pass)
export CTHULOID_KEYSTORE_PASS
# Every new-architecture library's codegen must exist before CMake configure
# (favela-surface-android's ci.yaml); unqualified so it covers all of them.
$T/gradle-8.13/bin/gradle -p android/android generateCodegenArtifactsFromSchema
$T/gradle-8.13/bin/gradle -p android/android assembleRelease
cp android/android/app/build/outputs/apk/release/app-release.apk dist/cthuloid-lights.apk
# The artifact, not the intent: every native lib's NEEDED entries are in the APK...
(cd android && node scripts/verify-apk-addons.mjs ../dist/cthuloid-lights.apk)
# ...and it is signed with THIS key, not a debug or stray one.
WANT=$($JAVA_HOME/bin/keytool -list -v -keystore $T/cthuloid.keystore -storepass:file $T/cthuloid.keystore.pass | sed -n 's/.*SHA256: //p' | head -1 | tr -d ': ' | tr 'A-F' 'a-f')
GOT=$($ANDROID_HOME/build-tools/34.0.0/apksigner verify --print-certs dist/cthuloid-lights.apk | sed -n 's/.*certificate SHA-256 digest: //p' | head -1)
echo "signer want=$WANT got=$GOT"
[ -n "$WANT" ] && [ "$WANT" = "$GOT" ]
(cd dist && sha256sum cthuloid-lights.apk > cthuloid-lights.apk.sha256)
ls -l dist
timeout: 2700
artifacts:
# A directory: files only on a release tag, skipped.txt otherwise.
- dist