Skip to content

Add ApplicationArtifact support for Windows App SDK - #35973

Open
Redth wants to merge 5 commits into
net11.0from
redth/plan-maui-artifacts-net11
Open

Add ApplicationArtifact support for Windows App SDK#35973
Redth wants to merge 5 commits into
net11.0from
redth/plan-maui-artifacts-net11

Conversation

@Redth

@Redth Redth commented Jun 17, 2026

Copy link
Copy Markdown
Member

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!

Description of Change

Adds ApplicationArtifact support for modern SDK-style Windows App SDK applications through a new targets-only Microsoft.Maui.ApplicationArtifacts.Windows package.

Artifact metadata is now owned by the platform that produces the final output instead of being inferred by MAUI from project inputs. Android and Apple expose authoritative common metadata in their platform follow-ups:

This PR supplies the missing Windows producer. It reads WinAppSDK's final output properties and item lists rather than searching output directories, de-duplicates normalized paths, excludes obj intermediates, and makes publish-path items available to AfterTargets="Publish" consumers.

Package and consumption

Microsoft.Maui.ApplicationArtifacts.Windows is a normal NuGet dependency package containing only a neutral buildTransitive target. It has no MAUI runtime, Resizetizer, or MAUI build-task dependency.

Microsoft.Maui.Core references it only for Windows target frameworks, and the maui-windows workload includes it. Non-MAUI WinUI, Uno, and other SDK-style Windows App SDK applications can also reference the package directly.

The targets activate only for real, non-design-time Windows App SDK application builds with Exe or WinExe output. Set EnableWindowsApplicationArtifacts=false to opt out.

Windows outputs

The producer supports unpackaged, MSIX, and sparse applications and classifies the final outputs WinAppSDK actually creates:

  • Primary
  • PayloadDirectory
  • Bundle
  • StoreUpload
  • DeploymentManifest
  • Symbols
  • Certificate
  • InstallScript
  • DependencyPackage
  • LandingPage
  • Support

Every item carries common application identity/title/version metadata plus Windows metadata such as PackageFormat, PackageType, PackageVersion, Architecture, RuntimeIdentifier, Signed, BundlePlatforms, EntryPoint, DeploymentDirectory, IsPrimary, and PrimaryArtifact when available.

Packaged metadata comes from the final Appx manifest and preserves unresolved ms-resource: references. Unpackaged metadata comes from the resolved application output. ApplicationIdGuid is emitted only when the effective Windows identity is a GUID.

Classic .wapproj packaging is intentionally deferred because it uses a different packaging pipeline owned outside this repository.

Testing

  • 12 focused package/target tests pass locally against the rebuilt package; Windows-only packaging scenarios run in CI.
  • Direct PackageReference restore of Microsoft.Maui.ApplicationArtifacts.Windows is verified.
  • Synthetic target tests cover unpackaged/MSIX/sparse collection, output classification and de-duplication, inert project shapes, downstream overrides, metadata mapping, and GetApplicationArtifacts;Publish in one MSBuild invocation.
  • Windows CI tests exercise MAUI unpackaged/MSIX Build and Publish plus standalone WinAppSDK unpackaged Build and Publish using a runtime-generated signing certificate where packaging requires it.
  • A Windows CI store-and-sideload scenario requests an MSIX bundle and .appinstaller, then verifies Bundle, StoreUpload, and DeploymentManifest roles.

Issues Fixed

N/A

Enrich platform-produced ApplicationArtifact items with MAUI application metadata via GetApplicationArtifactsDependsOn instead of introducing MAUI-specific artifact item groups.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35973

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35973"

@github-actions github-actions Bot added the area-tooling XAML & C# Hot Reload, XAML Editor, Live Visual Tree, Live Preview, Debugging label Jun 17, 2026
Redth and others added 2 commits June 17, 2026 16:02
Document that platform build/artifact-production steps populate ApplicationArtifact items before MAUI metadata enrichment.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-Model Review — PR #35973

Verdict: ❌ NEEDS_CHANGES
Confidence: High (unanimous across all 4 models)

All 4 models independently identified issues that should be addressed before merging.

Model agreement: gemini (NEEDS_CHANGES/1), gpt-5.5 (NEEDS_CHANGES/1), opus-4.6 (NEEDS_CHANGES/1), opus-4.8 (NEEDS_CHANGES/2)

Findings: 4 unique issues identified.

<ApplicationIdGuid>11111111-2222-3333-4444-555555555555</ApplicationIdGuid>
<ApplicationDisplayVersion>2.3</ApplicationDisplayVersion>
<ApplicationVersion>42</ApplicationVersion>
</PropertyGroup>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The relative path $(MSBuildThisFileDirectory)..\..\..\ assumes the test directory is located within the MAUI repository bin/ folder. However, on Azure Pipelines CI, TestDirectory is located under AGENT_TEMPDIRECTORY (e.g., .../test-dir/ApplicationArtifactsAreEnrichedWithMauiMetadata/). As a result, traversing up 3 directories points to AGENT_TEMPDIRECTORY/src/Workload/... instead of the repository, causing MSB4019 missing import errors and failing the integration tests.

