Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
ef43096
feat(mobile): add skywire-mobile lite multicall core (playbook Step −1)
mrpalide Aug 3, 2026
2ffaae8
fix(mobile): stamp a semver-safe version into pkg/buildinfo for skywi…
mrpalide Aug 3, 2026
238f05d
feat(android): app skeleton — theme, splash, bottom bar, apps hub
mrpalide Aug 3, 2026
1f9ad95
feat(android): brand assets — Skycoin logo, typeface, designed top bar
mrpalide Aug 3, 2026
c572957
feat(android): core service — on-device config, Connect flow, log viewer
mrpalide Aug 3, 2026
962ec16
feat(android): SkySOCKS screen — discovery list, connect, expose-port
mrpalide Aug 4, 2026
89e3114
feat(android): primary transport, and the dmsg servers Home never s…
mrpalide Aug 4, 2026
66db69f
Merge branch 'develop' into skywire-mobile
mrpalide Aug 4, 2026
0525718
feat(android): Chat tab — skychat embedded, gated, and made to fit a …
mrpalide Aug 4, 2026
6526ca2
Merge branch 'develop' into skywire-mobile
mrpalide Aug 4, 2026
066e1d7
feat(mobile): open skychat:// links, and make voice calls reachable…
mrpalide Aug 5, 2026
4c69219
feat(mobile): give calls audio, a screen and a log; notify on messages
mrpalide Aug 5, 2026
6f7fd95
fix(mobile): show the call screen for outgoing calls, and on every tab
mrpalide Aug 5, 2026
89221e7
refactor(mobile): make the phone a generic sink for the notificatio…
mrpalide Aug 5, 2026
8734d9a
feat(skychat): move the address book to the visor so names reach ev…
mrpalide Aug 5, 2026
acc9434
Merge branch 'develop' into skywire-mobile
mrpalide Aug 5, 2026
c2daeef
feat(mobile): SkyDEX on the phone — market key entered natively, tr…
mrpalide Aug 5, 2026
bf5277c
fix(mobile): SkyDEX tables as cards, and a Cancel button that actua…
mrpalide Aug 5, 2026
e099355
feat(mobile): SkyVPN — the phone's TUN, on loan from Android
mrpalide Aug 5, 2026
aa7be46
feat(skychat): a Settings screen, and a chat that can move between de…
mrpalide Aug 5, 2026
1d704c0
feat(mobile): Fleet — the visors you run elsewhere, seen from the phone
mrpalide Aug 6, 2026
365c808
feat(mobile): Settings — the identity, the file that carries it, an…
mrpalide Aug 6, 2026
9c0f14b
feat(skychat): playback speed, and the media controls WebView will no…
mrpalide Aug 6, 2026
936e6f0
fix(skychat): a file in Saved Messages is kept, not sent to a key tha…
mrpalide Aug 6, 2026
a0dd08b
feat(mobile): Wallet — SKY, fiber coins and BTC, keys that never leav…
mrpalide Aug 6, 2026
54afbb3
feat(mobile): SkyChat wears the Wallet's design, and every tab wear…
mrpalide Aug 6, 2026
1468f36
feat(mobile): hardening — what leaves the phone, and what is readab…
mrpalide Aug 6, 2026
43e7435
feat(mobile): redesign — one language for every screen, and the clo…
mrpalide Aug 7, 2026
6088043
fix(dmsg,visor): two periodic jobs the phone was paying for, one of…
mrpalide Aug 7, 2026
a456956
fix(visor): the phone stops shipping the deployment's survey whitelist
mrpalide Aug 7, 2026
d5af587
ci(android): gate app checks on android/, release the APK from a mo…
mrpalide Aug 7, 2026
48af1a6
build(android): make android-apk takes a version, and owns the vers…
mrpalide Aug 7, 2026
2451bb8
feat(mobile): SkyVPN — rates that move, a route-length control, and …
mrpalide Aug 7, 2026
bb6ff22
fix(mobile): country names off a Locale.Builder, and guarded
mrpalide Aug 7, 2026
205c12e
fix(mobile): six from a test pass — contrast, the nav cloud, back, ba…
mrpalide Aug 7, 2026
e98d99c
feat(mobile): a second pass from use — hub jump, hero exit line, live…
mrpalide Aug 7, 2026
e08ceb2
feat(wallet): Ethereum — native ETH and ERC-20 tokens, USDT built in
mrpalide Aug 7, 2026
deb2486
fix(mobile): a polish pass from device use — icon, light cards, batt…
mrpalide Aug 7, 2026
0730e35
fix(mobile): SkyDEX follows the app's theme, and three things about i…
mrpalide Aug 7, 2026
3a98524
Merge branch 'develop' into skywire-mobile
mrpalide Aug 7, 2026
39fd652
Merge branch 'develop' into skywire-mobile
mrpalide Aug 9, 2026
090fcc7
fix(dmsg): publish the first client entry even when the read shows no…
mrpalide Aug 9, 2026
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
71 changes: 71 additions & 0 deletions .github/workflows/android-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Android APP lane: Kotlin unit tests + an APK that actually assembles.
#
# Gated on `android/**` so a pull request that does not touch the app costs
# nothing here. The Go side is NOT gated the same way and is not duplicated:
# the `android` job in test.yml builds the arm64 payload and enforces its size
# budget on every PR, which is what catches a Go change breaking the mobile
# variant. This lane is the other half — the app that wraps it.
name: Android app

