dotnet / sdk

Core functionality needed to create .NET Core projects, that is shared between Visual Studio and CLI
https://dot.net/core
MIT License
2.65k stars 1.05k forks source link

Add path to global.json SDK version lock #8254

Open rainersigwald opened 7 years ago

rainersigwald commented 7 years ago

Builders of large systems often want to be able to explicitly specify an SDK version and avoid installing the CLI to a machine-global location.

Currently, global.json allows the version lock, but the specified version must be installed in either a machine-global location or a nonstandard location must be specified by the environment variable DOTNET_MSBUILD_SDK_RESOLVER_SDKS_DIR.

That environment means that you must launch Visual Studio from a specific environment to get the downloaded/private SDKs.

There could be an extension to the SDK resolver to respect a path specified in the global.json. Something like

{
    "sdk": {
        "version": "1.0.0",
        "path": "tools/downloadedsdk"
  }
}

could be equivalent to setting DOTNET_MSBUILD_SDK_RESOLVER_SDKS_DIR=%GlobalJsonPath%\tools\downloadedsdk before invoking the resolver.

This would be visible by any invocation (dotnet, msbuild, VS, or MSBuild API) since it's file-based.

richlander commented 1 year ago

Today, you can clone a repo, build it, and assume that your globally installed SDK was used, provided by a vendor you trust. With this model, the global.json could point to a directory in the repo like ~/malicious-dotnet/ that could perform arbitrary operations on your machine as a consequence of running dotnet build. Certainly, NuGet packages can also do bad things in the build. We're aiming to reduce not increase security concerns.

I'm not suggesting that cloning and building an arbitrary repo is a safe thing to do today.

akoeplinger commented 1 year ago

Not sure I follow your argument, how is that different from having a .csproj like this:

<Project Sdk="Microsoft.NET.Sdk">
   <Target Name="Build">
      <Exec Command="rm -rf /home/richlander" />
   </Target>
</Project>

The assumption today is that if you run dotnet build then you're trusting the code.

vitek-karas commented 1 year ago

If I remember correctly the difference was that with the global.json path, it would be enough to run dotnet --info in the "wrong" directory.

KirillOsenkov commented 1 year ago

After wasting another couple of hours on this today, I am more convinced than ever that we should allow specifying a path to the SDK in the global.json (as opposed to the environment variables, which are terrible UX for discoverability and automation). When you open the .sln in Visual Studio, for example, it doesn't have the environment variables, and so the design-time build fails completely (you get errors like "System.Object not found").

The tooling could even automate downloading the SDK into that folder instead of having arcane Arcade tools do it.

I have been shouting from the rooftops for years that for every repo in the .NET world anyone should be able to git clone && msbuild and it should just work, without custom build.cmd, build.sh etc. The environment variables get in the way of that. I absolutely do not buy the security argument because it doesn't make the status quo any worse (any risks from building with a local SDK are already present when restoring and/or building with vanilla SDK as Alexander explains). Both the project source or any of the NuGet packages that are restored can be malicious.

Not to mention that the current status quo is you run build.cmd. And of course, it still builds with that custom SDK, which still entails the same alleged security risks.

By ignoring this problem we exclude a whole class of tools and automation from working out of the box. If any repo is buildable out of the box, you could run security analysis, semantic indexing for SourceBrowser or other code search tools, etc. etc. These tools can't read your build.cmd as it's totally opaque imperative code. Had we had declarative configuration in global.json (download and use this exact SDK), it would open doors to automating analysis etc. Opening the .sln in VS would always "just work", whereas now it doesn't and causes errors in the design-time build that only a few of us know how to deal with.

I think the current situation is a major source of friction in the .NET ecosystem.

xoofx commented 1 year ago

Hey, just found this thread.

We are actually looking for a solution at Unity to have the SDK configurable by the global.json. Our scenario is that we will ship/lock-in a specific version of the .NET SDK/Runtime as part of the Unity Editor, and we need a way to resolve our shipped .NET SDK for the sln/csproj files that we will compile with MSBuild or that we will open from Visual Studio.

Having a way to override the SDK root path in global.json seems to be a good fit for our use case.

tmat commented 1 year ago

