Trial Nx affected/cache + per-project OS routing for monorepo builds#204
Draft
Redth wants to merge 21 commits into
Draft
Trial Nx affected/cache + per-project OS routing for monorepo builds#204Redth wants to merge 21 commits into
Redth wants to merge 21 commits into
Conversation
…d with no-op ProjectReference - Add Nx non-JS workspace (nx, nx.bat, .nx/nxw.js, nx.json) with @nx/dotnet - Add eng/nx/nx-msbuild-resolvers.js to bootstrap MSBuild SDK resolvers (Arcade) - Add report-only .github/workflows/nx-affected-report.yml + raegen/nx canary - Model hidden cross-tree edges via per-project project.json (DevFlow.Tests, Cli.UnitTests, plugins/dotnet-maui, tests/dotnet-maui, .github/plugin) - DevFlow integration tests reference samples/DevFlow.Sample as a no-op ProjectReference (ReferenceOutputAssembly=false, SkipGetTargetFrameworkProperties=true, SetTargetFramework=...) gated by DevFlowIntegrationPlatform; sample is now built by MSBuild as a real dependency, so fixtures no longer call BuildSampleAsync - Simplify devflow-integration.yml to invoke ./nx run :test:<platform> Verified locally: ./nx run Microsoft.Maui.DevFlow.Agent.IntegrationTests:test:maccatalyst builds DevFlow.Sample for net10.0-maccatalyst via the conditional ProjectReference and runs 113 tests (112 pass; 1 pre-existing WebView screenshot 400 unrelated to build). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Only Microsoft.Maui.DevFlow.Agent.IntegrationTests/project.json remains — it defines the per-platform test:<platform> targets that workflows invoke via Nx (and which @nx/dotnet cannot infer). Removed: - samples/DevFlow.Sample/project.json: build:<platform> targets are obsolete now that integration tests pull the sample in via no-op ProjectReference; @nx/dotnet infers a build target from the csproj. - src/Cli/Microsoft.Maui.Cli.UnitTests/project.json: cross-link to DevFlowAgentService.cs was speculative — no test references that file. - src/DevFlow/Microsoft.Maui.DevFlow.Tests/project.json: cache-correctness inputs to Agent's buildTransitive/*.targets are already covered by the project graph (Tests -> Agent project ref). - plugins/dotnet-maui/project.json + tests/dotnet-maui/project.json + .github/plugin/project.json: skills check/evaluate are run directly by skill-check.yml / skill-evaluation.yml workflows (which already invoke skill-validator binary), so they don't need to be in the Nx graph. Verified: ./nx show projects lists 23 real projects; DevFlow.Sample keeps inferred build/restore/clean/publish/run targets via @nx/dotnet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Goal: zero project.json files in the repo. Eliminates the last hand-written
Nx config wrapper around `dotnet test` per platform.
Restructure src/DevFlow/Microsoft.Maui.DevFlow.Agent.IntegrationTests/:
Android/Microsoft.Maui.DevFlow.Agent.IntegrationTests.Android.csproj
iOS/Microsoft.Maui.DevFlow.Agent.IntegrationTests.iOS.csproj
MacCatalyst/Microsoft.Maui.DevFlow.Agent.IntegrationTests.MacCatalyst.csproj
Windows/Microsoft.Maui.DevFlow.Agent.IntegrationTests.Windows.csproj
IntegrationTests.Common.props (shared MSBuild config)
*.cs (shared test sources)
Fixtures/*.cs
Each platform csproj is a 6-line wrapper that sets DevFlowSamplePlatform and
imports IntegrationTests.Common.props. The common props pulls in the test
sources via Compile globs reaching into the parent dir, references the
Driver, declares xunit/coverlet, stamps the platform identity into the
assembly via [AssemblyMetadata("DevFlowTestPlatform", ...)], and wires up
ONE unconditional no-op ProjectReference to DevFlow.Sample with
SetTargetFramework derived from the platform name. Each csproj sits in its
own subdirectory because @nx/dotnet keys project nodes by directory.
AppFixtureFactory now reads the platform from assembly metadata first,
falling back to DEVFLOW_TEST_PLATFORM env var (kept for ad-hoc overrides).
Nx now infers all four projects with their build/test targets directly from
MSBuild, and the project graph carries the real ProjectReference edge to
DevFlow.Sample (no implicitDependencies hack needed).
nx.json: drop --no-dependencies from the inferred build args because the
multi-TFM Sample ProjectReference fails GetTargetPath resolution under it;
add restore to the build dependsOn so MSBuild has assets before each build.
Workflow update: ./nx run ...IntegrationTests:test:<platform>
-> ./nx run ...IntegrationTests.<Platform>:test
Verified locally: ./nx run ...IntegrationTests.MacCatalyst:test runs 113
tests (111 pass; 2 UI flakes unrelated to build plumbing).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Non-gating GitHub Actions workflow that mirrors the existing per-product CI
across Linux/macOS/Windows but routes builds through Nx affected + raegen/nx
remote cache. Lets us measure cache hit rate and OS-by-OS work distribution
without disrupting the existing ci-devflow / ci-cli / ci-linux-gtk4 lanes.
- Single 'affected' job computes base/head once on Linux, exposes the count.
- Matrix build job runs on ubuntu-latest, macos-latest, windows-latest.
- Per-OS exclude lists keep each runner from attempting projects it can't
build (Linux skips MAUI multi-target; macOS/Windows skip Linux.Gtk4 family;
cross-OS integration test projects skipped on irrelevant lanes).
- Test step additionally skips platform integration tests (those still run
via devflow-integration.yml with proper device/sim/emulator setup).
- continue-on-error everywhere; this is purely shadow telemetry today.
Hardcoded exclusion lists are a stopgap. Long-term answer is project tags
(os:linux/macos/windows/any) emitted by an inference plugin from each
csproj's TFMs, then `--projects=tag:os:${{ matrix.os }}`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces the centralized exclusion list in nx-build-shadow.yml with a
per-project MSBuild property. Each project (or a Directory.Build.props in
its tree) declares where it can run:
<PropertyGroup>
<NxBuildableOn>linux;macos;windows</NxBuildableOn> <!-- default -->
<!-- or -->
<NxBuildableOn>linux</NxBuildableOn>
<NxBuildableOn>macos</NxBuildableOn>
</PropertyGroup>
eng/nx/nx-os-tags.js (createNodesV2) reads the property from the csproj
itself, then walks Directory.Build.props files up to the workspace root,
defaulting to all three OSes if nothing declares otherwise. Each value
becomes an os:<name> tag, plus an os:any tag when all three are present
so workflows can do --projects=tag:os:linux,tag:os:any.
Project-level overrides:
- platforms/Linux.Gtk4/Directory.Build.props -> linux (libgtk-4-dev,
libwebkitgtk-6.0-dev only ship on Linux)
- IntegrationTests.iOS/MacCatalyst -> macos (need real Apple sims/devices
for the tests, even though the assembly itself is managed-only)
- IntegrationTests.Windows -> windows (needs a Windows host)
- IntegrationTests.Android -> default (Android emulator works on any host)
- Everything else -> default (managed-only builds run anywhere with
workloads, including iOS/macOS class libraries on Windows)
Workflow change: nx-build-shadow.yml now uses
--projects=tag:os:${{ matrix.os-tag }}
instead of a hand-maintained exclude list. To onboard a project with a
host constraint in future, add one MSBuild line; nothing else changes.
Verified:
./nx show projects --projects=tag:os:linux -> 23 (Gtk4 family + Any)
./nx show projects --projects=tag:os:macos -> 19 (Apple ITs + Any)
./nx show projects --projects=tag:os:windows -> 18 (Windows IT + Any)
./nx show projects --projects=tag:os:any -> 17 (defaults only)
Limitation documented in the plugin: the regex doesn't evaluate MSBuild
conditions, so put NxBuildableOn directly in the csproj when the value
needs to depend on a property like $(TargetFramework).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces the hand-rolled JavaScript Nx plumbing with the experimental DotnetNx packages from Redth/Maui.BuildHelpers: - Removes eng/nx/nx-msbuild-resolvers.js (regex-based MSBuild SDK resolver bootstrap). nxdn computes the same environment via Microsoft.Build.Locator. - Removes eng/nx/nx-os-tags.js (regex-based <NxBuildableOn> tag emitter). Replaced by @redth/dotnet-nx Nx plugin, which delegates to 'nxdn project-metadata' for full MSBuild evaluation. This honours conditional PropertyGroups and imported props that the regex could not. - Rewrites nx and nx.bat as thin shims around 'dotnet nxdn nx --'. - Adds .config/dotnet-tools.json pinning DotnetNx.Tool 0.1.0-local. - Vendors eng/nx/redth-dotnet-nx-0.1.0-alpha.0.tgz (npm plugin not yet published to npmjs; lives on GitHub Packages under @Redth scope). - Adds scripts/nxdn(.cmd) shims so the npm plugin can locate the dotnet local tool via DOTNET_NX_NXDN. Verified locally: ./nx show projects returns 26 projects, OS tag filtering (tag:os:macos|windows|linux|any) produces the expected per-host subsets. Note: this is a local-only trial. CI workflows (.github/workflows/nx-affected-report.yml, nx-build-shadow.yml, devflow-integration.yml) still reference the deleted JS files via 'node eng/nx/nx-msbuild-resolvers.js --export-github-env' and will need to be updated to call 'dotnet nxdn export-env --format github' (or the setup-nxdn composite action from Maui.BuildHelpers) before this can be adopted on CI. Adoption also depends on DotnetNx.Tool and @redth/dotnet-nx being published to feeds CI can restore from. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Removes the last of the bespoke Nx plumbing in favour of nxdn and the
composite actions from Redth/Maui.BuildHelpers/DotnetNx:
- Deleted nx and nx.bat checked-in wrappers; devs and CI now invoke Nx
exclusively through 'dotnet nxdn nx -- ...' (or 'nxdn nx -- ...' once
DotnetNx.Tool is on PATH globally via the setup-nxdn action).
- Deleted .nx/nxw.js and removed nx.json's 'installation' block. Nx is
resolved through the standard package.json devDependency at
node_modules/.bin/nx, which nxdn falls through to automatically.
- Rewrote .github/workflows/nx-affected-report.yml and nx-build-shadow.yml
to use Redth/Maui.BuildHelpers/DotnetNx/actions/{setup-nxdn,
affected-info,run-affected} composite actions, replacing the inline
setup-dotnet/setup-node/npm-ci/resolver-export/raegen-nx steps.
- Updated devflow-integration.yml to call setup-nxdn per platform job
and run integration tests via 'nxdn nx -- run <project>:test'. Windows
job switches from pwsh + .\nx.bat to bash + nxdn.
Action references are pinned to commit
ab2fef3e080396a22b6fc3b6d1a9a9eb3c67b701 of Redth/Maui.BuildHelpers.
Note: setup-nxdn currently runs 'dotnet tool update --global DotnetNx.Tool'
and assumes the package is reachable from a default NuGet feed. Until
DotnetNx.Tool is published to nuget.org (or this repo's NuGet.config is
amended to include Redth's GitHub Packages feed), CI runs of these
workflows will fail at the tool-install step. Local dev keeps working via
the .config/dotnet-tools.json manifest committed earlier.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Removes the eng/nx/redth-dotnet-nx-0.1.0-alpha.0.tgz vendored copy. The plugin is now declared as a normal devDependency at version '0.1.0-alpha.0' and resolved through GitHub Packages via a new .npmrc: @Redth:registry=https://npm.pkg.github.com always-auth=true package-lock.json was hand-edited to swap the file: resolution for the GitHub Packages tarball URL and to drop the now-stale integrity hash. The first authenticated 'npm install' after the package is published will repopulate the integrity field. Same caveat as the DotnetNx.Tool migration: until Redth runs publish-dotnetnx.yml on Maui.BuildHelpers, neither local nor CI 'npm ci'/'npm install' can resolve @redth/dotnet-nx. Existing node_modules/ directories continue to work because the plugin is already extracted on disk. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@redth/dotnet-nx and DotnetNx.Tool are now published to GitHub Packages on Redth/Maui.BuildHelpers, and the composite actions have a v0.1 tag. Workflow / action updates: - Bumped every composite-action ref from the SHA pin to @v0.1. - Replaced the inline 'nxdn nx -- run <project>:test' calls in the iOS, MacCatalyst, and Windows integration jobs with the new Redth/Maui.BuildHelpers/DotnetNx/actions/run-target@v0.1 action. Android keeps the inline call because it must execute inside reactivecircus/android-emulator-runner's script (the emulator is only alive within that step). - Added .github/actions/auth-gh-packages as a local composite action that runs setup-dotnet + setup-node, configures NuGet credentials for the Redth-DotnetNx feed via 'dotnet nuget add source --store-password- in-clear-text' (user-level NuGet.Config), writes ~/.npmrc with an _authToken for npm.pkg.github.com, and runs 'npm ci'. This lives at user level so nothing sensitive lands in the repo. - Every Nx/integration workflow now declares 'permissions: contents: read, packages: read' and calls the auth action before setup-nxdn. setup-nxdn is invoked with 'setup-node: false' since the auth action has already provisioned Node. Package wiring: - .config/dotnet-tools.json: 0.1.0-local -> 0.1.0-alpha.1. - package.json + package-lock.json: @redth/dotnet-nx file:reference -> 0.1.0-alpha.1 resolved through https://npm.pkg.github.com. - NuGet.config gains the Redth-DotnetNx feed declaration (no creds; CI creds are written at runtime, devs use ~/.nuget/NuGet/NuGet.Config). - .npmrc declares '@Redth:registry=https://npm.pkg.github.com'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
v0.2 adds 'env' (multiline KEY=VALUE) and 'script' (Bash setup sourced before nxdn) inputs on run-target and run-affected. setup-nxdn and affected-info are unchanged. No call sites need new inputs today, but floating to v0.2 lets us compose per-step env later without falling back to extra 'run:' blocks. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Marketplace search found no comprehensive 'GH Packages auth' action that covers both npm and NuGet, but the official actions/setup-node@v6 has first-class support for scoped GH Packages registries via 'registry-url' + 'scope' + NODE_AUTH_TOKEN. That is strictly better than us hand-rolling ~/.npmrc. The auth-gh-packages composite now passes registry-url and scope to setup-node and supplies NODE_AUTH_TOKEN to 'npm ci' instead of writing .npmrc lines itself. The NuGet side keeps 'dotnet nuget add source ... --store-password-in-clear-text' at user-level, which is the canonical pattern (no widely-used marketplace action exists for GH Packages NuGet feeds today). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bumps every Redth/Maui.BuildHelpers/DotnetNx/actions/* pin from @v0.2
to @v0.3 and folds in three of the four new composites:
- affected-matrix replaces nx-build-shadow's static 3-OS matrix with a
dynamic matrix derived from per-OS affected sets. The 'affected' job
now emits 'matrix' + 'has-work' outputs; the 'build' job uses
matrix: ${{ fromJSON(needs.affected.outputs.matrix) }} and only
fans out the runners that have affected work. A docs-only PR spawns
zero build legs instead of three.
- setup-cache is added to every build leg (nx-build-shadow.yml 'build'
matrix and nx-affected-report.yml 'cache-canary' job). It caches
.nx/cache + artifacts/bin + artifacts/obj keyed on the hashes of
csproj/props/targets/global.json/Directory.Packages.props/nx.json/
package.json.
- doctor is added to nx-affected-report.yml's 'report' job so
'nxdn diagnose' output lands in the workflow step summary for free.
configure-nx is intentionally skipped: our nx.json already commits the
plugin entries, so we don't need bootstrap-time configuration.
Also collapses the per-OS install_workloads / native_deps matrix
columns into 'if: matrix.osTag == ...' guards now that affected-matrix
defines the matrix shape.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…auth GitHub Packages requires auth for public feeds (only ghcr.io ever got anonymous reads on the GH Packages stack), so we can't avoid the credential plumbing entirely - but we can replace the bespoke composite with two stock mechanisms. NuGet auth - workflow-level env var: NuGet honors NuGetPackageSourceCredentials_<SourceName> with the form 'Username=USER;Password=TOKEN'. Set once at workflow level, it provides credentials for the existing feed entry in NuGet.config, with no runtime 'dotnet nuget add source' call and no user-level NuGet.Config mutation. The feed key was renamed Redth-DotnetNx -> RedthDotnetNx so the env-var lookup name has no hyphens. npm auth - actions/setup-node@v6 native support: setup-node already manages a scoped ~/.npmrc when given registry-url + scope; npm ci consumes it via NODE_AUTH_TOKEN. This is the canonical pattern from the official Node action. Each of the 8 call sites in nx-affected-report.yml, nx-build-shadow.yml, and devflow-integration.yml now has three inline steps (setup-node, npm ci, setup-nxdn) instead of the composite + setup-nxdn pair. The .github/actions/auth-gh-packages directory is deleted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
reactivecircus/android-emulator-runner@v2 is a JS action that runs on the host runner, so the nxdn tool installed by the prior setup-nxdn step is at ~/.dotnet/tools/nxdn, but PATH propagation across the action's spawned script: subshell isn't guaranteed across runner-image revisions. Prepend $HOME/.dotnet/tools explicitly and add a 'which nxdn' line so failures surface clearly if the binary is missing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace reactivecircus/android-emulator-runner@v2 (warm-up + run) and
the AVD cache with the prebuilt
ghcr.io/maui-containers/maui-emulator-linux:android${API}-dotnet10.0
image. The image ships a baked AVD plus Appium and boots the emulator
on container start with a Docker HEALTHCHECK.
- Drop Setup Java, manual KVM udev rule, and AVD cache (the image
carries the AVD and runs the emulator under its own kvm-aware
startup scripts; passing --device /dev/kvm is sufficient).
- Drop both reactivecircus runner invocations.
- docker run -d the emulator container with --device /dev/kvm
--privileged and ports 5554/5555 mapped, then poll
.State.Health.Status until healthy and adb connect localhost:5555.
- Set DEVFLOW_TEST_ANDROID_SERIAL=localhost:5555 at job level.
- Use the standard run-target@v0.3 action for the nx invocation --
nothing has to live inside an emulator-runner subshell anymore.
- Tail emulator container logs on failure.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace six workflows (_build.yml, ci-cli.yml, ci-devflow.yml, ci-linux-gtk4.yml, nx-build-shadow.yml, nx-affected-report.yml) with one .github/workflows/ci.yml. The new workflow: - Runs DotnetNx doctor + affected-matrix to compute a per-OS build matrix from <NxBuildableOn> project tags. - Uploads the affected project graph and per-OS project lists as the nx-affected-report artifact. - Per OS leg, runs restore/build/test/pack via run-affected@v0.3. Integration test projects (Android/iOS/MacCatalyst/Windows) are excluded; they continue to run from devflow-integration.yml. - Conditionally runs the CLI NativeAOT publish smoke test on macOS when the CLI was actually built on this run. - Uploads test results and nupkgs per OS. Path filters and product-specific reusable workflows are gone -- project selection is entirely driven by Nx affected analysis. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The four integration-test projects share a stable 'Microsoft.Maui.DevFlow.Agent.IntegrationTests.*' prefix; Nx's --exclude accepts the same project-spec syntax as --projects (globs/tags/etc.), so we can drop the hand-maintained comma-separated list in favour of one glob. New IntegrationTests.<Platform> projects will be excluded automatically. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The maui-containers/maui-emulator-linux image ships with a Docker HEALTHCHECK that waits for sys.boot_completed=1 plus Appium /status. Two corrections to our adoption: - Drop --privileged; only --device /dev/kvm is required for KVM passthrough. --privileged adds capabilities we don't need and can hide permission bugs. - Remove the early-exit on 'unhealthy'. The HEALTHCHECK declares a 120s --start-period during which the container reports 'starting', not 'unhealthy', so our prior bail-after-5-iterations branch was unreachable but misleading. Just wait for 'healthy' and let the outer 10-minute timeout bound the wait. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- AGENTS.md: replace per-product GH Actions workflow guidance with the consolidated Nx-driven ci.yml story; document <NxBuildableOn> for controlling OS fan-out; note Comet remains self-contained. - AGENTS.md (Build Commands): add 'nx affected' commands; clarify cibuild.cmd is the AzDO official-pipeline path, not the CI you'll see on a GH PR. - copilot-instructions.md: drop the multi-page per-product workflow template -- new products now need zero GH Actions edits, just an optional <NxBuildableOn>. AzDO checklist unchanged. - testing.instructions.md: update CI Matrix section to reference ci.yml + 'nx affected -t test' instead of _build.yml + cibuild.sh. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cec998a to
aca43d6
Compare
Maui.BuildHelpers PR #3 adds <NxTags>/<NxTag> MSBuild inputs that the @redth/dotnet-nx plugin folds into Nx's tags field. Use this to remove the last hard-coded project names from workflow YAML. - IntegrationTests.Common.props: declare <NxTags>type:integration-test;device:$(DevFlowSamplePlatform)</NxTags> on the shared props consumed by all four platform integration projects. - ci.yml: replace the glob exclude 'Microsoft.Maui.DevFlow.Agent.IntegrationTests.*' with the semantic 'tag:type:integration-test'. - devflow-integration.yml: replace each run-target call (which named Microsoft.Maui.DevFlow.Agent.IntegrationTests.<Platform> explicitly) with nxdn nx -- run-many --projects=tag:device:<platform> -t test. - Bump every Maui.BuildHelpers/DotnetNx action ref from @v0.3 to @v0.4 for the tag metadata support. - AGENTS.md + copilot-instructions.md: document the <NxTags>/<NxTag> pattern and link to the upstream tag reference. Net effect: the YAML no longer mentions any integration-test project name. Adding a fifth platform variant (e.g. tvOS) is now a one-csproj change with no workflow edits. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…strategy # Conflicts: # .github/workflows/_build.yml # .github/workflows/ci-cli.yml # AGENTS.md # NuGet.config
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
As maui-labs grows into a true monorepo (DevFlow, CLI, Linux GTK4, samples, integration tests), the per-product workflow split is starting to feel coarse: a touch in
Directory.Packages.propsrebuilds every product, and shared infra likeeng/**orglobal.jsoninvalidates everything regardless of what actually depends on it. This PR sets up a non-gating trial of an Nx +@nx/dotnetdriven affected/cache pipeline alongside the existing CI so we can measure whether it pays off before committing to a migration.Nothing here changes the existing
ci-devflow,ci-cli,ci-linux-gtk4, ordevflow-integrationworkflows — they remain the source of truth for green PRs. Everything new is opt-in telemetry.What's in here
Nx workspace plumbing
@nx/dotnet22.7.0 with checked-innx/nx.batwrappers and a small Node bootstrap (eng/nx/nx-msbuild-resolvers.js) that exports the MSBuild SDK resolver environment so@nx/dotnet's evaluator can findMicrosoft.DotNet.Arcade.Sdkfrom the dnceng feeds.nx.jsontargetDefaultsto--no-restore(drops the inferred--no-dependencies), withrestoreadded tobuild.dependsOn. The fallback was needed because no-op multi-TFMProjectReferences failGetTargetPathresolution under--no-dependencies.Eliminated
project.jsonfiles (7 of them). Most metadata that was hand-rolled intoproject.jsonis now inferred by@nx/dotnetfrom MSBuild itself, or expressed as realProjectReferenceedges, or moved out of the graph entirely.plugins/dotnet-maui,tests/dotnet-maui,.github/plugin) were already invoked directly by their own workflows; removed from the graph.samples/DevFlow.Sample/andMicrosoft.Maui.Cli.UnitTests/are now fully inferred.project.jsonfor per-platformtest:android|ios|maccatalyst|windowstargets. Replaced with four per-platform csprojs (Android/,iOS/,MacCatalyst/,Windows/) each importing a sharedIntegrationTests.Common.props. Sources stay shared via<Compile Include="..\*.cs" />. Platform identity is stamped into each assembly via[AssemblyMetadata("DevFlowTestPlatform", ...)]soAppFixtureFactoryno longer needs an env var. The Sample is built via a real (no-op)ProjectReferencefrom each platform test project, which means the dependency edge appears in the Nx graph instead of needingimplicitDependencies.NxBuildableOnfor self-declared OS routing. Each project owns where it can run via an MSBuild property:The new
eng/nx/nx-os-tags.js(createNodesV2) reads it from the csproj first, then walksDirectory.Build.propsfiles up to the workspace root, and stampsos:<name>tags onto each project. Defaults to all three OSes plus anos:anyumbrella tag, matching the "managed-only code can build anywhere" reality (an-iosclass library still getsos:anyeven though it targets Apple APIs).Two new workflows, both
continue-on-error: true:nx-affected-report.yml: lists the affected projects and ships a Linux cache canary usingraegen/nx.nx-build-shadow.yml: matrix on Linux, macOS, and Windows. Each runner doesaffected -t restore/build/test --projects=tag:os:${{ matrix.os-tag }}. No centralized exclusion list; the only special case is skipping platform integration tests because those need real devices/sims handled bydevflow-integration.yml.Verified locally
./nx show projectsreturns 23 projects, all inferred (zeroproject.jsonfiles in the tree)../nx show projects --projects=tag:os:macosreturns 19 (Apple ITs + everyos:any)../nx run Microsoft.Maui.DevFlow.Agent.IntegrationTests.MacCatalyst:testbuilds the Sample fornet10.0-maccatalystvia the no-opProjectReference, launches the app, and runs 113 tests (111 pass; 2 unrelated UI-property flakes).artifacts/bin/objfrom cache after deletion.Things to keep an eye on
raegen/nxremote cache behavior on actual GitHub runs is still untested; the shadow workflow exists specifically to surface that signal.<NxBuildableOn>via regex, so values inside conditional<PropertyGroup Condition="...">blocks are picked up regardless of the condition. If you need a value that depends on$(TargetFramework)etc., put it directly in the csproj where you have that context.nx.jsontargetDefaults.buildnow omits--no-dependenciesglobally. Nx's^buildchain still orders things; MSBuild's incremental check makes the redundant resolution cheap, but worth watching on cold-cache CI runs.Marking draft because this is a trial — happy to keep iterating based on what the first few CI runs surface.