name: Android CI # Builds the app on every push / PR, and publishes a GitHub Release with the APK on v* tags. on: push: branches: [ "**" ] tags: [ "v*" ] pull_request: workflow_dispatch: permissions: contents: write # required so tag builds can create a Release concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build: runs-on: ubuntu-latest env: IS_TAG: ${{ startsWith(github.ref, 'refs/tags/v') }} # Set at runtime via $GITHUB_ENV in the "Name the APK" step. Declared here (empty) so static # linters don't flag the later ${{ env.APK_OUT }} references as "context access might be invalid". APK_OUT: "" steps: - name: Checkout uses: actions/checkout@v4 - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: temurin java-version: '21' # matches gradle/gradle-daemon-jvm.properties (toolchainVersion=21) - name: Set up Android SDK uses: android-actions/setup-android@v3 - name: Set up Gradle uses: gradle/actions/setup-gradle@v4 - name: Compute version (from the git tag on releases) id: ver run: | if [ "$IS_TAG" = "true" ]; then NAME="${GITHUB_REF_NAME#v}" # tag v1.2.0 -> 1.2.0 IFS='.' read -r MA MI PA <<< "$NAME" CODE=$(( ${MA:-0} * 10000 + ${MI:-0} * 100 + ${PA:-0} )) # 1.2.0 -> 10200 [ "$CODE" -gt 0 ] || CODE=1 else NAME="0.0.0" CODE=1 fi echo "name=$NAME" >> "$GITHUB_OUTPUT" echo "code=$CODE" >> "$GITHUB_OUTPUT" echo "Version: $NAME (code $CODE)" - name: Decode signing keystore (if configured) if: env.IS_TAG == 'true' env: KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} run: | if [ -n "$KEYSTORE_BASE64" ]; then echo "$KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/release.keystore" echo "KEYSTORE_FILE=$RUNNER_TEMP/release.keystore" >> "$GITHUB_ENV" echo "Release signing configured." else echo "No KEYSTORE_BASE64 secret set — the release tag will ship a debug-signed APK." fi - name: Build APK env: VERSION_NAME: ${{ steps.ver.outputs.name }} VERSION_CODE: ${{ steps.ver.outputs.code }} KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} KEY_ALIAS: ${{ secrets.KEY_ALIAS }} KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} run: | chmod +x ./gradlew if [ "$IS_TAG" = "true" ] && [ -n "$KEYSTORE_FILE" ]; then echo "Building signed release APK" ./gradlew assembleRelease SRC=$(ls app/build/outputs/apk/release/*.apk | head -n1) else echo "Building debug APK" ./gradlew assembleDebug SRC=$(ls app/build/outputs/apk/debug/*.apk | head -n1) fi echo "Built: $SRC" echo "APK_SRC=$SRC" >> "$GITHUB_ENV" - name: Name the APK (OwnTV-v) run: | mkdir -p dist if [ "$IS_TAG" = "true" ]; then OUT="OwnTV-v${{ steps.ver.outputs.name }}.apk" else OUT="OwnTV-dev-${GITHUB_SHA::7}.apk" fi cp "$APK_SRC" "dist/$OUT" echo "APK_OUT=$OUT" >> "$GITHUB_ENV" echo "Packaged dist/$OUT" # Tag builds also ship a stable-named copy so a fixed URL always serves the latest signed # release: https://github.com//releases/latest/download/OwnTV.apk # (used by the Fire TV Downloader code and any "always-latest" sideload link). if [ "$IS_TAG" = "true" ]; then cp "$APK_SRC" "dist/OwnTV.apk" fi - name: Upload APK artifact uses: actions/upload-artifact@v4 with: name: ${{ env.APK_OUT }} path: dist/${{ env.APK_OUT }} if-no-files-found: error - name: Extract release notes from CHANGELOG.md if: env.IS_TAG == 'true' run: | # Take the first "## ..." section of CHANGELOG.md (the newest release) as the release body. # The in-app updater shows this text as the "What's new" changelog. if [ -f CHANGELOG.md ]; then awk '/^## /{n++} n==1' CHANGELOG.md > dist/RELEASE_NOTES.md fi if [ ! -s dist/RELEASE_NOTES.md ]; then echo "See CHANGELOG.md for details." > dist/RELEASE_NOTES.md fi echo "--- Release notes ---" cat dist/RELEASE_NOTES.md - name: Publish GitHub Release if: env.IS_TAG == 'true' uses: softprops/action-gh-release@v2 with: name: OwnTV ${{ github.ref_name }} files: | dist/${{ env.APK_OUT }} dist/OwnTV.apk body_path: dist/RELEASE_NOTES.md generate_release_notes: true