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:
parent
6365790e4b
commit
f6397ae635
2 changed files with 120 additions and 142 deletions
|
|
@ -1,68 +1,52 @@
|
||||||
# Contributing / Release workflow
|
# Contributing
|
||||||
|
|
||||||
## Commit message format
|
## Commit format
|
||||||
|
|
||||||
This repo uses [Conventional Commits](https://www.conventionalcommits.org/).
|
[Conventional Commits](https://www.conventionalcommits.org). `.github/workflows/auto-version.yml`
|
||||||
The `.github/workflows/auto-version.yml` workflow reads your commit
|
parses messages since the last semver tag and decides whether to bump.
|
||||||
messages and decides whether to cut a release automatically.
|
|
||||||
|
|
||||||
### Cheat sheet
|
| Prefix | Bump | |
|
||||||
|
|
||||||
| Prefix | Release type | Example |
|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `fix:` | patch (6.1.1 → 6.1.2) | `fix: correct age parser off-by-one for "4y 11m"` |
|
| `fix:` | patch | bug fix |
|
||||||
| `feat:` | minor (6.1.1 → 6.2.0) | `feat: add vitals quick-copy button` |
|
| `feat:` | minor | new feature |
|
||||||
| `feat!:` (or `BREAKING CHANGE:` in body) | major (6.1.1 → 7.0.0) | `feat!: require re-login after argon2 migration` |
|
| `feat!:` / `fix!:` / `BREAKING CHANGE:` in body | major | breaking change |
|
||||||
| `docs:` | no release | `docs: update mobile build guide` |
|
| `docs:` `refactor:` `chore:` `test:` `style:` `ci:` `build:` | none | no release |
|
||||||
| `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` |
|
|
||||||
|
|
||||||
**Only `fix:`, `feat:`, and `!:` / `BREAKING CHANGE` trigger a version bump and a release.**
|
Append `[skip ci]` to suppress the run for that commit.
|
||||||
Everything else is committed and pushed but doesn't tag.
|
|
||||||
|
|
||||||
### 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
|
```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
|
## What a tag push triggers
|
||||||
script) fires two parallel workflows:
|
|
||||||
|
|
||||||
| Workflow | Output | Time |
|
| Workflow | Output |
|
||||||
|---|---|---|
|
|---|---|
|
||||||
| `android-release.yml` | signed `pedscribe-X.Y.Z.apk` on the GitHub release | ~8 min |
|
| `android-release.yml` | signed APK on GitHub release, `make_latest=true` |
|
||||||
| `docker-publish.yml` | `danielonyejesi/pediatric-ai-scribe-v3:X.Y.Z` + `:latest` on Docker Hub | ~4 min |
|
| `docker-publish.yml` | `danielonyejesi/pediatric-ai-scribe-v3:{version,latest}` on Docker Hub (amd64) |
|
||||||
|
|
||||||
Obtanium users, Docker Hub subscribers, and the login page's
|
## Local dev
|
||||||
"Download APK" link all update without further action.
|
|
||||||
|
```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`.
|
||||||
|
|
|
||||||
|
|
@ -1,115 +1,109 @@
|
||||||
# Mobile App Build & Release
|
# Mobile build & release
|
||||||
|
|
||||||
Capacitor wrappers for **PedScribe** (this repo) and **PedsHub Quiz**
|
Capacitor 6 wrapper. Android only today; iOS project exists but requires macOS
|
||||||
(`/home/danvics/docker/quiz`). Both ship as Android APKs and iOS builds.
|
+ Xcode to produce an `.ipa`.
|
||||||
|
|
||||||
## One-time setup
|
## One-time setup
|
||||||
|
|
||||||
- **Keystore** (reused for both apps):
|
### Keystore
|
||||||
```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
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/danvics/docker/ped-ai/mobile
|
keytool -genkeypair -v -keystore ~/pedscribe-release.jks \
|
||||||
npm install # picks up any new plugins
|
-keyalg RSA -keysize 2048 -validity 10000 -alias pedscribe
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 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
|
```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
|
npm install
|
||||||
npx cap sync android
|
npx cap sync android
|
||||||
cd android
|
cd android
|
||||||
./gradlew assembleRelease \
|
./gradlew assembleRelease \
|
||||||
-Pandroid.injected.signing.store.file=$HOME/pedscribe-release.jks \
|
-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.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
|
### Single-quote the password
|
||||||
`public/index.html` around line 114). Publishing a tagged release updates the
|
|
||||||
download link automatically — no site redeploy needed.
|
|
||||||
|
|
||||||
```bash
|
Keystore passwords with shell metacharacters (`)`, `$`, `!`, space, etc.) must
|
||||||
cd /home/danvics/docker/ped-ai
|
be single-quoted. Backslash line continuations get eaten by some terminal
|
||||||
gh release create v6.0.1 \
|
paste handlers — prefer one-line commands.
|
||||||
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."
|
|
||||||
```
|
|
||||||
|
|
||||||
For the quiz app:
|
## Reinstall on device
|
||||||
```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
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
adb install -r android/app/build/outputs/apk/release/app-release.apk
|
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
|
- **JDK 17 only.** Newer JDK (21/25) breaks Android Gradle Plugin. Set
|
||||||
from the repo root.
|
`org.gradle.java.home=/usr/lib/jvm/java-17-openjdk-amd64` in `~/.gradle/gradle.properties`
|
||||||
- **`Keystore was tampered with`** — wrong password. Do not generate a new
|
if the system default is different.
|
||||||
keystore unless you are ready to rotate the signing identity on Play Store.
|
- **QEMU multi-arch Docker builds fail** with SIGILL on native modules (argon2).
|
||||||
- **Microphone "denied" in the app** — open system settings, long-press the
|
Docker Hub workflow is x86-only. Use a native ARM runner if you need ARM64.
|
||||||
app icon → App info → Permissions → Microphone → Allow. Web-side prompt
|
- **`npx cap` must run inside `mobile/`**, not repo root.
|
||||||
does not always surface because the native layer intercepts it.
|
- **Foreground recording on Android 14+** requires `foregroundServiceType="microphone"`
|
||||||
- **Foreground recording stops on newer Android** — the service must declare
|
in `AndroidManifest.xml` plus the 3-arg `startForeground(id, notif, TYPE_MICROPHONE)`.
|
||||||
`foregroundServiceType="microphone"` in `AndroidManifest.xml`.
|
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 |
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue