When Deploy Prod runs, also build a signed Play AAB from Capacitor (llm-fe/) and publish it as a Gitea release asset, same pattern as dta_webapp.gitea/workflows/deploy-prod.yml → android-release job.
Today Deploy Prod only ships web-static via server-infra/scripts/deploy.sh. Android signing is still manual (llm-fe/ANDROID.md).
This supersedes the separate-button design in #58 — do not add a standalone deploy-android.yml unless we later need a rebuild-without-web-deploy escape hatch. Prefer one prod workflow like RealPath.
Reference release shape on dta: tags android-v<versionName>-<versionCode> with asset realpath-<versionName>-<versionCode>.aab (e.g. android-v1.0.2-3).
publish-android-gitea-release.mjs — tag android-v${versionName}-${versionCode}, asset e.g. hesychia-${versionName}-${versionCode}.aab, application id ai.hesychia.chat
3. npm / Capacitor store build
Ensure a store-oriented script (dta: build:android:store). For us, prod mobile is npm run build:mobile (.env.mobile → prod API). Either:
use build:mobile in the job, or
add an explicit build:android:store alias that documents “Play binary only”
Must end with cap sync android before bundleRelease.
4. Gradle version overrides
Mirror dta: floors in build.gradle, CI passes next version via -P… so prod runs never require a manual version commit.
5. Docs
Update llm-fe/ANDROID.md / android/README.md: Deploy Prod publishes AAB to Gitea releases; link example dta release
Note relationship to #58 (close#58 as superseded when this lands)
Runner/secrets still tracked in server-infra#9 — prefer dta’s live layout~/.android/ over the earlier Documents/secrets/chat_web_app/android/ proposal unless we deliberately standardize both apps
6. Out of scope
Auto-upload to Play Console (manual download from Gitea release → internal track is enough, same as dta today)
Extending deploy.sh / nginx for APK hosting
iOS / TestFlight
Changing Deploy Beta
Secrets / host setup (ops — do before first green run)
A. Host files on the self-hosted runner (not in git)
Match dta paths so the workflow can ln -sfn the same way:
Note: Older docs/ANDROID.md mention ~/Documents/secrets/aiml-chat-upload.jks. Either move/symlink into ~/.android/ to match dta, or point storeFile at the old path — workflow should test -f whatever path we standardize.
Note: If RealPath and Hesychia share one keystore.properties filename under ~/.android/, use separate property files (e.g. hesychia-keystore.properties vs RealPath’s keystore.properties) so the two apps do not overwrite each other’s symlink target. Ticket implementer should pick distinct filenames and document them in both workflows.
B. Gitea Actions secret on chat_web_app
Repo → Settings → Actions → Secrets:
Secret name
Purpose
GITHUB_TOKEN
Personal access token (or app token) with repo release write — same name dta uses; workflow maps it to GITEA_TOKEN for android-version.mjs + publish-android-gitea-release.mjs
Token must be allowed to create tags/releases and upload release assets on ai_ml_operations/chat_web_app.
No keystore passwords in Gitea secrets if we keep dta’s host-file model (preferred).
C. Optional later
play-service-account.json for Play Developer API upload (not required for Gitea release attachment)
Acceptance criteria
deploy-prod.yml has android-release after successful prod web deploy
Job builds signed app-release.aab for ai.hesychia.chat from the workflow SHA
Version auto-bumps from existing android-v* releases (reuse if same SHA)
Gitea release created/updated with AAB asset attached
Keystore / passwords never committed; loaded from runner ~/.android/ (or documented equivalent)
jarsigner -verify (or equivalent) on the AAB before publish
ANDROID.md documents the flow + secrets layout
#58 closed as superseded (or retargeted) when this ships
## Summary
When **Deploy Prod** runs, also build a **signed Play AAB** from Capacitor (`llm-fe/`) and publish it as a **Gitea release asset**, same pattern as [dta_webapp](https://git.aimloperations.com/Ditch_The_Agent/dta_webapp) `.gitea/workflows/deploy-prod.yml` → `android-release` job.
Today Deploy Prod only ships web-static via `server-infra/scripts/deploy.sh`. Android signing is still manual (`llm-fe/ANDROID.md`).
This **supersedes the separate-button design in [#58](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/58)** — do not add a standalone `deploy-android.yml` unless we later need a rebuild-without-web-deploy escape hatch. Prefer one prod workflow like RealPath.
Reference release shape on dta: tags `android-v<versionName>-<versionCode>` with asset `realpath-<versionName>-<versionCode>.aab` (e.g. [android-v1.0.2-3](https://git.aimloperations.com/Ditch_The_Agent/dta_webapp/releases/tag/android-v1.0.2-3)).
---
## How dta_webapp does it (copy this)
`Deploy Prod Webapp` (`workflow_dispatch`):
1. `unit-tests`
2. `deploy-prod` → `deploy.sh --app dta_webapp --env prod`
3. `android-release` (needs `deploy-prod`):
- checkout same `${{ gitea.sha }}`
- `node scripts/android-version.mjs` — looks up existing `android-v*` releases; reuses version for same SHA, else next patch + next `versionCode`
- `npm run build:android:store` — store web bundle + `cap sync`
- symlink host keystore into checkout:
- `/home/westfarn/.android/keystore.properties` → `android/keystore.properties`
- `/home/westfarn/.android/realpath-upload.jks` (referenced by props)
- write `android/local.properties` → `sdk.dir=/home/westfarn/Android/Sdk`
- `./gradlew bundleRelease` with `-PrealpathVersionCode` / `-PrealpathVersionName`
- `jarsigner -verify` on the AAB
- `node scripts/publish-android-gitea-release.mjs` — create/reuse Gitea release + upload AAB
Uses Actions secret `secrets.GITHUB_TOKEN` as `GITEA_TOKEN` for release list/create/upload.
Scripts/docs to port (adapt names for Hesychia):
| dta path | Role |
|----------|------|
| `ditch-the-agent/scripts/android-version.mjs` (+ test) | Auto bump / reuse Play version from Gitea tags |
| `ditch-the-agent/scripts/publish-android-gitea-release.mjs` | Create `android-v*` release + attach AAB |
| `ditch-the-agent/docs/android-play-store.md` | Ops notes |
| `android/app/build.gradle` floors + `-P` overrides | Avoid editing gradle on every prod run |
---
## Target for chat_web_app
### 1. Extend `.gitea/workflows/deploy-prod.yml`
Add `android-release` job after `deploy-prod` (working-directory `llm-fe`), same structure as dta.
### 2. Scripts under `llm-fe/scripts/`
Port/adapt:
- `android-version.mjs` — baseline floors from `android/app/build.gradle` (today `versionName "0.1.0"` / `versionCode 1`; introduce floor props like dta’s `*VersionCodeFloor` / `*VersionNameFloor`, Hesychia-named)
- `publish-android-gitea-release.mjs` — tag `android-v${versionName}-${versionCode}`, asset e.g. `hesychia-${versionName}-${versionCode}.aab`, application id `ai.hesychia.chat`
### 3. npm / Capacitor store build
Ensure a store-oriented script (dta: `build:android:store`). For us, prod mobile is `npm run build:mobile` (`.env.mobile` → prod API). Either:
- use `build:mobile` in the job, or
- add an explicit `build:android:store` alias that documents “Play binary only”
Must end with `cap sync android` before `bundleRelease`.
### 4. Gradle version overrides
Mirror dta: floors in `build.gradle`, CI passes next version via `-P…` so prod runs never require a manual version commit.
### 5. Docs
- Update `llm-fe/ANDROID.md` / `android/README.md`: Deploy Prod publishes AAB to Gitea releases; link example dta release
- Note relationship to [#58](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/58) (close #58 as superseded when this lands)
- Runner/secrets still tracked in [server-infra#9](https://git.aimloperations.com/ai_ml_operations/server-infra/issues/9) — **prefer dta’s live layout** `~/.android/` over the earlier `Documents/secrets/chat_web_app/android/` proposal unless we deliberately standardize both apps
### 6. Out of scope
- Auto-upload to Play Console (manual download from Gitea release → internal track is enough, same as dta today)
- Extending `deploy.sh` / nginx for APK hosting
- iOS / TestFlight
- Changing Deploy Beta
---
## Secrets / host setup (ops — do before first green run)
### A. Host files on the self-hosted runner (not in git)
Match dta paths so the workflow can `ln -sfn` the same way:
```text
/home/westfarn/.android/
hesychia-upload.jks # Play upload keystore (never commit)
keystore.properties # passwords + paths (mode 600)
```
`keystore.properties` example (paths relative to `android/app/` like dta, or absolute):
```properties
storeFile=/home/westfarn/.android/hesychia-upload.jks
storePassword=...
keyAlias=aiml-chat-upload
keyPassword=...
```
Create keystore once if missing (alias can match docs):
```bash
keytool -genkey -v \
-keystore /home/westfarn/.android/hesychia-upload.jks \
-keyalg RSA -keysize 2048 -validity 10000 \
-alias aiml-chat-upload
```
Also required on runner (dta already uses these):
- `/home/westfarn/Android/Sdk` (Platform 35 + build-tools)
- JDK 17+
**Note:** Older docs/`ANDROID.md` mention `~/Documents/secrets/aiml-chat-upload.jks`. Either move/symlink into `~/.android/` to match dta, or point `storeFile` at the old path — workflow should `test -f` whatever path we standardize.
**Note:** If RealPath and Hesychia share one `keystore.properties` filename under `~/.android/`, use **separate property files** (e.g. `hesychia-keystore.properties` vs RealPath’s `keystore.properties`) so the two apps do not overwrite each other’s symlink target. Ticket implementer should pick distinct filenames and document them in both workflows.
### B. Gitea Actions secret on `chat_web_app`
Repo → Settings → Actions → Secrets:
| Secret name | Purpose |
|-------------|---------|
| `GITHUB_TOKEN` | Personal access token (or app token) with **repo release write** — same name dta uses; workflow maps it to `GITEA_TOKEN` for `android-version.mjs` + `publish-android-gitea-release.mjs` |
Token must be allowed to create tags/releases and upload release assets on `ai_ml_operations/chat_web_app`.
No keystore passwords in Gitea secrets if we keep dta’s host-file model (preferred).
### C. Optional later
- `play-service-account.json` for Play Developer API upload (not required for Gitea release attachment)
---
## Acceptance criteria
- [ ] `deploy-prod.yml` has `android-release` after successful prod web deploy
- [ ] Job builds signed `app-release.aab` for `ai.hesychia.chat` from the workflow SHA
- [ ] Version auto-bumps from existing `android-v*` releases (reuse if same SHA)
- [ ] Gitea release created/updated with AAB asset attached
- [ ] Keystore / passwords never committed; loaded from runner `~/.android/` (or documented equivalent)
- [ ] `jarsigner -verify` (or equivalent) on the AAB before publish
- [ ] ANDROID.md documents the flow + secrets layout
- [ ] [#58](https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/58) closed as superseded (or retargeted) when this ships
## References
- Pattern: https://git.aimloperations.com/Ditch_The_Agent/dta_webapp/src/branch/main/.gitea/workflows/deploy-prod.yml
- Scripts: `ditch-the-agent/scripts/android-version.mjs`, `publish-android-gitea-release.mjs`
- Docs: `ditch-the-agent/docs/android-play-store.md`
- Local Capacitor: `llm-fe/ANDROID.md`, `llm-fe/android/README.md`
- Prior tickets: #58 (separate Deploy Android), #57 (debug CI), [server-infra#9](https://git.aimloperations.com/ai_ml_operations/server-infra/issues/9)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
When Deploy Prod runs, also build a signed Play AAB from Capacitor (
llm-fe/) and publish it as a Gitea release asset, same pattern as dta_webapp.gitea/workflows/deploy-prod.yml→android-releasejob.Today Deploy Prod only ships web-static via
server-infra/scripts/deploy.sh. Android signing is still manual (llm-fe/ANDROID.md).This supersedes the separate-button design in #58 — do not add a standalone
deploy-android.ymlunless we later need a rebuild-without-web-deploy escape hatch. Prefer one prod workflow like RealPath.Reference release shape on dta: tags
android-v<versionName>-<versionCode>with assetrealpath-<versionName>-<versionCode>.aab(e.g. android-v1.0.2-3).How dta_webapp does it (copy this)
Deploy Prod Webapp(workflow_dispatch):unit-testsdeploy-prod→deploy.sh --app dta_webapp --env prodandroid-release(needsdeploy-prod):${{ gitea.sha }}node scripts/android-version.mjs— looks up existingandroid-v*releases; reuses version for same SHA, else next patch + nextversionCodenpm run build:android:store— store web bundle +cap sync/home/westfarn/.android/keystore.properties→android/keystore.properties/home/westfarn/.android/realpath-upload.jks(referenced by props)android/local.properties→sdk.dir=/home/westfarn/Android/Sdk./gradlew bundleReleasewith-PrealpathVersionCode/-PrealpathVersionNamejarsigner -verifyon the AABnode scripts/publish-android-gitea-release.mjs— create/reuse Gitea release + upload AABUses Actions secret
secrets.GITHUB_TOKENasGITEA_TOKENfor release list/create/upload.Scripts/docs to port (adapt names for Hesychia):
ditch-the-agent/scripts/android-version.mjs(+ test)ditch-the-agent/scripts/publish-android-gitea-release.mjsandroid-v*release + attach AABditch-the-agent/docs/android-play-store.mdandroid/app/build.gradlefloors +-PoverridesTarget for chat_web_app
1. Extend
.gitea/workflows/deploy-prod.ymlAdd
android-releasejob afterdeploy-prod(working-directoryllm-fe), same structure as dta.2. Scripts under
llm-fe/scripts/Port/adapt:
android-version.mjs— baseline floors fromandroid/app/build.gradle(todayversionName "0.1.0"/versionCode 1; introduce floor props like dta’s*VersionCodeFloor/*VersionNameFloor, Hesychia-named)publish-android-gitea-release.mjs— tagandroid-v${versionName}-${versionCode}, asset e.g.hesychia-${versionName}-${versionCode}.aab, application idai.hesychia.chat3. npm / Capacitor store build
Ensure a store-oriented script (dta:
build:android:store). For us, prod mobile isnpm run build:mobile(.env.mobile→ prod API). Either:build:mobilein the job, orbuild:android:storealias that documents “Play binary only”Must end with
cap sync androidbeforebundleRelease.4. Gradle version overrides
Mirror dta: floors in
build.gradle, CI passes next version via-P…so prod runs never require a manual version commit.5. Docs
llm-fe/ANDROID.md/android/README.md: Deploy Prod publishes AAB to Gitea releases; link example dta release~/.android/over the earlierDocuments/secrets/chat_web_app/android/proposal unless we deliberately standardize both apps6. Out of scope
deploy.sh/ nginx for APK hostingSecrets / host setup (ops — do before first green run)
A. Host files on the self-hosted runner (not in git)
Match dta paths so the workflow can
ln -sfnthe same way:keystore.propertiesexample (paths relative toandroid/app/like dta, or absolute):Create keystore once if missing (alias can match docs):
Also required on runner (dta already uses these):
/home/westfarn/Android/Sdk(Platform 35 + build-tools)Note: Older docs/
ANDROID.mdmention~/Documents/secrets/aiml-chat-upload.jks. Either move/symlink into~/.android/to match dta, or pointstoreFileat the old path — workflow shouldtest -fwhatever path we standardize.Note: If RealPath and Hesychia share one
keystore.propertiesfilename under~/.android/, use separate property files (e.g.hesychia-keystore.propertiesvs RealPath’skeystore.properties) so the two apps do not overwrite each other’s symlink target. Ticket implementer should pick distinct filenames and document them in both workflows.B. Gitea Actions secret on
chat_web_appRepo → Settings → Actions → Secrets:
GITHUB_TOKENGITEA_TOKENforandroid-version.mjs+publish-android-gitea-release.mjsToken must be allowed to create tags/releases and upload release assets on
ai_ml_operations/chat_web_app.No keystore passwords in Gitea secrets if we keep dta’s host-file model (preferred).
C. Optional later
play-service-account.jsonfor Play Developer API upload (not required for Gitea release attachment)Acceptance criteria
deploy-prod.ymlhasandroid-releaseafter successful prod web deployapp-release.aabforai.hesychia.chatfrom the workflow SHAandroid-v*releases (reuse if same SHA)~/.android/(or documented equivalent)jarsigner -verify(or equivalent) on the AAB before publishReferences
ditch-the-agent/scripts/android-version.mjs,publish-android-gitea-release.mjsditch-the-agent/docs/android-play-store.mdllm-fe/ANDROID.md,llm-fe/android/README.md