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

Add support for additional DotNetRuntime #7158

Merged
merged 10 commits into from
Mar 31, 2021
Merged

Add support for additional DotNetRuntime #7158

merged 10 commits into from
Mar 31, 2021

Conversation

HaoK
Copy link
Member

@HaoK HaoK commented Mar 26, 2021

Enables

    <IncludeDotNetRuntime>true</IncludeDotNetRuntime>
    <DotNetCliVersion>6.0.100-preview.2.21155.3</DotNetCliVersion>
    <DotNetRuntimeVersion>6.0.0-preview.4.21175.1</DotNetRuntimeVersion>

Which will install both the sdk and the runtime into the same directory

<ItemGroup>
<HelixCorrelationPayload Include="dotnet-cli">
<Uri>$(DotNetCliPackageUri)</Uri>
<Destination>$(DotNetCliDestination)</Destination>
</HelixCorrelationPayload>
<HelixCorrelationPayload Include="dotnet-runtime" Condition="$(IncludeDotNetRuntime) == 'true'">
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what Include=dotnet-runtime means here, it still ends up installing the runtime bits to the same dotnet-cli which is what I want so...

@HaoK
Copy link
Member Author

HaoK commented Mar 26, 2021

@MattGal starting point which seems to do the right thing, I end up with both versions in the payload/correlation directory as I want:

https://helixre8s23ayyeko0k025g8.blob.core.windows.net/aspnetcore-local-66c19a1b48864d5b89/Microsoft.DotNet.Helix.Sdk.Tests.dll/console.ee215eb1.log?sv=2019-07-07&se=2021-04-15T16%3A29%3A54Z&sr=c&sp=rl&sig=QkFKo2BAQxak5acUV%2B%2FqcOy3ovlZMf1pkPUhzQ5EUGk%3D


 Directory of C:\h\w\A34108FE\p\dotnet-cli\host\fxr

03/26/2021  04:34 PM    <DIR>          .
03/26/2021  04:34 PM    <DIR>          ..
03/26/2021  04:34 PM    <DIR>          6.0.0-preview.2.21154.6
03/26/2021  04:34 PM    <DIR>          6.0.0-preview.4.21175.1
               0 File(s)              0 bytes

 Directory of C:\h\w\A34108FE\p\dotnet-cli\shared\Microsoft.NETCore.App

03/26/2021  04:34 PM    <DIR>          .
03/26/2021  04:34 PM    <DIR>          ..
03/26/2021  04:34 PM    <DIR>          6.0.0-preview.2.21154.6
03/26/2021  04:34 PM    <DIR>          6.0.0-preview.4.21175.1

Copy link
Member

@MattGal MattGal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome this seems so straightforward, but I think it should be more general-purpose than just explicitly allowing one additional runtime.

@HaoK
Copy link
Member Author

HaoK commented Mar 30, 2021

Could use some msbuild help here with batching, I'm trying to get the outputs of the task to go into an item list and then add it to the correlation payload in a batch, its not happy with my mangling of msbuild

    <ItemGroup>
      <_additionaRuntimeVersions Include="@(AdditionalDotNetRuntimes)" />
    </ItemGroup>
    <FindDotNetCliPackage Condition="'%(_additionaRuntimeVersions.Identity)' != ''" Version="$(_additionaRuntimeVersions.Identity)" Runtime="$(DotNetCliRuntime)" PackageType="runtime" Channel="$(DotNetCliChannel)">
      <Output TaskParameter="PackageUri" ItemName="DotNetRuntimePackageUri"/>
    </FindDotNetCliPackage>
    <ItemGroup>
      <HelixCorrelationPayload Include="dotnet-cli">
        <Uri>$(DotNetCliPackageUri)</Uri>
        <Destination>$(DotNetCliDestination)</Destination>
      </HelixCorrelationPayload>
      <HelixCorrelationPayload Include="dotnet-runtime" Condition="%(DotNetRuntimePackageUri) != ''">
        <Uri>%(DotNetRuntimePackageUri.Identity)</Uri>
        <Destination>$(DotNetCliDestination)</Destination>
      </HelixCorrelationPayload>
    </ItemGroup>

