Skip to content

fix(hud): keep phone WiFi lock asserted across the dial loop #634

fix(hud): keep phone WiFi lock asserted across the dial loop

fix(hud): keep phone WiFi lock asserted across the dial loop #634

Workflow file for this run

name: Branch builds
# On every push to a non-main branch, build the phone APK, the Wear OS APK
# (when the branch has a wear/ module), and the Garmin Connect IQ artifacts
# (when GARMIN_SDK_BUNDLE_URL is configured), then replace the rolling
# pre-release tagged `branch-<name>` with the freshly built assets.
# Testers always get the latest commit by visiting the same release URL.
#
# Output naming (device-first, matching the Gradle module outputs
# phone-debug.apk / wearos-debug.apk, and consistent with release-apk.yml):
# phone-<branch>-<short_sha>.apk
# wearos-<branch>-<short_sha>.apk
# hud-<branch>-<short_sha>.apk
# garmin-<branch>-<short_sha>.iq (multi-device Connect IQ bundle)
# garmin-<branch>-<short_sha>-prgs.zip (per-device .prg files bundled)
#
# Garmin CI is opt-in: set the GARMIN_SDK_BUNDLE_URL repo secret to a stable
# download URL for the bundle produced by tools/bundle-ciq-sdk-for-ci.ps1.
# Without that secret, the garmin job exits as `skipped` and the phone/wear
# jobs keep working.
#
# Pull-request runs build the APK as a workflow artifact (downloadable from
# the Actions tab for ~90 days) but do not touch the release list, so PRs
# from forks can't push tags.
on:
push:
branches-ignore:
- main
tags-ignore:
- "**"
pull_request:
branches:
- "**"
workflow_dispatch:
permissions:
contents: write
jobs:
meta:
runs-on: ubuntu-latest
outputs:
branch: ${{ steps.meta.outputs.branch }}
short_sha: ${{ steps.meta.outputs.short_sha }}
tag: ${{ steps.meta.outputs.tag }}
suffix: ${{ steps.meta.outputs.suffix }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # full history so we can produce a "since main" log
- name: Compute build metadata
id: meta
run: |
BRANCH="${GITHUB_REF##*/}"
SHORT_SHA="$(git rev-parse --short HEAD)"
TAG="branch-${BRANCH}"
SUFFIX="${BRANCH}-${SHORT_SHA}"
echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT"
echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
android:
runs-on: ubuntu-latest
needs: meta
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v3
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Build debug APKs
# Build the phone APK on every branch. Build the wear + HUD APKs
# too when the branch has their modules, so testers can grab the
# whole stack from the same rolling release.
run: |
targets=":app:assembleDebug"
if [ -f wear/build.gradle.kts ]; then
targets="$targets :wear:assembleDebug"
fi
if [ -f hud/build.gradle.kts ]; then
targets="$targets :hud:assembleDebug"
fi
./gradlew :app:testDebugUnitTest :hud-protocol:test $targets --no-daemon --stacktrace
env:
GRADLE_OPTS: -Xmx4g -Dorg.gradle.jvmargs=-Xmx4g
- name: Rename APKs to canonical filenames
run: |
mkdir -p /tmp/out
cp app/build/outputs/apk/debug/phone-debug.apk \
"/tmp/out/phone-${{ needs.meta.outputs.suffix }}.apk"
if [ -f wear/build/outputs/apk/debug/wearos-debug.apk ]; then
cp wear/build/outputs/apk/debug/wearos-debug.apk \
"/tmp/out/wearos-${{ needs.meta.outputs.suffix }}.apk"
fi
if [ -f hud/build/outputs/apk/debug/hud-debug.apk ]; then
cp hud/build/outputs/apk/debug/hud-debug.apk \
"/tmp/out/hud-${{ needs.meta.outputs.suffix }}.apk"
fi
ls -la /tmp/out
- name: Upload as workflow artifacts (always)
uses: actions/upload-artifact@v4
with:
name: android-${{ needs.meta.outputs.short_sha }}
path: /tmp/out/*.apk
retention-days: 90
- name: Stage for release
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v4
with:
name: stage-android-${{ needs.meta.outputs.short_sha }}
path: /tmp/out/*.apk
retention-days: 1
garmin:
runs-on: ubuntu-latest
needs: meta
# Skip cleanly when the bundle URL secret isn't set, so the rest of the
# workflow still works on forks / first-time setups. Note: secrets can't
# be referenced directly in `if:`, so we route through an env var.
env:
GARMIN_SDK_BUNDLE_URL: ${{ secrets.GARMIN_SDK_BUNDLE_URL }}
steps:
- name: Skip if bundle URL not configured
id: precheck
run: |
if [ -z "$GARMIN_SDK_BUNDLE_URL" ]; then
echo "::notice::GARMIN_SDK_BUNDLE_URL secret not set; skipping Garmin build."
echo "::notice::See tools/bundle-ciq-sdk-for-ci.ps1 to enable."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Checkout
if: steps.precheck.outputs.skip == 'false'
uses: actions/checkout@v4
- name: Set up JDK 17 (monkeyc runs on a JRE)
if: steps.precheck.outputs.skip == 'false'
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- name: Download CIQ SDK bundle
if: steps.precheck.outputs.skip == 'false'
run: |
mkdir -p /tmp/ciq
# GH release URLs are public for public repos; if you host on a
# private release, prepend `Authorization: token ${{ secrets.GITHUB_TOKEN }}`
# via -H. The bundle is the zip produced by bundle-ciq-sdk-for-ci.ps1.
curl -fL --retry 3 -o /tmp/ciq/bundle.zip "$GARMIN_SDK_BUNDLE_URL"
unzip -q /tmp/ciq/bundle.zip -d /tmp/ciq
test -x /tmp/ciq/ciq-bundle/sdk/bin/monkeyc || chmod +x /tmp/ciq/ciq-bundle/sdk/bin/monkeyc /tmp/ciq/ciq-bundle/sdk/bin/monkeydo
# monkeyc hard-codes ~/.Garmin/ConnectIQ/Devices as the device-
# profile root — symlink the bundled devices there so monkeyc
# finds every manifest entry. Otherwise it errors with "Device
# ids ... not recognized" for everything in manifest.xml.
mkdir -p "$HOME/.Garmin/ConnectIQ"
ln -sfn /tmp/ciq/ciq-bundle/devices "$HOME/.Garmin/ConnectIQ/Devices"
ls /tmp/ciq/ciq-bundle/devices | head -5
- name: Generate one-off developer key
if: steps.precheck.outputs.skip == 'false'
# The developer key only identifies the build to the CIQ Store; for
# sideloads (which is all CI produces) a fresh per-run key works fine
# and avoids checking the real key into secrets. The store-submission
# build still happens locally with the user's persistent key.
run: |
/tmp/ciq/ciq-bundle/sdk/bin/monkeybrains.jar 2>/dev/null || true
openssl genrsa -out /tmp/ciq/dev_key.pem 4096
openssl pkcs8 -topk8 -inform PEM -outform DER -in /tmp/ciq/dev_key.pem -out /tmp/ciq/dev_key.der -nocrypt
- name: Build .iq (multi-device bundle)
if: steps.precheck.outputs.skip == 'false'
run: |
mkdir -p /tmp/out
export PATH="/tmp/ciq/ciq-bundle/sdk/bin:$PATH"
# monkeyc resolves device profiles relative to the CIQ_DEVICES env
# var; point it at the bundled directory so it doesn't go looking
# for ~/.Garmin/ConnectIQ/Devices on the runner.
export CIQ_HOME=/tmp/ciq/ciq-bundle/sdk
export GARMIN_DEVICE_DIR=/tmp/ciq/ciq-bundle/devices
OUT="/tmp/out/garmin-${{ needs.meta.outputs.suffix }}.iq"
monkeyc \
-f garmin-watch-app/monkey.jungle \
-o "$OUT" \
-y /tmp/ciq/dev_key.der \
-e \
-w
ls -la "$OUT"
- name: Build per-device .prg files
if: steps.precheck.outputs.skip == 'false'
run: |
export PATH="/tmp/ciq/ciq-bundle/sdk/bin:$PATH"
export CIQ_HOME=/tmp/ciq/ciq-bundle/sdk
export GARMIN_DEVICE_DIR=/tmp/ciq/ciq-bundle/devices
# Stage per-device .prgs in /tmp/prgs first, then zip the lot
# into one garmin-<suffix>-prgs.zip. The branch's rolling pre-
# release used to carry 135 individual .prg files alongside the
# .iq, which buried the .iq under a wall of asset rows. The .iq
# is the universal artifact (multi-device, Connect IQ Store
# format); .prgs are advanced-sideload-only — bundling them keeps
# the release page scannable.
mkdir -p /tmp/prgs
for d in venu2 venu3 fenix843mm fenix847mm fenix6xpro fenix8solar47mm epix2pro47mm instinct2 instinct2s instinct2x instinct3amoled45mm instinct3amoled50mm instinct3solar45mm instinctcrossover instinctcrossoveramoled instincte40mm instincte45mm; do
OUT="/tmp/prgs/garmin-${{ needs.meta.outputs.suffix }}-${d}.prg"
echo "--- building $d ---"
monkeyc \
-f garmin-watch-app/monkey.jungle \
-o "$OUT" \
-y /tmp/ciq/dev_key.der \
-d "$d" \
-w
done
(cd /tmp/prgs && zip -q "/tmp/out/garmin-${{ needs.meta.outputs.suffix }}-prgs.zip" *.prg)
ls -la /tmp/out
- name: Upload as workflow artifacts (always)
if: steps.precheck.outputs.skip == 'false'
uses: actions/upload-artifact@v4
with:
name: garmin-${{ needs.meta.outputs.short_sha }}
path: /tmp/out/*
retention-days: 90
- name: Stage for release
if: steps.precheck.outputs.skip == 'false' && github.event_name != 'pull_request'
uses: actions/upload-artifact@v4
with:
name: stage-garmin-${{ needs.meta.outputs.short_sha }}
path: /tmp/out/*
retention-days: 1
publish:
runs-on: ubuntu-latest
needs: [meta, android, garmin]
# Run even if garmin was skipped, but not for PRs.
if: always() && github.event_name != 'pull_request' && needs.android.result == 'success'
steps:
- name: Checkout (for release body)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download staged artifacts
uses: actions/download-artifact@v4
with:
path: /tmp/stage
pattern: stage-*
merge-multiple: true
- name: Compose release body
id: body
run: |
BRANCH="${{ needs.meta.outputs.branch }}"
SHORT_SHA="${{ needs.meta.outputs.short_sha }}"
# Pull per-branch description if BRANCH.md exists.
if [ -f BRANCH.md ]; then
cp BRANCH.md /tmp/branch-notes.md
else
cat > /tmp/branch-notes.md <<EOF
# ${BRANCH}
No \`BRANCH.md\` found at repo root. Add one to describe what this
branch is for and who should test it.
EOF
fi
git log --pretty=format:'- %h %s' main..HEAD | head -50 > /tmp/commit-log.md || true
{
echo "**Pre-release from branch \`${BRANCH}\`**, automatic build at \`${SHORT_SHA}\`."
echo
echo "This is a CI-built debug APK. It can't update an existing Play Store install in place; uninstall the Play version first to install this build."
echo
cat /tmp/branch-notes.md
echo
echo "## Recent commits"
echo
cat /tmp/commit-log.md
} > /tmp/release-body.md
ls -la /tmp/stage
- name: Strip prior assets from rolling release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# action-gh-release attaches new files but doesn't remove previous
# builds; without this the rolling pre-release accumulates one set
# of artifacts per push.
tag="${{ needs.meta.outputs.tag }}"
if gh release view "$tag" >/dev/null 2>&1; then
gh release view "$tag" --json assets \
--jq '.assets[] | select(.name | test("\\.(apk|aab|iq|prg|zip)$")) | .name' \
| while read -r asset; do
[ -n "$asset" ] && gh release delete-asset "$tag" "$asset" --yes || true
done
fi
- name: Replace rolling pre-release with new build
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.meta.outputs.tag }}
name: ${{ needs.meta.outputs.tag }} (${{ needs.meta.outputs.short_sha }})
body_path: /tmp/release-body.md
prerelease: true
make_latest: "false"
files: /tmp/stage/*
- name: Announce build on Telegram
# Posts each branch build into the @EUCPlanetApp "Github" topic. Opt-in
# via the TELEGRAM_* secrets; failures never break the build (|| true).
# The commit message is passed via env (never inlined) to avoid
# expression injection.
env:
BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
CHAT_ID: ${{ secrets.TELEGRAM_RELEASES_CHAT_ID }}
THREAD_ID: ${{ secrets.TELEGRAM_RELEASES_THREAD_ID }}
GH_REPO: ${{ github.repository }}
SERVER_URL: ${{ github.server_url }}
BRANCH: ${{ needs.meta.outputs.branch }}
SHORT_SHA: ${{ needs.meta.outputs.short_sha }}
TAG: ${{ needs.meta.outputs.tag }}
COMMIT_MSG: ${{ github.event.head_commit.message }}
run: |
if [ -z "$BOT_TOKEN" ] || [ -z "$CHAT_ID" ] || [ -z "$THREAD_ID" ]; then
echo "::notice::Telegram secrets not set; skipping announcement."; exit 0
fi
url="${SERVER_URL}/${GH_REPO}/releases/tag/${TAG}"
subject=$(printf '%s' "$COMMIT_MSG" | head -n1)
esc() { printf '%s' "$1" | sed -e 's/&/\&amp;/g' -e 's/</\&lt;/g' -e 's/>/\&gt;/g'; }
text="🔨 <b>Build</b> on <code>$(esc "$BRANCH")</code> @ <code>$(esc "$SHORT_SHA")</code>
$(esc "$subject")
⬇️ Latest build (phone, Wear OS, HUD, Garmin):
${url}"
curl -sS -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
--data-urlencode "chat_id=${CHAT_ID}" \
-d "message_thread_id=${THREAD_ID}" \
--data-urlencode "text=${text}" \
-d "parse_mode=HTML" -d "disable_web_page_preview=true" \
-o /dev/null -w "Telegram HTTP %{http_code}\n" || true