<ApplicationDisplayVersion>2.3</ApplicationDisplayVersion>
<ApplicationVersion>42</ApplicationVersion>
</PropertyGroup>
<Import Project="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\src\Workload\Microsoft.Maui.Sdk\Sdk\Microsoft.Maui.Sdk.After.targets'))" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import is rooted at the generated test project's MSBuildThisFileDirectory and walks up three directories to find src/Workload. That happens to work for local fallback TestDirectory under the repo's bin/test-dir, but in Azure Pipelines TestEnvironment.GetTestDirectoryRoot() uses AGENT_TEMPDIRECTORY/test-dir, so ......\src does not point at the MAUI checkout and the new Build integration test cannot import Microsoft.Maui.Sdk.After.targets. The current CI failures in the Build integration lanes are consistent with this PR-introduced test path issue.

(found by: 5, 4)

<Target Name="SeedApplicationArtifacts">
<ItemGroup>
<ApplicationArtifact Include="$(MSBuildProjectDirectory)/artifacts/platform/android/MyArtifactApp-Signed.apk">
<PackageFormat>apk</PackageFormat>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The explicit <Import Project="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\\..\\..\\src\\Workload\\...'))" /> assumes the project directory is 3 levels deep inside the repo root. Locally this resolves from <repo>/bin/test-dir/<testname>/ to <repo>/. In CI, TestDirectory is under $AGENT_TEMPDIRECTORY/test-dir/<testname>/, so 3 parent traversals land in the agent temp directory—not the repo. The targets file won't exist there, causing a build error. This is the likely cause of the CI 'Build integration tests' failures on both macOS and Windows.

Lines="@(ApplicationArtifact->'%(Filename)%(Extension)|%(PackageFormat)|%(ApplicationTitle)|%(ApplicationName)|%(ApplicationId)|%(ApplicationIdGuid)|%(ApplicationDisplayVersion)|%(ApplicationVersion)|%(Signed)|%(PackageId)|%(PlatformName)|%(BundleIdentifier)')"
Overwrite="true" />
</Target>
<Target Name="WritePublishApplicationArtifactsMetadata" DependsOnTargets="SeedApplicationArtifacts;_AddMauiApplicationArtifactMetadata">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both WriteGetApplicationArtifactsMetadata and WritePublishApplicationArtifactsMetadata depend directly on SeedApplicationArtifacts;_AddMauiApplicationArtifactMetadata, so they exercise the metadata target in isolation rather than through a real GetApplicationArtifacts/Publish invocation; the actual wiring is only checked indirectly by the string Assert.Contains on the depends-on file (line 178). The Publish variant therefore adds no coverage beyond the Get variant. This is understandable while the platform GetApplicationArtifacts target is unavailable in net11, but consider collapsing the duplicate target (or asserting the real wiring) once the platform producers land.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 2 findings

See inline comments for details.

<ApplicationDisplayVersion>2.3</ApplicationDisplayVersion>
<ApplicationVersion>42</ApplicationVersion>
</PropertyGroup>
<Import Project="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\src\Workload\Microsoft.Maui.Sdk\Sdk\Microsoft.Maui.Sdk.After.targets'))" />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] Build & MSBuild — This import is resolved relative to the generated test project. In CI TestDirectory is under $(AGENT_TEMPDIRECTORY)/test-dir, so $(MSBuildThisFileDirectory)......\src... points outside the checkout (for example /home/vsts/work/src/...) and the test fails with MSB4019 before it can validate the target. Resolve the import from TestEnvironment.GetMauiDirectory()/a repo-root property instead of the temp project location.

</ApplicationArtifact>
</ItemGroup>
</Target>
<Target Name="WriteGetApplicationArtifactsMetadata" DependsOnTargets="SeedApplicationArtifacts;_AddMauiApplicationArtifactMetadata">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[moderate] Regression Prevention — The test target depends on _AddMauiApplicationArtifactMetadata directly, so it validates the private target body rather than the GetApplicationArtifactsDependsOn extension path this PR adds. A regression where the hook is not actually invoked by GetApplicationArtifacts/Publish could still pass because lines 157 and 167 call the target manually. Seed the item, invoke the targets through $(GetApplicationArtifactsDependsOn)/the public artifacts target, and assert the metadata after that path runs.

@MauiBot MauiBot added s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Jun 19, 2026
MauiBot

This comment was marked as outdated.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 2 findings

See inline comments for details.

<ApplicationDisplayVersion>2.3</ApplicationDisplayVersion>
<ApplicationVersion>42</ApplicationVersion>
</PropertyGroup>
<Import Project="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\src\Workload\Microsoft.Maui.Sdk\Sdk\Microsoft.Maui.Sdk.After.targets'))" />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ Error — This import is resolved relative to the generated temp test project via $(MSBuildThisFileDirectory), not relative to the MAUI repo. In CI, TestDirectory is under AGENT_TEMPDIRECTORY, so ..\..\..\src\Workload\... points outside /home/vsts/work/1/s and the test fails before exercising the PR fix. Please build the import path from TestEnvironment.GetMauiDirectory() or otherwise pass an absolute repo-rooted path into the generated project.

