Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/main-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,67 @@ jobs:
run_unit_tests: false
upload_artifacts: true
secrets: inherit

# Republishes the debug APKs validate-and-build already produced as a rolling "snapshot"
# prerelease that moves to HEAD on every push to main, so testers get a stable download
# link instead of digging through Actions artifacts (which require a GitHub login and
# expire after 7 days).
publish-snapshot:
needs: validate-and-build
if: github.repository == 'meshtastic/Meshtastic-Android'
runs-on: ubuntu-24.04-arm
permissions:
contents: write
env:
# CROWDIN_GITHUB_TOKEN (a PAT), not the default GITHUB_TOKEN, because the repo's tag
# rulesets block the default token from creating/deleting tags — same token every other
# tag-touching workflow here uses.
GH_TOKEN: ${{ secrets.CROWDIN_GITHUB_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v7.0.0
with:
fetch-depth: 0 # git rev-list --count needs full history for the versionCode
token: ${{ secrets.CROWDIN_GITHUB_TOKEN }}
Comment thread
jamesarich marked this conversation as resolved.
persist-credentials: false # no git push here; gh does the authed work via GH_TOKEN

- name: Download debug APKs
uses: actions/download-artifact@v8
with:
name: app-debug-apks
path: artifacts

# A moving tag reuses the same release URL forever, and Obtainium fingerprints the
# asset URL — so without a changing filename it would never detect a new build.
# Embed the (monotonic) versionCode in each APK name; same formula the app build uses.
- name: Rename APKs with versionCode
run: |
COMMIT_COUNT=$(git rev-list --count HEAD)
OFFSET=$(grep '^VERSION_CODE_OFFSET=' config.properties | cut -d'=' -f2)
VERSION_CODE=$((COMMIT_COUNT + OFFSET))
echo "VERSION_CODE=$VERSION_CODE" >> "$GITHUB_ENV"
mkdir -p upload
find artifacts -name '*.apk' | while read -r f; do
cp "$f" "upload/$(basename "$f" .apk)-${VERSION_CODE}.apk"
done
ls -l upload

# Delete the previous snapshot release AND its tag, then recreate both at HEAD. This
# prunes the now-stale (differently-named) APKs so they don't pile up, and sidesteps the
# Releases API refusing to retarget an already-existing tag.
- name: Remove previous snapshot release
run: gh release delete snapshot --yes --cleanup-tag || true

- name: Publish snapshot release
run: |
cat > notes.md <<EOF
Automated debug build from the latest commit on \`main\` ($GITHUB_SHA), versionCode $VERSION_CODE.
Unsigned/debug-keyed, F-Droid and Google flavors. Not for production use — this release is replaced on every push to main.

**Obtainium:** enable *Include prereleases*. Each build's APK filename carries the versionCode, so updates are detected.
EOF
gh release create snapshot upload/*.apk \
--title "Snapshot $VERSION_CODE ($GITHUB_SHA)" \
--target "$GITHUB_SHA" \
--prerelease \
--notes-file notes.md
8 changes: 8 additions & 0 deletions .github/workflows/reusable-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,14 @@ jobs:
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
cache_read_only: ${{ needs.setup.outputs.cache_read_only }}

# ponytail: -SNAPSHOT only on main pushes, so PR/merge-queue debug builds keep the plain
# base version. Publishing the resulting APKs is main-check.yml's job, not this one.
- name: Tag main-branch builds as -SNAPSHOT
if: github.event_name == 'push'
run: |
BASE=$(grep '^VERSION_NAME_BASE=' config.properties | cut -d'=' -f2)
echo "VERSION_NAME=${BASE}-SNAPSHOT" >> "$GITHUB_ENV"

- name: Build Android APKs
run: ./gradlew androidApp:assembleFdroidDebug androidApp:assembleGoogleDebug -Pci=true --parallel --configuration-cache --continue

Expand Down
49 changes: 39 additions & 10 deletions obtainium-test-builds.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,21 @@ release is published (un-drafted) when a build is promoted to closed or higher.
| **stable** | Production | published, marked *Latest* | ✅ Yes |
| **open** beta | Beta (Open) | published prerelease, tag `vX.Y.Z-open.N` | ✅ Yes |
| **closed** beta | Alpha (Closed) | published prerelease, tag `vX.Y.Z-closed.N` | ✅ Yes |

Only **one** test build is "live" on GitHub at a time: as a build is promoted,
its release object moves forward (its tag changes from `-closed.N` to `-open.N`
to the clean production tag). So a `-closed`/`-open` build is installable only
while it is currently parked in that channel — once promoted onward, the old
channel tag no longer has a release.
| **snapshot** | — (not on Play) | rolling prerelease, tag `snapshot` | ✅ Yes |

Only **one** promoted test build is "live" on GitHub at a time: as a build is
promoted, its release object moves forward (its tag changes from `-closed.N` to
`-open.N` to the clean production tag). So a `-closed`/`-open` build is
installable only while it is currently parked in that channel — once promoted
onward, the old channel tag no longer has a release.

**Snapshot** is different: it's an automated debug build of the latest commit on
`main`, rebuilt and re-published under the single moving `snapshot` tag on every
push. It never goes to Play. Because debug builds use a `.debug` application-ID
suffix (`com.geeksville.mesh.fdroid.debug` / `com.geeksville.mesh.google.debug`)
and the debug signing key, a snapshot installs as its **own separate app** — it
sits alongside a Play/stable/beta install, so the uninstall-first warning above
does **not** apply to it.

## Setup

Expand Down Expand Up @@ -60,14 +69,34 @@ channel tag no longer has a release.
- **Filter release titles by regular expression:** `-closed\.`
- **Filter APKs by regular expression:** see [Picking the APK](#picking-the-apk)

### Bleeding edge (newest test build, any channel)
### Bleeding edge (newest promoted test build, any channel)

- **Include prereleases:** on
- *(no release-title filter)*
- **Filter release titles by regular expression:** `-(closed|open)\.`
- **Filter APKs by regular expression:** see [Picking the APK](#picking-the-apk)

Obtainium installs the newest published prerelease — whatever is currently in
open or closed.
Obtainium installs the newest promoted prerelease — whatever is currently in
open or closed. The title filter is required to skip the always-newer `snapshot`
prerelease; without it Obtainium would follow snapshot instead.

### Snapshot (latest commit on `main`)

- **Include prereleases:** on
- **Filter release titles by regular expression:** `^Snapshot`
- **Filter APKs by regular expression:** debug-signed names, see below

Follows `main` directly — updates on every push. These are **debug builds**
(`.debug` package, debug key), so they install as a separate app and won't
disturb a stable/beta install. The APKs are named `…-debug-<versionCode>.apk`
(not `-release.apk`), so use debug-suffixed filters:

| You want | Regex |
|---|---|
| Google flavor, most phones (arm64) | `google-arm64-v8a-debug-\d+\.apk` |
| fdroid flavor, most phones (arm64) | `fdroid-arm64-v8a-debug-\d+\.apk` |
| fdroid flavor, one-size-fits-all | `fdroid-universal-debug-\d+\.apk` |

Snapshot releases attach only the debug APKs — no `.aab` or desktop installers.

> **If your channel filter finds nothing:** when no build is parked in that
> exact channel, the title filter matches no current release (old channel tags
Expand Down