on:
pull_request:
paths:
- 'android/**'
- '.github/workflows/android-app.yml'

# A new push to the same PR makes the previous run irrelevant.
concurrency:
group: android-app-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
fetch-tags: true

- uses: actions/setup-go@v6
with:
go-version: '1.26.5'
cache: true
cache-dependency-path: go.sum

# AGP 9 / Gradle 9.6 want a modern JDK; 21 is the LTS both are happy on.
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'

- uses: gradle/actions/setup-gradle@v4

# The pure-Go payload rather than the NDK one: this lane is a build
# check, and the cgo/bionic-DNS difference does not change whether the
# Kotlin compiles or the APK packages. Releases use the NDK lane.
# Built here even though test.yml also builds it, because an APK
# assembled without the .so is not the artifact we ship — packaging is
# part of what this job is checking.
- name: Build Go payload (pure-Go lane)
run: make android-mobile

# LiveNodeTest is skipped unless SKYWIRE_NET_TESTS=1, so this stays
# offline. :app has no unit tests yet; :wallet-core holds the crypto
# vectors and golden fixtures, which are the ones worth gating on.
- name: Unit tests
run: cd android && ./gradlew --no-daemon :wallet-core:test

- name: Assemble debug APK
run: cd android && ./gradlew --no-daemon :app:assembleDebug

# Kept for triage: when a run fails on a device-only problem, having the
# exact APK the job built beats rebuilding it locally and guessing.
- name: Upload debug APK
if: success()
uses: actions/upload-artifact@v4
with:
name: skywire-debug-apk
path: android/app/build/outputs/apk/debug/app-debug.apk
retention-days: 14
if-no-files-found: error
167 changes: 167 additions & 0 deletions .github/workflows/android-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Android release: push a `mobile-vX.Y.Z` tag, get a signed APK attached to a
# GitHub pre-release.
#
# The tag prefix keeps this lane separate from the desktop one: release.yml
# fires on `v*`, and `mobile-v1.0.0` does not match that, so tagging the phone
# never starts a Skywire release.
name: Android release

on:
push:
tags:
- 'mobile-v*'

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
fetch-tags: true