Re Arcade -- the purpose of Arcade is to unify build across dotnet repos by implementing as few additions on top of the shipping .NET SDK as possible. This goal has been achieved. The Arcade SDK has been pretty stable for a while now. The next step should be to review the additions/workarounds that were introduced by Arcade SDK. Those that are generally useful, i.e. not specific to Microsoft build process should be productized in the shipping .NET SDK/msbuild/VS and removed from Arcade SDK. SDK downloading should be one of those features. That said, you'd still need to have a build script in the repo that downloads the SDK for non-VS scenarios.

tmat commented 1 year ago

every repo in the .NET world anyone should be able to git clone && msbuild and it should just work, without custom build.cmd, build.sh et

Do you mean that VS msbuild or dotnet build would check the version of the SDK specified in global.json and download one if it is not the one available on the machine and then run dotnet build from that matching SDK?

RussKie commented 1 year ago

@tmat - yes. Or as an easier alternative to emit an error message directing developers how to get the required SDK.

hknielsen commented 1 year ago

To add to @xoofx's comment, for the work we are doing with MsBuild in Unity this is one of our big workflow issues. The only way i've found to handle SDK resolving to something specific is to add local nuget repo and pin the SDK version in the global.json.

I would really like Sdk Resolving to be much more flexible. Creating a SdkResolver instead of a path in global.json would maybe be better, but theres no way to have it automatically picked up across vs/dotnet/msbuild in a nice way. Or at least I havent found a way!

I would be super happy to handle the Sdk resolving myself, path or SdkResolver

xoofx commented 1 year ago

Do you mean that VS msbuild or dotnet build would check the version of the SDK specified in global.json and download one if it is not the one available on the machine and then run dotnet build from that matching SDK?

If possible, I would actually prefer to split the requirement/concern of the automatic downloading of a SDK version in a separate issue and keep this issue for allowing to specify a specific location for a SDK. It seems that allowing to override the path would require a small change to the existing MSBuild Sdk Resolver (I don't know if it would require lots of change to VS), while downloading a version that is not installed and put it on the drive look more cumbersome to align (and where, and how to cleanup the cache and...etc.).

simonferquel commented 1 year ago

As VS already support downloading custom SDKs (and using them) and use the global.json file to know which version to download, I feel the change is not more complex than it is for dotnet build (which seem quite trivial from an external point of view, but I could be wrong). Maybe it uses the same code as dotnet CLI for implementing that ?

If the issue is accepted, I think someone in Unity Scripting team can implement it for dotnet CLI, and see if there is any additional work to do for VS.

hknielsen commented 1 year ago

@rainersigwald I can do the work, as we need to have a solution for moving to MSBuild.

Two possible solutions;

  1. Path to an additional location for picking up SDKResolver`s, that will be loaded by the SDKResolverLoader.
  2. Path that can Resolve additional MSBuild SDK's from

Im ok with both, but I could see more flexibility with option 1. Ie. if someone wanted a SDKResolver that downloaded the msbuild SDK from another location, they could create their SDK resolver that automatically would do that, and it would work in VS/MSBuild/Dotnet cli

rainersigwald commented 1 year ago

@hknielsen there's no consensus on what should be done/would be approved here so I don't think there's work to be done yet.

sharwell commented 1 year ago

We decided NOT to put any new location content into global.json. It is too easy to trick developers into running malicious code. ... Today, you can clone a repo, build it, and assume that your globally installed SDK was used, provided by a vendor you trust.

@richlander This is not even remotely correct. The default use of NuGet allows for arbitrary code to be executed by dotnet restore, including the ability to bypass anything we might put on nuget.org for detection mechanisms by redirecting to a privately hosted feed. Please reconsider the basis for the argument, as teams are being unnecessarily penalized by this decision.

KirillOsenkov commented 10 months ago

I wish we had this support. Environment variables are causing us so much pain during local dev with a pre-release SDK. The need to set several environment variables and ensure your IDE also inherits them is a constant source of friction.

jaredpar commented 10 months ago

I've written up a design proposal that seeks to address the problems shared on this thread as well as others I've seen. Appreciate any feedback you all have on this.

https://github.com/dotnet/designs/pull/303

KirillOsenkov commented 4 months ago

long sad story