C:\Github\arcade\src\Microsoft.DotNet.Helix\Sdk\tools\dotnet-cli\DotNetCli.targets(15,5): error MSB4096: The item "C:\Github\arcade\tests\archivepayload.zip" in item list "HelixCorrelationPayload" does not define a value for metadata "DotNetRuntimePackageUri". In order to use this metadata, either qualify it by specifying %(HelixCorrelationPayload.DotNetRuntimePackageUri), or ensure that all items in this list define a value for this metadata. [C:\Github\arcade\tests\UnitTests.proj]
The results in:

@MattGal
Copy link
Member

MattGal commented Mar 30, 2021

@HaoK taking a peek. (also: "_additiona -> _additional")

@HaoK
Copy link
Member Author

HaoK commented Mar 30, 2021

I think the issue is that I'm not properly aggregating the output DotNetRuntimePackageUris, for the correlation payloads to batch. In the binlog it looks like each of those is getting stored into its own Item as opposed to building up a list of packageuris like I want

@HaoK
Copy link
Member Author

HaoK commented Mar 30, 2021

Updated msbuild stuff

    <ItemGroup>
      <_additionalRuntimeVersions Include="$(AdditionalDotNetRuntimes)" />
    </ItemGroup>
    <FindDotNetCliPackage Condition="'%(_additionalRuntimeVersions.Identity)' != ''" Version="%(_additionalRuntimeVersions.Identity)" Runtime="$(DotNetCliRuntime)" PackageType="runtime" Channel="$(DotNetCliChannel)">
      <Output TaskParameter="PackageUri" ItemName="DotNetRuntimePackageUri"/>
    </FindDotNetCliPackage>
    <ItemGroup>
      <HelixCorrelationPayload Include="dotnet-cli">
        <Uri>$(DotNetCliPackageUri)</Uri>
        <Destination>$(DotNetCliDestination)</Destination>
      </HelixCorrelationPayload>
      <HelixCorrelationPayload Include="dotnet-runtime" Condition="%(DotNetRuntimePackageUri) != ''">
        <Uri>%(DotNetRuntimePackageUri.Identity)</Uri>
        <Destination>$(DotNetCliDestination)</Destination>
      </HelixCorrelationPayload>
    </ItemGroup>

image

@MattGal
Copy link
Member

MattGal commented Mar 30, 2021

You've already updated again after I wandered off to make a self-contained sample. I am thinking something like this:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <ItemGroup>
      <AdditionalDotNetRuntime Include="SomeRuntime1">
        <Version>someversion</Version>
        <Runtime>someruntime</Runtime>
        <PackageType>somepackagetype</PackageType>
        <Channel>somechannel</Channel>
      </AdditionalDotNetRuntime>
      <AdditionalDotNetRuntime Include="SomeRuntime2">
        <Version>someotherversion</Version>
        <Runtime>someotherruntime</Runtime>
        <PackageType>someotherpackagetype</PackageType>
        <Channel>someotherchannel</Channel>
      </AdditionalDotNetRuntime>
    </ItemGroup>

  <Target Name="AddAdditionalRuntimes" 
          Condition="@(AdditionalDotNetRuntime->Count()) != 0"
          AfterTargets="Build" 
          Inputs="@(AdditionalDotNetRuntime)"
          Outputs="SomeUris">
    <Message Text = "FindDotNetCliPackage '%(AdditionalDotNetRuntime.Version)' '%(AdditionalDotNetRuntime.Runtime)' '%(AdditionalDotNetRuntime.PackageType)' '%(AdditionalDotNetRuntime.Channel)' "/>
  <!-- you'd actually invoke the Target here... -->
  </Target>

  <Target Name="Build">
    <Message Importance="High" Text="Here's an example maybe?" />
  </Target>

</Project>

@HaoK
Copy link
Member Author

HaoK commented Mar 30, 2021

