Skip to content

Commit 4915dac

Browse files
jamesarichclaude
andauthored
ci: publish rolling -SNAPSHOT build of main to a GitHub prerelease (#6160)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f4a71a4 commit 4915dac

3 files changed

Lines changed: 111 additions & 10 deletions

File tree

.github/workflows/main-check.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,67 @@ jobs:
2323
run_unit_tests: false
2424
upload_artifacts: true
2525
secrets: inherit
26+
27+
# Republishes the debug APKs validate-and-build already produced as a rolling "snapshot"
28+
# prerelease that moves to HEAD on every push to main, so testers get a stable download
29+
# link instead of digging through Actions artifacts (which require a GitHub login and
30+
# expire after 7 days).
31+
publish-snapshot:
32+
needs: validate-and-build
33+
if: github.repository == 'meshtastic/Meshtastic-Android'
34+
runs-on: ubuntu-24.04-arm
35+
permissions:
36+
contents: write
37+
env:
38+
# CROWDIN_GITHUB_TOKEN (a PAT), not the default GITHUB_TOKEN, because the repo's tag
39+
# rulesets block the default token from creating/deleting tags — same token every other
40+
# tag-touching workflow here uses.
41+
GH_TOKEN: ${{ secrets.CROWDIN_GITHUB_TOKEN }}
42+
steps:
43+
- name: Checkout code
44+
uses: actions/checkout@v7.0.0
45+
with:
46+
fetch-depth: 0 # git rev-list --count needs full history for the versionCode
47+
token: ${{ secrets.CROWDIN_GITHUB_TOKEN }}
48+
persist-credentials: false # no git push here; gh does the authed work via GH_TOKEN
49+
50+
- name: Download debug APKs
51+
uses: actions/download-artifact@v8
52+
with:
53+
name: app-debug-apks
54+
path: artifacts
55+
56+
# A moving tag reuses the same release URL forever, and Obtainium fingerprints the
57+
# asset URL — so without a changing filename it would never detect a new build.
58+
# Embed the (monotonic) versionCode in each APK name; same formula the app build uses.
59+
- name: Rename APKs with versionCode
60+
run: |
61+
COMMIT_COUNT=$(git rev-list --count HEAD)
62+
OFFSET=$(grep '^VERSION_CODE_OFFSET=' config.properties | cut -d'=' -f2)
63+
VERSION_CODE=$((COMMIT_COUNT + OFFSET))
64+
echo "VERSION_CODE=$VERSION_CODE" >> "$GITHUB_ENV"
65+
mkdir -p upload
66+
find artifacts -name '*.apk' | while read -r f; do
67+
cp "$f" "upload/$(basename "$f" .apk)-${VERSION_CODE}.apk"
68+
done
69+
ls -l upload
70+
71+
# Delete the previous snapshot release AND its tag, then recreate both at HEAD. This
72+
# prunes the now-stale (differently-named) APKs so they don't pile up, and sidesteps the
73+
# Releases API refusing to retarget an already-existing tag.
74+
- name: Remove previous snapshot release
75+
run: gh release delete snapshot --yes --cleanup-tag || true
76+
77+
- name: Publish snapshot release
78+
run: |
79+
cat > notes.md <<EOF
80+
Automated debug build from the latest commit on \`main\` ($GITHUB_SHA), versionCode $VERSION_CODE.
81+
Unsigned/debug-keyed, F-Droid and Google flavors. Not for production use — this release is replaced on every push to main.
82+
83+
**Obtainium:** enable *Include prereleases*. Each build's APK filename carries the versionCode, so updates are detected.
84+
EOF
85+
gh release create snapshot upload/*.apk \
86+
--title "Snapshot $VERSION_CODE ($GITHUB_SHA)" \
87+
--target "$GITHUB_SHA" \
88+
--prerelease \
89+
--notes-file notes.md

.github/workflows/reusable-check.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,14 @@ jobs:
488488
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
489489
cache_read_only: ${{ needs.setup.outputs.cache_read_only }}
490490

491+
# ponytail: -SNAPSHOT only on main pushes, so PR/merge-queue debug builds keep the plain
492+
# base version. Publishing the resulting APKs is main-check.yml's job, not this one.
493+
- name: Tag main-branch builds as -SNAPSHOT
494+
if: github.event_name == 'push'
495+
run: |
496+
BASE=$(grep '^VERSION_NAME_BASE=' config.properties | cut -d'=' -f2)
497+
echo "VERSION_NAME=${BASE}-SNAPSHOT" >> "$GITHUB_ENV"
498+
491499
- name: Build Android APKs
492500
run: ./gradlew androidApp:assembleFdroidDebug androidApp:assembleGoogleDebug -Pci=true --parallel --configuration-cache --continue
493501

obtainium-test-builds.md

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,21 @@ release is published (un-drafted) when a build is promoted to closed or higher.
2323
| **stable** | Production | published, marked *Latest* | ✅ Yes |
2424
| **open** beta | Beta (Open) | published prerelease, tag `vX.Y.Z-open.N` | ✅ Yes |
2525
| **closed** beta | Alpha (Closed) | published prerelease, tag `vX.Y.Z-closed.N` | ✅ Yes |
26-
27-
Only **one** test build is "live" on GitHub at a time: as a build is promoted,
28-
its release object moves forward (its tag changes from `-closed.N` to `-open.N`
29-
to the clean production tag). So a `-closed`/`-open` build is installable only
30-
while it is currently parked in that channel — once promoted onward, the old
31-
channel tag no longer has a release.
26+
| **snapshot** | — (not on Play) | rolling prerelease, tag `snapshot` | ✅ Yes |
27+
28+
Only **one** promoted test build is "live" on GitHub at a time: as a build is
29+
promoted, its release object moves forward (its tag changes from `-closed.N` to
30+
`-open.N` to the clean production tag). So a `-closed`/`-open` build is
31+
installable only while it is currently parked in that channel — once promoted
32+
onward, the old channel tag no longer has a release.
33+
34+
**Snapshot** is different: it's an automated debug build of the latest commit on
35+
`main`, rebuilt and re-published under the single moving `snapshot` tag on every
36+
push. It never goes to Play. Because debug builds use a `.debug` application-ID
37+
suffix (`com.geeksville.mesh.fdroid.debug` / `com.geeksville.mesh.google.debug`)
38+
and the debug signing key, a snapshot installs as its **own separate app** — it
39+
sits alongside a Play/stable/beta install, so the uninstall-first warning above
40+
does **not** apply to it.
3241

3342
## Setup
3443

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

63-
### Bleeding edge (newest test build, any channel)
72+
### Bleeding edge (newest promoted test build, any channel)
6473

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

69-
Obtainium installs the newest published prerelease — whatever is currently in
70-
open or closed.
78+
Obtainium installs the newest promoted prerelease — whatever is currently in
79+
open or closed. The title filter is required to skip the always-newer `snapshot`
80+
prerelease; without it Obtainium would follow snapshot instead.
81+
82+
### Snapshot (latest commit on `main`)
83+
84+
- **Include prereleases:** on
85+
- **Filter release titles by regular expression:** `^Snapshot`
86+
- **Filter APKs by regular expression:** debug-signed names, see below
87+
88+
Follows `main` directly — updates on every push. These are **debug builds**
89+
(`.debug` package, debug key), so they install as a separate app and won't
90+
disturb a stable/beta install. The APKs are named `…-debug-<versionCode>.apk`
91+
(not `-release.apk`), so use debug-suffixed filters:
92+
93+
| You want | Regex |
94+
|---|---|
95+
| Google flavor, most phones (arm64) | `google-arm64-v8a-debug-\d+\.apk` |
96+
| fdroid flavor, most phones (arm64) | `fdroid-arm64-v8a-debug-\d+\.apk` |
97+
| fdroid flavor, one-size-fits-all | `fdroid-universal-debug-\d+\.apk` |
98+
99+
Snapshot releases attach only the debug APKs — no `.aab` or desktop installers.
71100

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

0 commit comments

Comments
 (0)