CI/CD pre .NET MAUI v GitHub Actions: iOS podpisovanie a Android distribúcia (2026)
Kompletný GitHub Actions workflow pre .NET MAUI 10: build iOS aj Android v jednej pipeline, podpisovanie cez App Store Connect API kľúč, Android base64 keystore a automatický upload do TestFlight a Google Play Internal Track. Bez Fastlane.
CI/CD pipeline pre .NET MAUI v roku 2026 najjednoduchšie postavíte v GitHub Actions: jeden workflow s úlohami na macos-14 (iOS) a ubuntu-latest (Android), dotnet workload install maui, kódový podpis cez App Store Connect API kľúč a Android signing cez base64 keystore v Secrets. V tomto návode prejdeme kompletný .yml súbor vrátane TestFlight a Google Play Internal Track uploadu, ktorý reálne beží v produkcii. Bez Fastlane, bez paywallu, bez Azure DevOps.
iOS build vyžaduje macOS runner. macos-14 má .NET 10 SDK a Xcode 16 predinštalované, takže šetrí 8 až 10 minút oproti macos-13.
App Store Connect API kľúč (.p8) je v roku 2026 jediný oficiálne podporovaný spôsob TestFlight uploadu; Apple ID heslá a app-specific passwords boli deprecated v marci 2025.
Android keystore sa do GitHub Actions dostáva ako base64-encoded secret a dekóduje sa v kroku echo $SECRET | base64 -d > keystore.jks.
Build number pre iOS aj Android odvodzujte z github.run_number. Manuálne udržiavanie v csproj je zdroj merge konfliktov.
Cachovanie ~/.nuget/packages a ~/.dotnet/workload-packs zníži čas studeného buildu z 12 minút na 4 až 5.
Pre interný preview používajte Google Play Internal Testing track a TestFlight Internal, obidva preskakujú review proces.
Prečo GitHub Actions a nie Azure DevOps
Azure DevOps mal historicky lepšiu integráciu s Microsoftom, hlavne Microsoft-hosted agentov s predinštalovaným .NET MAUI workloadom a prirodzenú väzbu na Visual Studio App Center. Lenže App Center Microsoft retired 31. marca 2025, takže to už nie je výhoda. V roku 2026 vyhráva GitHub Actions v troch bodoch, ktoré v reálnom tíme rozhodujú.
Po prvé: macos-14 runner od septembra 2024 obsahuje predinštalované .NET 10 SDK aj maui workload. Netreba inštalovať. Po druhé: 2 000 minút mesačne zadarmo na privátnych repách pokryje malý tím so 4 až 5 buildmi denne. Po tretie: actions/upload-artifact@v4 má rádovo lepší throughput ako stará Azure DevOps Pipeline Artifacts.
Praktický rozdiel? Typický iOS build .NET MAUI aplikácie na macos-14 bežne dokončím za 7 až 9 minút vrátane podpisovania a TestFlight uploadu. Ten istý workflow na Azure DevOps Microsoft-hosted macOS agente trvá 11 až 14 minút, lebo agent ešte stále beží na macOS 13 s Xcode 15.4 a vyžaduje upgrade Xcode v každom buildi.
Ak ste predtým migrovali zo Xamarin.Forms, pravdepodobne ste mali pipeline v App Center alebo Azure DevOps. Pri prechode odporúčam prečítať si aj praktického sprievodcu migráciou z Xamarin.Forms na .NET MAUI, kde rozoberáme aj infraštruktúrne kroky.
Základná štruktúra workflow súboru
Štandardná pipeline pre .NET MAUI 10 má tri logické úlohy. build-android beží na ubuntu-latest (alebo windows-latest, funguje obidvoje, no ubuntu je rýchlejší a lacnejší v billing minútach), build-ios beží na macos-14, a distribute krok beží paralelne po obidvoch a posiela artefakty do TestFlight a Play. Maticový build do jednej úlohy nepoužívajte. iOS sa nedá buildnúť na Windows runneri a naopak, takže matrix len pridáva komplexitu bez benefitu.
Všimnite si -p:AndroidPackageFormat=aab. Google Play od augusta 2021 vyžaduje Android App Bundle, nie APK. APK použijete len pre internú distribúciu cez Firebase App Distribution alebo priame stiahnutie. ApplicationVersion je versionCode v Android termíne. Musí monotónne rásť, preto github.run_number, ktorý sa nikdy nevracia.
Android build a podpis APK/AAB
Android signing v CI/CD znamená dostať .jks alebo .keystore súbor do runner-a bez toho, aby ste ho commitli do repa. Štandardný postup v roku 2026 vyzerá takto: lokálne vygenerovaný keystore zakódujte do base64, uložte ako GitHub Secret, v workflow ho dekódujete a odovzdáte MSBuildu cez properties.
Tri secrety potrebujete: ANDROID_KEYSTORE_BASE64, ANDROID_STORE_PASSWORD a ANDROID_KEY_PASSWORD. Ak storepass a keypass sú rovnaké (default z keytool), pokojne použite ten istý secret. Apostrofy okolo hesiel v príkaze sú dôležité, bash by inak interpretoval $ alebo špeciálne znaky.
Ako podpisovať iOS aplikácie v CI/CD?
iOS podpisovanie v CI/CD bolo desaťročie najbolestivejšia časť mobilnej pipeline. Úprimne, ešte v roku 2022 som si pri prvom produkčnom workflow nadával celý víkend. V roku 2026 sa to dramaticky zlepšilo vďaka App Store Connect API kľúčom. Náš workflow potrebuje tri veci: distribučný certifikát (.p12), provisioning profile (.mobileprovision) a App Store Connect API kľúč (.p8). Najjednoduchší prístup, ktorý reálne funguje a nepotrebuje Fastlane, je použiť oficiálne Apple-Actions GitHub akcie.
Tri dôležité detaily, ktoré dokumentácia .NET MAUI veľmi nezdôrazňuje:
ArchiveOnBuild=true spôsobí, že MSBuild vyprodukuje .ipa namiesto .app bundlu. Bez toho sa do TestFlight nedostanete.
CodesignKey musí byť presný názov identity z keychainu vrátane Team ID v zátvorke. Skontrolujete cez security find-identity -v -p codesigning.
CodesignProvision je názov profilu, NIE UUID. apple-actions/download-provisioning-profiles ho stiahne do ~/Library/MobileDevice/Provisioning Profiles/ a MAUI ho tam nájde podľa bundle-id matchu.
Apple ID a app-specific password autentifikácia bola pre altool deprecated v marci 2025. App Store Connect API kľúč (vytvorený v App Store Connect > Users and Access > Integrations) je teraz jediný oficiálne podporovaný spôsob. Kľúč .p8 sa generuje iba raz, takže si ho ihneď uložte do password managera aj GitHub Secrets.
Automatický upload do TestFlight
Po úspešnom buildi IPA súboru ho nahrajte do App Store Connect cez xcrun altool. Pre TestFlight je altool v roku 2026 stále najspoľahlivejšia cesta, hoci pre niektoré operácie (notarizácia macOS aplikácií) ho Apple nahradil notarytool.
Tu je háčik, na ktorý som narazil pri prvej produkčnej pipeline (a stál ma asi tri hodiny v Slack threadoch s kolegom). altool hľadá .p8 súbor v konkrétnej ceste ~/.appstoreconnect/private_keys/AuthKey_{KEY_ID}.p8, NIE cez parameter. Súbor musíte fyzicky položiť na disk pred volaním. Toto je dokumentované v Apple App Store Connect API príručke, sekcia "API keys for command-line tools".
Build numbery v iOS musia byť unikátne na každý upload, inak App Store Connect odmietne IPA s chybou ITMS-90060. github.run_number rastie monotónne, čo je presne to, čo Apple vyžaduje. Marketing version (ApplicationDisplayVersion, t.j. CFBundleShortVersionString) môže ostať statická, Apple kontroluje len CFBundleVersion (build).
Google Play distribúcia cez Internal Track
Google Play API očakáva aab súbor a service account JSON. Service account vytvoríte v Google Play Console > Setup > API access > Create new service account, dáte mu rolu "Release manager" obmedzenú na konkrétnu aplikáciu. JSON kľúč uložte ako secret PLAY_SERVICE_ACCOUNT_JSON. Použijeme r0adkll/upload-google-play action, ktorá je v roku 2026 stále najpoužívanejšia v open-source.
- name: Upload to Play Store Internal Track
uses: r0adkll/[email protected]
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.mycompany.myapp
releaseFiles: ./publish/android/*.aab
track: internal
status: completed
whatsNewDirectory: ./whatsnew
Vlastnosť
TestFlight Internal
Play Internal Track
Review proces
Žiadny (do 100 testerov)
Žiadny
Čakacia doba
10 až 30 minút
5 až 15 minút
Limit testerov
100 interných používateľov
100 e-mailov
Promote do produkcie
1 klik z Connect
1 klik z Play Console
Build identita
CFBundleVersion unikátne
versionCode monotónne
API autentifikácia
ASC API kľúč (.p8)
Service account JSON
Pre rýchlejší preview cyklus pre QA tím odporúčam interný track ako predvolený výstup z main branch buildu a manuálny promote do alpha/beta až po sprintovom review. Detaily o tom, ako štruktúrovať servisnú vrstvu pre rôzne build varianty, nájdete v našom článku o REST API architektúre v .NET MAUI.
Verzovanie a cachovanie
Verzovanie odporúčam riešiť na úrovni workflow, nie v csproj. Dôvod je jednoduchý: csproj v repe je živý súbor a každý merge do main, ktorý zvyšuje verziu, je potenciálny konflikt. Namiesto toho odovzdajte verziu cez MSBuild property počítanú v samostatnom kroku.
# Marketing version z tag-u, build z run_number
- name: Compute version
id: version
run: |
TAG=${GITHUB_REF#refs/tags/v}
if [[ "$GITHUB_REF" != refs/tags/* ]]; then TAG="1.4.0-dev"; fi
echo "marketing=$TAG" >> $GITHUB_OUTPUT
echo "build=${{ github.run_number }}" >> $GITHUB_OUTPUT
Potom v build kroku odovzdajte -p:ApplicationDisplayVersion=${{ steps.version.outputs.marketing }}. Tag v1.4.0 push-nutý na main spustí produkčný build s marketing verziou 1.4.0; bez tagu sa použije dev placeholder.
A teraz cachovanie. .NET MAUI workload má približne 2,5 GB a inštalácia trvá 3 až 4 minúty pri studenom runneri. NuGet packages sú typicky 200 až 500 MB. Pridajte tieto dva cache kroky pred dotnet workload install:
Po zavedení cachingu mi klesli produkčné buildy z 12 minút (cold) na 4 až 5 minút (warm). To je rozdiel medzi "počkám si na PR check" a "idem si zatiaľ uvariť kávu". Pre väčšie tímy tento rozdiel znamená aj reálnu úsporu billing minút. Pri 50 PR-och denne to sú hodiny strojového času mesačne.
Bezpečnosť a správa secrets
Pri CI/CD pre mobilné aplikácie pracujete s vysoko citlivými artefaktmi: privátne kľúče, certifikáty, API tokeny. Pár pravidiel, ktoré sa naozaj oplatí dodržiavať a o ktorých sa veľa nehovorí:
Použite GitHub Environments pre release secrety, nie repo-level. Environments umožňujú required reviewers, takže ktokoľvek nemôže spustiť production deploy.
Rotácia App Store Connect API kľúčov minimálne raz ročne. Apple umožňuje mať aktívnych viacero kľúčov súčasne, takže rotáciu zvládnete bez downtime-u.
Android upload key oddeľte od signing key. Play App Signing (zapnuté default pre nové appy od 2021) udržuje skutočný signing key v Google Play KMS. Ak upload key kompromitujete, vymeníte ho cez Play Console. Stratený signing key bez Play App Signing = app sa už nedá update-ovať, ever.
Nepoužívajte echo v debug krokoch na secret-y. GitHub maskuje secrety v output-e, ale ak ich transformujete (napríklad base64 dekódovanie), maska sa stratí. Vždy pipe-ujte priamo do súboru.
Ďalšia praktická zásada, ktorú mi raz pripomenul DevOps konzultant: separátne developer a production Apple Developer Account teamy. Production prístup obmedzený na 2 až 3 osoby, CI/CD používa dedikovaný "Build bot" Apple ID s App Manager rolou (nie Account Holder!) a vlastným ASC API kľúčom. Aj keď máte malý tím, oddelenie identít zlepší audit trail a obmedzí blast radius pri leaknutí secrets.
Môžem buildiť iOS .NET MAUI aplikáciu na Linux runneri?
Nie. iOS build vyžaduje Xcode, ktorý beží len na macOS. .NET MAUI net10.0-ios target hard-failuje, ak nenájde Xcode toolchain. Použite macos-14 alebo macos-15 runner v GitHub Actions.
Ako rýchlo dostanem build do TestFlight po push-i?
Typicky 12 až 18 minút end-to-end: 7 až 9 minút build, 1 až 2 minúty upload, 5 až 8 minút App Store Connect processing pred sprístupnením testerom. Použitím cache môžete zísť na 8 až 10 minút celkovo.
Potrebujem Fastlane pre .NET MAUI CI/CD?
Nepotrebujete. Fastlane bol nevyhnutný v ére Xcode 12 a starého altool, ale od roku 2024 vystačíte s natívnymi GitHub actions ako apple-actions/import-codesign-certs a r0adkll/upload-google-play. Fastlane pridáva Ruby dependency, ktorá komplikuje runner setup a často láme po Xcode update-och.
Aký je rozdiel medzi AAB a APK pri Google Play upload-e?
AAB (Android App Bundle) je publikačný formát, ktorý Google Play konvertuje na device-specific APK pri inštalácii. Play od augusta 2021 vyžaduje AAB pre nové aplikácie. APK použijete len pre side-load distribúciu, Firebase App Distribution alebo Amazon Appstore. .NET MAUI generuje AAB cez -p:AndroidPackageFormat=aab.
Ako spravovať build numbers, aby som neporušil App Store Connect?
iOS CFBundleVersion musí byť unikátne v rámci tej istej CFBundleShortVersionString. Najspoľahlivejšie je github.run_number, ktorý je monotónne rastúce celé číslo na úrovni repa. Pre Android versionCode platí to isté pravidlo a Google Play tiež odmietne re-upload rovnakého versionCode.
Praktický návod, ako v .NET MAUI 10 spojazdniť App Links na Androide a Universal Links na iOS — od overovacích súborov cez Shell routing až po lokálne testovanie.
Hot Reload v .NET MAUI 10 prestal fungovať? Prejdite si presné príčiny rude edits, iOS interpreter setup, multi-target footguny a šesťkrokový diagnostický checklist pre rok 2026.