Cool that makes sense, I'll try that, but assuming I don't refactor the FindDotNetCliPackage, how do I call it repeatedly and combine the Output from each call into a single list? That's the msbuild piece I'm stuck on.

each call gives me a single uri, which i can attach to a property name or item, but how do I tell it to append to something?

    <FindDotNetCliPackage Condition="'%(_additionalRuntimeVersions.Identity)' != ''" Version="%(_additionalRuntimeVersions.Identity)" Runtime="$(DotNetCliRuntime)" PackageType="runtime" Channel="$(DotNetCliChannel)">
      <Output TaskParameter="PackageUri" ItemName="DotNetRuntimePackageUri"/>
    </FindDotNetCliPackage>

This call, I'd like to build up DotNetRuntimePackageUri to contain "url to version1;url to version 2"

@alexperovich
Copy link
Member

You don't need to append it to something, just add additional correlation payloads.

@MattGal
Copy link
Member

MattGal commented Mar 30, 2021

It should be possible to add items in batches as well, but agree with @alexperovich . Note for batching in targets like my sample you are required to have outputs, but can totally ignore them.

@HaoK
Copy link
Member Author

HaoK commented Mar 30, 2021

One question @MattGal what is the "SomeRuntime1" a friendly moniker? Why wouldn't that just be the version?

Maybe?

<AdditionalDotNetRuntime Include="Version">
        <Runtime>someruntime</Runtime>
        <PackageType>somepackagetype</PackageType>
        <Channel>somechannel</Channel>
</AdditionalDotNetRuntime>

@HaoK
Copy link
Member Author

HaoK commented Mar 30, 2021

Also does it really make sense to mix runtimes like this? When would you want to have an arm64 runtime mixed with an osx for example? I would assume the rid should be shared. But i'll defer to you guys to determine if that makes sense

@MattGal
Copy link
Member

MattGal commented Mar 30, 2021

One question @MattGal what is the "SomeRuntime1" a friendly moniker? Why wouldn't that just be the version?

Maybe?

<AdditionalDotNetRuntime Include="Version">
        <Runtime>someruntime</Runtime>
        <PackageType>somepackagetype</PackageType>
        <Channel>somechannel</Channel>
</AdditionalDotNetRuntime>

Sure, you can use any string you want as an ITaskItem's identity, but for anything beyond the first string you use metadata as shown (or you could do something funky with string splitting... please don't). Identity is just a string and should strive to but doesn't have to be unique.

@HaoK
Copy link
Member Author

HaoK commented Mar 30, 2021

I'm still struggling with how to add payloads given the task structure, the message thing works since its just printing, how do I add a payload though... Here's what I have where I tried to do the actual payload generation instead of the message which works fine, I am unsure how to batch against the list of DotNetCliPackageUri.

  <Target Name="AddAdditionalRuntimes" 
          Condition="@(AdditionalDotNetPackage->Count()) != 0"
          AfterTargets="Build" 
          Inputs="@(AdditionalDotNetPackage)"
          Outputs="SomeUris">

      <Message Text = "FindDotNetCliPackage '%(AdditionalDotNetPackage.Identity)' '$(DotNetCliRuntime)' '%(AdditionalDotNetPackage.PackageType)' '%(AdditionalDotNetPackage.Channel)' "/>
  <!-- you'd actually invoke the Target here... -->
    <FindDotNetCliPackage Version="%(AdditionalDotNetPackage.Identity)" Runtime="$(DotNetCliRuntime)" PackageType="%(AdditionalDotNetPackage.PackageType)" Channel="$(DotNetCliChannel)">
      <Output TaskParameter="PackageUri" PropertyName="DotNetCliPackageUri"/>
    </FindDotNetCliPackage>
    <ItemGroup>
      <HelixCorrelationPayload Include="dotnet-additional">
        <Uri>$(DotNetCliPackageUri)</Uri>
        <Destination>$(DotNetCliDestination)</Destination>
      </HelixCorrelationPayload>
    </ItemGroup>
  </Target>

