Skip to content

Build Android APK

Build Android APK #24

Workflow file for this run

name: Build Android APK
on:
workflow_run:
workflows: ["Create Release"]
types:
- completed
workflow_dispatch:
env:
DOTNET_VERSION: '8.0.x'
jobs:
build-android:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
permissions:
contents: write
steps:
- name: Validate signing secrets
run: |
MISSING=""
if [ -z "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" ]; then MISSING="$MISSING ANDROID_KEYSTORE_BASE64"; fi
if [ -z "${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" ]; then MISSING="$MISSING ANDROID_KEYSTORE_PASSWORD"; fi
if [ -z "${{ secrets.ANDROID_KEY_ALIAS }}" ]; then MISSING="$MISSING ANDROID_KEY_ALIAS"; fi
if [ -z "${{ secrets.ANDROID_KEY_PASSWORD }}" ]; then MISSING="$MISSING ANDROID_KEY_PASSWORD"; fi
if [ -n "$MISSING" ]; then
echo "::error::Missing required secrets:$MISSING"
echo "Configure them in: Settings → Secrets and variables → Actions"
exit 1
fi
echo "✅ All signing secrets are configured"
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: false
- name: Setup .NET 8
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Pin .NET 8 SDK only
run: |
echo '{"sdk":{"version":"8.0.100","rollForward":"latestFeature","allowPrerelease":false}}' > global.json
echo "Installed SDKs:"
dotnet --list-sdks
echo ""
echo "Active SDK (pinned to 8.0.x):"
dotnet --version
- name: Install MAUI Android workload
run: dotnet workload install maui-android --source https://api.nuget.org/v3/index.json
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v0.10.2
with:
versionSpec: '5.x'
- name: Determine Version
id: gitversion
uses: gittools/actions/gitversion/execute@v0.10.2
with:
useConfigFile: true
configFilePath: GitVersion.yml
- name: Display version
run: |
echo "SemVer: ${{ steps.gitversion.outputs.semVer }}"
echo "NuGetVersionV2: ${{ steps.gitversion.outputs.nuGetVersionV2 }}"
- name: Create appsettings.json placeholder
run: cp DevNote/appsettings.example.json DevNote/appsettings.json
- name: Restore dependencies
run: dotnet restore DevNote.sln
- name: Decode keystore
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > ${{ github.workspace }}/devnote.keystore
- name: Publish Android APK (signed)
run: |
dotnet publish DevNote/DevNote.csproj \
-f net8.0-android \
-c Release \
-p:AndroidPackageFormat=apk \
-p:ApplicationDisplayVersion=${{ steps.gitversion.outputs.nuGetVersionV2 }} \
-p:ApplicationVersion=${{ github.run_number }} \
-p:AndroidKeyStore=true \
-p:AndroidSigningKeyStore=${{ github.workspace }}/devnote.keystore \
-p:AndroidSigningKeyAlias=${{ secrets.ANDROID_KEY_ALIAS }} \
-p:AndroidSigningKeyPass=${{ secrets.ANDROID_KEY_PASSWORD }} \
-p:AndroidSigningStorePass=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
- name: Find APK file
id: find_apk
run: |
echo "All APKs found:"
find DevNote/bin/Release/net8.0-android -name "*.apk"
APK_PATH=$(find DevNote/bin/Release/net8.0-android -name "*-Signed.apk" | head -1)
if [ -z "$APK_PATH" ]; then
echo "No signed APK found, falling back to any APK"
APK_PATH=$(find DevNote/bin/Release/net8.0-android -name "*.apk" | head -1)
fi
echo "Selected APK: $APK_PATH"
echo "apk_path=$APK_PATH" >> $GITHUB_OUTPUT
APK_NAME="DevNote-v${{ steps.gitversion.outputs.nuGetVersionV2 }}.apk"
cp "$APK_PATH" "$APK_NAME"
echo "apk_name=$APK_NAME" >> $GITHUB_OUTPUT
- name: Verify APK signature
run: |
echo "Checking APK signature..."
"$ANDROID_HOME/build-tools/$(ls $ANDROID_HOME/build-tools/ | tail -1)/apksigner" verify --print-certs "${{ steps.find_apk.outputs.apk_name }}" || echo "⚠️ APK signature verification failed"
- name: Upload APK artifact
uses: actions/upload-artifact@v4
with:
name: DevNote-v${{ steps.gitversion.outputs.nuGetVersionV2 }}
path: ${{ steps.find_apk.outputs.apk_name }}
retention-days: 90
- name: Attach APK to GitHub Release
if: startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_NAME="${GITHUB_REF#refs/tags/}"
gh release upload "$TAG_NAME" "${{ steps.find_apk.outputs.apk_name }}" --clobber
- name: Attach APK to latest release
if: github.event_name == 'workflow_run' || github.event_name == 'workflow_dispatch'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
LATEST_RELEASE=$(gh release list --limit 1 --json tagName --jq '.[0].tagName' 2>/dev/null || echo "")
if [ -n "$LATEST_RELEASE" ]; then
echo "Attaching APK to release $LATEST_RELEASE"
gh release upload "$LATEST_RELEASE" "${{ steps.find_apk.outputs.apk_name }}" --clobber
else
echo "No GitHub Release found, APK available as artifact only"
fi
- name: Summary
run: |
echo "## Android APK Built" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Version:** \`${{ steps.gitversion.outputs.nuGetVersionV2 }}\`" >> $GITHUB_STEP_SUMMARY
echo "- **Build Number:** \`${{ github.run_number }}\`" >> $GITHUB_STEP_SUMMARY
echo "- **APK:** \`${{ steps.find_apk.outputs.apk_name }}\`" >> $GITHUB_STEP_SUMMARY
echo "- **SemVer:** \`${{ steps.gitversion.outputs.semVer }}\`" >> $GITHUB_STEP_SUMMARY