You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
winapp find-api landed in #744 (the port of the winmd tool, tracked by #652). One of #652's goals was to avoid standing up a second copy of logic winapp already has. The port did that where it mattered most — Services/ApiSearch/ uses the shared PathSafety helper in 14 places, and #744 extended that helper rather than forking it.
Two places were missed. Both are in new find-api code only, neither is a regression, and find-api has not shipped in a release yet — so this is cleanup, not a fix for a broken user contract. Filed as follow-up so #744 could merge on its existing review.
1. find-api resolves the NuGet global packages folder itself, and gets it wrong
What is wrong:NuGetResolver.GetNuGetPackagesDir() (src/winapp-CLI/WinApp.Cli/Services/ApiSearch/NuGetResolver.cs:1597) looks only at the NUGET_PACKAGES environment variable, then falls back to %USERPROFILE%\.nuget\packages. It ignores the globalPackagesFolder setting in nuget.config.
winapp already resolves this correctly. INugetService.GetNuGetGlobalPackagesDir() (Services/INugetService.cs:52) is implemented with SettingsUtility.GetGlobalPackagesFolder (Services/NugetService.cs:82), which honors the environment variable and the nuget.config setting.
Show me: in a repo whose nuget.config sets <add key="globalPackagesFolder" value="D:\packages" />:
winapp find-api "app notification" on a packages.config project → the package directories are looked up under %USERPROFILE%\.nuget\packages, are not there, and the APIs report as not found (FindPackagesFromConfig, NuGetResolver.cs:1199).
On any project → Windows SDK projection XML docs are not found, so results come back without descriptions (DiscoverSdkXmlDocs, NuGetResolver.cs:416).
Expected in both cases: the packages are found in the configured folder.
Modern SDK-style projects are not affected on the main path — FindPackagesFromAssets reads the packageFolders recorded in project.assets.json instead of guessing.
Why it matters:find-api exists to answer from the project's real metadata. On a machine with a redirected package folder it silently answers "does not exist" for APIs the project can actually compile against, which is the exact failure mode the command is meant to prevent.
Smallest fix: have the two call sites use INugetService.GetNuGetGlobalPackagesDir() instead of the private helper, and delete the helper.
This is not a drop-in swap — there is a design question first.NugetSourceProvider loads its settings rooted at _configRoot ?? currentDirectory (Services/NugetSourceProvider.cs:74-75), but find-api indexes whatever directory --project points at, which need not be the working directory. Reusing the existing service as-is would read the wrong nuget.config hierarchy for an out-of-tree project. Decide whether to:
construct/scope the settings root to the project directory being indexed (preferred — it matches what the rest of find-api already does), or
accept cwd-rooted settings and document the limitation.
Wiring cost, once that is settled: inject INugetService into ApiMetadataService (Services/ApiSearch/ApiMetadataService.cs:65-69, which currently takes four unrelated services), pass the resolved directory through the static ApiCacheBuilder (ApiCacheBuilder.cs:22, called at :76) into NuGetResolver.FindPackagesWithWinMd (NuGetResolver.cs:21), and down to the two call sites.
2. find-api hardcodes the Windows Kits install path
What is wrong:NuGetResolver.FindWindowsSdkWinMd (NuGetResolver.cs:1263) builds its search root as %ProgramFiles(x86)%\Windows Kits\10\UnionMetadata (NuGetResolver.cs:1265) and returns nothing when that directory is absent.
winapp elsewhere resolves the kit root properly: CsWinRTMetadataShimService (Services/CsWinRTMetadataShimService.cs:196-197) reads HKLM\SOFTWARE\Microsoft\Windows Kits\Installed Roots\KitsRoot10, mirroring what cswinrt itself does.
Show me: on a machine with the Windows SDK installed to a non-default root (for example D:\Kits\10), winapp find-api "app notification" --project sdk finds no Windows SDK metadata and reports version unknown, while winapp's cswinrt path on the same machine locates the kit fine. Expected: find-api finds the same SDK.
Why it matters:--project sdk is documented as the way to query before a project exists. On a non-default SDK install it silently has nothing to query.
Smallest fix: extract the KitsRoot10 registry resolution out of CsWinRTMetadataShimService into a small shared helper (registry first, %ProgramFiles(x86)% fallback) and call it from both. Do not add a third inline copy inside NuGetResolver — that would defeat the point of this issue.
Deliberately out of scope
Listed so they are not re-litigated:
Helpers/ProjectAssetsFileReader.cs vs NuGetResolver.FindPackagesFromAssets — both read project.assets.json, but produce different things: the helper returns a dotnet package list–shaped package list, the resolver returns per-package .winmd and XML-doc paths. Merging them would mean one function serving two unrelated outputs.
Helpers/NuGetVersionHelper.Compare vs NuGetResolver.MajorMinor/ReleaseSortKey — the helper implements full SemVer precedence; the resolver matches WinAppSDK release channels on major.minor. Different grammars for different jobs; sharing would muddy both.
NuGetResolver.cs file size (~1,700 lines) — it is long because it handles five discovery sources (assets file, packages.config, winmds.lock.json, project references, SDK/runtime probing), not because it repeats itself. There is no dedupe fix here; if it is worth revisiting it is a separate cohesion question.
NuGetResolver.FindPackagesFromWinmdsLockfile vs IWinmdsLockfileService.TryReadAsync — the resolver already shares WinmdsLockfileService.LockfileName and WinmdsLockfileJsonContext; only the deserialize-plus-schema-check is duplicated. Reusing the service would mean threading a DI dependency into a static path for a handful of lines. Cost is roughly equal to the benefit; skip unless item 1 makes that plumbing available anyway.
Acceptance
find-api honors globalPackagesFolder from nuget.config, with a test covering the packages.config path.
find-api locates a Windows SDK installed to a non-default root, sharing one KitsRoot10 resolution with CsWinRTMetadataShimService.
No new duplicate of either resolution is introduced.
Context
winapp find-apilanded in #744 (the port of thewinmdtool, tracked by #652). One of #652's goals was to avoid standing up a second copy of logicwinappalready has. The port did that where it mattered most —Services/ApiSearch/uses the sharedPathSafetyhelper in 14 places, and #744 extended that helper rather than forking it.Two places were missed. Both are in new
find-apicode only, neither is a regression, andfind-apihas not shipped in a release yet — so this is cleanup, not a fix for a broken user contract. Filed as follow-up so #744 could merge on its existing review.1.
find-apiresolves the NuGet global packages folder itself, and gets it wrongWhat is wrong:
NuGetResolver.GetNuGetPackagesDir()(src/winapp-CLI/WinApp.Cli/Services/ApiSearch/NuGetResolver.cs:1597) looks only at theNUGET_PACKAGESenvironment variable, then falls back to%USERPROFILE%\.nuget\packages. It ignores theglobalPackagesFoldersetting innuget.config.winappalready resolves this correctly.INugetService.GetNuGetGlobalPackagesDir()(Services/INugetService.cs:52) is implemented withSettingsUtility.GetGlobalPackagesFolder(Services/NugetService.cs:82), which honors the environment variable and thenuget.configsetting.Show me: in a repo whose
nuget.configsets<add key="globalPackagesFolder" value="D:\packages" />:winapp find-api "app notification"on a packages.config project → the package directories are looked up under%USERPROFILE%\.nuget\packages, are not there, and the APIs report as not found (FindPackagesFromConfig,NuGetResolver.cs:1199).DiscoverSdkXmlDocs,NuGetResolver.cs:416).Expected in both cases: the packages are found in the configured folder.
Modern SDK-style projects are not affected on the main path —
FindPackagesFromAssetsreads thepackageFoldersrecorded inproject.assets.jsoninstead of guessing.Why it matters:
find-apiexists to answer from the project's real metadata. On a machine with a redirected package folder it silently answers "does not exist" for APIs the project can actually compile against, which is the exact failure mode the command is meant to prevent.Smallest fix: have the two call sites use
INugetService.GetNuGetGlobalPackagesDir()instead of the private helper, and delete the helper.This is not a drop-in swap — there is a design question first.
NugetSourceProviderloads its settings rooted at_configRoot ?? currentDirectory(Services/NugetSourceProvider.cs:74-75), butfind-apiindexes whatever directory--projectpoints at, which need not be the working directory. Reusing the existing service as-is would read the wrongnuget.confighierarchy for an out-of-tree project. Decide whether to:find-apialready does), orWiring cost, once that is settled: inject
INugetServiceintoApiMetadataService(Services/ApiSearch/ApiMetadataService.cs:65-69, which currently takes four unrelated services), pass the resolved directory through the staticApiCacheBuilder(ApiCacheBuilder.cs:22, called at:76) intoNuGetResolver.FindPackagesWithWinMd(NuGetResolver.cs:21), and down to the two call sites.2.
find-apihardcodes the Windows Kits install pathWhat is wrong:
NuGetResolver.FindWindowsSdkWinMd(NuGetResolver.cs:1263) builds its search root as%ProgramFiles(x86)%\Windows Kits\10\UnionMetadata(NuGetResolver.cs:1265) and returns nothing when that directory is absent.winappelsewhere resolves the kit root properly:CsWinRTMetadataShimService(Services/CsWinRTMetadataShimService.cs:196-197) readsHKLM\SOFTWARE\Microsoft\Windows Kits\Installed Roots\KitsRoot10, mirroring whatcswinrtitself does.Show me: on a machine with the Windows SDK installed to a non-default root (for example
D:\Kits\10),winapp find-api "app notification" --project sdkfinds no Windows SDK metadata and reports versionunknown, whilewinapp's cswinrt path on the same machine locates the kit fine. Expected:find-apifinds the same SDK.Why it matters:
--project sdkis documented as the way to query before a project exists. On a non-default SDK install it silently has nothing to query.Smallest fix: extract the
KitsRoot10registry resolution out ofCsWinRTMetadataShimServiceinto a small shared helper (registry first,%ProgramFiles(x86)%fallback) and call it from both. Do not add a third inline copy insideNuGetResolver— that would defeat the point of this issue.Deliberately out of scope
Listed so they are not re-litigated:
Helpers/ProjectAssetsFileReader.csvsNuGetResolver.FindPackagesFromAssets— both readproject.assets.json, but produce different things: the helper returns adotnet package list–shaped package list, the resolver returns per-package.winmdand XML-doc paths. Merging them would mean one function serving two unrelated outputs.Helpers/NuGetVersionHelper.ComparevsNuGetResolver.MajorMinor/ReleaseSortKey— the helper implements full SemVer precedence; the resolver matches WinAppSDK release channels on major.minor. Different grammars for different jobs; sharing would muddy both.Services/WinmdService.csvsServices/ApiSearch/WinMdParser.cs— [Feature]: Portwinmd(lexical Windows API metadata search) intowinapp#652 asked to reconcile these, but they turned out to do different work:IWinmdServiceenumerates activatable classes for manifest generation,WinMdParserextracts the full type/member surface for search. Keeping them separate is the right call; noting it here so the open question in [Feature]: Portwinmd(lexical Windows API metadata search) intowinapp#652 is closed rather than left dangling.NuGetResolver.csfile size (~1,700 lines) — it is long because it handles five discovery sources (assets file, packages.config,winmds.lock.json, project references, SDK/runtime probing), not because it repeats itself. There is no dedupe fix here; if it is worth revisiting it is a separate cohesion question.NuGetResolver.FindPackagesFromWinmdsLockfilevsIWinmdsLockfileService.TryReadAsync— the resolver already sharesWinmdsLockfileService.LockfileNameandWinmdsLockfileJsonContext; only the deserialize-plus-schema-check is duplicated. Reusing the service would mean threading a DI dependency into a static path for a handful of lines. Cost is roughly equal to the benefit; skip unless item 1 makes that plumbing available anyway.Acceptance
find-apihonorsglobalPackagesFolderfromnuget.config, with a test covering the packages.config path.find-apilocates a Windows SDK installed to a non-default root, sharing oneKitsRoot10resolution withCsWinRTMetadataShimService.