diff --git a/.gitignore b/.gitignore index 3bdd52e..0ce32ec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ dist/ .DS_Store +*.jks diff --git a/docker/Dockerfile b/docker/Dockerfile index 2237a90..ac28665 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -76,11 +76,29 @@ RUN rm -rf android/app/src/main/res/mipmap-anydpi-v26 && \ # Fix kotlin-stdlib duplicate class conflict (stdlib 1.8+ already includes jdk7/jdk8) RUN printf '\nsubprojects {\n configurations.all {\n resolutionStrategy {\n force "org.jetbrains.kotlin:kotlin-stdlib:1.8.22"\n force "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.22"\n force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22"\n }\n }\n}\n' >> android/build.gradle -# ── Runtime: dist/ viene montato come volume dall'host ──────────────────────── +# ── Runtime: dist/ e (opzionale) keystore montati come volumi dall'host ─────── # build.sh esegue: docker run -v ./dist:/app/dist ... -# Qui cap sync copia dist/ in android/assets, poi Gradle builda l'APK +# Con --release: aggiunge -e BUILD_TYPE=release -v biteplan.jks:/app/biteplan.jks:ro +# BUILD_TYPE=release → assembleRelease + zipalign + apksigner +# BUILD_TYPE vuoto → assembleDebug (default) CMD npx cap sync android && \ - cd android && chmod +x gradlew && ./gradlew assembleDebug --no-daemon && \ - cp app/build/outputs/apk/debug/app-debug.apk /app/dist/biteplan.apk && \ - echo "APK generato: /app/dist/biteplan.apk" + cd android && chmod +x gradlew && \ + if [ "$BUILD_TYPE" = "release" ]; then \ + ./gradlew assembleRelease --no-daemon && \ + $ANDROID_HOME/build-tools/34.0.0/zipalign -v 4 \ + app/build/outputs/apk/release/app-release-unsigned.apk \ + /tmp/aligned.apk && \ + $ANDROID_HOME/build-tools/34.0.0/apksigner sign \ + --ks /app/biteplan.jks \ + --ks-key-alias biteplan \ + --ks-pass pass:$KEYSTORE_PASS \ + --key-pass pass:$KEY_PASS \ + --out /app/dist/biteplan.apk \ + /tmp/aligned.apk && \ + echo "APK release firmato: /app/dist/biteplan.apk"; \ + else \ + ./gradlew assembleDebug --no-daemon && \ + cp app/build/outputs/apk/debug/app-debug.apk /app/dist/biteplan.apk && \ + echo "APK debug: /app/dist/biteplan.apk"; \ + fi diff --git a/docker/README.md b/docker/README.md index b254e27..e0ea1a7 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,6 +1,6 @@ # Build APK — Docker -Genera un APK Android debug senza installare nulla sull'host oltre a Docker. +Genera un APK Android senza installare nulla sull'host oltre a Docker. ## Requisiti host @@ -11,21 +11,75 @@ Genera un APK Android debug senza installare nulla sull'host oltre a Docker. | **Docker** | Installato e avviato | | **Node.js** | v18+ (per il build Vite locale) | -> Gli Android build-tools (`aapt2`, `zipalign`, ecc.) sono binari nativi x86_64 e non girano su host ARM senza emulazione QEMU. +> Gli Android build-tools (`aapt2`, `zipalign`, `apksigner`) sono binari nativi x86_64 e non girano su host ARM senza emulazione QEMU. -## Utilizzo +--- -Dalla root del progetto: +## Build debug (default) + +APK firmato con il debug keystore di Android. Adatto per test su dispositivo. ```bash -# Build dalla working directory (file attuali) bash docker/build.sh - -# Build da HEAD (ignora modifiche non committate) -bash docker/build.sh --head ``` -L'APK viene generato in `dist/biteplan.apk`. +--- + +## Build release (distribuzione) + +APK firmato con il tuo keystore personale. Necessario per distribuire l'app. + +### 1. Genera il keystore (una volta sola) + +```bash +keytool -genkey -v \ + -keystore docker/biteplan.jks \ + -alias biteplan \ + -keyalg RSA \ + -keysize 2048 \ + -validity 10000 +``` + +> Il file `docker/biteplan.jks` è già in `.gitignore` — non verrà mai committato. +> Conservalo in un posto sicuro: senza di esso non puoi aggiornare l'app. + +### 2. Esegui la build release + +```bash +bash docker/build.sh --release +# chiede interattivamente: Password keystore / Password chiave +``` + +Oppure passando le password come variabili d'ambiente (utile in CI): + +```bash +KEYSTORE_PASS=tuapassword KEY_PASS=tuapassword bash docker/build.sh --release +``` + +Per usare un keystore in un percorso diverso da `docker/biteplan.jks`: + +```bash +KEYSTORE_PATH=/percorso/biteplan.jks bash docker/build.sh --release +``` + +### 3. Verifica la firma + +```bash +$ANDROID_HOME/build-tools/34.0.0/apksigner verify --verbose dist/biteplan.apk +``` + +--- + +## Flag combinabili + +| Comando | Risultato | +|---------|-----------| +| `bash docker/build.sh` | APK debug dalla working directory | +| `bash docker/build.sh --head` | APK debug dall'ultimo commit git | +| `bash docker/build.sh --release` | APK release firmato dalla working directory | +| `bash docker/build.sh --head --release` | APK release firmato dall'ultimo commit git | + +--- ## Prima build @@ -34,8 +88,6 @@ Le build successive usano la cache Docker e sono molto più rapide. ## Installazione su dispositivo -Con ADB: - ```bash adb install dist/biteplan.apk ``` @@ -43,14 +95,15 @@ adb install dist/biteplan.apk ## Pipeline ``` -[host] npm run build → dist/ (montato come volume in sola lettura) -[docker] cap sync → copia dist/ in android/assets/ -[docker] gradlew assembleDebug -[host] output/biteplan.apk +[host] npm run build → dist/ +[docker] cap sync → copia dist/ in android/assets/ +[docker] gradlew assembleDebug/Release +[docker] zipalign + apksigner → solo in modalità release +[host] dist/biteplan.apk ``` ## Note -- APK di tipo **debug**, non firmato per la produzione - App ID: `com.davide.biteplan` - Android target: API 34 +- Il keystore non viene mai copiato nell'immagine Docker (montato come volume read-only) diff --git a/docker/build.sh b/docker/build.sh index faf0e5c..841e752 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -6,10 +6,31 @@ PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" DIST_DIR="$PROJECT_ROOT/dist" FROM_HEAD=false +RELEASE=false for arg in "$@"; do - [[ "$arg" == "--head" ]] && FROM_HEAD=true + [[ "$arg" == "--head" ]] && FROM_HEAD=true + [[ "$arg" == "--release" ]] && RELEASE=true done +# ── Keystore (solo in modalità release) ─────────────────────────────────────── +KEYSTORE_PATH="${KEYSTORE_PATH:-$SCRIPT_DIR/biteplan.jks}" + +if $RELEASE; then + if [[ ! -f "$KEYSTORE_PATH" ]]; then + echo "Errore: keystore non trovato in $KEYSTORE_PATH" + echo "Generalo con:" + echo " keytool -genkey -v -keystore docker/biteplan.jks -alias biteplan -keyalg RSA -keysize 2048 -validity 10000" + exit 1 + fi + + if [[ -z "${KEYSTORE_PASS:-}" ]]; then + read -rsp "Password keystore: " KEYSTORE_PASS; echo + fi + if [[ -z "${KEY_PASS:-}" ]]; then + read -rsp "Password chiave: " KEY_PASS; echo + fi +fi + # ── Build Vite ──────────────────────────────────────────────────────────────── if $FROM_HEAD; then COMMIT_SHA=$(git -C "$PROJECT_ROOT" rev-parse --short HEAD) @@ -36,11 +57,24 @@ docker build \ -t biteplan-builder \ "$PROJECT_ROOT" -# ── Generazione APK (dist/ montato come volume) ─────────────────────────────── -echo "==> Generazione APK..." -docker run --rm \ - -v "$DIST_DIR:/app/dist" \ - biteplan-builder +# ── Generazione APK ─────────────────────────────────────────────────────────── +echo "==> Generazione APK${RELEASE:+ release}..." + +DOCKER_ARGS=( + --rm + -v "$DIST_DIR:/app/dist" +) + +if $RELEASE; then + DOCKER_ARGS+=( + -e BUILD_TYPE=release + -e KEYSTORE_PASS="$KEYSTORE_PASS" + -e KEY_PASS="$KEY_PASS" + -v "$KEYSTORE_PATH:/app/biteplan.jks:ro" + ) +fi + +docker run "${DOCKER_ARGS[@]}" biteplan-builder echo "" echo "APK pronto in: $DIST_DIR/biteplan.apk"