Repository navigation
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36571Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36571" |
|
Azure Pipelines: 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR introduces a new manually-triggered “Template App Distribution” GitHub Actions workflow plus supporting scripts to (1) pack the MAUI templates, (2) generate per-platform apps (blank + sample), (3) produce sideloadable artifacts for dry-runs, and (4) optionally publish to Google Play / TestFlight using fastlane behind a protected environment.
Changes:
- Adds
.github/workflows/template-app-distribution.ymlwith a prepare → dry-run-build / publish job split and a platform/variant matrix. - Adds PowerShell scripts under
.github/scripts/template-app-distribution/to resolve the source ref + SDK, pack templates, generate apps, and build platform-appropriate distributables. - Adds a dedicated fastlane bundle (Gemfile/Gemfile.lock + Fastfile) and a README documenting artifact types and tester install steps.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| .github/workflows/template-app-distribution.yml | New workflow_dispatch pipeline to build and (optionally) publish template-based tester apps across platforms/variants. |
| .github/scripts/template-app-distribution/Resolve-SourceRef.ps1 | Resolves/validates source_ref and enforces trust rules for publish=true. |
| .github/scripts/template-app-distribution/Resolve-DotNetSdk.ps1 | Resolves SDK version (global.json) and derives the netX.Y TFM used by matrix generation. |
| .github/scripts/template-app-distribution/Prepare-Matrix.ps1 | Builds the variant/platform matrix and default app identifiers/bundle IDs. |
| .github/scripts/template-app-distribution/Pack-Templates.ps1 | Builds/packs Microsoft.Maui.Templates into a local nupkg for dotnet new install. |
| .github/scripts/template-app-distribution/New-TemplateApp.ps1 | Generates an app from the packed templates and normalizes app identity/version settings. |
| .github/scripts/template-app-distribution/Install-AppleSigningAssets.ps1 | Installs Apple certs/profiles into a temporary keychain and exports signing env vars. |
| .github/scripts/template-app-distribution/Build-TemplateApp.ps1 | Builds platform-specific outputs (APK/AAB, iOS IPA + simulator .app zip, Mac Catalyst .pkg + .app zip, Windows self-contained zip). |
| .github/scripts/template-app-distribution/README.md | Tester-facing documentation for what artifacts are produced and how to install/run them. |
| .github/scripts/template-app-distribution/fastlane/Gemfile | Pins fastlane dependency for publish lanes. |
| .github/scripts/template-app-distribution/fastlane/Gemfile.lock | Bundler lockfile for deterministic fastlane installs in CI. |
| .github/scripts/template-app-distribution/fastlane/Fastfile | Implements the fastlane lanes used to upload to Google Play / TestFlight. |
Historical iOS dry-run output: unsigned device IPA and Simulator appThe July change added an unsigned The inspected device archive had an arm64 iOS executable, no embedded provisioning profile and no code signature. Those checks proved archive contents, not physical-device installability. The Simulator app is a separate artifact and does not establish that the device IPA can be installed. Physical-device delivery requires a successfully processed TestFlight build with tester access, or a distribution-signed ad-hoc IPA whose provisioning profile includes the tester's device. No such delivery or invitation is established by this comment. Current sample-download status is maintained in the distribution index. |
|
Addressed the Copilot review feedback in
All 5 review threads resolved. Ready for re-review.
|
|
Addressed the latest Copilot re-review in
All six threads addressed and resolved. Ready for another look, @copilot.
|
|
@copilot-pull-request-reviewer this round is addressed in
All review threads are resolved. Ready for re-review. Thanks!
|
…late args via env - Build-TemplateApp.ps1: Write-Base64File now trims surrounding whitespace/newlines before [Convert]::FromBase64String (secrets often carry a trailing newline, which otherwise throws and breaks keystore/cert materialization). Matches the existing trim pattern used for the Android keystore later in the same script. - template-app-distribution.yml: pin all 6 actions/checkout@v7 -> @v4 to match the repo standard and avoid a runtime 'unresolved action' failure (v7 does not exist). - template-app-distribution.yml: pass matrix.templateArgsJson to New-TemplateApp.ps1 via a TEMPLATE_ARGS_JSON env var ($env:TEMPLATE_ARGS_JSON) in both the dry-run and publish jobs, instead of single-quote-interpolating it into the PowerShell command text. Removes the quoting/command-injection vector if the JSON ever contains a single quote. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
- README: iOS dry-run artifact is "one or two files" (unsigned device .ipa is best-effort, so it may be absent, leaving only the Simulator .app.zip). - Header: TEMPLATE_APP_ANDROID_KEY_PASSWORD is Optional (defaults to the keystore password when unset), not Required. - Pre-flight validation no longer hard-requires MAC_INSTALLER_CERTIFICATE_* for maccatalyst: Install-AppleSigningAssets.ps1 accepts an installer identity from any imported cert (the main IOS_CERTIFICATE p12 can already contain one), so requiring the dedicated secret was a false-negative. - App Store Connect private-key check message now says "iOS/Mac Catalyst publishing" (the step runs for both platforms). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
… codes - actions/upload-artifact@v7 -> @v4 (v7 does not exist; @v4 is the repo standard) in both the dry-run and publish jobs. - Pack-Templates.ps1: throw when `dotnet build`/`dotnet pack` fail. With $ErrorActionPreference='Stop', native-command failures still don't throw in PowerShell, so an earlier failing build could otherwise fall through to a stale package. Matches the explicit $LASTEXITCODE checks in Build-TemplateApp.ps1. - New-TemplateApp.ps1: throw when `dotnet new install` or `dotnet new <template>` fail, for the same reason. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
- template-app-distribution.yml: add fetch-tags: true to the source checkout so tag-based source_ref values resolve/trust correctly (Resolve-SourceRef.ps1 uses git rev-list refs/tags/<tag>). - New-TemplateApp.ps1: anchor the missing-key injection to the root </dict> immediately preceding </plist> and replace only once, so a stray column-0 </dict> can't duplicate the injected key. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
codesign warns 'Specifying ":" in the path is deprecated and will not work in a future release' for the -d --entitlements :<path> form. Switch to '-d --xml --entitlements -' (verified on macOS 26.5.2: identical XML, no deprecation warning), capture stdout, and write the file only when the bundle actually declares entitlements so the hardened-runtime re-sign preserves them instead of silently dropping them. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
The Android path built the APK/AAB without -r, so the RuntimeIdentifier matrix value (e.g. android-arm64) was ignored and the parameter was misleading. Append -r $RuntimeIdentifier to the shared $commonArgs when non-empty (so both the APK and AAB builds inherit it), consistent with the iOS/Windows/Developer ID paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Pack-Templates.ps1 ran dotnet build -t:Rebuild then dotnet pack without --no-build, so the project built twice. The template package is content- only (IncludeBuildOutput=false) and its content is assembled by the project's BeforePack target (_UpdateTemplateVersions), which runs during pack regardless of --no-build, so packing with --no-build drops the duplicate build without losing any packaged content. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
- Upload the dry-run artifact with if: always() so the (partial) binlog is still collected when the build step fails, matching the publish job. - Emit binlog_path to $GITHUB_OUTPUT up-front in Build-TemplateApp.ps1 so a throwing build still exposes the binlog path to the upload step. - Mention the IOS_PROVISIONING_* fallbacks in the missing-profile error so the message isn't misleading when those variables are configured. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Addresses review feedback (PureWeen): - Security: pass every matrix-controlled value (project/display names, template, application/bundle IDs, platform, TFM, RID, variant) through env: and consume $env:... in the PowerShell/Fastlane steps, instead of interpolating repo- variable-derived values directly into command text. A quote/backtick/$() in a configured value is now treated as literal data, not parsed as script — matching the existing TEMPLATE_ARGS_JSON pattern. This matters most in publish, which holds signing/store secrets. - Error handling: the publish build now passes -CreateBinlog and uploads the binlog via a dedicated always() step, so signing/Xcode/MSBuild failures leave a binlog even when no package output is produced. - Regression: stop applying the matrix RID to the shared Android args. The sideload APK is built universal again (installs on any device/emulator, per the README); the RID is applied only to the Google Play AAB. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Reset LASTEXITCODE after expected Git trust probes so valid untrusted full-SHA dry runs do not fail after resolving successfully. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0e65f721-890b-4b33-939b-af632286de45
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0e65f721-890b-4b33-939b-af632286de45
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Validate paired Developer ID assets and required custom variant metadata. Fail TestFlight review conflicts when external distribution was requested, and replace source-text assertions with executable fake-tool regression tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Run the behavioral suite before matrix preparation, pin trusted checkouts to the tested SHA, and upload dedicated iOS ad-hoc and Mac Catalyst Developer ID binlogs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Reject custom matrix variant names that are not safe single path segments before they can reach recursive build-directory cleanup. Cover accepted normalized names and traversal or separator rejection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Build framework, SDK and templates from the same source commit. Restrict MAUI restore to verified local packages and validate resolved package hashes plus archive assembly provenance before exposing app artifacts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep duplicate identity validation while staging only runtime nupkgs and use the existing snupkg symbol format. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Exercise the production entry point without test-only helper injection and verify exact managed payload bytes inside Android assembly wrappers. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Override Arcade's default commit omission for non-date-stamped builds, preserving fail-closed payload verification. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 21e641be-a7f9-431e-b067-f011fa79aabf
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 21e641be-a7f9-431e-b067-f011fa79aabf
Keep workload installation and generated app builds on the source-selected SDK instead of different preinstalled feature bands. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 77db17ed-2e47-4ac3-aa4b-cdd72966c34d
This comment has been minimized.
This comment has been minimized.
|
Azure Pipelines: 3 pipeline(s) were filtered out due to trigger conditions. |
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Summary
Supersedes #36136.
Adds a manually triggered Template App Distribution workflow that builds the MAUI templates into tester apps for Android, iOS, Mac Catalyst, and Windows, in both blank and sample-content variants. The dry-run path (
publish=false) uploads installable or sideloadable GitHub artifacts;publish=trueoptionally ships to Google Play test tracks and TestFlight via fastlane, gated on the protectedtemplate-app-distributionenvironment secrets.Why this supersedes #36136
#36136 produced store-shaped artifacts that testers could not install or launch. Reported failures: Android had no APK, Windows showed an "install the .NET runtime" screen, iOS failed with
0xe800801f Attempted to install a Beta profile without the proper entitlement, and Mac Catalyst crashed on launch withEXC_CRASH (SIGKILL - Code Signature Invalid).This version provides a usable test path for every platform:
.aabwas produced (cannot be sideloaded)SelfContained=true+WindowsAppSDKSelfContained=true).app/ TestFlight-profile IPA installs nowhere.pkg/ Rosetta x64 build thenSIGKILL Code Signature Invalid.apponmacos-26, zip withditto, and ad-hoc re-sign inside-out so it launches on current macOS runners and test machinesCurrent .NET 11 Apple toolchain
Current .NET 11 Apple workloads require Xcode 26.6 with the iOS/macOS 26.5 SDK. The previous
macos-15jobs selected Xcode 26.3 / SDK 26.2 and failed withMT0180andMT4162. Both iOS and Mac Catalyst jobs now run onmacos-26and select Xcode 26.6.Full-SHA dry runs also reset PowerShell's native exit status after expected Git trust probes, preventing a successfully resolved source SHA from being reported as a failed prepare job.
Validation
Workflow run 31022037238 passed all eight blank/sample jobs for Android, iOS, Mac Catalyst, and Windows. Its Apple logs confirmed Xcode 26.6 and SDK 26.5.
Artifacts were then validated end-to-end from CI output, not only built:
adb install, launched, rendered, and remained alive.The sample-content app also exposed a separate template-source seed initialization defect on iOS 26.5. That source issue is fixed and empirically validated across iOS 26.5, iOS 18.5, Android 16, and Mac Catalyst in #37122; it is not a distribution-workflow failure.
See
.github/scripts/template-app-distribution/README.mdfor per-platform tester install steps, including the AltStore/Sideloadly requirement for the unsigned iOS device IPA.Setup still needed before
publish=trueworksThe
template-app-distributionGitHub Environment and its signing / store secrets must be created indotnet/maui. Dry-run artifact builds run without any secrets.Publishing from any source branch beyond the default branch also requires the repository variable
TEMPLATE_APP_TRUSTED_PUBLISH_BRANCHESto list that exact protected branch name. Wildcards such asrelease/*are rejected. Publish jobs deliberately do not create or upload MSBuild binlogs because structured logs can capture environment-derived signing and store credentials.Note on scope
Unlike #36136 this PR does not touch
src/Templates/src/templates/maui-mobile/MauiApp.1.csproj; that change was an incidental package-version edit from an older base and is now stale relative tomain(which has since moved those versions forward via #36074 and #35608). The workflow does not require it./cc @PureWeen @jfversluis
🔍 This PR was prepared by GitHub Copilot CLI on behalf of @kubaflo, standing in for @jfversluis while he is away. All four platform artifacts were launched, not just built.