# Fail before doing ten minutes of work, and fail with the fix rather
# than a stack trace. Publishing an unsigned APK would mean shipping a
# file that cannot be installed; the debug-signed one is debuggable and
# must never be handed to users, least of all for an app holding wallet
# seeds. So an unsigned build is not a fallback here — it is the error.
- name: Require signing secrets
env:
KEYSTORE: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
STORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
KEY_PASS: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
missing=""
[ -n "$KEYSTORE" ] || missing="$missing ANDROID_KEYSTORE_BASE64"
[ -n "$STORE_PASS" ] || missing="$missing ANDROID_KEYSTORE_PASSWORD"
[ -n "$KEY_ALIAS" ] || missing="$missing ANDROID_KEY_ALIAS"
[ -n "$KEY_PASS" ] || missing="$missing ANDROID_KEY_PASSWORD"
if [ -n "$missing" ]; then
echo "::error::Missing repository secrets:$missing"
echo ""
echo "Create the signing key ONCE and keep it forever — Android"
echo "refuses to upgrade an app signed with a different key, so"
echo "this key is what lets every future release reach the people"
echo "who installed this one. Back it up somewhere you will still"
echo "have in five years."
echo ""
echo " keytool -genkeypair -v -keystore skywire-release.jks \\"
echo " -alias skywire -keyalg RSA -keysize 4096 -validity 10000"
echo ""
echo " gh secret set ANDROID_KEYSTORE_BASE64 < <(base64 -i skywire-release.jks)"
echo " gh secret set ANDROID_KEYSTORE_PASSWORD"
echo " gh secret set ANDROID_KEY_ALIAS # 'skywire' above"
echo " gh secret set ANDROID_KEY_PASSWORD"
exit 1
fi

