docs: strip PedsHub Quiz refs from mobile-build + terser CONTRIBUTING

mobile-build.md:
  - Removed "PedsHub Quiz" sections. That app lives in a separate
    repo (quiz/mobile/) and has its own build pipeline. Docs here
    are PedScribe-only now.
  - Reorganized around CI as the primary flow, local build as
    fallback. Added explicit secret names, JDK requirement, single-
    quote-password caveat, QEMU/argon2 note.
  - File-map section at the end so the native sources are
    discoverable without grepping.

CONTRIBUTING.md:
  - Cut the narrative prose. Dev-facing tables + single-line
    commands only. Decision-tree removed (the table suffices).
  - Release pipeline and mobile build link out rather than
    duplicating content.
This commit is contained in:
Daniel 2026-04-14 23:54:32 +02:00
parent 6365790e4b
commit f6397ae635
2 changed files with 120 additions and 142 deletions

View file

@ -1,68 +1,52 @@
# Contributing / Release workflow
# Contributing
## Commit message format
## Commit format
This repo uses [Conventional Commits](https://www.conventionalcommits.org/).
The `.github/workflows/auto-version.yml` workflow reads your commit
messages and decides whether to cut a release automatically.
[Conventional Commits](https://www.conventionalcommits.org). `.github/workflows/auto-version.yml`
parses messages since the last semver tag and decides whether to bump.
### Cheat sheet
| Prefix | Release type | Example |
| Prefix | Bump | |
|---|---|---|
| `fix:` | patch (6.1.1 → 6.1.2) | `fix: correct age parser off-by-one for "4y 11m"` |
| `feat:` | minor (6.1.1 → 6.2.0) | `feat: add vitals quick-copy button` |
| `feat!:` (or `BREAKING CHANGE:` in body) | major (6.1.1 → 7.0.0) | `feat!: require re-login after argon2 migration` |
| `docs:` | no release | `docs: update mobile build guide` |
| `refactor:` | no release | `refactor: split audit queue into its own module` |
| `chore:` | no release | `chore: bump eslint dep` |
| `test:` | no release | `test: add encounter version-lock test` |
| `style:` | no release | `style: prettier pass on calculators.js` |
| `ci:` | no release | `ci: cache node_modules in Actions` |
| `build:` | no release | `build: add DATA_ENCRYPTION_KEY to .env.example` |
| `fix:` | patch | bug fix |
| `feat:` | minor | new feature |
| `feat!:` / `fix!:` / `BREAKING CHANGE:` in body | major | breaking change |
| `docs:` `refactor:` `chore:` `test:` `style:` `ci:` `build:` | none | no release |
**Only `fix:`, `feat:`, and `!:` / `BREAKING CHANGE` trigger a version bump and a release.**
Everything else is committed and pushed but doesn't tag.
Append `[skip ci]` to suppress the run for that commit.
### Decision tree
## Manual release
Ask yourself:
1. **Did behavior change for the user?**
- No → `docs:`, `refactor:`, `chore:`, `test:`, `style:`, `ci:` (no release)
2. **Yes. Is it a bug fix?**
- Yes → `fix:` (patch)
3. **New feature or enhancement?**
- Yes → `feat:` (minor)
4. **Does it break existing behavior** (users have to log out, re-configure, migrate data, etc.)?
- Yes → `feat!:` or `fix!:` (major)
### Skip the workflow entirely
Append `[skip ci]` anywhere in the commit message to suppress the
auto-version run for that commit (e.g., for emergency one-off fixes
you want to batch under a later release).
## Manual release (emergency override)
From the Actions tab → **Version bump & release** → Run workflow →
pick patch / minor / major (or type exact version) → Run. Skips
commit-message parsing and bumps exactly as requested.
Or locally:
```bash
scripts/release.sh 6.1.2 --push
scripts/release.sh 6.2.0 --push # local
```
## After a release is cut
or Actions tab → **Version bump & release** → Run workflow → pick bump type.
The tag push (whether from auto-version, manual dispatch, or local
script) fires two parallel workflows:
## What a tag push triggers
| Workflow | Output | Time |
|---|---|---|
| `android-release.yml` | signed `pedscribe-X.Y.Z.apk` on the GitHub release | ~8 min |
| `docker-publish.yml` | `danielonyejesi/pediatric-ai-scribe-v3:X.Y.Z` + `:latest` on Docker Hub | ~4 min |
| Workflow | Output |
|---|---|
| `android-release.yml` | signed APK on GitHub release, `make_latest=true` |
| `docker-publish.yml` | `danielonyejesi/pediatric-ai-scribe-v3:{version,latest}` on Docker Hub (amd64) |
Obtanium users, Docker Hub subscribers, and the login page's
"Download APK" link all update without further action.
## Local dev
```bash
docker compose up -d # Postgres + app
docker logs -f pediatric-ai-scribe
```
Web changes hot-reload via browser refresh (JS/CSS cached 1h — add `?v=` query
or clear cache; the build-ID server-side cache-buster appends `?v=<git SHA>`
automatically on fresh page loads).
Server code changes require `docker compose build pediatric-scribe && docker compose up -d`.
## Mobile
See `docs/mobile-build.md`.
## DB migrations
`src/db/database.js` is the baseline (idempotent CREATE-IF-NOT-EXISTS). New
changes go in `migrations/` via `node-pg-migrate`. See `docs/migrations.md`.

View file

@ -1,115 +1,109 @@
# Mobile App Build & Release
# Mobile build & release
Capacitor wrappers for **PedScribe** (this repo) and **PedsHub Quiz**
(`/home/danvics/docker/quiz`). Both ship as Android APKs and iOS builds.
Capacitor 6 wrapper. Android only today; iOS project exists but requires macOS
+ Xcode to produce an `.ipa`.
## One-time setup
- **Keystore** (reused for both apps):
```bash
keytool -genkeypair -v -keystore ~/pedscribe-release.jks \
-keyalg RSA -keysize 2048 -validity 10000 -alias pedscribe
```
Store the password somewhere safe — losing it means rotating signing keys.
- **Android Studio path** (required when you want to open the IDE):
```bash
export CAPACITOR_ANDROID_STUDIO_PATH="/snap/android-studio/209/bin/studio.sh"
```
Put it in your `~/.bashrc` if you want it permanent.
## Release build — PedScribe
### Keystore
```bash
cd /home/danvics/docker/ped-ai/mobile
npm install # picks up any new plugins
npx cap sync android # copies web assets + plugin glue
cd android
./gradlew assembleRelease \
-Pandroid.injected.signing.store.file=$HOME/pedscribe-release.jks \
-Pandroid.injected.signing.store.password=YOUR_KEYSTORE_PASSWORD \
-Pandroid.injected.signing.key.alias=pedscribe \
-Pandroid.injected.signing.key.password=YOUR_KEY_PASSWORD
# APK lands at: android/app/build/outputs/apk/release/app-release.apk
keytool -genkeypair -v -keystore ~/pedscribe-release.jks \
-keyalg RSA -keysize 2048 -validity 10000 -alias pedscribe
```
## Release build — PedsHub Quiz
Store the password in a password manager. Back up the `.jks` file off the
machine. Losing it = can't sign updates; Play Store requires signature
continuity (unless you're on Play App Signing).
### Android Studio (optional, IDE workflow only)
```bash
cd /home/danvics/docker/quiz/mobile
export CAPACITOR_ANDROID_STUDIO_PATH="/snap/android-studio/current/bin/studio.sh"
npx cap open android
```
## CI build (preferred)
Tag-triggered. Push any `vX.Y.Z` tag → `.github/workflows/android-release.yml`
builds a signed APK on a GitHub runner and attaches it to the matching release.
Required repo secrets (set once, via Settings → Secrets and variables → Actions
or `gh secret set`):
- `ANDROID_KEYSTORE_BASE64``base64 -w0 ~/pedscribe-release.jks`
- `ANDROID_KEYSTORE_PASSWORD`
- `ANDROID_KEY_ALIAS``pedscribe`
- `ANDROID_KEY_PASSWORD`
Tag a release:
```bash
# conventional-commits prefix auto-tags (see CONTRIBUTING.md)
git commit -m "feat: ..." && git push # auto-version workflow bumps minor
git commit -m "fix: ..." && git push # auto-version workflow bumps patch
# or force an exact version
scripts/release.sh 6.2.0 --push
```
APK lands at the GitHub release; `/releases/latest` link in the login page
resolves to it automatically. Obtanium subscribers (`github.com/<owner>/<repo>`)
pick up the update on next poll.
## Local build (fallback / debugging)
```bash
cd mobile
npm install
npx cap sync android
cd android
./gradlew assembleRelease \
-Pandroid.injected.signing.store.file=$HOME/pedscribe-release.jks \
-Pandroid.injected.signing.store.password=YOUR_KEYSTORE_PASSWORD \
-Pandroid.injected.signing.store.password='<pass>' \
-Pandroid.injected.signing.key.alias=pedscribe \
-Pandroid.injected.signing.key.password=YOUR_KEY_PASSWORD
-Pandroid.injected.signing.key.password='<pass>'
```
## Publish APK on GitHub Releases
Output: `android/app/build/outputs/apk/release/app-release.apk`
For Play Store, swap `assembleRelease``bundleRelease`; output: `.aab` under
`bundle/release/`.
The login page links to `github.com/<owner>/<repo>/releases/latest` (see
`public/index.html` around line 114). Publishing a tagged release updates the
download link automatically — no site redeploy needed.
### Single-quote the password
```bash
cd /home/danvics/docker/ped-ai
gh release create v6.0.1 \
mobile/android/app/build/outputs/apk/release/app-release.apk \
--title "PedScribe 6.0.1" \
--notes "Hardware-backed secure storage for auth token on mobile."
```
Keystore passwords with shell metacharacters (`)`, `$`, `!`, space, etc.) must
be single-quoted. Backslash line continuations get eaten by some terminal
paste handlers — prefer one-line commands.
For the quiz app:
```bash
cd /home/danvics/docker/quiz
gh release create v1.0.0 \
mobile/android/app/build/outputs/apk/release/app-release.apk \
--title "PedsHub 1.0.0" \
--notes "Initial Android release."
```
## Push source changes to git
Standard flow — the mobile project lives alongside the web app:
```bash
cd /home/danvics/docker/ped-ai
git add mobile/ public/ src/
git commit -m "Your message"
git push
```
Same for quiz at `/home/danvics/docker/quiz`.
## iOS
iOS requires macOS + Xcode. On Linux the sync still runs but you cannot build
the `.ipa`:
```bash
cd /home/danvics/docker/ped-ai/mobile
npx cap sync ios
# Then on a Mac: open ios/App/App.xcworkspace and Archive.
```
## Reinstall on device after rebuild
## Reinstall on device
```bash
adb install -r android/app/build/outputs/apk/release/app-release.apk
```
`-r` preserves app data (saved server URL, cached sessions).
`-r` keeps app data (saved server URL, auth token in Keystore, IndexedDB).
## Troubleshooting
## Gotchas
- **`npx cap` can't find the project** — you must `cd mobile/` first, not run
from the repo root.
- **`Keystore was tampered with`** — wrong password. Do not generate a new
keystore unless you are ready to rotate the signing identity on Play Store.
- **Microphone "denied" in the app** — open system settings, long-press the
app icon → App info → Permissions → Microphone → Allow. Web-side prompt
does not always surface because the native layer intercepts it.
- **Foreground recording stops on newer Android** — the service must declare
`foregroundServiceType="microphone"` in `AndroidManifest.xml`.
- **JDK 17 only.** Newer JDK (21/25) breaks Android Gradle Plugin. Set
`org.gradle.java.home=/usr/lib/jvm/java-17-openjdk-amd64` in `~/.gradle/gradle.properties`
if the system default is different.
- **QEMU multi-arch Docker builds fail** with SIGILL on native modules (argon2).
Docker Hub workflow is x86-only. Use a native ARM runner if you need ARM64.
- **`npx cap` must run inside `mobile/`**, not repo root.
- **Foreground recording on Android 14+** requires `foregroundServiceType="microphone"`
in `AndroidManifest.xml` plus the 3-arg `startForeground(id, notif, TYPE_MICROPHONE)`.
Already applied.
- **Mic "denied" after permission grant** — WebView intercepts the prompt.
Fix: long-press app icon → App info → Permissions → Microphone → Allow.
## Files
| Path | Purpose |
|---|---|
| `mobile/capacitor.config.json` | app ID, name, WebView config, plugin opts |
| `mobile/src/` | launcher HTML (server URL entry) |
| `mobile/android/app/src/main/java/com/pedshub/scribe/MainActivity.java` | JS bridge + WebView mic permission |
| `mobile/android/app/src/main/java/com/pedshub/scribe/AudioRecordingService.java` | foreground service for background recording |
| `mobile/android/app/src/main/AndroidManifest.xml` | permissions, intents, backup rules |
| `.github/workflows/android-release.yml` | CI build |