</ApplicationArtifact>
</ItemGroup>
</Target>
<Target Name="WriteGetApplicationArtifactsMetadata" DependsOnTargets="SeedApplicationArtifacts;_AddMauiApplicationArtifactMetadata">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Warning — The test drives the private _AddMauiApplicationArtifactMetadata target directly, so it can pass even if the public $(GetApplicationArtifactsDependsOn) hook is not honored by GetApplicationArtifacts/Publish or runs in the wrong order. Please exercise the public target path, or make this target depend on $(GetApplicationArtifactsDependsOn), so the regression test covers the actual integration contract added in Microsoft.Maui.Sdk.After.targets.

@MauiBot MauiBot added s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates and removed s/agent-fix-win AI found a better alternative fix than the PR labels Jun 19, 2026
MauiBot

This comment was marked as outdated.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 2 findings

See inline comments for details.

<ApplicationDisplayVersion>2.3</ApplicationDisplayVersion>
<ApplicationVersion>42</ApplicationVersion>
</PropertyGroup>
<Import Project="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\src\Workload\Microsoft.Maui.Sdk\Sdk\Microsoft.Maui.Sdk.After.targets'))" />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ Error — This import resolves from the generated temp project directory, not the MAUI repo root. In CI TestDirectory is under the agent temp/test-dir (and locally under bin/test-dir), so ..\\..\\..\\src\\Workload... points at a non-existent sibling src tree and the test will fail before exercising the target. Build the path from TestEnvironment.GetMauiDirectory() or pass the repo-root path as an MSBuild property instead.

</ApplicationArtifact>
</ItemGroup>
</Target>
<Target Name="WriteGetApplicationArtifactsMetadata" DependsOnTargets="SeedApplicationArtifacts;_AddMauiApplicationArtifactMetadata">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Warning — The test seeds fake ApplicationArtifact items and directly invokes the private _AddMauiApplicationArtifactMetadata target, so it does not prove the public GetApplicationArtifacts/publish extension path actually returns enriched platform artifacts. A regression in target ordering or platform artifact production could still ship while this test passes; prefer invoking the real public targets or depending through $(GetApplicationArtifactsDependsOn) in the harness.

MauiBot

This comment was marked as outdated.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 5 findings

See inline comments for details.

<ApplicationDisplayVersion>2.3</ApplicationDisplayVersion>
<ApplicationVersion>42</ApplicationVersion>
</PropertyGroup>
<Import Project="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\src\Workload\Microsoft.Maui.Sdk\Sdk\Microsoft.Maui.Sdk.After.targets'))" />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[critical] Build & MSBuild — Import path is broken on CI and on Linux/macOS

$(MSBuildThisFileDirectory) evaluates to the generated project directory under the integration-test temp root, not the MAUI repo root. On CI that makes ..\..\..\src\Workload\... resolve outside the checkout, and on Linux/macOS the backslashes are literal path characters rather than directory separators. The test can hard-fail before exercising the metadata target.

Resolve the targets file from C# using TestEnvironment.GetMauiDirectory() and inject/pass an absolute path with platform-correct separators.

<ApplicationDisplayVersion>2.3</ApplicationDisplayVersion>
<ApplicationVersion>42</ApplicationVersion>
</PropertyGroup>
<Import Project="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\src\Workload\Microsoft.Maui.Sdk\Sdk\Microsoft.Maui.Sdk.After.targets'))" />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [major] Regression Prevention — Test double-imports a targets file already imported by the MAUI workload

The generated maui template project already imports Microsoft.Maui.Sdk.After.targets through the installed workload. The explicit source import can append _AddMauiApplicationArtifactMetadata a second time and makes the GetApplicationArtifactsDependsOn string assertion ambiguous. Prefer a minimal non-MAUI MSBuild project for this unit-style target test, or otherwise ensure the source targets are imported exactly once.

</ApplicationArtifact>
</ItemGroup>
</Target>
<Target Name="WriteGetApplicationArtifactsMetadata" DependsOnTargets="SeedApplicationArtifacts;_AddMauiApplicationArtifactMetadata">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [major] Regression Prevention — Test calls the private implementation target instead of the public hook

WriteGetApplicationArtifactsMetadata and WritePublishApplicationArtifactsMetadata depend directly on _AddMauiApplicationArtifactMetadata, so the test would still pass if GetApplicationArtifactsDependsOn were removed, misspelled, or pointed at the wrong target. Invoke the public target/dependency chain and verify the enriched @(ApplicationArtifact) items are returned through that path.

Assert.Contains("_AddMauiApplicationArtifactMetadata", File.ReadAllText(dependsOnFile), StringComparison.Ordinal);
AssertApplicationArtifactMetadata(File.ReadAllLines(getArtifactsFile));

