Skip to content

Versions

Versions #123

Workflow file for this run

name: Android APK
on:
workflow_dispatch:
push:
branches: [ main ]
paths:
- 'Valour/Client.Maui/**'
- 'Valour/Client/**'
jobs:
check-version:
runs-on: ubuntu-latest
outputs:
version_changed: ${{ steps.check.outputs.changed }}
version: ${{ steps.check.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Check if release already exists
id: check
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
CURRENT=$(grep -oP '(?<=<ApplicationDisplayVersion>)[^<]+' Valour/Client.Maui/Valour.Client.Maui.csproj)
echo "version=$CURRENT" >> $GITHUB_OUTPUT
if gh release view "v$CURRENT" > /dev/null 2>&1; then
echo "Release v$CURRENT already exists, skipping build"
echo "changed=false" >> $GITHUB_OUTPUT
else
echo "No release for v$CURRENT, will build"
echo "changed=true" >> $GITHUB_OUTPUT
fi
build-android:
needs: check-version
if: needs.check-version.outputs.version_changed == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Install MAUI workload
run: dotnet workload install maui-android
- name: Materialize Android signing keystore
id: signing-keystore
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
run: |
if [ -z "$ANDROID_KEYSTORE_BASE64" ]; then
echo "Missing required secret: ANDROID_KEYSTORE_BASE64"
exit 1
fi
KEYSTORE_PATH="$RUNNER_TEMP/valour-release.keystore"
echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > "$KEYSTORE_PATH"
echo "keystore_path=$KEYSTORE_PATH" >> $GITHUB_OUTPUT
- name: Validate Android signing secrets
env:
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: |
if [ -z "$ANDROID_SIGNING_STORE_PASSWORD" ]; then
echo "Missing required secret: ANDROID_SIGNING_STORE_PASSWORD"
exit 1
fi
if [ -z "$ANDROID_SIGNING_KEY_ALIAS" ]; then
echo "Missing required secret: ANDROID_SIGNING_KEY_ALIAS"
exit 1
fi
if [ -z "$ANDROID_SIGNING_KEY_PASSWORD" ]; then
echo "Missing required secret: ANDROID_SIGNING_KEY_PASSWORD"
exit 1
fi
- name: Validate keystore and alias
env:
ANDROID_SIGNING_STORE_PASSWORD: ${{ secrets.ANDROID_SIGNING_STORE_PASSWORD }}
ANDROID_SIGNING_KEY_ALIAS: ${{ secrets.ANDROID_SIGNING_KEY_ALIAS }}
run: |
keytool -list \
-keystore "${{ steps.signing-keystore.outputs.keystore_path }}" \
-storepass "$ANDROID_SIGNING_STORE_PASSWORD" > /dev/null
if ! keytool -list \
-keystore "${{ steps.signing-keystore.outputs.keystore_path }}" \
-storepass "$ANDROID_SIGNING_STORE_PASSWORD" \
-alias "$ANDROID_SIGNING_KEY_ALIAS" > /dev/null; then
echo "Alias '$ANDROID_SIGNING_KEY_ALIAS' was not found in the keystore."
echo "Configured aliases are:"
keytool -list \
-keystore "${{ steps.signing-keystore.outputs.keystore_path }}" \
-storepass "$ANDROID_SIGNING_STORE_PASSWORD"
exit 1
fi
- name: Get short SHA
id: short-sha
run: echo "short_sha=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
- name: Replace asset version hashes
uses: mingjun97/file-regex-replace@v1
with:
regex: '\$\(SHORTHASH\)'
replacement: '${{ steps.short-sha.outputs.short_sha }}'
flags: "g"
include: '\.(js|cs|razor|cshtml|html)$'
exclude: '.^'
encoding: 'utf8'
path: '.'
- name: Restore dependencies
run: |
dotnet restore Valour/BuildTools/CssBundler/CssBundler.csproj
dotnet restore Valour/Client.Maui/Valour.Client.Maui.csproj -p:AndroidSdkDirectory="${{ env.ANDROID_HOME }}"
- name: Build Client (generates CSS bundle)
run: dotnet build Valour/Client/Valour.Client.csproj -c Release
- name: Build APK
env:
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: >
dotnet publish Valour/Client.Maui/Valour.Client.Maui.csproj
-f net10.0-android
-c Release
-o ./artifacts
-p:AndroidKeyStore=true
-p:AndroidSigningKeyStore="${{ steps.signing-keystore.outputs.keystore_path }}"
-p:AndroidSigningStorePass="$ANDROID_SIGNING_STORE_PASSWORD"
-p:AndroidSigningKeyAlias="$ANDROID_SIGNING_KEY_ALIAS"
-p:AndroidSigningKeyPass="$ANDROID_SIGNING_KEY_PASSWORD"
-p:AndroidPackageFormat=apk
-p:AndroidUseApkSigner=true
-p:AndroidApkSignerAdditionalArguments=--verbose
-p:AndroidSdkDirectory="${{ env.ANDROID_HOME }}"
- name: Find Signed APK
id: find-apk
run: |
echo "APK candidates found in ./artifacts:"
find ./artifacts -name '*.apk' | sort || true
APKSIGNER=$(find "${ANDROID_HOME}/build-tools" -type f -name apksigner | sort -V | tail -1)
if [ -z "$APKSIGNER" ]; then
echo "Could not find apksigner in ${ANDROID_HOME}/build-tools"
exit 1
fi
APK_PATH=""
while IFS= read -r CANDIDATE; do
if "$APKSIGNER" verify "$CANDIDATE" > /dev/null 2>&1; then
APK_PATH="$CANDIDATE"
break
fi
done < <(find ./artifacts -name '*.apk' | sort)
if [ -z "$APK_PATH" ]; then
echo "No signed APK was found. Refusing to create a release from unsigned artifacts."
exit 1
fi
echo "Signed APK selected: $APK_PATH"
"$APKSIGNER" verify --print-certs "$APK_PATH"
echo "apk_path=$APK_PATH" >> $GITHUB_OUTPUT
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.check-version.outputs.version }}
name: Valour v${{ needs.check-version.outputs.version }}
files: ${{ steps.find-apk.outputs.apk_path }}
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}