# mobile-v1.2.3 -> 1.2.3. The versionCode is NOT computed here: `make
# android-apk` derives it, so the tag and a local build cannot disagree.
# The same shape is validated up front anyway, so a malformed tag fails
# in seconds instead of after the payload build.
- name: Derive version from tag
id: version
run: |
raw="${GITHUB_REF_NAME#mobile-v}"
if ! printf '%s' "$raw" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "::error::Tag '${GITHUB_REF_NAME}' is not mobile-vX.Y.Z"
exit 1
fi
rest=${raw#*.}; minor=${rest%%.*}; patch=${rest#*.}
if [ "$minor" -gt 99 ] || [ "$patch" -gt 99 ]; then
echo "::error::minor/patch must be <= 99 to keep versionCode monotonic"
exit 1
fi
if [ "$raw" = "0.0.0" ]; then
echo "::error::0.0.0 derives versionCode 0; Android requires a positive integer (lowest is 0.0.1)"
exit 1
fi
echo "name=$raw" >> "$GITHUB_OUTPUT"

- uses: actions/setup-go@v6
with:
go-version: '1.26.5'
cache: true
cache-dependency-path: go.sum

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'

- uses: gradle/actions/setup-gradle@v4

# The NDK lane, not the pure-Go one. cgo builds resolve DNS through
# bionic's getaddrinfo, which is the only path that honours the phone's
# actual resolver configuration — the pure-Go resolver is fine for CI
# and the emulator but is not what should ship.
# ANDROID_NDK_LATEST_HOME is set by the runner image, which means it is
# a shell variable and NOT in the `env` context — ${{ env.X }} would
# expand to empty and the Makefile would stop on its own guard.
- name: Build Go payload (NDK/cgo release lane)
run: |
test -n "$ANDROID_NDK_LATEST_HOME" || {
echo "::error::runner exposes no Android NDK"; exit 1; }
ANDROID_NDK_HOME="$ANDROID_NDK_LATEST_HOME" make android-mobile-ndk

- name: Decode keystore
env:
KEYSTORE: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
run: printf '%s' "$KEYSTORE" | base64 -d > "$RUNNER_TEMP/release.jks"

# Through make, so this is the same command a maintainer runs locally.
# ANDROID_JAVA_HOME points at the runner's JDK instead of the Makefile's
# macOS default.
- name: Assemble signed release APK
env:
ANDROID_KEYSTORE_FILE: ${{ runner.temp }}/release.jks
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: |
make android-apk \
APK_VERSION="${{ steps.version.outputs.name }}" \
ANDROID_JAVA_HOME="$JAVA_HOME"

# An unsigned APK reaching this point would mean the signingConfig
# silently did not apply — catch it here rather than after someone
# downloads a file that will not install.
- name: Verify the APK is signed
run: |
apk=android/app/build/outputs/apk/release/app-release.apk
test -f "$apk" || { echo "::error::$apk not found"; exit 1; }
# Newest build-tools present, rather than a pinned version that
# silently disappears the next time the runner image is rebuilt.
signer=$(find "$ANDROID_HOME/build-tools" -maxdepth 2 -name apksigner \
2>/dev/null | sort -V | tail -n1)
test -n "$signer" || { echo "::error::apksigner not found"; exit 1; }
"$signer" verify --print-certs "$apk" \
|| { echo "::error::APK is not signed"; exit 1; }
out="skywire-${{ steps.version.outputs.name }}-arm64-v8a.apk"
mv "$apk" "$out"
sha256sum "$out" > "$out.sha256"

- name: Publish pre-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
out="skywire-${{ steps.version.outputs.name }}-arm64-v8a.apk"
# Range the notes over the previous MOBILE tag. Left to itself
# --generate-notes walks back to whatever tag came last, which on
# this repo is usually a desktop `v*` release, and the changelog
# would be every Skywire commit since then.
args=(--prerelease --generate-notes)
prev=$(git describe --tags --abbrev=0 --match 'mobile-v*' \
"${GITHUB_REF_NAME}^" 2>/dev/null || true)
if [ -n "$prev" ]; then
args+=(--notes-start-tag "$prev")
fi
gh release create "${GITHUB_REF_NAME}" \
--repo "${{ github.repository }}" \
--title "Skywire Mobile ${{ steps.version.outputs.name }}" \
"${args[@]}" \
"$out" "$out.sha256"
22 changes: 22 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,28 @@ jobs:
- name: Compile-check js/wasm binaries
run: make build-wasm

# Build lane for the android payload: cmd/skywire-mobile with tags
# mobile,withoutsystray (GOOS=android GOARCH=arm64, pure-Go), then the size
# budget — so the lite variant can't silently rot or regain stripped assets.
# fetch-depth/tags mirror the linux job: `git describe` needs them so the
# stamped $(BUILDINFO) version isn't a bare short SHA.
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
fetch-tags: true

- uses: actions/setup-go@v6
with:
go-version: '1.26.5'
cache: true
cache-dependency-path: go.sum

- name: Build android payload + enforce size budget
run: make android-mobile-check

# Headless RUNTIME smoke of the real wasm-visor blob (Tier B of the
# wasm-visor test plan): Node ≥22 executes the compiled js/wasm binary via
# Go's wasm_exec.js — no browser — boots the visor against a loopback dmsg
Expand Down
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,17 @@ skysocks-client

!cmd/wasm-visor/cacert.pem
/skywire-cli


# Android app (android/ Gradle project lands in Step 1; the Go payload
# libskywire-mobile.so is already covered by the bare *.so above).
# Build outputs + machine-local config + signing keys — keys NEVER enter git.
android/.gradle/
android/build/
android/app/build/
android/local.properties
android/app/src/main/jniLibs/
*.keystore
*.jks
/wasm-visor

86 changes: 85 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.PHONY : check lint install-linters dep test lint-extra
.PHONY : update-deps update-dmsg update-skycoin push-deps
.PHONY : build clean install format bin build-race deploy wasm-visor embed-wasm-visor embed-wasm-visor-tinygo prune-wasm-embed-history
.PHONY : build-mobile android-mobile android-mobile-check android-mobile-ndk android-apk android-apk-debug
.PHONY : host-apps bin
.PHONY : docker-image docker-clean docker-network
.PHONY : docker-apps docker-bin docker-volume
Expand Down Expand Up @@ -100,7 +101,13 @@ INFO?=$(VERSION) $(DATE) $(COMMIT) $(BUILDTAG)
# so the embedded blob carries a CLEAN version from a possibly-uncommitted tree
# instead of Go's VCS "+dirty" stamp — and is deterministic (same source → same
# blob), which matters for the committed pkg/wasmhv/wasmbin blob.
WASM_BUILDINFO_PATH := $(PROJECT_BASE)/pkg/buildinfo
# The repo's OWN pkg/buildinfo (vs skywire-utilities' above). Despite the
# historical WASM_ prefix there is nothing wasm about the path — the wasm-visor
# was simply the first target that needed to stamp it. The skywire-mobile
# targets stamp it too (MOBILE_APPINFO below): /api/about + cobra --version
# read this package.
SKYWIRE_BUILDINFO_PATH := $(PROJECT_BASE)/pkg/buildinfo
WASM_BUILDINFO_PATH := $(SKYWIRE_BUILDINFO_PATH)
WASM_BUILDINFO := -X $(WASM_BUILDINFO_PATH).version=$(VERSION) -X $(WASM_BUILDINFO_PATH).commit=$(COMMIT) -X $(WASM_BUILDINFO_PATH).date=$(DATE)

BUILD_OPTS?="-ldflags=$(BUILDINFO)" -mod=vendor
Expand Down Expand Up @@ -191,6 +198,83 @@ build-merged: ## Install dependencies, build apps and binaries. `go build` with
build-merged-cgo: ## Build with CGO optimization for faster DMSG handshakes (requires libsecp256k1-dev)
${OPTS} go build -tags=cgo ${BUILD_OPTS} -o $(BUILD_PATH)skywire .

# ---- skywire-mobile: the lite multicall core for the Android app ----------
# One binary: visor + cli `config` subtree + the 4 client apps (in-proc via the
# launcher registry). The `mobile` tag strips the embedded desktop assets
# (geoip db 30 MB, manager UI 6.8 MB, vendored browser wallet 11 MB, tpviz
# legacy 2.8 MB). Output lands in the android project's jniLibs so Android
# Studio picks it up directly. -checklinkname=0 is REQUIRED on GOOS=android:
# the vendored wlynxg/anet uses //go:linkname into net (Go ≥1.23 blocks it).
ANDROID_JNILIBS := android/app/src/main/jniLibs/arm64-v8a
MOBILE_TAGS := mobile,withoutsystray
# Size budget for the android payload, in bytes (80 MB). The CI lane fails
# over it so the lite variant can't silently rot or regain the stripped fat.
ANDROID_MOBILE_MAX_BYTES := 83886080

# Both buildinfo paths are stamped: the visor internals read skywire-utilities'
# buildinfo ($(BUILDINFO)), but /api/about + cobra --version — what the phone
# app's info card shows — read the repo-local pkg/buildinfo. That version MUST
# parse as semver (visorconfig.Parse semver-checks it and FATALs otherwise), and
# on a checkout with no reachable tag `git describe --always` is a bare hash —
# so fall back to a v0.0.0-<sha> pseudo-version.
MOBILE_VERSION := $(shell git describe --tags 2>/dev/null || echo "v0.0.0-$(VERSION)")
MOBILE_APPINFO := -X $(SKYWIRE_BUILDINFO_PATH).version=$(MOBILE_VERSION) -X $(SKYWIRE_BUILDINFO_PATH).commit=$(COMMIT) -X $(SKYWIRE_BUILDINFO_PATH).date=$(DATE)
build-mobile: ## Build the skywire-mobile lite core for the HOST OS (desktop smoke of the mobile build variant)
${OPTS} go build -tags $(MOBILE_TAGS) "-ldflags=$(BUILDINFO) $(MOBILE_APPINFO)" -mod=vendor -o $(BUILD_PATH)skywire-mobile ./cmd/skywire-mobile

android-mobile: ## Build libskywire-mobile.so (android/arm64) into android jniLibs — pure-Go lane (CI/emulator); use android-mobile-ndk for release (bionic DNS)
mkdir -p $(ANDROID_JNILIBS)
GOOS=android GOARCH=arm64 CGO_ENABLED=0 ${OPTS} go build -tags $(MOBILE_TAGS) "-ldflags=$(BUILDINFO) $(MOBILE_APPINFO) -w -s -checklinkname=0" -mod=vendor -o $(ANDROID_JNILIBS)/libskywire-mobile.so ./cmd/skywire-mobile
@ls -la $(ANDROID_JNILIBS)/libskywire-mobile.so

android-mobile-check: android-mobile ## CI lane: android-mobile + fail over the size budget
@size=$$(wc -c < $(ANDROID_JNILIBS)/libskywire-mobile.so | tr -d '[:space:]'); \
echo "libskywire-mobile.so: $$size bytes (budget $(ANDROID_MOBILE_MAX_BYTES))"; \
if [ "$$size" -gt "$(ANDROID_MOBILE_MAX_BYTES)" ]; then \
echo "ERROR: libskywire-mobile.so exceeds the size budget — the mobile variant regained fat"; \
exit 1; \
fi

android-mobile-ndk: ## Release lane: NDK/cgo android build (DNS via bionic getaddrinfo); requires ANDROID_NDK_HOME
@set -e; \
test -n "$(ANDROID_NDK_HOME)" || { echo "ANDROID_NDK_HOME is not set"; exit 1; }; \
CC_BIN=$$(ls $(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/*/bin/aarch64-linux-android26-clang 2>/dev/null | head -n1); \
test -n "$$CC_BIN" || { echo "aarch64-linux-android26-clang not found under ANDROID_NDK_HOME"; exit 1; }; \
mkdir -p $(ANDROID_JNILIBS); \
GOOS=android GOARCH=arm64 CGO_ENABLED=1 CC="$$CC_BIN" go build -tags $(MOBILE_TAGS) "-ldflags=$(BUILDINFO) $(MOBILE_APPINFO) -w -s -checklinkname=0" -mod=vendor -o $(ANDROID_JNILIBS)/libskywire-mobile.so ./cmd/skywire-mobile; \
ls -la $(ANDROID_JNILIBS)/libskywire-mobile.so

# Android Studio's bundled JDK (gradle needs JDK 17+); override if yours differs.
ANDROID_JAVA_HOME ?= /Applications/Android Studio.app/Contents/jbr/Contents/Home

# APK_VERSION=X.Y.Z stamps the build; without it the committed gradle
# fallbacks apply. The versionCode formula lives HERE and nowhere else — the
# release workflow calls this target rather than computing its own — so a tag
# build and a local build can never disagree about the number. Minor and patch
# are capped at 99, past which the scheme stops being monotonic.
ifdef APK_VERSION
APK_VERSION_CODE := $(shell printf '%s' '$(APK_VERSION)' | awk -F. \
'{ if (NF==3 && $$1$$2$$3 ~ /^[0-9]+$$/ && $$2<=99 && $$3<=99) print $$1*10000+$$2*100+$$3 }')
APK_GRADLE_ARGS := -PskywireVersionName=$(APK_VERSION) -PskywireVersionCode=$(APK_VERSION_CODE)
endif

android-apk: ## Build the Android APK (release; APK_VERSION=X.Y.Z to stamp it; signed when ANDROID_KEYSTORE_FILE/_PASSWORD + ANDROID_KEY_ALIAS/_PASSWORD are set, else unsigned) — run android-mobile-ndk first for a fresh Go payload
@if [ -n "$(APK_VERSION)" ] && [ -z "$(APK_VERSION_CODE)" ]; then \
echo "APK_VERSION='$(APK_VERSION)' is not X.Y.Z with minor/patch <= 99"; exit 1; fi
@if [ "$(APK_VERSION_CODE)" = "0" ]; then \
echo "APK_VERSION=0.0.0 derives versionCode 0, and Android requires a"; \
echo "positive integer — the lowest usable version is 0.0.1."; exit 1; fi
@if [ -n "$(APK_VERSION)" ]; then \
echo "version: $(APK_VERSION) (versionCode $(APK_VERSION_CODE))"; fi
@if [ -n "$$ANDROID_KEYSTORE_FILE" ]; then \
echo "signing with $$ANDROID_KEYSTORE_FILE"; \
else \
echo "ANDROID_KEYSTORE_FILE unset — building UNSIGNED (app-release-unsigned.apk)"; fi
cd android && JAVA_HOME="$(ANDROID_JAVA_HOME)" ./gradlew assembleRelease $(APK_GRADLE_ARGS)

android-apk-debug: ## Build + the debug-signed APK (installable via adb) — the dev loop's CLI twin of Android Studio Run
cd android && JAVA_HOME="$(ANDROID_JAVA_HOME)" ./gradlew assembleDebug

build-merged-windows: clean-windows
powershell '${OPTS} go build ${BUILD_OPTS} -o $(BUILD_PATH)skywire.exe .'

Expand Down
Loading
Loading