#!/usr/bin/env bash # # Construye la APK, la verifica y (si se le pasa un token) publica la release en # gitea. Pensado para lanzarlo a mano o desde un runner; hace lo mismo en los dos # sitios, que es lo que evita el clasico "en mi maquina si sale". # # Uso: # ./scripts/release.sh # compila debug y verifica # ./scripts/release.sh --release # compila release firmada # ./scripts/release.sh --release --publish # # Variables: # OASIS_KEYSTORE, OASIS_KEYSTORE_PASS, OASIS_KEY_ALIAS firma (obligatorias con --release) # OASIS_GITEA_TOKEN publicar (obligatoria con --publish) # OASIS_APK_BASE APK del que sacar node_modules la primera vez # OASIS_GITEA_URL por defecto https://gitea.laenre.net # OASIS_GITEA_REPO por defecto hacklab/OASIS_MOBILE # ANDROID_HOME por defecto ~/Android/Sdk # JAVA_HOME un JDK 17 o superior CON compilador # set -euo pipefail HERE="$(cd "$(dirname "$0")/.." && pwd)" REPO="$(cd "$HERE/.." && pwd)" cd "$HERE" MODE=debug PUBLISH=0 for arg in "$@"; do case "$arg" in --release) MODE=release ;; --publish) PUBLISH=1 ;; *) echo "opcion desconocida: $arg"; exit 2 ;; esac done GITEA_URL="${OASIS_GITEA_URL:-https://gitea.laenre.net}" GITEA_REPO="${OASIS_GITEA_REPO:-hacklab/OASIS_MOBILE}" SDK="${ANDROID_HOME:-$HOME/Android/Sdk}" say() { printf '\n== %s\n' "$*"; } die() { printf '\n!! %s\n' "$*" >&2; exit 1; } # --- 1) version ------------------------------------------------------------- # El nombre sale de package.json, que es de donde lo saca tambien el backend, para # que la version que muestra la aplicacion y la de la APK no puedan discrepar. VERSION_NAME="${OASIS_VERSION_NAME:-$(node -p "require('$REPO/package.json').version" 2>/dev/null || echo 0.0.0)}" # El codigo tiene que subir en cada publicacion o Android rechaza la instalacion: # el numero de commits sirve y no hay que llevarlo a mano. VERSION_CODE="${OASIS_VERSION_CODE:-$(git -C "$REPO" rev-list --count HEAD 2>/dev/null || echo 1)}" TAG="v${VERSION_NAME}-${VERSION_CODE}" export OASIS_VERSION_NAME="$VERSION_NAME" OASIS_VERSION_CODE="$VERSION_CODE" say "version ${VERSION_NAME} (codigo ${VERSION_CODE}), modo ${MODE}" # --- 2) comprobaciones previas --------------------------------------------- [ -n "${JAVA_HOME:-}" ] || die "define JAVA_HOME apuntando a un JDK con compilador" [ -x "${JAVA_HOME}/bin/javac" ] || die "en ${JAVA_HOME} no hay javac: es un runtime, no un JDK" [ -d "$SDK" ] || die "no encuentro el SDK de Android en $SDK (define ANDROID_HOME)" if [ "$MODE" = release ]; then [ -n "${OASIS_KEYSTORE:-}" ] || die "--release necesita OASIS_KEYSTORE" [ -f "$OASIS_KEYSTORE" ] || die "no existe el almacen de claves: $OASIS_KEYSTORE" [ -n "${OASIS_KEYSTORE_PASS:-}" ] || die "--release necesita OASIS_KEYSTORE_PASS" fi if [ "$PUBLISH" = 1 ]; then [ -n "${OASIS_GITEA_TOKEN:-}" ] || die "--publish necesita OASIS_GITEA_TOKEN" [ "$MODE" = release ] || die "no se publica una APK de depuracion" fi # --- 3) backend empaquetado ------------------------------------------------- # prepare.sh es idempotente pero tarda; se rehace siempre porque el backend cambia # en cada commit y una APK con el backend viejo es peor que no tener APK. # La primera vez hace falta un APK base del que sacar node_modules: el del movil # esta podado y sin binarios nativos, y un npm install normal no lo reproduce. say "empaquetando el backend" ./scripts/prepare.sh ${OASIS_APK_BASE:+"$OASIS_APK_BASE"} \ || die "prepare.sh ha fallado (define OASIS_APK_BASE con un APK del que sacar node_modules)" ZIP=app/src/main/assets/nodejs-project.zip [ -f "$ZIP" ] || die "falta $ZIP" ZIP_MB=$(( $(stat -c%s "$ZIP") / 1024 / 1024 )) [ "$ZIP_MB" -ge 20 ] || die "el backend empaquetado son $ZIP_MB MB: se ha quedado a medias" say "backend: ${ZIP_MB} MB" # --- 4) compilar ------------------------------------------------------------ say "compilando" if [ "$MODE" = release ]; then ./gradlew --quiet assembleRelease APK=$(ls app/build/outputs/apk/release/*.apk | head -1) else ./gradlew --quiet assembleDebug APK=$(ls app/build/outputs/apk/debug/*.apk | head -1) fi [ -f "$APK" ] || die "no se ha generado ninguna APK" say "APK: $APK ($(( $(stat -c%s "$APK") / 1024 / 1024 )) MB)" # --- 5) verificar ----------------------------------------------------------- # Estas tres comprobaciones son las que han cazado fallos de verdad: una APK que # compila e instala puede no llevar el backend dentro, o haber perdido los permisos. BT=$(ls -d "$SDK"/build-tools/* 2>/dev/null | sort -V | tail -1) [ -n "$BT" ] || die "no encuentro build-tools en $SDK" say "verificando" # ojo con pipefail: grep -q cierra la tuberia y el productor muere con SIGPIPE, # asi que se vuelca a fichero antes de mirar LISTING=$(mktemp); PERMS=$(mktemp) trap 'rm -f "$LISTING" "$PERMS"' EXIT unzip -l "$APK" > "$LISTING" "$BT/aapt2" dump permissions "$APK" > "$PERMS" 2>/dev/null || true grep -q "assets/nodejs-project.zip" "$LISTING" || die "la APK no lleva el backend dentro" grep -q "android.permission.CAMERA" "$PERMS" || die "la APK no declara CAMERA: no podria hacer videollamadas" grep -q "android.permission.RECORD_AUDIO" "$PERMS" || die "la APK no declara RECORD_AUDIO" grep -q "libnode.so" "$LISTING" || die "la APK no lleva el runtime de node" if [ "$MODE" = release ]; then "$BT/apksigner" verify --print-certs "$APK" > /tmp/oasis-signer.txt 2>&1 \ || die "la APK no esta firmada correctamente" FINGERPRINT=$(grep -m1 "SHA-256 digest" /tmp/oasis-signer.txt | awk '{print $NF}') say "huella del certificado: $FINGERPRINT" fi SHA=$(sha256sum "$APK" | cut -d' ' -f1) say "sha256 de la APK: $SHA" # --- 6) publicar ------------------------------------------------------------ if [ "$PUBLISH" != 1 ]; then say "listo (sin publicar)" exit 0 fi say "publicando $TAG en $GITEA_REPO" API="$GITEA_URL/api/v1/repos/$GITEA_REPO" AUTH="Authorization: token $OASIS_GITEA_TOKEN" NOTES=$(printf 'Rama experimental del hacklab sobre el Oasis de epsylon.\n\nNO es Oasis oficial: https://github.com/epsylon/oasis\n\nVersion base: %s (codigo %s)\n\nsha256 de la APK: %s\nHuella del certificado: %s\n\nComprueba la huella antes de instalar:\n\n apksigner verify --print-certs oasis.apk\n' \ "$VERSION_NAME" "$VERSION_CODE" "$SHA" "${FINGERPRINT:-sin firmar}") RELEASE_ID=$(curl -sf -X POST "$API/releases" -H "$AUTH" -H "Content-Type: application/json" \ -d "$(node -e ' const [tag, name, body] = process.argv.slice(1); process.stdout.write(JSON.stringify({ tag_name: tag, name, body, draft: true, prerelease: true })); ' "$TAG" "Oasis Mobile $TAG" "$NOTES")" | node -pe 'JSON.parse(require("fs").readFileSync(0)).id' 2>/dev/null) \ || die "no se ha podido crear la release (token sin permiso, o el tag ya existe)" curl -sf -X POST "$API/releases/$RELEASE_ID/assets?name=oasis-$TAG.apk" \ -H "$AUTH" -F "attachment=@$APK" >/dev/null \ || die "la release se ha creado pero no se ha podido subir la APK" say "publicada como borrador: $GITEA_URL/$GITEA_REPO/releases" say "revisala y quita el borrador a mano; no se publica sola a proposito"