# Build, prove e rilascio

Si lavora in `~/dev/spotthespy`: dal NAS Xcode e Gradle non compilano. Le
cartelle `ios/` e `android/` non stanno nel repository, le rigenera
`npx expo prebuild` da `app.json` e dai plugin in `plugins/`.

```bash
npm test          # logica di gioco e testi (node --test)
npm run typecheck # tsc
```

## iOS

```bash
npx expo prebuild --clean --no-install && (cd ios && pod install)
cd ios && xcodebuild -workspace SpotTheSpy.xcworkspace -scheme SpotTheSpy \
  -configuration Release -sdk iphonesimulator \
  -destination 'generic/platform=iOS Simulator' -derivedDataPath ../build/sim -quiet
xcrun simctl boot "iPhone 17"
xcrun simctl install booted ../build/sim/Build/Products/Release-iphonesimulator/SpotTheSpy.app
xcrun simctl launch booted paoloalby.SpotTheSpy
xcrun simctl io booted screenshot /tmp/sts.png
```

Il prebuild serve solo quando cambiano `app.json`, `plugins/` o le dipendenze
native; per il codice JavaScript basta rifare `xcodebuild`.

Su questo Mac Xcode non ha Simulator.app: il simulatore gira senza finestra.
Per toccare lo schermo si usa Maestro (sotto).

iOS 27 vuole il ciclo di vita a scene: ci pensa
`plugins/with-ios-scene-delegate.js` (copiato da Quanto Basta, dove è
spiegato in `docs/BUILD.md`). Se l'app parte nera, il primo sospetto è lui.

Lingua del simulatore:
`xcrun simctl spawn booted defaults write -g AppleLanguages -array fr`, poi
si riavvia l'app (o il simulatore).

## Android

Emulatori propri: `SpotTheSpy_Pixel` e `SpotTheSpy_Tablet` (immagine
android-36 google_apis arm64). Si avviano sulla porta 5580 per non toccare
quelli delle altre app (BlueStacks è `emulator-5554`), e `adb` va sempre con
`-s emulator-5580`.

```bash
/opt/homebrew/share/android-commandlinetools/emulator/emulator -avd SpotTheSpy_Pixel -port 5580 -no-snapshot-save &

npx expo prebuild --clean --no-install
echo "sdk.dir=/opt/homebrew/share/android-commandlinetools" > android/local.properties
cd android && JAVA_HOME=~/.jdks/jdk-17.0.20.1+1/Contents/Home \
  ./gradlew assembleRelease -PreactNativeArchitectures=arm64-v8a -q
adb -s emulator-5580 push app/build/outputs/apk/release/app-release.apk /data/local/tmp/sts.apk
adb -s emulator-5580 shell pm install -r /data/local/tmp/sts.apk
adb -s emulator-5580 shell monkey -p it.duebytes.spotthespy -c android.intent.category.LAUNCHER 1
adb -s emulator-5580 exec-out screencap -p > /tmp/sts.png
```

JDK 17, mai il 25 di Android Studio. `adb install` di un APK grande si
pianta: per questo push e `pm install`. Lingua della sola app:
`adb -s emulator-5580 shell cmd locale set-app-locales it.duebytes.spotthespy --user 0 --locales fr-FR`.
Cambiare la lingua di sistema con `setprop persist.sys.locale` e il riavvio di
zygote ha mandato in tilt l'emulatore due volte: meglio evitarlo.