Assert.True(DotnetInternal.Build(projectFile, "Debug", target: "WritePublishApplicationArtifactsMetadata", framework: $"{DotNetCurrent}-android", properties: BuildProps, output: _output),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [moderate] Logic and Correctness — Publish coverage duplicates the GetApplicationArtifacts path

The publish assertion builds a custom target with the same direct dependency on _AddMauiApplicationArtifactMetadata, so it does not verify whether the real Publish path triggers artifact enrichment. Either document and test that Publish consumes the same GetApplicationArtifactsDependsOn extension path, or add the missing publish hook and verify it through the real publish target.

</ItemGroup>

<PropertyGroup>
<GetApplicationArtifactsDependsOn>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 [minor] Build & MSBuild — Consider guarding the dependency-chain extension

The property group appends _AddMauiApplicationArtifactMetadata for every project importing Microsoft.Maui.Sdk.After.targets, including configurations such as UseMauiEssentials=true that may never produce application artifacts. The target is a no-op when @(ApplicationArtifact) is empty, so this is likely harmless, but a UseMaui/artifact-intent condition or documentation would make the global extension explicit.

MauiBot

This comment was marked as outdated.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 2 findings

See inline comments for details.

<ApplicationDisplayVersion>2.3</ApplicationDisplayVersion>
<ApplicationVersion>42</ApplicationVersion>
</PropertyGroup>
<Import Project="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\src\Workload\Microsoft.Maui.Sdk\Sdk\Microsoft.Maui.Sdk.After.targets'))" />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[critical] Build & MSBuild — This import resolves from $(MSBuildThisFileDirectory), which is the generated temp test project directory, not the repo root. In CI the path walks up from the temp project and can miss src/Workload/Microsoft.Maui.Sdk/Sdk/Microsoft.Maui.Sdk.After.targets, causing MSB4019 before the regression test can run. Use the repo MAUI directory/test environment path instead of a project-relative path.

</ApplicationArtifact>
</ItemGroup>
</Target>
<Target Name="WriteGetApplicationArtifactsMetadata" DependsOnTargets="SeedApplicationArtifacts;_AddMauiApplicationArtifactMetadata">

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] Regression Prevention/Test Coverage — The test directly depends on the private _AddMauiApplicationArtifactMetadata target here and again in WritePublishApplicationArtifactsMetadata, so it can pass even if the production GetApplicationArtifactsDependsOn hook is not actually used by GetApplicationArtifacts/Publish. Exercise the public depends-on path rather than invoking the private target directly.

MauiBot

This comment was marked as outdated.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 1 findings

See inline comments for details.

<ApplicationDisplayVersion>2.3</ApplicationDisplayVersion>
<ApplicationVersion>42</ApplicationVersion>
</PropertyGroup>
<Import Project="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\..\..\src\Workload\Microsoft.Maui.Sdk\Sdk\Microsoft.Maui.Sdk.After.targets'))" />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] Build & MSBuild / integration test correctness — This import resolves relative to the temporary test project directory, not the MAUI repo root. In CI TestDirectory is under AGENT_TEMPDIRECTORY/test-dir, and locally it is under bin/test-dir, so $(MSBuildThisFileDirectory)..\..\..\src\Workload\... points at a non-existent src sibling and the test will fail before exercising the metadata target. It can also double-import Microsoft.Maui.Sdk.After.targets when the SDK already imports it. Use TestEnvironment.GetMauiDirectory() to build an absolute path if the test must import source targets, or rely on the SDK import from the built workload.

MauiBot

This comment was marked as outdated.

Align MAUI augmentation with the landed Android and Apple artifact contracts, including Android publish item recreation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e7b83c8e-9ff9-4712-87cf-f7a4c970f792
Copilot AI review requested due to automatic review settings July 14, 2026 17:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds .NET MAUI-provided metadata defaults onto the shared @(ApplicationArtifact) item type so that platform SDKs’ app artifact items (including items recreated during publish) automatically carry consistent MAUI application metadata for net11p6 scenarios.

Changes:

  • Define ApplicationArtifact item-definition defaults in Microsoft.Maui.Sdk.After.targets for MAUI app metadata (ID, title/name, versions), without overriding platform-supplied metadata.
  • Add an integration test that seeds @(ApplicationArtifact) items and then simulates an Android-style “publish recreation” to verify the MAUI metadata persists via item-definition defaults.
  • Add a design doc describing ownership boundaries and the metadata enrichment model for @(ApplicationArtifact).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/Workload/Microsoft.Maui.Sdk/Sdk/Microsoft.Maui.Sdk.After.targets Adds ItemDefinitionGroup defaults for MAUI ApplicationArtifact metadata driven by project properties.
src/TestUtils/src/Microsoft.Maui.IntegrationTests/SimpleTemplateTest.cs Adds a test that validates MAUI metadata is present on both initial and recreated @(ApplicationArtifact) items.
docs/design/ApplicationArtifact.md Documents how platform SDKs own artifact creation while MAUI supplies shared metadata defaults.

