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
44 changes: 44 additions & 0 deletions .github/workflows/release-apk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,46 @@ jobs:
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > "$KEYSTORE_PATH"
echo "Decoded keystore to $KEYSTORE_PATH"

# Derive the Android versionName + versionCode from the release tag so the
# installed APK reports the same version the GitHub Release advertises.
# Without this, every build ships the csproj defaults (versionName 1.0,
# versionCode 1): Obtainium then shows "1.0" against a "v0.2" release, and
# Android refuses in-place updates because the versionCode never changes.
# versionName = tag with leading 'v', pre-release (-...) and build (+...) stripped
# versionCode = major*100000000 + minor*10000 + patch (monotonic with semver;
# room for minor/patch < 10000, major <= 20 to stay under Android's
# 2,100,000,000 versionCode cap). Fails fast on out-of-range/bad tags.
# Manual (workflow_dispatch) runs only upload to the run (never attached to a
# release), so they use a fixed 0.0.0-dev / versionCode 1 that can never overtake a
# real release and block its in-place update.
- name: Compute version from release tag
id: ver
run: |
if [ "${{ github.event_name }}" != "release" ]; then
echo "display=0.0.0-dev" >> "$GITHUB_OUTPUT"
echo "code=1" >> "$GITHUB_OUTPUT"
echo "Non-release build: versionName=0.0.0-dev versionCode=1"
exit 0
fi
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}" # v0.2.1 -> 0.2.1
VERSION="${VERSION%%+*}" # 0.2.1+build -> 0.2.1 (strip build metadata)
VERSION="${VERSION%%-*}" # 0.2.1-rc1 -> 0.2.1 (strip pre-release)
IFS='.' read -r MAJ MIN PAT <<< "$VERSION"
MAJ=${MAJ:-0}; MIN=${MIN:-0}; PAT=${PAT:-0}
if ! [[ "$MAJ" =~ ^[0-9]+$ && "$MIN" =~ ^[0-9]+$ && "$PAT" =~ ^[0-9]+$ ]]; then
echo "::error::Release tag '${{ github.event.release.tag_name }}' is not a numeric semver (parsed major='$MAJ' minor='$MIN' patch='$PAT')." >&2
exit 1
fi
if [ "$MAJ" -gt 20 ] || [ "$MIN" -gt 9999 ] || [ "$PAT" -gt 9999 ]; then
echo "::error::Version $VERSION out of versionCode range (require major<=20, minor/patch<=9999)." >&2
exit 1
fi
CODE=$(( 10#$MAJ * 100000000 + 10#$MIN * 10000 + 10#$PAT ))
echo "display=$VERSION" >> "$GITHUB_OUTPUT"
echo "code=$CODE" >> "$GITHUB_OUTPUT"
echo "Resolved versionName=$VERSION versionCode=$CODE"

# Publish a SIGNED .apk (sideload). AndroidKeyStore=true + the signing
# properties make dotnet sign with our release keystore instead of the
# auto-generated debug key. AndroidPackageFormat=apk is the default but is
Expand All @@ -80,6 +120,8 @@ jobs:
-p:JavaSdkDirectory="$JAVA_HOME"
-p:AndroidSdkDirectory="$ANDROID_SDK_ROOT"
-p:AcceptAndroidSdkLicenses=True
-p:ApplicationDisplayVersion=${{ steps.ver.outputs.display }}
-p:ApplicationVersion=${{ steps.ver.outputs.code }}
-p:AndroidPackageFormat=apk
-p:AndroidKeyStore=true
-p:AndroidSigningKeyStore="$KEYSTORE_PATH"
Expand All @@ -97,6 +139,8 @@ jobs:
-p:JavaSdkDirectory="$JAVA_HOME"
-p:AndroidSdkDirectory="$ANDROID_SDK_ROOT"
-p:AcceptAndroidSdkLicenses=True
-p:ApplicationDisplayVersion=${{ steps.ver.outputs.display }}
-p:ApplicationVersion=${{ steps.ver.outputs.code }}
-p:AndroidPackageFormat=aab
-p:AndroidKeyStore=true
-p:AndroidSigningKeyStore="$KEYSTORE_PATH"
Expand Down
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@ over telnet, using [TelnetNegotiationCore](https://www.nuget.org/packages/Telnet
Android-primary. See [`docs/superpowers/specs`](docs/superpowers/specs) for the
design spec and visual-design brief.

## Installing on Android

Signed APKs are attached to every [GitHub Release](https://github.com/SharpMUSH/SharpClient/releases).
You can sideload the `.apk` directly, but the easiest way to install **and stay
updated** is [Obtainium](https://github.com/ImranR98/Obtainium), which tracks this
repo's releases for you:

1. Install Obtainium.
2. Add an app with this source URL:
```
https://github.com/SharpMUSH/SharpClient
```
or, from your phone, paste this Obtainium deep link (GitHub can't render it as
a clickable link, so copy it as-is):
```
obtainium://add/https://github.com/SharpMUSH/SharpClient
```
3. Obtainium picks up the universal signed APK from each release and notifies you
when a new version ships.

Each release ships a single universal APK, so no ABI/architecture filter is
needed. The APK's `versionName`/`versionCode` are derived from the release tag
(e.g. `v0.2` → `0.2`), so Obtainium and Android both see the correct version and
can update in place. If a release is marked **pre-release** on GitHub, enable
"include prereleases" for the app in Obtainium or it will be skipped.

## Solution layout

| Project | Kind | Purpose |
Expand Down
10 changes: 6 additions & 4 deletions src/SharpClient.App/SharpClient.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,13 @@
<!--
App icon — SharpMUSH logo (fetched from SharpMUSH/SharpMUSH, Solution Files/).
appicon.svg : Logo.svg — the full teal (#00f5b7) logo, used as the icon base.
appiconfg.svg : LogoBlackFg.svg — dark (#111) foreground variant for adaptive icons.
Color="#0b0e12": near-black background complements the teal logo on adaptive-icon
aware launchers; also used as the legacy icon background.
appiconfg.svg : LogoBlackFg.svg — dark (#111) adaptive-icon foreground.
Color="#00f5b7": SharpMUSH brand green background. The dark foreground mark reads
cleanly on it (adaptive icons, Android 8+); the teal base blends into
the matching green so the background looks solid. Previously #0b0e12,
which put the dark mark on near-black and rendered as "black on grey".
-->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#0b0e12" />
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#00f5b7" />

<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
Expand Down
Loading