Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fallback host rid is broken on non-portable builds #81654

Closed
tmds opened this issue Feb 5, 2023 · 45 comments · Fixed by #82163
Closed

Fallback host rid is broken on non-portable builds #81654

tmds opened this issue Feb 5, 2023 · 45 comments · Fixed by #82163
Assignees
Labels
area-Host source-build Issues relating to dotnet/source-build
Milestone

Comments

@tmds
Copy link
Member

tmds commented Feb 5, 2023

Microsoft's portable builds perform a fall-back to the portable rid when the /etc/os-release/ rid is not known.

This doesn't work for non-portable builds because the information for the portable rid is missing in Microsoft.NETCore.App.deps.json.

For example, below is the full runtimes section of a Fedora 37 build. It does not include a section for linux-x64 (which is the fallback host rid).

  "runtimes": {
    "fedora.37-x64": [
      "fedora.37",
      "fedora-x64",
      "fedora",
      "linux-x64",
      "linux",
      "unix-x64",
      "unix",
      "any",
      "base"
    ]

For source-build, there should be an exact match with the rid found here and the /etc/os-release rid.
That may not be the case:

In these two cases, the Microsoft build will continue to work, while the source-build builds start failing.

We should include this information in the non-portable builds so the fallback works.

cc @ViktorHofer @ericstj @am11 @omajid

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Feb 5, 2023
@ghost
Copy link

ghost commented Feb 5, 2023

Tagging subscribers to this area: @dotnet/area-infrastructure-libraries
See info in area-owners.md if you want to be subscribed.

Issue Details

Microsoft's portable builds perform a fall-back to the portable rid when the /etc/os-release/ rid is not known.

This doesn't work for non-portable builds because the information for the portable rid is missing in Microsoft.NETCore.App.deps.json.

For example, below is the full runtimes section of a Fedora 37 build. It does not include a section for linux-x64 (which is the fallback host rid).

  "runtimes": {
    "fedora.37-x64": [
      "fedora.37",
      "fedora-x64",
      "fedora",
      "linux-x64",
      "linux",
      "unix-x64",
      "unix",
      "any",
      "base"
    ]

For source-build, there should be an exact match with the rid found here and the /etc/os-release rid.
That may not be the case:

In these two cases, the Microsoft build will continue to work, while the source-build builds start failing.

We should include this information in the non-portable builds so the fallback works.

cc @ViktorHofer @ericstj @am11 @omajid

Author: tmds
Assignees: -
Labels:

area-Infrastructure-libraries, untriaged

Milestone: -

@am11
Copy link
Member

am11 commented Feb 5, 2023

I think we should work towards breaking free of /etc/os-release dependency. It does not scale at all.

Some context: In .NET 7, work was put to remove hard dependency on procfs (/proc) and now we don't fail in runtime and SDK even if you unmount /proc. The reason being, even on linux, there is no guarantee that procfs is present or functioning everywhere despite it is highly recommended facility provided by the linux kernel (vs., for instance, on FreeBSD, /proc is optional and not present by default).

/etc/os-release, in comparison to /proc, is a fragile resource for an ecosystem to depend on. It is a wrong contract for any serious operation. It was invented as part of systemd which not all distros use as their init system; but regardless, most distros add /etc/os-release as part of their base installation. We are parsing it in number of places and failing the execution if we find something (which we think is) wrong. If the fragility of /etc/os-release doesn't concern us then even better; we should write our own (equally fragile) ~/.dotnet/distro-info.json file and depend on that instead of something out of /etc which we don't control.

@tmds
Copy link
Member Author

tmds commented Feb 5, 2023

I think we should work towards breaking free of /etc/os-release dependency. It does not scale at all.

The behavior described in this issue is exactly that: a non-portable build should be able to consume portable assets (per fallback RID) independent of what's in /etc/os-release.

@ViktorHofer
Copy link
Member

ViktorHofer commented Feb 5, 2023

It also happens when a distro gets upgraded, and the new version does not (yet) include packages for that .NET version. Then the existing .NET build will not know the new rid (fedora.38-x64 is unknown).

What we discussed in NuGet/Home#5862 (especially NuGet/Home#5862 (comment)) touches on the bigger picture and would solve the uber issue instead of band-aiding broken source build scenarios like this N+1 scenario. Unfortunately, I don't think anyone currently considers picking this up from the backlog.

For example, below is the full runtimes section of a Fedora 37 build. It does not include a section for linux-x64 (which is the fallback host rid).

As I don't know much about the host fallback, can you please elaborate or link to how that fallback works today? Does a linux-x64 built host automatically fallback to "linux-x64" but then fails to find the associated parent rids because that information is missing from the graph? In respect to this ask, which fallback RID would we choose when source building for "fedora.37-x64"?

Anyway, this sounds like we are again working around the lack of a more flexible RID selection algorithm which presumably could supports floating versions. That isn't a problem per-se but if this will continue to seriously impact source build partners, we might instead or additionally strongly advocate for fixing the above linked issue.

@tmds
Copy link
Member Author

tmds commented Feb 5, 2023

Yes, I've been part of this discussion.

Early on, I was advocating to make the rids 'semantical'.

After @richlander put out the Simplify RID Model design, I think this much simpler model may be enough.

With that model, non-portable rids are for distinguishing source-built assets from portable ones (cfr dotnet/source-build#2932 (comment)).

What's proposed here is not a band-aid or a workaround. It's in line with this simplified model.

As I don't know much about the host fallback

The fallback is what makes Microsoft's build pick linux-x64 assets when the /etc/os-release rid is unknown.
That's why #79196 doesn't affect Microsoft builds.
This issue is about making the non-portable builds fall back too.

@am11
Copy link
Member

am11 commented Feb 5, 2023

The behavior described in this issue is exactly that:

Sure. The only difference is how we get there. I was proposing that we give this control to distros rather than owning ourselves. Ideally, it can be done by removing all calls to open(/etc/os-release) in the system. Be it building dotnet from source, publishing user projects (SDK) or during the run-time of user applications. Instead, we use something like open(/path/to/dotnet/root/distro-info.json) a resource which we will generate as part of the build using existing mechanics (source-build-dotnet.sh -p:TargetRid=..) for each distro differently, that will be installed on end-user's system. This way, we can completely remove these ever-growing chains of conditions

We can read our format with simple code without specializing stuff like which distro uses version, how many parts of versions should be read, what if that version wasn't there etc. That would make it much easier to handle and rationalize proper fallbacks. Needless to say, this should be implemented in a way that it does not compromise any existing functionality for non-portable/portable scenarios and set us free from /etc/os-release for good.

@tmds
Copy link
Member Author

tmds commented Feb 5, 2023

@am11 it's an interesting idea, you should write it up in an issue.

I'd like to scope this to bring the non-portable build behavior in-line with the portable build when /etc/os-release has an unknown rid.

@tmds
Copy link
Member Author

tmds commented Feb 6, 2023

Rather than changing Microsoft.NETCore.App.deps.json to include the fallback rid, we may want to change the the hostpolicy so the fallback rid matches the source-build target rid.

@ViktorHofer the code for the fallback is here:

// If the current RID is not present in the RID fallback graph, then the platform
// is unknown to us. At this point, we will fallback to using the base RIDs and attempt
// asset lookup using them.
//
// We do the same even when the RID is empty.
if (currentRid.empty() || (rid_fallback_graph.count(currentRid) == 0))
{
currentRid = pal::get_current_os_fallback_rid() + pal::string_t(_X("-")) + get_current_arch_name();
trace::info(_X("Falling back to base HostRID: %s"), currentRid.c_str());
}

It falls back to the corresponding portable rid defined here:

#if defined(TARGET_FREEBSD)
#define FALLBACK_HOST_RID _X("freebsd")
#elif defined(TARGET_ILLUMOS)
#define FALLBACK_HOST_RID _X("illumos")
#elif defined(TARGET_SUNOS)
#define FALLBACK_HOST_RID _X("solaris")
#elif defined(TARGET_LINUX_MUSL)
#define FALLBACK_HOST_RID _X("linux-musl")
#elif defined(TARGET_ANDROID)
#define FALLBACK_HOST_RID _X("linux-bionic")
#else
#define FALLBACK_HOST_RID _X("linux")
#endif

Information about this portable rid is not present in a source-built Microsoft.NETCore.App.deps.json. So the fallback is broken.

The fix is either to add the fallback rid to Microsoft.NETCore.App.deps.json.
Or to change the fallback rid to one that is present in Microsoft.NETCore.App.deps.json: the target rid.

@ViktorHofer
Copy link
Member

ViktorHofer commented Feb 6, 2023

Or to change the fallback rid to one that is present in Microsoft.NETCore.App.deps.json: the target rid.

I spent some time yesterday thinking about this issue and what you propose is exactly what I imagined would be the right solution. Instead of finding a portable RID, just make sure that the target RID is honored in all scenarios (direct match and host fallback).

cc @vitek-karas

@tmds tmds changed the title Microsoft.NETCore.App.deps.json is missing a runtime section for the fallback rid on non-portable builds Fallback host rid is broken on non-portable builds Feb 6, 2023
@ghost
Copy link

ghost commented Feb 6, 2023

Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.

Issue Details

Microsoft's portable builds perform a fall-back to the portable rid when the /etc/os-release/ rid is not known.

This doesn't work for non-portable builds because the information for the portable rid is missing in Microsoft.NETCore.App.deps.json.

For example, below is the full runtimes section of a Fedora 37 build. It does not include a section for linux-x64 (which is the fallback host rid).

  "runtimes": {
    "fedora.37-x64": [
      "fedora.37",
      "fedora-x64",
      "fedora",
      "linux-x64",
      "linux",
      "unix-x64",
      "unix",
      "any",
      "base"
    ]

For source-build, there should be an exact match with the rid found here and the /etc/os-release rid.
That may not be the case:

In these two cases, the Microsoft build will continue to work, while the source-build builds start failing.

We should include this information in the non-portable builds so the fallback works.

cc @ViktorHofer @ericstj @am11 @omajid

Author: tmds
Assignees: -
Labels:

area-Host, untriaged

Milestone: -

@dotnet dotnet deleted a comment Feb 6, 2023
@ViktorHofer ViktorHofer added this to the 8.0.0 milestone Feb 6, 2023
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Feb 6, 2023
@ViktorHofer ViktorHofer added source-build Issues relating to dotnet/source-build untriaged New issue has not been triaged by the area owner labels Feb 6, 2023
@vitek-karas
Copy link
Member

cc @elinor-fung

@vitek-karas
Copy link
Member

Or to change the fallback rid to one that is present in Microsoft.NETCore.App.deps.json: the target rid.

That was what I was thinking we would do when we changed the fallback RIDs (but didn't finish it unfortunately). This obviously assumes that the Microsoft.NETCore.App.deps.json contains the complete fallback graph for the target rid - I assume that is already the case for source builds (I don't know).

@tmds
Copy link
Member Author

tmds commented Feb 6, 2023

That was what I was thinking we would do when we changed the fallback RIDs (but didn't finish it unfortunately).

I can look into making this change, if you want.

This obviously assumes that the Microsoft.NETCore.App.deps.json contains the complete fallback graph for the target rid - I assume that is already the case for source builds (I don't know).

Yes, it does. See the first comment, it shows what is generated for Fedora 37.

@vitek-karas
Copy link
Member

I can look into making this change, if you want.

That would be greatly appreciated. We're not experts in the source-build problem space, so someone who is would be perfect :-)

@richlander
Copy link
Member

richlander commented Feb 7, 2023

I'm probably missing something basic from the start. I see "no mention of linux-x64" and then the following example includes it. Can you explain @tmds?

Also, It seems like fedora.36 content would not be compatible. That's presumably because there is no compat guarantee across that boundary (like that could be the OpenSSL 1.x to 3.0 transition). Correct?

@tmds
Copy link
Member Author

tmds commented Feb 7, 2023

@richlander when the host doesn't recognize the rid in /etc/os-release it uses a fallback rid. That fallback rid is the portable rid that corresponds to the platform. On Fedora 36 for example, that is linux-x64 both for the Microsoft portable build, as the source-build non-portable build.
However, the runtime on the non-portable build doesn't understand what linux-x64 means (it's not defined in Microsoft.NETCore.App.deps.json as shown in the first comment), which breaks the fallback on the non-portable build.

We'll make the fallback work on non-portable by changing the fallback rid to match the non-portable rid.

The bottom line is that both portable and non-portable builds will be able to use portable assets independent of what's in /etc/os-release.

@vitek-karas you can assign this to me.

@vitek-karas
Copy link
Member

@agocke and I discussed this yesterday and Andy had a great point to make.

There are two changes at play here:

  • This proposed change, where the fedora source-built host would fallback to the fedora.36-x64 (or similar) RID
  • Change in the SDK where the SDK will default to the RID of the SDK itself when not specified. Meaning that running dotnet build will produce an executable which is for the RID of the SDK used to run the build.

With source-built SDK for fedora, the RID for the SDK is going to be fedora.36-x64. So running dotnet build will produce an executable which is the apphost built by the source-build which will have fallback to fedora.36-x64. This means that such executable will only run on Linuxes where the specific RID exists in the shared framework, if the fallback kicks in, it will fail on anything but Fedora (since it will fallback to fedora.36-x64)

I don't know what is the current behavior of source-built SDK on fedora, but given this issue I would suspect it produces the app either with the portable host (falls back to linux-x64) or the fedora source-built host (which also falls back to linux-x64).

I also don't know what happens on source-built SDK if the app is build with dotnet build -r linux-x64 - where does it take the host from (does it fallback to the Microsoft published nugets and thus gets the portable host). But it would basically mean that to get the existing behavior one would have to start running dotnet build -r linux-x64 instead of the RID-less version.

Andy has a great point that the host is special as it should not target the RID of the source-built target, but instead should try to be as portable as possible so that the app produced by the source-built SDK can run on as many targets as possible.

That's obviously assuming that is the intention - I don't know for sure.

@tmds
Copy link
Member Author

tmds commented Feb 7, 2023

But that runtimes section includes linux-x64. I assume the section is from that deps.json file. That doesn't seem to match what you are saying. I'm sorry, but I'm still having trouble piecing all this together.

Ah, yes. There is a linux-x64, but it is not the one we need.

Under runtimes there's an entry for each supported host rid which maps to an ordered list that was generated from the rid graph.

For linux-x64 to be supported, there needs to be an entry which would look like this:

"linux-x64": [
    "linux",
    "unix-x64",
    "unix",
    "any",
    "base"
]

Definitely. Just curious - does it "hurt" any of the scenarios which are needed from source-built SDK?

Not one that is supposed to be working/supported.

@richlander
Copy link
Member

Thank you. That helps me better understand the issue.

@richlander
Copy link
Member

We'll make the fallback work on non-portable by changing the fallback rid to match the non-portable rid.

How do you know what the non-portable RID is? I'm assuming that means the value isn't available in /etc/os-release. Is the following pattern what you'll use to know the non-portable RID?

#81589 (comment)

@tmds
Copy link
Member Author

tmds commented Feb 7, 2023

The non-portable rid is determined by calling RuntimeInformation.RuntimeIdentifier (which reads /etc/os-release) during source-build.
The package maintainer can chose to override it.

Its value is stored in the .NET installation.

@richlander
Copy link
Member

richlander commented Feb 7, 2023

That helps. Which build of .NET do you use to call that API? I'm seeing a chicken/egg challenge.

Separately, I see the fully specified fedora.37-x64 RID. The scenario for that is obvious. I also see some non-fully-specified ones. I get the scenario for fedora.37. That's a managed library that P/Invokes into some code that is known to be on Fedora 37. I have less clarity on fedora-x64 and fedora. What would the scenarios be for those, where the code would (A) have a dependency on Fedora, but (B) be guaranteed to be compatible?

For context, @elinor-fung and I are trying to define all the requirements so that we can propose an updated plan.

@tmds
Copy link
Member Author

tmds commented Feb 7, 2023

That helps. Which build of .NET do you use to call that API? I'm seeing a chicken/egg challenge.

The expectation is that RuntimeInformation.RuntimeIdentifier returns a sensible identifier for a distro and that its implementation is stable.

Separately, I see the fully specified fedora.37-x64 RID. The scenario for that is obvious. I also see some non-fully-specified ones. I get the scenario for fedora.37. That's a managed library that P/Invokes into some code that is known to be on Fedora 37. I have less clarity on fedora-x64 and fedora. What would the scenarios be for those, where the code would (A) have a dependency on Fedora, but (B) be guaranteed to be compatible?

I agree, there is little you can assume if your environment is fedora or fedora-x64.
Even for a rid like fedora.37 the effective use-cases may be limited. I think most managed code that P/Invokes either carries the native bits, or versions with the library it P/Invokes (and not the distro).

@richlander
Copy link
Member

That helps.

On the first question, I was asking something more specific. Do you rely on the last-known-good Fedora build to call that API during source-build or do you use the "just built" runtime to generate those assets? It doesn't really matter. I'm just curious.

@tmds
Copy link
Member Author

tmds commented Feb 8, 2023

Source-build calls it on whatever sdk you are using for the build: https://github.com/dotnet/dotnet/blob/903545fbfc0c80d94803eeaf73f22ae1a1de290d/Directory.Build.props#L161.

The packages on Fedora are built using an sdk that was already built from source.
For a new major version, that sdk gets bootstrapped using a Microsoft SDK.
Then for successive builds, we use the available Fedora sdk.

@richlander
Copy link
Member

richlander commented Feb 8, 2023

Thanks. What I expected.

Here's a brief sense of what @elinor-fung and I are talking about. We need some more info still.

Some quick context:

# cat /usr/share/dotnet/shared/Microsoft.NETCore.App/7.0.2/Microsoft.NETCore.App.deps.json | wc -l
2025
# cat /usr/share/dotnet/shared/Microsoft.NETCore.App/7.0.2/Microsoft.NETCore.App.deps.json | jq .runtimes | wc -l
1289

That's a ridiculous number of lines!

Plan:

  • Switch host to algorithmic model.
  • Enable using the .deps.json as a compat backup (with an eventual plan to remove that option).
  • Continue using .deps.json for SDK (again with an eventual, likely longer, plan to remove that options)

Using Fedora as the example, the algorithm would probe for these assets:

  • fedora.37-x64
  • linux-x64
  • unix

As we discussed, there are other possible RIDs to probe for. It isn't obvious that any of them are broadly useful.

@tmds
Copy link
Member Author

tmds commented Feb 8, 2023

Switch host to algorithmic model.

The algorithmic model enables the runtime to consume fedora.37-x64 from nuget packages.
I'm not sure there are many of these.
Such packages aren't scalable because you need to create assets per distro version.
Providing this algorithm depends on whether you want to support this use-case.

The main use-case for the specific rid (fedora.37-x64) is to distinguish portable assets from source-built assets.
This already works because the source-build runtime recognizes the rid, allowing it to use assets that have been source-built for that rid.

@richlander
Copy link
Member

richlander commented Feb 8, 2023

I think you are right. @jkotas was providing similar feedback, yesterday. Now that I think more about it, I have an alternate proposal.

The algorithm should probe for:

  • linux-x64
  • unix

Let's step back. This whole scenario is for portable apps. RID specific apps (FDD or SCD) don't have this need, at least not for the host.

So, should we support these districts specific RIDs? Yes, but in a narrow way.

  • Source build distros can continue to inject their own RID, just like today.
  • dotnet restore should enable passing one of these RIDs in a way that restore knows that the portable RID is the fallback. That means that these special RIDs would only be supported for RID specific apps not portable ones.

If we had that, I think we could freeze the RID graph and over time make it optional and then remove it.

This is similar to the original proposal, but provides more of a story (while narrow) for these distro specific RIDs. It also better defines what portable apps are for.

Fair?

@vitek-karas
Copy link
Member

RID specific apps (FDD or SCD) don't have this need, at least not for the host.

That's not 100% true. If the app acts as a host for plugins, the plugins may need some kind of RID fallback graph (or algorithmic alternative). For example, even if I build the host app for fedora-x64, it should be able to load a plugin which is portable and includes linux-x64 assets. In that case something will need to know that linux-x64 is an OK fallback for fedora-x64. Currently we suggest people use AssemblyDependencyResolver for these things and that relies on the same code in the host (as portable apps do).

@am11
Copy link
Member

am11 commented Feb 8, 2023

Currently we suggest people use AssemblyDependencyResolver for these things and that relies on the same code in the host (as portable apps do).

Interesting. Now I'm wondering how does / should it work with fake RIDs, e.g.

targetRid: banana.24-x64
which is: impersonate as any distro name and we will figure out the base OS / kernel (linux, darwin, freebsd, solaris etc.). Note that this does not have the entire chain of relationships.

Either distro-RID-specific plugins are not in use or distros which switched to -p:TargetRID=<name of choice> mechanism (without their entry in central RID graph) have that scenario broken. Some data might help demystifying it.

@richlander
Copy link
Member

If the app acts as a host for plugins

That sounds pretty rough. I don't think we should support distro-specific plugins. That seems like way too niche a scenario.

@vitek-karas
Copy link
Member

I agree that distro specific plugins are unlikely... but distro specific app with cross-plat plugins (so plugin which has windows/linux/mac specific assets) seems very possible. I mean our plugin sample app does that. And since the plugins are using the same RID resolution code as the app itself, even distro specific apps will need to be able to figure out what linux-x64/linux means.

@richlander
Copy link
Member

Yes. I'm suggesting that it is OK to support portable RIDs for that scenario.

@richlander
Copy link
Member

richlander commented Feb 8, 2023

It also occurs to me if we adopt this plan that dotnet --info showing a distro-specific RID would make no sense, at least for a portable Linux build.

@elinor-fung
Copy link
Member

it is OK to support portable RIDs for that scenario.

The proposal has the host (distro-specific or not) supporting portable RIDs - via the algorithmic model - in all scenarios, right? (Hence distro-specific app and cross-plat plugins should still work.)

@elinor-fung
Copy link
Member

It also occurs to me if we adopt this plan that dotnet --info showing a distro-specific RID would make no sense, at least for a portable Linux build.

I just realized that the host itself does not print the RID at all - only the SDK. Although it does rely on the host, since that is what RuntimeInformation.RuntimeIdentifier comes from.

@richlander
Copy link
Member

richlander commented Feb 9, 2023

We could support distro-specific RIDs with portable apps, too. It's just a question of whether we really have a scenario for it. I'd rather see that be a thing for:

  • RID-specific FDD and SCD apps using portable .NET.
  • Distro-specific .NET builds.

That seems like it would cover the bases.

Also, wouldn't it be wonderful if .NET became more popular on Linux and devs started putting distro-specific libraries into package managers instead of NuGet. portable content in NuGet and distro-specific content in the distro archive. That seems like the sweet-spot.

Another option is to make portable apps with distro-specific runtime folders opt-in. There could be a property for DistroAware or similar. The host would then figure out what the distro was an probe for the right runtime folders. Otherwise, it would just probe for the portable RIDs. dotnet build could tell you (based on the packages you restore) that you have non-portable assets and should enable DistroAware.

@tmds
Copy link
Member Author

tmds commented Feb 15, 2023

That seems like it would cover the bases.

I agree.

The non-portable rid is used as 'namespace' for identifying source build artifacts on the distro.

That namespace is defined and used by the distro .NET build. I doesn't need to mean anything elsewhere.

The proposal has the host (distro-specific or not) supporting portable RIDs - via the algorithmic model -

The host already supports consuming portable rids without the algorithm model.

The algorithmic model (that dynamically makes the /etc/os-release rid known) would allow a portable host to consume non-portable rids.
As mentioned, the use-cases may be limited, and not scalable (due to needing to create assets per rid).

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Feb 15, 2023
@tmds
Copy link
Member Author

tmds commented Feb 16, 2023

and not scalable (due to needing to create assets per rid).

note: the scalability issue is higher on distros that release frequent with short support cycles, like Fedora, than on long supported distros, like RHEL.

@tmds
Copy link
Member Author

tmds commented Feb 16, 2023

There is an aspect of the rid graph that wasn't discussed yet: compatibility.
The rid graph for example expresses that Oracle Linux 7 derives from RHEL 7.
.NET on Oracle Linux 7 (ol.7) is happy to consume assets published for the rhel.7 rid.

@ghost ghost removed in-pr There is an active PR which will close this issue when it is merged untriaged New issue has not been triaged by the area owner labels Feb 22, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Mar 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Host source-build Issues relating to dotnet/source-build
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants