Skip to content

Fix resolution of the external dependencies. - #11742

Open
Nikolay Rovinskiy (nick863) wants to merge 2 commits into
mainfrom
nirovins/fix_external_package_resolution
Open

Fix resolution of the external dependencies.#11742
Nikolay Rovinskiy (nick863) wants to merge 2 commits into
mainfrom
nirovins/fix_external_package_resolution

Conversation

@nick863

@nick863 Nikolay Rovinskiy (nick863) commented Aug 21, 2026

Copy link
Copy Markdown
Member

Problem: Assume, we have the external assembly defined in a typespec as follows:

@@alternateType(
  Azure.AI.Projects.BingCustomSearchPreviewTool,
  {
    identity: "Azure.AI.Extensions.OpenAI.BingCustomSearchPreviewTool",
    package: "Azure.AI.Extensions.OpenAI",
    minVersion: "3.0.0-alpha.20260820.5",
  },
  "csharp"
);

If the version 3.0.0-alpha.20260820.5 is not present in the repository, the ExternalTypeReferenceResolver will not download the needed assembly and the one already present will be used. This will result in some classes not being found as by default the latest stable version is being downloaded.

Solution: Currently, the external package is resolved as follows:

  1. Try to get the assembly of minVersion from available repository
  2. If it fails, use the version, which has been already downloaded.

In this PR we are adding more logic:

  1. If minVersion is provided, try to download if
  2. If it is not available, get the latest version; If minVersion is prerelease, use the latest version, including the prerelease one.
  3. If minVersion is not provided, use the latest stable version.
  4. Use anything already available.

@pkg-pr-new

pkg-pr-new Bot commented Aug 21, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/http-client-csharp@11742

commit: 6a3f530

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

Updates the C# generator’s external NuGet dependency resolution so it can (when needed) fall back to the latest available version (optionally including prereleases) instead of only using a requested minimum version or whatever is already cached.

Changes:

  • Added a helper to enumerate available package versions across enabled NuGet sources.
  • Updated external type resolution to select a version based on MinVersion presence and prerelease status before downloading.

Reviewed changes

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

File Description
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Utilities/NugetPackageResolver.cs Adds GetAllVersions helper for collecting versions from enabled NuGet sources.
packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Utilities/ExternalTypeReferenceResolver.cs Uses version enumeration to choose a download version when the requested MinVersion isn’t available and to include prereleases when appropriate.
Suppressed comments (1)

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Utilities/ExternalTypeReferenceResolver.cs:260

  • new NuGetVersion(external.MinVersion) will throw for an invalid/unsupported version string, which changes behavior compared to the previous string-based flow and ends up being swallowed by the broad catch (reported as "package not found"). Also, versions.Max() throws on an empty sequence, so missing packages/feeds can trigger an exception and skip the intended fallback selection.
                        NuGetVersion minVersion = new(external.MinVersion);
                        IList<NuGetVersion> versions = await NugetPackageResolver.GetAllVersions(external.Package!, nugetSettings, allowPrerelease: minVersion.IsPrerelease);
                        if (versions.Any(x => x == minVersion))
                        {
                            resolvedVersion = external.MinVersion;
                        }
                        else
                        {
                            resolvedVersion = versions.Max()?.ToString();
                        }

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

Copy link
Copy Markdown
Contributor

No changes needing a change description found.

@JoshLove-msft

Copy link
Copy Markdown
Contributor

The resolved version should be based on the csproj/central package management. The MinVersion from the decorator is only meant to be used as a safety check - it doesn't influence the version that is used, but it can trigger an error if the min version doesn't align with the resolved version. Can you clarify what issue this is solving?

@nick863

Copy link
Copy Markdown
Member Author

The resolved version should be based on the csproj/central package management. The MinVersion from the decorator is only meant to be used as a safety check - it doesn't influence the version that is used, but it can trigger an error if the min version doesn't align with the resolved version. Can you clarify what issue this is solving?

I have updated the description.

@JoshLove-msft

Copy link
Copy Markdown
Contributor

The resolved version should be based on the csproj/central package management. The MinVersion from the decorator is only meant to be used as a safety check - it doesn't influence the version that is used, but it can trigger an error if the min version doesn't align with the resolved version. Can you clarify what issue this is solving?

I have updated the description.

The minVersion should not be used to influence the version that is downloaded by the generator. It is only meant to be used as a compatibility floor. It is optional - it doesn't have to be specified at all. I'm not sure what problem this is solving.

