name: Release Android app
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: Existing release tag to build and update
required: true
type: string
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
env:
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}
steps:
- uses: actions/checkout@v5
with:
ref: ${{ env.RELEASE_TAG }}
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "17"
- uses: android-actions/setup-android@v3
- uses: gradle/actions/setup-gradle@v5
- name: Restore release signing key
env:
SIGNING_KEY_BASE64: ${{ secrets.ANDROID_SIGNING_KEY_BASE64 }}
run: echo "$SIGNING_KEY_BASE64" | base64 --decode > "$RUNNER_TEMP/timeline-visualizer-release.jks"
- name: Test and build signed GitHub APK and Play bundle
env:
CARTO_BASEMAP_API_KEY: ${{ secrets.CARTO_BASEMAP_API_KEY }}
ANDROID_SIGNING_STORE_FILE: ${{ runner.temp }}/timeline-visualizer-release.jks
ANDROID_SIGNING_STORE_PASSWORD: ${{ secrets.ANDROID_SIGNING_STORE_PASSWORD }}
ANDROID_SIGNING_KEY_ALIAS: ${{ secrets.ANDROID_SIGNING_KEY_ALIAS }}
ANDROID_SIGNING_KEY_PASSWORD: ${{ secrets.ANDROID_SIGNING_KEY_PASSWORD }}
run: |
test -n "$CARTO_BASEMAP_API_KEY"
./gradlew test lint assembleGithubRelease bundlePlayRelease --stacktrace
- name: Verify signed GitHub APK
run: |
apk_path="app/build/outputs/apk/github/release/app-github-release.apk"
build_tools_dir="$(find "$ANDROID_HOME/build-tools" -mindepth 1 -maxdepth 1 -type d | sort -V | tail -n 1)"
test -n "$build_tools_dir"
"$build_tools_dir/apksigner" verify --verbose --print-certs "$apk_path"
package_line="$("$build_tools_dir/aapt" dump badging "$apk_path" | head -n 1)"
package_name="$(sed -n "s/^package: name='\([^']*\)'.*/\1/p" <<< "$package_line")"
version_code="$(sed -n "s/.*versionCode='\([^']*\)'.*/\1/p" <<< "$package_line")"
version_name="$(sed -n "s/.*versionName='\([^']*\)'.*/\1/p" <<< "$package_line")"
expected_version_code="$(sed -nE 's/^[[:space:]]*versionCode = ([0-9]+).*$/\1/p' app/build.gradle.kts | head -n 1)"
expected_version_name="$(sed -nE 's/^[[:space:]]*versionName = "([^"]+)".*$/\1/p' app/build.gradle.kts | head -n 1)"
test "$package_name" = "dev.mahlernim.timelinevisualizer"
test "$version_code" = "$expected_version_code"
test "$version_name" = "$expected_version_name"
test "$version_name" = "${RELEASE_TAG#v}"
sha256sum "$apk_path"
- name: Save Play Console bundle
uses: actions/upload-artifact@v4
with:
name: timeline-visualizer-play-${{ env.RELEASE_TAG }}
path: app/build/outputs/bundle/playRelease/app-play-release.aab
if-no-files-found: error
retention-days: 30
- name: Publish GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
apk="TimelineVisualizer-${RELEASE_TAG}.apk"
cp app/build/outputs/apk/github/release/app-github-release.apk "$apk"
sha256sum "$apk" > "$apk.sha256"
version_code="$(sed -nE 's/^[[:space:]]*versionCode = ([0-9]+).*$/\1/p' app/build.gradle.kts | head -n 1)"
version_name="$(sed -nE 's/^[[:space:]]*versionName = "([^"]+)".*$/\1/p' app/build.gradle.kts | head -n 1)"
jq -n \
--argjson versionCode "$version_code" \
--arg versionName "$version_name" \
--arg releaseUrl "https://github.com/${GITHUB_REPOSITORY}/releases/tag/${RELEASE_TAG}" \
'{versionCode: $versionCode, versionName: $versionName, releaseUrl: $releaseUrl}' \
> update.json
if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
gh release upload "$RELEASE_TAG" "$apk" "$apk.sha256" update.json --clobber
else
gh release create "$RELEASE_TAG" \
"$apk" \
"$apk.sha256" \
update.json \
--title "Timeline Visualizer ${RELEASE_TAG#v}" \
--notes-file "docs/release-notes-${RELEASE_TAG}.md" \
--verify-tag
fi