My instinct is to just write a new task that takes a list of AddAdditionalRuntimes and does the right thing in C# to avoid this msbuild stuff, but I'm guessing there's an easy way to do this in msbuild?

@HaoK
Copy link
Member Author

HaoK commented Mar 30, 2021

This binlog demonstrates the current issue I'm seeing, each invocation of the FindDotNetCli task overwrites the last output url, so it seems to be using the last package url instead of adding 3 payloads with different package uris

image

@alexperovich
Copy link
Member

@HaoK that is happening because you aren't batching the target, you are using task batching because you put "%(AdditionalDotNetPackage.Identity)" in the FindDotNetCliPackage task invocation. You need to stick that in the Outputs of the target too for it to batch at the target level.

What you have right now is equivalent to this pseudo code:

foreach package in @(AdditionalDotNetPackage)
  <Message/>
foreach package in @(AdditionalDotNetPackage)
  <FindDotNetCliPackage/>
<ItemGroup>

If you stick the %(AdditionalDotNetPackage.Identity) into the Outputs of the target, then msbuild will switch to target batching and run the entire target once for each unique value of %(AdditionalDotNetPackage.Identity)

@alexperovich
Copy link
Member

Also, the target shouldn't have any Inputs, otherwise msbuild will try to do "up-to-date" checks and possibly not run parts of the target if files of the right names exist.

@HaoK
Copy link
Member Author

HaoK commented Mar 31, 2021

Thanks @alexperovich that output seems to have done the trick, I tried the following additional payloads:

    <AdditionalDotNetPackage Include="6.0.0-preview.4.21175.1">
      <PackageType>runtime</PackageType>
      <Channel>Current</Channel>
    </AdditionalDotNetPackage>
    <AdditionalDotNetPackage Include="6.0.0-preview.4.21178.6">
      <PackageType>runtime</PackageType>
      <Channel>Current</Channel>
    </AdditionalDotNetPackage>
    <AdditionalDotNetPackage Include="6.0.0-preview.3.21168.5">
      <PackageType>aspnetcore-runtime</PackageType>
      <Channel>Current</Channel>
    </AdditionalDotNetPackage>

@HaoK
Copy link
Member Author

HaoK commented Mar 31, 2021

Test run looks good to me,

Input:

    <AdditionalDotNetPackage Include="6.0.0-preview.4.21175.1" />
    <AdditionalDotNetPackage Include="6.0.0-preview.4.21178.6">
      <PackageType>runtime</PackageType>
      <Channel>Current</Channel>
    </AdditionalDotNetPackage>
    <AdditionalDotNetPackage Include="6.0.0-preview.3.21168.5">
      <PackageType>aspnetcore-runtime</PackageType>
    </AdditionalDotNetPackage>

Resulted in the expected versions on disk in the sdk.tests.dll:

https://helixre8s23ayyeko0k025g8.blob.core.windows.net/aspnetcore-local-3cb65732833747d0af/Microsoft.DotNet.Helix.Sdk.Tests.dll/console.93c9f799.log?sv=2019-07-07&se=2021-04-20T18%3A23%3A48Z&sr=c&sp=rl&sig=As74YPyvDjSe2XfnUcZOKYYiVaE5OvJM%2BiAMF%2BTkg1U%3D

@HaoK HaoK marked this pull request as ready for review March 31, 2021 18:57
<_package Condition=" '$(_package)' == '' ">runtime</_package>
</PropertyGroup>

<Message Text = "FindDotNetCliPackage '%(AdditionalDotNetPackage.Identity)' '$(DotNetCliRuntime)' '$(_package)' '$(_channel)' "/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is cruft from my example? If not, you could improve the text like "Adding package ..."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to you, I found it nice for debugging, I can remove it or update the text, what do you prefer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's up to me I definitely favor logging it with normal verbosity, something like

<Message Text = "Adding correlation payload for additional .NET Core package: Version: '%(AdditionalDotNetPackage.Identity)'  Runtime: '$(DotNetCliRuntime)' Type: '$(_package)' Channel: '$(_channel)' "/>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, updated

@MattGal
Copy link
Member

MattGal commented Mar 31, 2021

Copy link
Member

@MattGal MattGal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, please do update the readme before merging.

@@ -152,6 +152,23 @@ Given a local folder `$(TestFolder)` containing `runtests.cmd`, this will run `r
<HelixPostCommands>$(HelixPostCommands);echo 'One Pepperoni Pizza'</HelixPostCommands>
</PropertyGroup>

<!--
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MattGal want to review the doc update too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, some suggestions:

  • Including additional dotnet correlation payloads -> could be more verbose, e.g. "Optional additional dotnet runtimes or SDKs for correlation payloads"
  • Where your examples say "the specified" it couldn't hurt to have (version that's actually in the example) for clarity.
  • I would put parenthesis around defaults (e.g. "PackageType (defaults to runtime)")

@@ -152,6 +152,23 @@ Given a local folder `$(TestFolder)` containing `runtests.cmd`, this will run `r
<HelixPostCommands>$(HelixPostCommands);echo 'One Pepperoni Pizza'</HelixPostCommands>
</PropertyGroup>

<!--
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, some suggestions:

  • Including additional dotnet correlation payloads -> could be more verbose, e.g. "Optional additional dotnet runtimes or SDKs for correlation payloads"
  • Where your examples say "the specified" it couldn't hurt to have (version that's actually in the example) for clarity.
  • I would put parenthesis around defaults (e.g. "PackageType (defaults to runtime)")

@HaoK HaoK added the auto-merge Automatically merge PR once CI passes. label Mar 31, 2021
@ghost
Copy link

ghost commented Mar 31, 2021

Hello @HaoK!

Because this pull request has the auto-merge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@HaoK HaoK linked an issue Mar 31, 2021 that may be closed by this pull request
2 tasks
@ghost
Copy link

ghost commented Mar 31, 2021

Apologies, while this PR appears ready to be merged, I've been configured to only merge when all checks have explicitly passed. The following integrations have not reported any progress on their checks and are blocking auto-merge:

  1. DotNet Maestro - Int
  2. Codecov
  3. DotNet Maestro
  4. Dependabot
  5. .NET Helix

These integrations are possibly never going to report a check, and unblocking auto-merge likely requires a human being to update my configuration to exempt these integrations from requiring a passing check.

Give feedback on this
From the bot dev team

We've tried to tune the bot such that it posts a comment like this only when auto-merge is blocked for exceptional, non-intuitive reasons. When the bot's auto-merge capability is properly configured, auto-merge should operate as you would intuitively expect and you should not see any spurious comments.

Please reach out to us at [email protected] to provide feedback if you believe you're seeing this comment appear spuriously. Please note that we usually are unable to update your bot configuration on your team's behalf, but we're happy to help you identify your bot admin.

@HaoK HaoK merged commit b61eaae into main Mar 31, 2021
@HaoK
Copy link
Member Author

HaoK commented Mar 31, 2021

What's the best way for me to track when this will be available for us in dotnet/aspnetcore?

@MattGal
Copy link
Member

MattGal commented Mar 31, 2021

@riarenas can correct what I get wrong here but I'll take a stab:

  • Wait for this build to complete the "publish using darc" phase successfully
  • Once it has, you can go into your repo and do a darc update-dependencies with the build id listed in the publish phase (while CD'ed inside your repo)
  • Make a PR with what these changes back to your repo(s)

This will eventually flow via regular Arcade dependency flow automation, as well.

@dougbu
Copy link
Member

dougbu commented Mar 31, 2021

@HaoK another way to do this would be a bit more inline with the regular promotion process

  1. Watch the https://dev.azure.com/dnceng/internal/_build/results?buildId=1066687 and wait for it to complete (it's done now 😀)
  2. Watch https://dev.azure.com/dnceng/internal/_build?definitionId=282 for a new build w/ a title like "Update dependencies from https://github.com/dotnet/arcade build 20210331.5" (or later)
  3. Watch that build 'til it completes
  4. Double-check https://maestro-prod.westus2.cloudapp.azure.com/2/https:%2F%2Fdev.azure.com%2Fdnceng%2Finternal%2F_git%2Fdotnet-arcade/latest/graph to confirm Maestro++ is aware of the new build (BAR ID - 84605 in this case)
  5. darc update-dependencies --channel '.NET Eng - Latest' --source-repo arcade while in dotnet/aspnetcore or darc trigger-subscriptions --source-repo arcade --target-repo aspnetcore$ --regex --target-branch main
    • The first command is useful to include in a personal PR, the second option creates a dependency PR (necessary because our subscription is weekly) and allows you to react separately.

Main difference between that list and @MattGal's suggestion is it includes the promotion checks. Downside is it'll take longer.

@HaoK
Copy link
Member Author

HaoK commented Mar 31, 2021

@dougbu since I'm OOF next week, will this likely have flowed into our repo in 2 weeks?

@HaoK HaoK deleted the haok/arcade branch March 31, 2021 23:41
@dougbu
Copy link
Member

dougbu commented Mar 31, 2021

This fix absolutely should flow into dotnet/aspnetcore by next Tuesday at the latest. We took an update in dotnet/aspnetcore#31349 yesterday and that's the only reason I am not confident the PR won't be created "naturally" on Sunday.

@MattGal
Copy link
Member

MattGal commented Mar 31, 2021

Either way you do it is fine by me, but I was thinking about how once the package version is up on a feed, you can start experimenting with fitting its usage into the ASP.NET's Helix tests in a private branch faster, then land it once it promotes naturally or otherwise.

@HaoK
Copy link
Member Author

HaoK commented Mar 31, 2021

Yeah I probably won't have time to play with it before I get back, hopefully it will be trivial (set the new versions in our helix proj), remove our invocations of dotnet-install from our helix bootstrappers and just trust that the correlation payload paths have the right bits... It 'should' just work, since we are basically doing the same thing today (installing sdk/runtime into a workitem directory)

@MattGal
Copy link
Member

MattGal commented Mar 31, 2021

If it gets sent to Helix and then breaks, you can pretty much always expect me to help investigate regardless of what my boss wants. I will be OOF next week though.

@dougbu
Copy link
Member

dougbu commented Mar 31, 2021

Whoa, how will the rest of us deal❕ Thinking about taking a few Wellness days to avoid the fallout.

@HaoK
Copy link
Member Author

HaoK commented Mar 31, 2021

My changes are opt in, if someone else is brave enough to try it first, they are far braver than I would be :)

@dougbu
Copy link
Member

dougbu commented Apr 1, 2021

/fyi we're at step 3 in my list. Promotion build is https://dev.azure.com/dnceng/internal/_build/results?buildId=1066875

@dougbu
Copy link
Member

dougbu commented Apr 1, 2021

/fyi I manually triggered dotnet/aspnetcore#31427 That brings in the Arcade build 20210331.6 and so contains your fix @HaoK

MattGal referenced this pull request in Tratcher/aspnetcore Apr 2, 2021
- changes may help figure out dotnet/core-eng#11424
- will likely go away in the next Arcade update PR
  - if not, we should revert this change after dotnet/core-eng#11424 is done for realz

FYI @MattGal I didn't find a way to add similar logging in tools.sh. Tried
- adding `chmod +x "$install_script"` in `GetDotNetInstallScript`
- changing commands to `bash "verbose=true \"$install_script\" ..."` in `InstallDotNet`
but that didn't get the `say_verbose` or `eval $invocation` bits in the script going.
akoeplinger pushed a commit to akoeplinger/arcade that referenced this pull request Apr 12, 2021
riarenas pushed a commit to riarenas/arcade that referenced this pull request May 12, 2021
riarenas added a commit that referenced this pull request May 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auto-merge Automatically merge PR once CI passes.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Helix SDK] Support installing specific .NET SDKs **and** runtimes
4 participants