diff --git a/.github/workflows/release-apk.yml b/.github/workflows/release-apk.yml index cdaefc9..49adacd 100644 --- a/.github/workflows/release-apk.yml +++ b/.github/workflows/release-apk.yml @@ -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 @@ -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" @@ -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" diff --git a/README.md b/README.md index 3518e01..e168c04 100644 --- a/README.md +++ b/README.md @@ -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 | diff --git a/src/SharpClient.App/SharpClient.App.csproj b/src/SharpClient.App/SharpClient.App.csproj index a4dc0dd..1f3863f 100644 --- a/src/SharpClient.App/SharpClient.App.csproj +++ b/src/SharpClient.App/SharpClient.App.csproj @@ -69,11 +69,13 @@ - +