Skip to content

publish signed mobile artifacts #240

publish signed mobile artifacts

publish signed mobile artifacts #240

Workflow file for this run

name: publish signed mobile artifacts
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
inputs:
versionNumber:
description: 'version number'
required: true
default: '0.1.1'
env:
DOTNET_NOLOGO: true # Disable the .NET logo
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true # Disable the .NET first time experience
DOTNET_CLI_TELEMETRY_OPTOUT: true # Disable sending .NET CLI telemetry
DOTNET_VERSION: '10.0.x' # .NET version to use
XCODE_VERSION: '26.1' # Xcode version that supports iOS 26 for dotnet maui build
IOS_FRAMEWORK: 'net10.0-ios' # iOS framework target
ANDROID_FRAMEWORK: 'net10.0-android' # Android framework target
jobs:
publish-android:
runs-on: windows-2022
name: Android Build
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Pull Version from Tag
uses: nowsprinting/check-version-format-action@v3
id: version
with:
prefix: 'v'
- name: Use Version From Tag
shell: bash
if: steps.version.outputs.is_valid == 'true'
run: |
echo "VERSION=${{steps.version.outputs.full_without_prefix}}" >> $GITHUB_ENV
echo "${VERSION} is our version number"
- name: Use Version from User Input
shell: bash
if: steps.version.outputs.is_valid == 'false'
run: |
echo "VERSION=${{github.event.inputs.versionNumber}}" >> $GITHUB_ENV
echo "${VERSION} is our version number"
- name: create a custom version using run number offset by 1000 (run_number + 1000)
shell: bash
run: |
echo "VERSION_NUMBER_WITH_OFFSET=$((1000+GITHUB_RUN_NUMBER))" >> $GITHUB_ENV
- name: App Settings Variable Substitution
uses: microsoft/variable-substitution@v1
with:
files: 'appsettings.json'
env:
AppSettings.IfpaApiKey: ${{ secrets.IFPA_API_KEY }}
- name: Add Google Maps API Key to Android Manifest
run: |
$pathToXml = "Platforms/Android/AndroidManifest.xml"
$xml=[xml](Get-Content $pathToXml)
$xml.manifest.application."meta-data".value = "${{ secrets.GOOGLE_MAPS_API_KEY }}"
$xml.Save($pathToXml)
# # Store Android Signing Keystore and password in Secrets using base64 encoding
# # https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/certutil
# # commandline util to encode to base64 on windows
- name: Setup Keystore File
run: |
echo "${{ secrets.IFPA_ANDROID_KEYSTORE }}" > ifpamaui.keystore.asc
certutil -decode ifpamaui.keystore.asc ifpa-keystore.jks
- name: Setup .NET
uses: actions/setup-dotnet@4d4a70f4a5b2a5a5329f13be4ac933f2c9206ac0
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Install MAUI Workload
run: dotnet workload install maui android
- name: Restore Dependencies
run: dotnet restore IfpaMaui.csproj
- name: Build MAUI Android
run: dotnet publish IfpaMaui.csproj -c Release -f ${{ env.ANDROID_FRAMEWORK }} -p:Version=$Env:VERSION -p:ApplicationDisplayVersion=$Env:VERSION -p:ApplicationVersion=${{ env.VERSION_NUMBER_WITH_OFFSET }} --no-restore -p:AndroidKeyStore=True /p:AndroidSigningKeyStore=ifpa-keystore.jks /p:AndroidSigningKeyAlias=upload /p:AndroidSigningKeyPass="${{ secrets.IFPA_ANDROID_KEYSTORE_PASSWORD }}" /p:AndroidSigningStorePass="${{ secrets.IFPA_ANDROID_KEYSTORE_PASSWORD }}" --no-restore
- name: Upload Android Artifact
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: ifpamaui-android-signed-build
path: bin/Release/net9.0-android/publish/com.edgiardina.ifpa-Signed.aab
- name: Deploy to Google Play (Production)
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
packageName: com.edgiardina.ifpa
releaseFiles: bin/Release/net9.0-android/publish/com.edgiardina.ifpa-Signed.aab
releaseName: ${{ env.VERSION }}
track: production
publish-ios:
runs-on: macos-15
environment: Release
name: iOS Build
steps:
- uses: actions/checkout@v2
- name: Install the Apple certificate and provisioning profiles
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.DISTRIBUTION_CERT_BASE64 }}
P12_PASSWORD: ${{ secrets.DISTRIBUTION_CERT_PW }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.DISTRIBUTION_PROVISION_PROFILE_BASE64 }}
DISTRIBUTION_WIDGET_PROVISION_PROFILE_BASE64: ${{ secrets.DISTRIBUTION_WIDGET_PROVISION_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
APP_PP_PATH=$RUNNER_TEMP/app_pp.mobileprovision
WIDGET_PP_PATH=$RUNNER_TEMP/widget_pp.mobileprovision
KEYCHAIN_PATH=$HOME/Library/Keychains/app-signing.keychain-db
# Decode cert and both provisioning profiles
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $APP_PP_PATH
echo -n "$DISTRIBUTION_WIDGET_PROVISION_PROFILE_BASE64" | base64 --decode -o $WIDGET_PP_PATH
# Create and unlock custom keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# Import cert with access for codesign and security
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH -T /usr/bin/codesign -T /usr/bin/security
# Set keychain search and default to make it visible to MSBuild
security list-keychains -s $KEYCHAIN_PATH login.keychain-db
security default-keychain -s $KEYCHAIN_PATH
# Grant signing tools access to the private key
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# OPTIONAL: Try to trust the certificate globally
CERT_SHA=$(security find-certificate -c "Apple Distribution" -Z $KEYCHAIN_PATH | awk '/SHA-1/ { print $NF }')
if [[ -n "$CERT_SHA" ]]; then
CERT_PATH=$(security find-certificate -Z -a -p $KEYCHAIN_PATH | awk 'BEGIN {p=0} /BEGIN CERTIFICATE/ {p=1} {if(p)print} /END CERTIFICATE/ {p=0}' > temp_cert.cer && echo "temp_cert.cer")
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "$CERT_PATH" || echo "⚠️ Could not trust cert — continuing anyway"
fi
# ✅ Install main app provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
APP_PROFILE_UUID=$(grep -aA1 UUID $APP_PP_PATH | grep -io "[-A-F0-9]\{36\}" | head -1)
if [[ -z "$APP_PROFILE_UUID" ]]; then
echo "❌ Failed to extract UUID from main app provisioning profile"
exit 1
fi
cp $APP_PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles/$APP_PROFILE_UUID.mobileprovision
echo "PROVISIONING_UUID=$APP_PROFILE_UUID" >> $GITHUB_ENV
# ✅ Install widget provisioning profile
WIDGET_PROFILE_UUID=$(grep -aA1 UUID $WIDGET_PP_PATH | grep -io "[-A-F0-9]\{36\}" | head -1)
if [[ -z "$WIDGET_PROFILE_UUID" ]]; then
echo "❌ Failed to extract UUID from widget provisioning profile"
exit 1
fi
cp $WIDGET_PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles/$WIDGET_PROFILE_UUID.mobileprovision
echo "WIDGET_PROVISIONING_UUID=$WIDGET_PROFILE_UUID" >> $GITHUB_ENV
# Debug output to confirm identity is available
echo "🔍 Signing identities:"
security find-identity -v -p codesigning $KEYCHAIN_PATH
- name: Pull Version from Tag
uses: nowsprinting/check-version-format-action@v3
id: version
with:
prefix: 'v'
- name: Use Version From Tag
shell: bash
if: steps.version.outputs.is_valid == 'true'
run: |
echo "VERSION=${{steps.version.outputs.full_without_prefix}}" >> $GITHUB_ENV
echo "${VERSION} is our version number"
- name: Use Version from User Input
shell: bash
if: steps.version.outputs.is_valid == 'false'
run: |
echo "VERSION=${{github.event.inputs.versionNumber}}" >> $GITHUB_ENV
echo "${VERSION} is our version number"
- name: create a custom version using run number offset by 1000 (run_number + 1000)
run: |
echo "VERSION_NUMBER_WITH_OFFSET=$((1000+GITHUB_RUN_NUMBER))" >> $GITHUB_ENV
- name: App Settings Variable Substitution
uses: microsoft/variable-substitution@v1
with:
files: 'appsettings.json'
env:
AppSettings.IfpaApiKey: ${{ secrets.IFPA_API_KEY }}
- name: Update Widget API key in Info.plist
env:
BUILD_NUMBER: ${{ env.VERSION_NUMBER_WITH_OFFSET }}
APP_VERSION: ${{ env.VERSION }}
IFPA_API_KEY: ${{ secrets.IFPA_API_KEY }}
run: |
WIDGET_PLIST="NativeIFPA/RankWidget/Info.plist"
echo "🔐 Replacing IFPAApiKey"
/usr/libexec/PlistBuddy -c "Set :IFPAApiKey $IFPA_API_KEY" "$WIDGET_PLIST" || \
/usr/libexec/PlistBuddy -c "Add :IFPAApiKey string $IFPA_API_KEY" "$WIDGET_PLIST"
- name: Use Xcode Version For Widget Build
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}
# Download iOS platform + simulators if missing
- name: Install iOS platform + simulator runtime
run: |
xcodebuild -downloadPlatform iOS
xcrun simctl list runtimes
- name: Build RankWidgetExtension
run: |
xcodebuild \
-project NativeIFPA/IFPA.xcodeproj \
-scheme RankWidgetExtension \
-configuration Release \
-destination 'generic/platform=iOS' \
-sdk iphoneos \
-derivedDataPath NativeIFPA/DerivedData \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="iPhone Distribution: Ed Giardina (KH5JKUPW2Q)" \
CODE_SIGN_ENTITLEMENTS=RankWidgetExtension.entitlements \
DEVELOPMENT_TEAM=KH5JKUPW2Q \
PROVISIONING_PROFILE="$WIDGET_PROVISIONING_UUID"
- name: Setup .NET
uses: actions/setup-dotnet@4d4a70f4a5b2a5a5329f13be4ac933f2c9206ac0
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
# The following command is due to some sort of maui build bug and iOS won't accept the binary:
# run: dotnet workload install ios maui --source https://api.nuget.org/v3/index.json --version 8.0.401
# ITMS-91017: Invalid SDK usage - The following frameworks aren't permitted for apps using an alternative web browser engine: [BrowserEngineKit].
- name: Install MAUI Workload
run: dotnet workload install ios maui --source https://api.nuget.org/v3/index.json
- name: Restore nuget packages
run: dotnet restore IfpaMaui.csproj
- name : Publish iOS App
shell: bash
run: |
dotnet publish -f ${{ env.IOS_FRAMEWORK }} -c Release \
-p:ArchiveOnBuild=true \
-p:Version=${VERSION} \
-p:ApplicationDisplayVersion=${VERSION} \
-p:ApplicationVersion=${{ env.VERSION_NUMBER_WITH_OFFSET }} \
-p:CodesignKey="iPhone Distribution: Ed Giardina (KH5JKUPW2Q)" \
-p:ProvisioningProfileUuid=${{ env.PROVISIONING_UUID }}
- uses: actions/upload-artifact@v4
with:
name: ifpamaui-ios-signed-build
if-no-files-found: error
path: |
bin/Release/${{ env.IOS_FRAMEWORK }}/**/*.ipa
- name: Upload app to App Store Connect
env:
APP_STORE_CONNECT_USERNAME: ${{ secrets.APP_STORE_CONNECT_USERNAME }}
APP_STORE_CONNECT_PASSWORD: ${{ secrets.APP_STORE_CONNECT_PASSWORD }}
run: |
xcrun altool --upload-app -t ios -f "bin/Release/${{ env.IOS_FRAMEWORK }}/ios-arm64/publish/IfpaMaui.ipa" -u "$APP_STORE_CONNECT_USERNAME" -p "$APP_STORE_CONNECT_PASSWORD"