MapSwipe Mobile releases are tag-driven, and deploy.sh at
the repo root is the supported way to cut one. It has two modes:
| Mode | Run from | Tag | CI it triggers |
|---|---|---|---|
| production | develop only |
vX.Y.Z |
staging build → manual approval → production release |
| test | any branch | test-vX.Y.Z-bN |
staging-only build, no approval |
You type the version and build number by hand in both modes; the script only warns on regressions and on version changes during test releases.
⚠️ A production tag starts a real dual-platform release whose production stage waits for a manual approval. A test tag only ever produces a staging build for internal testing.
./deploy.shBoth modes share the same flow:
- Release type —
1) productionor2) test. - Preconditions — checked up front; the script aborts before changing anything if they fail (see each mode below).
- Version — free-form, validated as strict
X.Y.Z(current value shown). - Build number — free-form integer (current value shown).
- Description — one line; becomes the commit subject suffix and the tag's annotation message.
- Confirmation — a summary box (plus any warnings) is shown; type
yesto proceed, anything else aborts with no changes.
| Action | Detail |
|---|---|
Sets version |
in package.json, app.prod.json, app.staging.json |
Sets ios.buildNumber |
in app.prod.json, app.staging.json |
| Commits | production: release(vX.Y.Z): <desc> · test: test(vX.Y.Z-bN): <desc> |
| Tags (annotated) | production: vX.Y.Z · test: test-vX.Y.Z-bN |
| Pushes | production: develop + tag · test: current branch + tag |
- Version regression — new version
<current. - Build regression — same version and new build
≤current (TestFlight / Play reject non-increasing builds within a version). - Version change in test mode — a test release that changes the version.
That change lands on
develop(directly if you're on it, or via merge from a feature branch); version bumps belong to production releases.
Prerequisites (all enforced by the script):
- On
develop. - Clean working tree.
- Local
developexactly in sync withorigin/develop(not ahead, behind, or diverged) —git pullfirst if needed. - The three version files already agree, and the target
vX.Y.Ztag is unused. - Push access to
originand permission to approve theproductionEnvironment.
The vX.Y.Z tag triggers two workflows in parallel:
Each runs in two stages:
tag vX.Y.Z pushed
├── release-android.yml : build-staging (staging) ──▶ build-production (production)
│ ▲ manual approval
└── release-ios.yml : build-staging (staging) ──▶ build-production (production)
▲ manual approval
Stage 1 — Staging (automatic).
- Android: signed staging APK (
org.missingmaps.mapswipe.dev), published as a pre-releasevX.Y.Z-betawith the APK attached. - iOS: signed staging IPA uploaded as a workflow artifact.
Stage 2 — Production (build-production
targets the protected production Environment and will not run until approved:
- Open the Actions tab and click into the running run for your tag.
- The
build-productionjob waits with a “Review deployments” button. - Approve and deploy. (iOS and Android are separate workflows — approve each.)
Once approved:
- Android: production APK (
org.missingmaps.mapswipe) → final GitHub ReleasevX.Y.Zwith the APK attached. - iOS: production IPA uploaded as a workflow artifact — download it and upload to App Store Connect manually (no automatic TestFlight/App Store push yet).
For putting a build on a device without going through the production pipeline — QA a feature branch, iterate on a TestFlight build, etc. Runs from any branch.
Prerequisites:
- Clean working tree.
- The target
test-vX.Y.Z-bNtag is unused (re-using the same version + build number aborts — this is what stops duplicate builds). - If the branch is behind
origin/develop, the script warns ("N commits behind — testing against a stale base") and asks you to confirm; it does not block.
The test-* tag triggers the staging-only workflows (no production, no
approval):
Both build the staging variant (APP_ENV=staging, the .dev app, against
the staging database):
- Android: signed staging APK, published as a pre-release named after the tag
(
test-vX.Y.Z-bN) with the APK attached — download and sideload it. - iOS: app-store IPA uploaded as a workflow artifact. Download it and upload to TestFlight manually (CI-side TestFlight upload is a planned follow-up). This is why you set the build number by hand — TestFlight requires it to increase for each upload of the same version.
Test releases commit to your branch, so squash-merge the branch into
develop to collapse those test(...) commits. Note: squashing removes the
commits but keeps the net file diff — so the buildNumber value still lands
on develop. That's harmless (the next production release resets it via a fresh
version, and develop's CI only lint/typechecks). But a version change would
leak, which is exactly what warning #3 exists to catch — keep test releases to
build-number-only unless you mean it.
A test release run from develop itself commits straight to develop (no squash
to clean it up) — prefer a feature branch for iterative test builds.
The tag triggers everything, so undoing a release means removing the tag (and any releases it created). For production, do this before approving the prod stage:
# production
git tag -d vX.Y.Z && git push origin :refs/tags/vX.Y.Z
gh release delete vX.Y.Z-beta --yes
gh release delete vX.Y.Z --yes
# test
git tag -d test-vX.Y.Z-bN && git push origin :refs/tags/test-vX.Y.Z-bN
gh release delete test-vX.Y.Z-bN --yesTo cancel only the production build, reject the deployment in the Actions "Review deployments" dialog instead.
- Release notes: GitHub Release bodies come from a hardcoded template in the
workflows — your
deploy.shdescription is not included there (it lives in the commit and tag). - iOS uploads are manual: both production and test IPAs are workflow artifacts; there's no automated TestFlight/App Store Connect upload yet.
- Android
versionCode: neither app config setsandroid.versionCode, so Expo prebuild uses its default (1). Google Play rejects re-uploads with a non-incrementingversionCode— set/increment it before shipping to Play. - Test-tag cleanup: test pre-releases/tags accumulate; prune them manually for now.