fix(wrapper-generator): fail loudly on cmdlet file collisions - #3713
Conversation
A second operation resolving to an already-written cmdlet file now fails generation with the full collision list instead of silently overwriting it, which is the silent-drop failure mode AutoRest had. OData cast list/item pairs (owners/graph.user) now merge like plain pairs, and the sweep's collisions land as cited NamingOverrides entries: termStore and agreement-file stitches, default-singleton renames (SubSite, DefaultDrive, DefaultCalendarEvent), and nested navs the SDK never shipped. Remaining families are tracked on #3704.
There was a problem hiding this comment.
Pull request overview
This PR hardens the WrapperGenerator to prevent silent cmdlet loss by detecting and failing on cmdlet file name collisions, and expands the naming-override data/model to encode published-SDK renames/suppressions (including broad “suffix” matching for recurring navigations). It also adjusts GET list/item pairing to merge OData cast list/item pairs into a single dispatcher cmdlet, and updates tests/docs accordingly.
Changes:
- Detect cmdlet
.g.csfile collisions during generation and fail with a consolidated, operation-identifying error message. - Extend
NamingOverridesto support exact/prefix/suffix path matching and add many oracle-/directive-cited rename/suppression entries to resolve known collisions. - Merge cast list/item GET pairs (e.g.,
.../owners/graph.userwith.../owners/{id}/graph.user) into one Get-* dispatcher; add regression/unit tests and documentation updates.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/WrapperGenerator/README.md | Updates documentation to reflect collision-guard behavior and expanded overrides/testing counts. |
| tools/WrapperGenerator/PowerShellWrapperGenerationService.cs | Tracks written files to detect collisions and throws a consolidated exception at end of generation. |
| tools/WrapperGenerator/NamingOverrides.cs | Reworks override matching to Exact/Prefix/Suffix and adds many new rename/suppression entries with citations. |
| tools/WrapperGenerator/edge-cases/naming-edge-cases.md | Documents collision families and how they’re handled/resolved. |
| tools/WrapperGenerator/CmdletNaming.cs | Enhances list/item merge detection to support OData cast list/item pairing. |
| tools/WrapperGenerator.Tests/NamingTests.cs | Adds unit coverage for new overrides and cast list/item pairing behavior. |
| tools/WrapperGenerator.Tests/GenerationServiceRegressionTests.cs | Adds regression test ensuring collisions fail loudly and identify both operations. |
| tools/Build-WrapperModule.ps1 | Improves failure capture to surface the generator exception text in build output summaries. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
… data Derive-CollisionResolutions.ps1 replays the checked-in collision inventory (212 lines, 365 contested routes) against MgCommandMetadata and emits exact-match resolution data: 191 suppressions (routes the published SDK prunes) and 64 renames (published nouns), each entry carrying its oracle evidence. The files embed into the generator and apply only when UseCollisionData is set; -Validate fails on drift, and a new xunit test runs it on every `dotnet test` so staleness fails the suite instead of depending on someone remembering to run the script by hand. Derivation itself fails on any unclassified or ambiguous route. Only 2 cross-path variant merges exist in all of v1.0 (GroupPhoto, ShareListItem) - deferred with the singleton side kept, cataloged in crosspath-merge-edge-cases.md. Full 39-module v1.0 generation now produces zero collisions; 20 published commands that lost filename races are recovered; exact-name matches rise 5,042 -> 5,098. Also: cmdlets emit into a per-module namespace derived from the client namespace instead of the leftover MgPoC placeholder; Build-WrapperModule's generated csproj references Authentication by a relative path instead of an absolute one; its -Configuration parameter now actually reaches the wrapper generator's own build, not just the final module build; and a pre-existing nullable warning in the list/item pairing check is fixed. 121 tests pass.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 22 changed files in this pull request and generated no new comments.
Suppressed comments (2)
tools/Build-WrapperModule.ps1:152
- $lines is a PowerShell array; it does not reliably expose an instance IndexOf() method, so
$lines.IndexOf($exception)can throw when the wrapper generator fails (the exact path this new error-surfacing logic is meant to improve). Use[Array]::IndexOf($lines, $exception)(or a manual loop) to compute the index safely.
$lines = @($wrapperOut | ForEach-Object { "$_" })
$exception = $lines | Where-Object { $_ -match 'Unhandled exception|Exception:' } | Select-Object -First 1
$exceptionIndex = if ($exception) { $lines.IndexOf($exception) } else { -1 }
$result.Error = if ($exceptionIndex -ge 0) {
tools/WrapperGenerator/DerivedCollisionResolutions.cs:46
- Derived collision data lookup is currently API-version case-sensitive (
StringComparer.Ordinal). If a caller suppliesApiVersionwith different casing (e.g. "Beta" / "V1.0"), derived suppressions/renames will silently not apply and the generator may fail with avoidable collisions. Consider making the API-version dictionary case-insensitive (or normalizingApiVersion).
var result = new Dictionary<string, Tables>(StringComparer.Ordinal);
var assembly = typeof(DerivedCollisionResolutions).Assembly;
Peter Ombwa (peombwa)
left a comment
There was a problem hiding this comment.
Joywambui-maina - great work here on detecting and including the collision inventory. I wanted to share some feedback on PR structure that'll help both you and the reviewers going forward.
This PR bundles several independently valuable changes into one 6,600-line diff, which makes it quite hard to review confidently. As a general practice, each PR should do one thing - a single, self-contained change that a reviewer can understand, evaluate, and approve (or request changes on) without needing to hold the entire context of unrelated changes in their head at the same time.
Looking at the description, I'd suggest this could have been split into roughly four focused PRs, merged in order to avoid conflicts:
-
Collision guard - the
writtenCmdletFilestracking inPowerShellWrapperGenerationService.csand its regression test. This is the core safety net and could land first with zero naming-data dependencies. -
OData cast list/item pairing - the
IsListItemPairenhancement inCmdletNaming.cs, theTrailingCastMemberhelper, and its unit tests. A standalone behavior change with its own tests. -
Curated NamingOverrides expansion - the
PathMatch.Suffixmodel change, the ~30 new curated entries (SubSite, DefaultDrive, termStore stitching, Security nested navs, etc.), their naming tests, and the edge-case docs. This is the domain-knowledge-heavy part reviewers need to scrutinize most carefully. -
Derived collision-resolution pipeline -
Derive-CollisionResolutions.ps1,DerivedCollisionResolutions.cs, the embedded JSON data files, the drift-gate test, and the--api-version/--no-collision-dataCLI flags. This is a whole new subsystem with its own derivation, validation, and embedding strategy.
Each of these tells a clear story, is independently testable, and is small enough that a reviewer can give it proper attention. When they're combined, it's easy for reviewers to rubber-stamp the data files (which are the bulk of the diff) instead of carefully evaluating whether each entry is correct - which defeats the purpose of code review.
No need to split this one retroactively - let's get it across the line as-is. But for future work, let's keep the PRs focused on just one thing.
Let's also address the copilot comments.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2ed1ab3
into
feature/wrapper-module-packaging
Changes proposed in this pull request
Other links