15 — Release runbook
This is the operational checklist for shipping a change to the staging testers. It assumes you’ve read note 14 for the repo/branch/environment map. There is no CI release pipeline — every build happens on an engineer’s laptop.
Two halves move independently:
- Backend (Frappe app) — push to
development, staging auto-pulls and migrates. - Mobile (Flutter app) — build the APK locally, upload to Firebase, notify testers.
If a release only touches one half, you only run that half.
A. Backend release (Frappe app)
Repo: frappe_mform_swasti · Staging branch: development → auto-deploys to stg.swasti.mform.in.
Branch policy:
developmentis the only branch that accepts direct pushes and auto-deploys to staging.master→ uat is PR-only. Never push straight to uat/main.
- Commit your changes. Doctype JSON, controllers, patches, fixtures.
- Push to
development. Staging’s autopull picks it up and runsbench migrateautomatically — no need to ping DevOps if the migration is clean. - Wait ~2–5 min, then verify. Probe the API for any new doctype:
POST /api/method/login usr=Administrator&pwd=<staging pwd> GET /api/method/frappe.client.get_count?doctype=<New Doctype>200= the doctype migrated.404= it didn’t (see the checklist below).
Adding a NEW doctype? Three things must travel together
A new doctype is invisible to the mobile app until all three are in place. Miss any one and the form will render but its dropdowns show “No options available” (the app couldn’t fetch the doctype’s metaJson).
| # | What | Where |
|---|---|---|
| 1 | The doctype JSON + controller + __init__.py | mform_swasti/<module>/doctype/<name>/ |
| 2 | A patch granting Mobile User + Swasti Surveyor read/create perms | patches/v3_0/_NNN_*.py + patches.txt |
| 3 | The same patch appends the doctype to the Mobile Configuration manifest (table_lwis) | same patch |
The SDK only pulls metaJson for doctypes listed in the Mobile Configuration manifest. No manifest row → no meta on the device → empty dropdowns. (Perms baked into the doctype JSON are not enough on their own — the manifest entry is what triggers the metaJson sync.)
Migrate-failure checklist
If bench migrate fails on staging (or new doctypes stay 404), it’s almost always one of these — all seen on this project:
ModuleNotFoundErrorfor a patch → the patch.pyis listed inpatches.txtbut the file was never committed. Rungit ls-filesand confirm every patch inpatches.txtis tracked.- A doctype’s files are untracked → the JSON returns
404but exists locally. Same fix: commit the untracked files. Unknown column/OperationalErrormid-migrate → a patch reads or writes a field that isn’t in the committed doctype JSON. One crashing patch aborts the entire migration, so every patch after it silently never runs. Confirm any column a patch touches is in a committed.json.- Patch ran but had no effect → check
Patch Log(list it filtered by your patch name). A patch only runs once; if it ran with a bug, fixing the code won’t re-run it — bump it to a new_NNNnumber or clear its Patch Log row.
Lesson from 27 May: a
Document Master.is_applyable_documentcolumn was added locally but never pushed. Patch_013read it, crashed on the unknown column, and aborted the migrate — so_014/_015never ran and two whole forms had empty dropdowns. The fix was to commit the field, not to touch the app.
B. Mobile release (Flutter app)
Repo: frappe_mform_swasti_app · builds are local, distribution is Firebase (staging) / Play Store via DevOps (uat/prod).
-
Build the staging APK (from the app root):
flutter build apk --flavor staging -t lib/main_staging.dartOutput:
build/app/outputs/flutter-apk/app-staging-debug.apk(~200 MB). -
Write release notes — plain-language, surveyor-facing, “what to test.” Keep a file (e.g.
/tmp/swasti-release-notes-<date>.txt). -
Upload to Firebase App Distribution:
firebase appdistribution:distribute \ build/app/outputs/flutter-apk/app-staging-release.apk \ --app "1:79062936320:android:cf185872e152002cae61e5" \ --groups "<group-alias[,group-alias…]>" \ --release-notes-file /tmp/swasti-release-notes-<date>.txtSuccess prints the release version + console/tester links. Testers in the named group(s) get a notification.
Which group(s) to distribute to
Two groups exist on the Firebase project (
swasti-mform-v3):Group alias Display name Who When to include dhwaniDhwani 7 internal testers (Abhijit, Ankit, Ritesh, Fahim, etc.) Every drop — internal dev/PM + Swasti PMs swasti-clientSwasti Client Testers Catalysts staff (shanmathi@, dharshini.m@, santosh.r@) Stable drops only — once the change has been verified on the Vivo / dhwani round and the KB tracker isn’t in the middle of a triage burst Default for an in-flight triage commit:
--groups "dhwani". Default for a stable / weekly-summary drop:--groups "dhwani,swasti-client". Don’t fire client drops for every internal fix — they’ll get notification fatigue and the App-Tester install backlog grows.Adding new testers
firebase appdistribution:testers:add <email1> [<email2>…] \ --group-alias <dhwani|swasti-client> \ --project swasti-mform-v3Adding a new group
firebase appdistribution:group:create "<Display Name>" <alias> \ --project swasti-mform-v3 -
Notify the testers on Teams — post the highlights to the mForm V3 ↔ Swasti group chat (always send as HTML). Lead with what’s new and what to test, not the changelog.
Direct install for your own device (skip Firebase)
adb -s <device-id> install -r -d build/app/outputs/flutter-apk/app-staging-debug.apk
C. After the release
- New doctypes need a sync on-device. Once the manifest is updated server-side, the tester must pull-to-refresh / tap the Home sync pill to pull the new
metaJson. Dropdowns populate after that sync, not on first launch. - Log feedback in the tracker. Triage each batch with the PM before fixing — not every report is in scope.
Standing gaps
- No
release.shwrapper yet — the build/upload/notify steps are run by hand. - No CI. Builds depend on one laptop’s Flutter + Android toolchain.
- The mobile SDK does not yet
ALTERexistingdocs__tables when a synced doctype gains a column — a device that already synced an older schema may need a reinstall to pick up new columns (tracked separately).