mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
release: publish signed builds on v* tags
Set applicationId to com.subtracks and add a GitHub Actions workflow that, on a v* tag, builds and signs the release APK and AAB, creates the GitHub release with the APK, and uploads the AAB to Google Play. Release notes come from the matching metadata changelog. Signing reads the keystore from the environment (RELEASE_KEYSTORE, RELEASE_KEYSTORE_PASSWORD, RELEASE_KEY_ALIAS, RELEASE_KEY_PASSWORD) and falls back to the debug key locally.
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "21"
|
||||
- name: Set up Android SDK
|
||||
uses: android-actions/setup-android@v4
|
||||
- name: Install platform and build tools
|
||||
run: sdkmanager "platforms;android-37" "build-tools;37.0.0"
|
||||
- name: Decode signing material
|
||||
id: signing
|
||||
env:
|
||||
KEYSTORE_BASE64: ${{ secrets.SUBTRACKS_KEYSTORE_BASE64 }}
|
||||
KEYPROPERTIES_BASE64: ${{ secrets.SUBTRACKS_KEYPROPERTIES_BASE64 }}
|
||||
run: |
|
||||
echo "$KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/release.keystore"
|
||||
alias=$(printf '%s' "$KEYPROPERTIES_BASE64" | base64 -d | sed -n 's/^keyAlias=//p' | tr -d '\r')
|
||||
test -n "$alias" || { echo "no keyAlias found in SUBTRACKS_KEYPROPERTIES_BASE64" >&2; exit 1; }
|
||||
echo "alias=$alias" >> "$GITHUB_OUTPUT"
|
||||
- name: Build APK and bundle
|
||||
env:
|
||||
RELEASE_KEYSTORE: ${{ runner.temp }}/release.keystore
|
||||
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.SUBTRACKS_UPLOAD_STORE_PASSWORD }}
|
||||
RELEASE_KEY_ALIAS: ${{ steps.signing.outputs.alias }}
|
||||
RELEASE_KEY_PASSWORD: ${{ secrets.SUBTRACKS_UPLOAD_KEY_PASSWORD }}
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew :app:assembleRelease :app:bundleRelease --no-daemon
|
||||
- name: Read version code
|
||||
id: version
|
||||
run: echo "code=$(grep -oP 'versionCode = \K\d+' app/build.gradle.kts)" >> "$GITHUB_OUTPUT"
|
||||
- name: Stage Play release notes
|
||||
run: |
|
||||
mkdir -p whatsnew
|
||||
cp "metadata/en-US/changelogs/${{ steps.version.outputs.code }}.txt" whatsnew/whatsnew-en-US
|
||||
- name: Upload to Google Play
|
||||
uses: r0adkll/upload-google-play@v1
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
|
||||
packageName: com.subtracks
|
||||
releaseFiles: app/build/outputs/bundle/release/app-release.aab
|
||||
tracks: ${{ vars.PLAY_TRACK || 'internal' }}
|
||||
status: completed
|
||||
whatsNewDirectory: whatsnew
|
||||
mappingFile: app/build/outputs/mapping/release/mapping.txt
|
||||
- name: Collect release APK
|
||||
run: cp app/build/outputs/apk/release/app-release.apk subtracks.apk
|
||||
- name: Create GitHub release
|
||||
uses: softprops/action-gh-release@v3
|
||||
with:
|
||||
body_path: metadata/en-US/changelogs/${{ steps.version.outputs.code }}.txt
|
||||
files: subtracks.apk
|
||||
@@ -4,7 +4,7 @@ Guidance for agents (and humans) working in this repository.
|
||||
|
||||
## What this is
|
||||
|
||||
A native Android rewrite of Subtracks, a client for Subsonic-compatible servers (Navidrome, gonic, Airsonic, ...), in Kotlin and Jetpack Compose. The `main` history still contains the earlier Flutter app; the native app is being built fresh under `app/` and ships as `com.subtracks.next` for side-by-side beta. The aim is a clean, F-Droid-friendly client: free/libre dependencies only, no telemetry.
|
||||
A native Android rewrite of Subtracks, a client for Subsonic-compatible servers (Navidrome, gonic, Airsonic, ...), in Kotlin and Jetpack Compose. The `main` history still contains the earlier Flutter app; the native app is being built fresh under `app/` and ships as `com.subtracks`. The aim is a clean, F-Droid-friendly client: free/libre dependencies only, no telemetry.
|
||||
|
||||
## Environment
|
||||
|
||||
@@ -59,7 +59,6 @@ Everything comes from the Nix flake devshell; do not install toolchains by hand.
|
||||
- Room 3 does not map `PagingSource` automatically: a DAO that returns it needs `@DaoReturnTypeConverters(PagingSourceDaoReturnTypeConverter::class)`.
|
||||
- Material icons are not pulled in by `material3`; use the BOM-managed `material-icons-core`/`material-icons-extended` (frozen at 1.7.8). Extended is large in debug builds but R8 strips unused icons from release.
|
||||
- Robolectric creates a fresh `Application` per test in one JVM, so `SubtracksApp.onCreate` stops any running Koin before `startKoin`.
|
||||
- `applicationId` is `com.subtracks.next` for the beta; change it to `com.subtracks` before any store release.
|
||||
- The CI image provides `nix-ld`, `jq`, `sqlite`, `node`, `zstd` and a `runner` user that `cache-nix-action` expects. None of that is needed locally beyond the devshell.
|
||||
- Integration servers: navidrome on 4533 (`admin`/`password`), gonic on 4747 (`admin`/`admin`), lms on 5082 (`admin`/`subtracks-lms`), Nextcloud Music on 8090 (`admin`/`subtracks-nextcloud`, base URL `http://localhost:8090/index.php/apps/music/subsonic/`). The test music is cached in `.integration/music` (gitignored).
|
||||
- lms and Nextcloud Music authenticate the Subsonic API with per-user **API keys** instead of passwords. The harness seeds a key and most tests use `useTokenAuth = false`; `SubsonicAuthIntegrationTest` drives `useTokenAuth = true` and asserts the fallback to plaintext (lms answers code 42, Nextcloud 41).
|
||||
|
||||
+15
-3
@@ -20,7 +20,7 @@ android {
|
||||
buildToolsVersion = "37.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.subtracks.next"
|
||||
applicationId = "com.subtracks"
|
||||
minSdk = 24
|
||||
targetSdk = 37
|
||||
versionCode = 13
|
||||
@@ -79,13 +79,25 @@ android {
|
||||
.kotlin.directories
|
||||
.add("src/integrationTest/kotlin")
|
||||
|
||||
signingConfigs {
|
||||
val keystorePath = providers.environmentVariable("RELEASE_KEYSTORE").orNull
|
||||
if (keystorePath != null) {
|
||||
create("release") {
|
||||
storeFile = file(keystorePath)
|
||||
storePassword = providers.environmentVariable("RELEASE_KEYSTORE_PASSWORD").orNull
|
||||
keyAlias = providers.environmentVariable("RELEASE_KEY_ALIAS").orNull
|
||||
keyPassword = providers.environmentVariable("RELEASE_KEY_PASSWORD").orNull
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||
// Debug key so `installRelease` works locally; store and F-Droid builds re-sign.
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
// Release signing when the keystore is supplied (CI); debug elsewhere so local build/install works.
|
||||
signingConfig = signingConfigs.findByName("release") ?: signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user