La notifica di fine tempo su Android non è esatta al secondo: senza il
permesso `SCHEDULE_EXACT_ALARM` il sistema può consegnarla fino a qualche
minuto dopo (sull'emulatore la finestra era di 3 minuti). Serve solo con
l'app in background; con l'app aperta il suono lo fa il timer.

## Prove con Maestro

Maestro (in `~/.maestro/bin`) tocca lo schermo sia sul simulatore iOS senza
finestra sia sull'emulatore Android. I flussi stanno in `maestro/`.

```bash
export JAVA_HOME=~/.jdks/jdk-17.0.20.1+1/Contents/Home
~/.maestro/bin/maestro --device <UDID o emulator-5580> test maestro/giro.yaml
~/.maestro/bin/maestro --device <UDID> hierarchy   # testi ed etichette a schermo
```

- I flussi hanno `appId: paoloalby.SpotTheSpy` (iOS). Per Android se ne fa
  una copia temporanea con `sed 's/paoloalby.SpotTheSpy/it.duebytes.spotthespy/'`:
  `-e APP_ID=...` non sostituisce la variabile nella testata.
- All'avvio iOS può mostrare la richiesta di recensione («Non ora») e alla
  prima partita quella del permesso per le notifiche («Consenti»): i flussi le
  chiudono con un `tapOn` opzionale.
- Le etichette con un a capo (`Spia\nscovata`) si cercano con una regex
  (`.*scovata.*`). Quando un dialogo sta sopra un pulsante con lo stesso testo,
  `index: 0` prende quello del dialogo.
- `takeScreenshot` di Maestro con un percorso assoluto fallisce: gli screenshot
  si fanno con `simctl` o `adb`, come sopra.
- `timer-indovina.yaml` e `timer-due-spie.yaml` sbagliano apposta scegliendo
  «Zoo» e «Vigneto»: se il luogo segreto è proprio uno di quelli (una volta su
  59 circa) il flusso fallisce, e basta rilanciarlo.
- `luoghi-regole.yaml` è pensato per l'iPhone: su iPad gli swipe a
  percentuale cadono fuori dalla colonna centrata.
- Sull'emulatore Android, con il Mac carico, Maestro può diventare lento:
  in quel caso `adb shell input tap x y` con le coordinate lette da
  `adb shell uiautomator dump`.

## Rilascio

Numeri da alzare a ogni caricamento, in `app.json`: `ios.buildNumber` e
`android.versionCode`.

**iOS** (stessa scheda della 1.5, bundle `paoloalby.SpotTheSpy`, team
`686XK96PF6`), come in `../comune/pubblicare.md`:

```bash
cd ios && xcodebuild -workspace SpotTheSpy.xcworkspace -scheme SpotTheSpy -configuration Release \
  -destination 'generic/platform=iOS' -archivePath build/SpotTheSpy.xcarchive archive \
  -allowProvisioningUpdates DEVELOPMENT_TEAM=686XK96PF6 CODE_SIGN_STYLE=Automatic
xcodebuild -exportArchive -archivePath build/SpotTheSpy.xcarchive \
  -exportOptionsPlist "/Users/paolo/Server/Siti Web/_private/ - App/comune/ios/ExportOptions.plist" \
  -exportPath build/export -allowProvisioningUpdates
```

**Android** (`it.duebytes.spotthespy`): la release si firma con la chiave di
caricamento in `_private/ - App/SpotTheSpy - firma Android/` (da creare prima
della prima release), letta da `plugins/with-android-upload-key.js` tramite
`STS_UPLOAD_STORE_FILE`, `STS_UPLOAD_STORE_PASSWORD`, `STS_UPLOAD_KEY_ALIAS`,
`STS_UPLOAD_KEY_PASSWORD` in `~/.gradle/gradle.properties`. Senza, la release
esce firmata con la chiave di debug (va bene solo per l'emulatore).

```bash
cd android && JAVA_HOME=~/.jdks/jdk-17.0.20.1+1/Contents/Home ./gradlew bundleRelease
keytool -printcert -jarfile app/build/outputs/bundle/release/app-release.aab
```

## La copia sul NAS

Dopo ogni push: `git -C "/Users/paolo/Server/Siti Web/_private/ - App/SpotTheSpy" pull --ff-only`.
A volte resta un `.git/index.lock` vecchio (lo lascia Xcode o un pull
interrotto): si toglie solo se nessun processo git sta girando.