Comment on lines +16 to +25
<ItemDefinitionGroup>
<ApplicationArtifact>
<ApplicationId Condition="'$(ApplicationId)' != ''">$(ApplicationId)</ApplicationId>
<ApplicationIdGuid Condition="'$(ApplicationIdGuid)' != ''">$(ApplicationIdGuid)</ApplicationIdGuid>
<ApplicationName Condition="'$(ApplicationTitle)' != ''">$(ApplicationTitle)</ApplicationName>
<ApplicationTitle Condition="'$(ApplicationTitle)' != ''">$(ApplicationTitle)</ApplicationTitle>
<ApplicationDisplayVersion Condition="'$(ApplicationDisplayVersion)' != ''">$(ApplicationDisplayVersion)</ApplicationDisplayVersion>
<ApplicationVersion Condition="'$(ApplicationVersion)' != ''">$(ApplicationVersion)</ApplicationVersion>
</ApplicationArtifact>
</ItemDefinitionGroup>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this actually go in dotnet/android and dotnet/macios?

What if you are an Uno or MonoGame project? ... then what about WindowsAppSDK?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think maybe the android/macios stuff can get pushed down into those repos - i'll make PR's for that. For windows I'm thinking maybe another package that could be added to any windowsappsdk app that achieves the same, though I'll check with someone on that team if they might consider just a PR to their repo directly to add this too.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e7b83c8e-9ff9-4712-87cf-f7a4c970f792
Copilot AI review requested due to automatic review settings July 15, 2026 17:26
@Redth Redth changed the title Add MAUI ApplicationArtifact metadata for net11p6 Add ApplicationArtifact support for Windows App SDK Jul 15, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 8 comments.