Copilot AI review requested due to automatic review settings August 22, 2026 00:22

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 2 out of 2 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Utilities/ExternalTypeReferenceResolver.cs:260

  • If the configured feeds return no versions (e.g., package doesn't exist, or only prerelease versions exist but allowPrerelease is false), versions.Max() will throw on an empty sequence and the resolver will fall into the catch path. Handle the empty list explicitly so resolution can fail cleanly without relying on exceptions.
                        NuGetVersion minVersion = new(external.MinVersion);
                        IList<NuGetVersion> versions = await NugetPackageResolver.GetAllVersions(external.Package!, nugetSettings, allowPrerelease: minVersion.IsPrerelease);
                        if (versions.Any(x => x == minVersion))
                        {
                            resolvedVersion = external.MinVersion;
                        }
                        else
                        {
                            resolvedVersion = versions.Max()?.ToString();
                        }

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Utilities/ExternalTypeReferenceResolver.cs:266

  • New behavior adds multiple version-selection branches (minVersion present + exact version missing -> pick latest; prerelease minVersion -> include prerelease; minVersion absent -> latest stable). There are existing unit tests for ExternalTypeReferenceResolver, but none cover these new branches. Add tests that exercise: (1) minVersion not in feed selects latest available version; (2) prerelease minVersion allows selecting a prerelease latest; (3) stable minVersion does not select prerelease when only prerelease versions exist.
                    if (!string.IsNullOrEmpty(external.MinVersion))
                    {
                        // If min version was provided, we
                        // 1. Search if it is in our repositories;
                        // 2. Get the latest one if it is not.
                        // 3. If our version is a pre release, include pre released versions in our search.
                        NuGetVersion minVersion = new(external.MinVersion);
                        IList<NuGetVersion> versions = await NugetPackageResolver.GetAllVersions(external.Package!, nugetSettings, allowPrerelease: minVersion.IsPrerelease);
                        if (versions.Any(x => x == minVersion))
                        {
                            resolvedVersion = external.MinVersion;
                        }
                        else
                        {
                            resolvedVersion = versions.Max()?.ToString();
                        }
                    }
                    else
                    {
                        // If min version was not provided, get the latest stable version.
                        resolvedVersion = await NugetPackageResolver.ResolveLatestPackageVersion(external.Package!, nugetSettings);
                    }

@nick863

Copy link
Copy Markdown
Member Author

The resolved version should be based on the csproj/central package management. The MinVersion from the decorator is only meant to be used as a safety check - it doesn't influence the version that is used, but it can trigger an error if the min version doesn't align with the resolved version. Can you clarify what issue this is solving?

I have updated the description.

The minVersion should not be used to influence the version that is downloaded by the generator. It is only meant to be used as a compatibility floor. It is optional - it doesn't have to be specified at all. I'm not sure what problem this is solving.

The problem is that we did not released the new stable version yet, while the downloaded version is 2.0.0. The logic in ExternalTypeReferenceResolver will try to download the compatible assembly. In this PR I am changing the download logic to help situation when the exact version is not present in the repository.
Without this fix if minVersion is not present, the code generation will fail with cryptic error, because it will try to use the incompatible latest stable version.

@JoshLove-msft

Copy link
Copy Markdown
Contributor

Thanks, that clarifies the reproduction. I think the root fix should be in project-reference resolution rather than selecting a package version from minVersion:

  1. Resolve the target project's ProjectAssetsFile (normally obj/project.assets.json) from the evaluated project and read the NuGet restore graph. This gives us the exact package version selected by the .csproj/central package management, including prereleases, ranges, and transitive dependencies.
  2. For each external package, locate that exact package/version and use the compile asset selected for the applicable target framework instead of probing the highest cached version or querying feeds for a latest version. Register dependency assemblies from the same assets target so the generator and eventual SDK build use one consistent graph.
  3. Parse minVersion only as a compatibility floor. If the resolved project version is lower, emit an actionable diagnostic containing the package name, resolved version, and required minimum. If it is equal or higher, use the project-resolved version even when the exact minimum version was never published.
  4. If the assets file is missing/stale, or the external package is absent from the restored graph, report that the project must be restored or add the required PackageReference; do not silently choose a different feed version. Improving this diagnostic also addresses the current cryptic failure.
  5. Add tests for a centrally managed prerelease, a resolved version newer than a nonexistent minimum, omitted minVersion, a resolved version below the floor, multiple cached versions (the assets-selected version must win), and missing assets/package entries.

With that flow, the reported case resolves the project's 3.0.0-alpha... package regardless of whether the decorator's floor exists as an exact package version, while avoiding loading an assembly different from the one used to compile the SDK. The new GetAllVersions/latest-version selection would not be needed.

--generated by Copilot

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

Labels

emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants