Skip to content

Conversation

@jungaretti
Copy link
Contributor

@jungaretti jungaretti commented Mar 1, 2023

Closes #464
Fixes #293
Fixes #389

Refactors the .NET feature to use dotnet-install.sh to install the .NET SDK and runtime. This PR bumps the .NET feature to a new major version (v2) in order to remove the apt option.

This PR changes the behavior of dotnet when additionalVersions are installed. v1 installs each .NET version to its own directory. These directories are independent. If you install an additionalVersion of .NET with v1, you cannot use that version with dotnet until you modify $PATH.

My team's codebase uses both .NET 3 and 6. We cannot build our project using v1 of the .NET feature because of this bug.

v2 installs additionalVersions to the same directory as the primary version. This enables you to --list-sdks all of the SDKs from version and additionalVersions. v2 unblocks users who need to use additionalVersions of .NET.

v2 with --version 7 and specified version 6

When dotnet --list-sdks includes multiple versions of .NET, dotnet --version (and other SDK commands) use "the latest SDK installed on the machine by default." Uses can override this behavior with a globals.json file.

@jungaretti jungaretti marked this pull request as ready for review March 1, 2023 17:24
@joshspicer
Copy link
Member

It would be cool to silence the wget output. Looking at the test runs, it's quite noisy and not too helpful:
image

Copy link
Member

@samruddhikhandale samruddhikhandale left a comment

Choose a reason for hiding this comment

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

Thank you for refactoring! ✨
Left some comments/thoughts

{
"id": "dotnet",
"version": "1.1.3",
"version": "2.0.0",
Copy link
Member

Choose a reason for hiding this comment

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

Can we avoid bumping the major version? People pin to the major version and aren't proactive to update their dev config once new major version is released. Hence, they would stay back on older releases.

I bumped the version to 2.0 in order to break the version format. dotnet-install.sh requires a version to be latest, have the form X, or have the form X.Y.Z. For example, 7 and 7.0.3. The old schema allowed version to have the for X.Y (e.g., 3.1). We need to come up with a way to map X.Y to X.Y.Z in order to preserve this option.

Can we find three part version when X.Y is provided with GH releases/tags?
Look at find_version_from_git_tags() and we could probably use https://github.com/dotnet/installer/releases

If you update the Feature, then we could avoid avoid bumping the major version

Copy link
Member

Choose a reason for hiding this comment

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

The intention of this change is to rip out the complexity that using a hybrid of apt + github tags + official install scripts brings.

If it's really unfavorable to bump to 2.0.0 (which I understand is a real concern in leaving people behind), perhaps figuring out a way to deprecate the arguments would be a nicer solution.

I agree that if the three part versioning was the only thing holding us back, we should definitely keep that for compatibility.

Copy link
Contributor

Choose a reason for hiding this comment

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

@samruddhikhandale @joshspicer it's possible to support 'X.Y' if you use --channel:

./dotnet-install.sh --channel 3.1

Is the same as:

./dotnet-install.sh --version 3.1.426

Copy link
Contributor

Choose a reason for hiding this comment

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

This is also how the script currently works when you only pass a major version (form 'X').

# If version is just a major value (form 'X'), assume it is a channel
if [[ "$version" =~ ^[0-9]+$ ]]; then
channel="$version.0"
version="latest"
fi

So all you have to do is add a special case for form 'X.Y':

if [[ "$version" =~ ^[0-9]+.[0-9]+$ ]]; then
    channel="$version"
    version="latest"
fi

Works perfectly.

"type": "boolean",
"default": false,
"description": "Install just the dotnet runtime if true, and sdk if false."
},
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we should keep this option as is?
Some people might prefer install .NET from apt as it's quicker and provides a fallback if the the .NET install script regresses.

@jungaretti
Copy link
Contributor Author

jungaretti commented Mar 2, 2023

Here are more details regarding how this feature installs .NET along with a comparison to the current implementation.

Both the "old" (current) and "new" (this PR) features install a version of .NET to /usr/local/dotnet/current. In the old feature, that path is a symlink to /usr/share/dotnet. In the new feature, it's a directory containing an installation of .NET. Here's where the new feature specifies this path.

Old directory

New directory

Single version

With just 1 .NET installation, there is no difference between the two features except for the version that latest targets. The old features installs .NET 7, but the new feature installs .NET 6. Note that .NET 7 is not an LTS release. Beyond that, dotnet --version, dotnet --list-sdks, and which dotnet are the same.

Old dotnet output

New dotnet output

Additional versions

The old and new features differ noticeably when you install more than one version of .NET. The old feature makes 2 isolated installations of .NET in /usr/local/dotnet. Although both installations are on disk, the dotnet CLI only knows about one of them. The additionalVersion binary is not on $PATH.

Old feature makes two installations

On the other hand, the new feature installs both versions to the same directory. This allows dotnet to see both versions.

New feature installs two versions

Links

@samruddhikhandale
Copy link
Member

Thanks for the detailed explanation!

Few clarifying questions,

With just 1 .NET installation, there is no difference between the two features except for the version that latest targets. The old features installs .NET 7, but the new feature installs .NET 6. Note that .NET 7 is not an LTS release.

latest and lts are two different things and shouldn't matter how the SDK's are installed. Said that, why isn't latest .NET 7 in both cases?

On the other hand, the new feature installs both versions to the same directory. This allows dotnet to see both versions.

"ghcr.io/devcontainers/features/dotnet:1": {
            "version": "7",
            "installUsingApt": "false",
            "additionalVersions": "6"
 }

What is returned with dotnet --version command? Is it the .NET 6 with these PR changes?
If that's the case, then can we rearrange the code and install additionalVersions (first) followed by version. This way the behavior will stay intact and both the old and this PR changes will return dotnet --version as v7

@jungaretti
Copy link
Contributor Author

latest and lts are two different things and shouldn't matter how the SDK's are installed. Said that, why isn't latest .NET 7 in both cases?

That behavior mirrored dotnet-install.sh's behavior of defaulting to the latest LTS release. .NET 7 isn't an LTS release, so the script ignores it (by default).

New behavior for latest

I added a new path for an lts version. With this change, both the old and new features have the same behavior for latest:

Old feature latest

New feature latest

LTS version

The new feature also accepts an lts version that installs .NET 6 (as expected). This does not work in the previous feature:

Old feature lts

New feature lts

dotnet --version in new feature

dotnet --version always prints the latest version of .NET that is installed. This behavior matches Microsoft's .NET documentation that states dotnet "uses the latest SDK installed on the machine by default." Here are a couple of demos:

New feature --version is 7 with option version 7

New feature --version is 7 with option version 6

This is not a bug. In fact, this is a bugfix: the old feature's dotnet binary cannot use additionalVersions to build or run without modifying $PATH.

dotnet --version in the old feature

For reference, here's what the same scenarios look like with the old feature:

Old feature --version is 7 with option version 7

Old feature --version is 6 with option version 6

I think the old feature's dotnet --version prints the same version because of a bug that causes two isolated installations of .NET instead of 1 installation with several SDKs.

@jungaretti
Copy link
Contributor Author

Unfortunately, my work situation has changed recently and I'm no longer in a position to maintain this contribution. I'm going to close this PR.

I believe that this change is still needed in order to enable additionalVersions to install alongside each other. Feel free to use my code in the future!

@jungaretti jungaretti closed this Apr 21, 2023
@sliekens
Copy link
Contributor

Dang, I just found this PR after a few hours experimenting with different combinations of additionalVersions and installUsingApt. I'm not knowledgable enough to continue working on this, but I can say this change is definitely still needed. It's currently impossible to use a Dev Containers from container templates/features if you have a .NET project that targets multiple frameworks because of how the additional versions are installed.

@sliekens
Copy link
Contributor

I was able to modify the script from this PR and get it working perfectly.

A few of my notes:

  • Other tools like setup-dotnet are much happier when you install to /usr/share/dotnet
    instead of /usr/local/dotnet
  • Don't create a current subdirectory because multiple versions are meant to be installed to the same location
    • /usr/local/dotnet/sdk/6.0.100
    • /usr/local/dotnet/sdk/7.0.100
    • etc
  • You can pass MAJOR.MINOR as the channel to the install script
    • e.g. ./dotnet-install.sh --channel 3.1

Here is the modified script that I'm currently using
https://github.com/sliekens/gw2sdk/blob/4b19d1d44c36c8b1d4ffcb306455c1d7c492f3ed/.devcontainer/dotnet/install.sh

@samruddhikhandale
Copy link
Member

Thank you for all the wonderful feedback and insights.

Here is the modified script that I'm currently using
https://github.com/sliekens/gw2sdk/blob/4b19d1d44c36c8b1d4ffcb306455c1d7c492f3ed/.devcontainer/dotnet/install.sh

We appreciate and value community contributions, @sliekens would you be interested in opening a PR which modifies the dotnet Feature?

@jungaretti
Copy link
Contributor Author

jungaretti commented May 30, 2023

Other tools like setup-dotnet are much happier when you install to /usr/share/dotnet instead of /usr/local/dotnet

Changing the install path seems reasonable to me. I stuck with /usr/local/dotnet because that's what v1 of this Feature does. Per these docs from the Linux Foundation, /usr/share is for "local architecture-independent" programs. The install script is just adding binaries (NOT compiling), so I think /usr/share is the "correct" choice.

Don't create a current subdirectory because multiple versions are meant to be installed to the same location

Agreed. I don't think current is necessary once we fix the installation location for additionalVersions.

You can pass MAJOR.MINOR as the channel to the install script

Great news! That means v2 can support MAJOR, MAJOR.MINOR, MAJOR.MINOR.PATCH, lts, or latest. Seems like an improvement over v1!

@jungaretti
Copy link
Contributor Author

jungaretti commented May 30, 2023

@samruddhikhandale Now that we've answered the versioning/install path questions, do you have updated thoughts on bumping this Feature to v2 and ditching the apt option?

This PR stalled because of the v2 bump, the install path confusion, and the additionalVersions confusion.

@sliekens I'm traveling abroad the next few weeks, but I can help you make an updated PR if you're patient :)

@sliekens
Copy link
Contributor

Absolutely, whenever you have time @jungaretti.

About the version bump, what is considered a breaking change? One could make the case this should be a minor/patch update because nothing changes in the way you eventually use the dotnet cli? (Added to PATH same as before.)

@jungaretti
Copy link
Contributor Author

Although the CLI's usage is (nearly) the same, I assumed this would be a major version bump because:

  • additionalVersions behaves quite differently (it's an improvement, but it's different)
  • Structure of the dotnet install directory is quite different
  • I removed the installUsingApt option

@sliekens
Copy link
Contributor

@jungaretti checking if your schedule has freed up yet? I'm still interested in getting this merged. There are too many improvements in this script over the original for me to do nothing about it.

@samruddhikhandale
Copy link
Member

@samruddhikhandale Now that we've answered the versioning/install path questions, do you have updated thoughts on bumping this Feature to v2 and ditching the apt option?

Thank you for answering all the questions.@jungaretti Sounds like a good plan to me. Would appreciate your contribution, thank you! ✨ Let us know how we could help.

@sliekens
Copy link
Contributor

I'll create a PR, still working on improving the test scenarios a bit.

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

Labels

None yet

Projects

None yet

5 participants