Comment on lines 52 to 56
if (TestEnvironment.IsMacOS)
{
if (true) return; // Skip: "This test is designed for testing a windows build."
if (true)
return; // Skip: "This test is designed for testing a windows build."
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would maybe just discard the changes like this in this file as it's whitepace only for no reason.

Comment on lines 87 to 91
if (TestEnvironment.IsMacOS)
{
if (true) return; // Skip: "This test is designed for testing a windows build."
if (true)
return; // Skip: "This test is designed for testing a windows build."
}
Comment on lines 121 to 125
if (!TestEnvironment.IsWindows)
{
if (true) return; // Skip: "Running Windows templates is only supported on Windows."
if (true)
return; // Skip: "Running Windows templates is only supported on Windows."
}
Comment on lines 165 to 169
if (!TestEnvironment.IsWindows)
{
if (true) return; // Skip: "Running Windows templates is only supported on Windows."
if (true)
return; // Skip: "Running Windows templates is only supported on Windows."
}
Comment on lines +232 to +236
if (!TestEnvironment.IsWindows)
{
if (true)
return; // Skip: "Running Windows templates is only supported on Windows."
}
Comment on lines +286 to +290
if (!TestEnvironment.IsWindows)
{
if (true)
return; // Skip: "Running Windows templates is only supported on Windows."
}
Comment on lines +338 to +342
if (!TestEnvironment.IsWindows)
{
if (true)
return; // Skip: "Running Windows templates is only supported on Windows."
}
Comment on lines +47 to +51
if (!TestEnvironment.IsWindows)
{
if (true)
return; // Skip: "Packing the Windows Core target framework is only supported on Windows CI."
}
jonathanpeppers pushed a commit to dotnet/android that referenced this pull request Jul 17, 2026
## Why this change is necessary

The platform SDK owns the authoritative final application artifact metadata for all Android consumers. Project properties are not sufficient because custom manifests can override them and `GenerateApplicationManifest=false` bypasses generated values entirely.

This follows the `@(ApplicationArtifact)` contract introduced by [#11674](#11674) and provides the producer-owned metadata consumed by [dotnet/maui#35973](dotnet/maui#35973) without making the values MAUI-specific defaults.

## What changed

- Extended the existing final merged-manifest read path to expose the resolved package, application `android:label`, `android:versionName`, and `android:versionCode`.
- Stamped every APK/AAB `@(ApplicationArtifact)` with:
  - `ApplicationId`
  - `ApplicationTitle`
  - `ApplicationName`
  - `ApplicationDisplayVersion`
  - `ApplicationVersion`
- Preserved `PackageId`, `PackageFormat`, `Signed`, and per-ABI `Abi` metadata.
- Preserved all producer-owned common and Android metadata when `Publish` recreates artifact items under `$(PublishDir)`.
- Kept `GetApplicationArtifactsDependsOn` overrides effective for both direct target results and Publish recreation.
- Kept resource-backed labels such as `@string/app_name` unchanged instead of resolving a locale.
- Documented final merged-manifest precedence and resource-reference behavior.

## Tests

Focused host-side coverage validates generated manifests, custom manifest precedence, `GenerateApplicationManifest=false`, resource-backed labels, APK/AAB signed and unsigned outputs, per-ABI items, extension overrides, `GetApplicationArtifacts`, and Publish recreation.

```text
Passed: 9
Failed: 0
Skipped: 0
Duration: 40 s
```

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
rolfbjarne pushed a commit to dotnet/macios that referenced this pull request Jul 23, 2026
…6110)

## Summary

Adds producer-owned common application metadata to every Apple `@(ApplicationArtifact)` item.

- Extends `ReadAppManifest` with `CFBundleName` and `CFBundleShortVersionString`.
- Reads metadata from the final compiled app bundle `Info.plist`, so custom manifest values win.
- Preserves unresolved or localized plist references as written instead of selecting a locale.
- Stamps `.app`, `.ipa`, `.pkg`, and `.xcarchive` artifacts with `ApplicationId`, `ApplicationTitle`, `ApplicationName`, `ApplicationDisplayVersion`, and `ApplicationVersion` while retaining existing Apple metadata.
- Runs before downstream `GetApplicationArtifactsDependsOn` extensions, preserving their ability to override producer metadata.
- Documents the common metadata contract and adds task/post-build coverage across Apple platforms, output formats, generated/custom manifests, `GenerateApplicationManifest=false`, `GetApplicationArtifacts`, and `Publish`.

Follow-up to #25723. Coordinated with dotnet/maui#35973 and dotnet/android#12123.

---------

Co-authored-by: redth <jondick@gmail.com>
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 23, 2026
@MauiBot MauiBot added the s/agent-changes-requested AI agent recommends changes - found a better alternative or issues label Jul 24, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

@Redth — new AI review results are available based on this last commit: 860afcb.

Gate No Tests Confidence Low Platform Windows Build


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ⚠️ SKIPPED

No tests were detected in this PR.

Recommendation: Add tests to verify the fix using the write-tests-agent.


📱 UI Tests — Button,Label,Layout

Detected UI test categories: Button,Label,Layout

Deep UI tests — 339 passed, 0 failed across 3 categories on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Button 65/66 ✓
Label 91/94 ✓
Layout 183/187 ✓
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

📋 Pre-Flight — Context & Validation

Issue: #35973 - Add ApplicationArtifact support for Windows App SDK
PR: #35973 - Add ApplicationArtifact support for Windows App SDK
Platforms Affected: Windows build/publish tooling; package/workload insertion surfaces
Files Changed: 15 implementation/documentation/build, 2 test

Key Findings

  • PR adds a targets-only Microsoft.Maui.ApplicationArtifacts.Windows package that defines GetApplicationArtifacts for Windows App SDK projects and adds the package to workload manifests/solutions/package flows.
  • Current implementation collects Windows App SDK build/publish outputs from authoritative MSBuild properties/items and classifies primary packages, payload directories, bundles, store uploads, appinstaller manifests, symbols, certs, install scripts, dependencies, landing pages, and support files.
  • Prior review comments on an earlier commit identified CI-invalid test imports and weak hook coverage; current head no longer contains that SimpleTemplateTest.cs path-based import and now uses Windows-specific integration coverage.
  • New expert review risk: the target works around MSBuild single-execution behavior for unpackaged publish with a special refresh target, but packaged publish collection still reuses _CollectMicrosoftMauiWindowsApplicationArtifacts, which may be stale if GetApplicationArtifacts ran before Publish in the same project instance.
  • Gate was skipped externally because no tests were detected by the gate phase; per instructions, gate verification was not re-run.

Code Review Summary

Verdict: NEEDS_DISCUSSION
Confidence: low
Errors: 0 | Warnings: 1 | Suggestions: 1

Key code review findings:

  • ⚠️ src/Workload/Microsoft.Maui.ApplicationArtifacts.Windows/buildTransitive/Microsoft.Maui.ApplicationArtifacts.Windows.targets:38 publish collection depends on the same _CollectMicrosoftMauiWindowsApplicationArtifacts target used by GetApplicationArtifacts; MSBuild executes a target once, so GetApplicationArtifacts;Publish can leave packaged publish-path artifacts stale for AfterTargets="Publish" consumers.
  • 💡 src/TestUtils/src/Microsoft.Maui.IntegrationTests/WindowsApplicationArtifactsPackageTest.cs:412 has synthetic coverage for unpackaged WriteBuildApplicationArtifacts;Publish, but not an equivalent packaged same-invocation stale-target scenario.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #35973 Add a new Windows App SDK ApplicationArtifact producer package, reference it from Core/workload manifest, and cover synthetic + template Windows artifact scenarios ⚠️ PENDING (Gate skipped) src/Workload/Microsoft.Maui.ApplicationArtifacts.Windows/**, src/Core/src/Core.csproj, integration tests, workload/package files Original PR

🔬 Code Review — Deep Analysis

Code Review — PR #35973

Independent Assessment

What this changes: Adds a targets-only Windows App SDK artifact producer package and wires it into MAUI package/workload flows. The target creates @(ApplicationArtifact) items for Windows build/publish outputs and enriches them with common identity/version metadata.
Inferred motivation: Complete the cross-platform ApplicationArtifact contract for Windows so downstream consumers can inspect final app artifacts without MAUI scanning output directories.

Reconciliation with PR Narrative

Author claims: Windows should produce authoritative ApplicationArtifact items from WinAppSDK output properties/items, support unpackaged/MSIX/sparse outputs, and expose metadata for MAUI and standalone Windows App SDK consumers.
Agreement/disagreement: The implementation broadly matches the narrative and current tests cover many roles. The remaining concern is target re-execution semantics when build artifact collection and publish happen in a single MSBuild project instance.

Prior Review Reconciliation

Prior ❌ Error Finding Source Status Evidence
Earlier integration test imported MAUI targets using a generated-project-relative path invalid in CI kubaflo / MauiBot prior reviews on commit 45c2182 ✅ Obsolete Current PR head no longer changes SimpleTemplateTest.cs; Windows artifact tests use TestEnvironment.GetMauiDirectory() or normal template/package imports.
Earlier test invoked a private metadata target directly instead of the public hook kubaflo / MauiBot prior reviews on commit 45c2182 ✅ Obsolete Current tests exercise GetApplicationArtifacts, package references, and Windows template build/publish scenarios.

Blast Radius Assessment

  • Runs for all instances: Only for projects with Windows target platform, Windows App SDK package directory, Exe/WinExe, non-design-time, and not .wapproj.
  • Startup impact: No runtime startup impact; build/publish target graph only.
  • Static/shared state: No runtime static state; MSBuild item/property state can persist within a project instance and is the main failure mode.

CI Status

  • Required-check result: undetermined locally because gh is unauthenticated; gate result supplied by caller was skipped.
  • Classification: coverage gap / undetermined.
  • Action taken: Capped confidence low and generated try-fix candidates instead of marking LGTM.

Findings

⚠️ Warning — Packaged publish collection can be stale after build artifact collection

src/Workload/Microsoft.Maui.ApplicationArtifacts.Windows/buildTransitive/Microsoft.Maui.ApplicationArtifacts.Windows.targets:38

GetApplicationArtifacts and the publish hook both depend on _CollectMicrosoftMauiWindowsApplicationArtifacts. MSBuild executes a target at most once per project instance. The PR handles the same-invocation problem for unpackaged publish with _RefreshMicrosoftMauiWindowsUnpackagedApplicationArtifactsForPublish, but packaged publish still relies on the shared collector target. If a consumer runs GetApplicationArtifacts;Publish, AfterTargets="Publish" consumers can observe build-time packaged artifacts rather than a freshly collected publish set.

💡 Suggestion — Add packaged same-invocation coverage

src/TestUtils/src/Microsoft.Maui.IntegrationTests/WindowsApplicationArtifactsPackageTest.cs:412

The synthetic unpackaged test covers WriteBuildApplicationArtifacts;Publish, but packaged synthetic coverage should also prove that publish-only packaged outputs are visible to AfterTargets="Publish" consumers when GetApplicationArtifacts ran earlier in the invocation.

Failure-Mode Probing

  • GetApplicationArtifacts;Publish for unpackaged app: PR has a dedicated refresh target and synthetic coverage.
  • GetApplicationArtifacts;Publish for packaged app: shared collector may already be marked complete, leaving stale ApplicationArtifact items.
  • Design-time build: disabled by target conditions.
  • Non-Windows or library project: disabled by target conditions.

Verdict: NEEDS_DISCUSSION

Confidence: low
Summary: The product direction and most implementation details are sound, but packaged publish target ordering deserves either a target-graph fix or explicit regression coverage proving the current graph is safe.


🛠️ Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix-1 Add packaged publish refresh target that rematerializes previously collected packaged items ❌ Fail 1 target file Smoke test passed, but self-review found it preserves stale cached item identities.
2 try-fix-2 Remove main collector from publish path and re-evaluate packaged Primary/PayloadDirectory from publish-time properties ❌ Fail 1 target file Focused stale-primary synthetic test passed, but auxiliary packaged roles can remain stale/missing.
3 try-fix-3 Use nested MSBuild project instance to bypass target-once semantics and return fresh publish artifacts ❌ Fail 1 target file Refreshed property-backed roles, but lost item-backed outputs such as AppInstallerFilePath/DeploymentManifest.
PR PR #35973 Targets-only Windows App SDK artifact producer with synthetic and template integration coverage ⚠️ Gate skipped 17 files Current PR remains preferable to failed alternatives; consider adding packaged same-invocation regression coverage.

Cross-Pollination

Model Round New Ideas? Details
gpt-5.5 / maui-expert-reviewer 1 Yes Identified target-once risk for packaged GetApplicationArtifacts;Publish; proposed separate build/publish collectors.
claude-opus-4.6 / maui-expert-reviewer 2 Yes Proposed publish hook self-sufficiency via current publish-time properties; became try-fix-2.
claude-opus-4.7 / maui-expert-reviewer 3 Yes Proposed nested MSBuild fresh instance; became try-fix-3.
gpt-5.3-codex / maui-expert-reviewer 4 No runnable candidate Suggested a non-cached same-instance recollector after packaging, but that requires broad collector-body extraction/duplication or a test-only change, both outside a meaningfully better surgical alternative.

Exhausted: Yes — meaningful alternatives were explored across cached rematerialization, publish-time property refresh, and nested project-instance refresh. Remaining options are either broad refactors duplicating the collector body or test-only additions.
Selected Fix: PR #35973 — no alternative candidate passed all regression checks or was demonstrably better. Recommended follow-up is to add a focused packaged GetApplicationArtifacts;Publish same-invocation regression test if the team wants to lock down the suspected target-ordering edge case.


🏁 Report — Final Recommendation

Comparative Analysis — PR #35973

Candidates compared

Rank Candidate Regression result Assessment
1 pr Gate skipped; no candidate regression failure recorded Best available implementation. It adds the Windows App SDK ApplicationArtifact producer, package/workload wiring, and broad synthetic plus Windows template coverage. Expert review found one packaged same-invocation publish risk, but no STEP 5a alternative resolved that risk without failing another scenario.
2 pr-plus-reviewer Not a complete production fix; reviewer feedback is test-only Applies the expert reviewer's safely actionable feedback as a sandbox coverage candidate: add packaged GetApplicationArtifacts;Publish regression coverage. This is useful follow-up work, but without a safe production target-graph change it does not improve the shipped fix and may simply expose the unresolved edge.
3 try-fix-2 Failed Best failed alternative. It refreshes packaged Primary and PayloadDirectory from publish-time properties and passed the focused stale-primary assertion, but it leaves auxiliary packaged roles such as Bundle, StoreUpload, DeploymentManifest, Symbols, Certificate, InstallScript, DependencyPackage, and Support stale or missing.
4 try-fix-3 Failed Bypasses target-once semantics with a nested MSBuild project instance and refreshes property-backed roles, but loses target-created item-backed outputs such as AppInstallerFilePath/DeploymentManifest unless broad serialization plumbing is added.
5 try-fix-1 Failed Adds a packaged publish refresh target, but it re-adds @(_MicrosoftMauiWindowsApplicationArtifactDistinct) from the previous collector run, preserving stale identities rather than forcing fresh publish collection.

Winning candidate

Winner: pr

The raw PR is the single winning candidate because every STEP 5a try-fix candidate failed regression/self-review checks, and candidates that failed regression tests must rank below candidates without a recorded regression failure. The reviewer-enhanced variant is valuable as follow-up test coverage but is not a complete production fix; it does not safely address the packaged target-once issue that the reviewer identified.

Recommended follow-up

Before merge, the author should add packaged same-invocation coverage for GetApplicationArtifacts;Publish or otherwise prove that packaged publish ApplicationArtifact items are fresh for AfterTargets="Publish" consumers. If that test exposes the suspected stale-state failure, a broader packaged recollection design is needed rather than the simple STEP 5a alternatives.


🧭 Next Steps — review latest findings

No alternative fix was selected for this run. Review the session findings and CI results before merging.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 1 findings

See inline comments for details.

and '$(DesignTimeBuild)' != 'true'
and '$(BuildingProject)' == 'true'"
BeforeTargets="Publish"
DependsOnTargets="_PrepareMicrosoftMauiWindowsApplicationArtifactsForPublish;_CollectMicrosoftMauiWindowsApplicationArtifacts;_RefreshMicrosoftMauiWindowsUnpackagedApplicationArtifactsForPublish" />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

[major] Build & MSBuild_CollectMicrosoftMauiWindowsApplicationArtifactsForPublish still depends on _CollectMicrosoftMauiWindowsApplicationArtifacts, the same target that GetApplicationArtifacts runs. MSBuild executes a target only once per project instance, so a packaged Windows consumer that invokes GetApplicationArtifacts;Publish will not recollect after GenerateMsixPackage has produced publish-time AppPackages properties/items; AfterTargets="Publish" consumers can keep stale build-path ApplicationArtifact items or miss publish-created roles. The PR already adds a distinct unpackaged refresh for this target-once issue, but the packaged path needs equivalent fresh collection or regression coverage proving this same-invocation path is safe.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 24, 2026
Comment on lines +134 to +137
"Microsoft.Maui.ApplicationArtifacts.Windows": {
"kind": "library",
"version": "@VERSION@"
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a new package? If this is meant to be optional, could it just be in the Build.Tasks package instead? You'd opt into it with an MSBuild property.

Comment on lines 52 to 56
if (TestEnvironment.IsMacOS)
{
if (true) return; // Skip: "This test is designed for testing a windows build."
if (true)
return; // Skip: "This test is designed for testing a windows build."
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would maybe just discard the changes like this in this file as it's whitepace only for no reason.

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.Build.NoTargets">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like removing this project (and putting the changes in an existing one) would significantly simplify this PR: ProjectReference, sln files, etc.

Comment on lines +22 to +24
## Windows App SDK

`Microsoft.Maui.ApplicationArtifacts.Windows` is a targets-only `buildTransitive` package with no MAUI runtime, Resizetizer, or MAUI build-task dependency. `Microsoft.Maui.Core` depends on it only for Windows target frameworks, and non-MAUI Windows App SDK applications can reference it directly. It activates for real, non-design-time Windows App SDK application builds with `Exe` or `WinExe` output. Set `EnableWindowsApplicationArtifacts` to `false` to disable it. Classic `.wapproj` packaging is not supported.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not understanding why a new package is needed at all.

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please check Jon's suggestions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-tooling XAML & C# Hot Reload, XAML Editor, Live Visual Tree, Live Preview, Debugging s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants