The web client already has a full mobile UI (MobileShell and 17 sibling screens) and ships as a PWA, so what was missing to get an APK was a native container, not an interface. The WebView loads the instance over the network instead of bundling a copy of the web build. Bundling would mean republishing an APK on every site deploy — there is no electron-updater equivalent here — and it would move the page to https://localhost, turning every same-origin /api and /ws call into a cross-origin one. The cost is no offline mode, which for a realtime chat app costs nothing: public/index.html covers the unreachable-server case. packages/mobile sits outside the pnpm workspace on purpose. Capacitor's CLI and Gradle both assume a flat node_modules, and keeping it out leaves pnpm-lock.yaml and the existing web/desktop CI untouched by it. Icons are generated from the existing brand art. The launch screen is a layer-list over the brand background rather than Capacitor's per-density splash.png set, which is light and flashed white before the WebView painted. CI builds an *unsigned* APK. The signing key stays on the VPS and is applied there — an APK signature is what proves an update came from us, so the key has no reason to exist on a second machine. Background voice (foreground service) is not in this commit: a plain WebView is throttled when the screen goes off and the call drops. That is the next phase. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
87 lines
3.1 KiB
YAML
87 lines
3.1 KiB
YAML
# Compila o APK do Android no GitHub e o devolve como artefato.
|
|
#
|
|
# O runner entra só como máquina de build, pelo mesmo motivo do desktop: a VPS
|
|
# tem ~2 GB de RAM livres e roda a produção, então um Gradle com o SDK do
|
|
# Android ali dentro arrisca derrubar as chamadas de quem está usando o app.
|
|
#
|
|
# O APK sai **sem assinatura**. A chave de assinatura fica na VPS e não sobe
|
|
# para cá: quem assina é /opt/ops/release-android.sh, depois de baixar este
|
|
# artefato. Um APK assinado é instalável por qualquer um, e a chave é o que
|
|
# prova que uma atualização veio de nós — ela não tem por que existir em duas
|
|
# máquinas.
|
|
name: Android APK
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
push:
|
|
tags: ['android-v*']
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
|
|
|
|
- name: Setup Java
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: '21'
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
|
with:
|
|
node-version: 22
|
|
|
|
# npm, não pnpm: packages/mobile fica fora do workspace de propósito
|
|
# (ver pnpm-workspace.yaml). Ele tem o próprio package-lock.json.
|
|
- name: Install Capacitor
|
|
working-directory: packages/mobile
|
|
run: npm ci
|
|
|
|
# Copia a config e os arquivos de public/ para dentro do projeto nativo.
|
|
# Sem isso o APK sai com o que estiver commitado em assets/, que pode
|
|
# estar velho — esses arquivos são gerados, não versionados.
|
|
- name: Sync Capacitor
|
|
working-directory: packages/mobile
|
|
run: npx cap sync android
|
|
|
|
- name: Cache Gradle
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
key: gradle-${{ hashFiles('packages/mobile/android/**/*.gradle', 'packages/mobile/android/gradle/wrapper/gradle-wrapper.properties') }}
|
|
restore-keys: gradle-
|
|
|
|
- name: Build unsigned release APK
|
|
working-directory: packages/mobile/android
|
|
run: ./gradlew assembleRelease --no-daemon --stacktrace
|
|
|
|
# O nome do arquivo muda conforme a versão do AGP (app-release-unsigned.apk
|
|
# ou app-release.apk quando não há config de assinatura). Localiza em vez
|
|
# de supor, e falha alto se nada saiu — um artefato vazio só apareceria
|
|
# como erro lá na frente, na hora de assinar.
|
|
- name: Collect APK
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p out
|
|
found=$(find packages/mobile/android/app/build/outputs/apk/release -name '*.apk' | head -1)
|
|
[ -n "$found" ] || { echo "::error::o build não produziu APK nenhum"; exit 1; }
|
|
cp "$found" out/resenhacord-unsigned.apk
|
|
ls -l out/
|
|
|
|
- name: Upload APK artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: resenhacord-apk-unsigned
|
|
path: out/resenhacord-unsigned.apk
|
|
retention-days: 14
|
|
if-no-files-found: error
|