Dependabot-core: Add support for updating the .NET Core SDK specified by global.json

Created on 10 Aug 2020  路  17Comments  路  Source: dependabot/dependabot-core

Projects that build with the .NET Core SDK can 'pin' the SDK version they use to improve build reliability over time and across machines by adding a global.json file. But it's preferred that this 'pin' be periodically updated to the latest SDK.

Can Dependabot also update the sdk.version property of this JSON file?

I know you can at least get the latest feature band and patch version with an HTTP GET at a URL like this: https://dotnetcli.blob.core.windows.net/dotnet/Sdk/3.1/latest.version. So you could at least help bump someone from 3.1.301 to 3.1.302 for example using this method.
Discovering when a 3.1 could be updated to 3.2 or even 5.0 would require a different URL perhaps, but I don't know what it is.

feature-request

Most helpful comment

Just thought I'd highlight, there is a related issue (https://github.com/dependabot/dependabot-core/issues/1697) to also add Dependabot support for updating Global tools installed to dotnet-tools.json manifest files:

{
  "version": 1,
  "isRoot": true,
  "tools": {
    "dotnet-format": {
      "version": "3.1.37601",
      "commands": [
        "dotnet-format"
      ]
    }
  }
}

Upvotes appreciated. This post is now near the top in terms of upvotes, so hopefully it'll be implemented soon.

All 17 comments

+1 for this feature request

That would be great!

IMO a pinned SDK version in global.json adds too much friction for anyone who clones your repo and wants to build your solution. I understand the reasons to pin the SDK version for CI, but there's no need to conflate that with local development. For CI, the SDK version may be pinned in GH actions YAML. I'd rather see a dependabot enhancement to update the SDK version in GH actions YAML than in global.json.

there's no need to conflate that with local development

There is when the set of warnings is different. Newer SDKs often add new warnings. If the dev box has a different SDK version than the CI machine, and the repo is set to reject warnings as errors in PR/CI builds to keep the build clean, then you'll sometimes get nasty surprises when the PR build fails although you see no warnings in your local builds.
Sometimes the output of the build is functionally different, or even won't build at all, if the SDK version isn't what was intended.

So ya, I get the friction and I definitely feel it, although I have a nice init.ps1 script that installs the SDK quickly along with any other dependencies that repo may need. So it's not too bad.

I guess it's a matter of context and trade-offs rather than "a correct answer".

If the dev box has a different SDK version than the CI machine, and the repo is set to reject warnings as errors in PR/CI builds to keep the build clean, then you'll sometimes get nasty surprises when the PR build fails although you see no warnings in your local builds.

For the poducts I'm involved in, this scenario is rare. Almost always, you can develop locally with an SDK satisfiying the TFM's specified in csproj and your local build outcome will match that of CI.

Sometimes the output of the build is functionally different, or even won't build at all, if the SDK version isn't what was intended.

Again, unlikely for the products I'm involved in.

a nice init.ps1 script that installs the SDK quickly along with any other dependencies that repo may need

Back to trade-offs. While it may be relatively "nice" in some contexts, to build the products I'm involved in, the costs of insisting on installation of highly specific versions of prerequisites tend to outweigh the benefits.

So, in retrospect, I guess support for updating _either_ global.json _or_ GH actions YAML is desirable, depending on context.

@adamralph You may be able to get the best of both worlds (for your projects) by using global.json but with a very liberal roll forward policy. If you set minor or even major as the roll-forward, then global.json becomes a sort of "you need at _least_ this version" file and will tolerate most/all newer ones the dev box may have.
The way my YAML files are written, I install the SDK version as specified by global.json so that I don't have to also define that version in my YAML file as well. If you did it that way, then dependabot would work for you if all it did was update global.json.

@AArnott I guess what I really want is to pin CI to a specific version, e.g. 3.1.402, and to constrain local dev to at least that minor range, i.e. >= 3.1.0. If that can be done somehow with global.json and YAML trickery then I'm all for it. But note that I don't want the CI version to be the minimum, because I try and keep that updated to the latest, so effectively I'd be pinning local dev. I want local dev to work with both newer and _older_ versions than CI.

The above is effectively what I have now, but the local dev constraint is only enforced deep in MSBuild when it tries to build for TFM's that are not supported by the local SDK. If the constraint were lifted to global.json I guess the error message would be more friendly.

As an extension to that, I guess it would be good for CI to build using both 3.1.100 (earliest) and 3.1.402 (latest) to prove the local dev constraint, but I would still like the latest version to be pinned, to keep the build deterministic.

Good points, @adamralph. If I was going for what you wanted, I might keep the dev copy of global.json at the root of the repo and the CI copy in my azure-pipelines folder (where I keep all my CI related files). Then as an early step in my CI I would copy the CI version of global.json over the one in the root directory.
That doesn't achieve everything you described, but I think it reaches some of it.

@AArnott come to think of it, I can achieve what I want by leaving the SDK pinned to 3.1.402 in the YAML, and adding a global.json like so at the root:

{
  "sdk": {
    "version": "3.1.100",
    "rollForward": "latestMajor"
  }
}

I don't see what value would be added by overwriting global.json in CI.

How do you 'pin' the SDK in the YAML? AFAIK dotnet will observe the global.json that it finds based on the current directory at the time it is invoked (not the directory containing the projects). So do you set the current working directory to underneath your YAML directory? Merely _installing_ a particular SDK version in your YAML doesn't force dotnet to use it unless it's the only one on the machine (or otherwise discoverable based on env vars).

@AArnott I do it like so: https://github.com/adamralph/minver/blob/dd95de67bc23e79405fccf7ef836ccbcf580ad1f/.github/workflows/ci.yml#L20-L22. I believe the setup-dotnet action ensures that the specified version is used by subsequent steps. If that were not the case, then matrix testing example in the README would not work.

I suspect that doc is misleading. Even that doc toward the bottom says that it leaves DOTNET_MULTILEVEL_LOOKUP set to its default of true, which suggests that it does _not_ hide any other SDKs/runtimes that were already on the box. I've always taken that task to install the desired SDK -- not to ensure that it is the one chosen. But that's easy enough to test. I'll see if I can whip one up.

@adamralph I ran some tests. The setup-dotnet action does not guarantee the build uses the sdk it installs, as you can see here where it installs 2.1.x but then the build proceeds to succeed using 3.1.402 per my global.json file.
But to make that happen I had to set the DOTNET_MULTILEVEL_LOOKUP env var to 1, which was 0 by default. So by default it does what you want, but it's more of a side-effect than the guaranteed effect, because it depends on how the agent is set up, whether it's clean, etc. If you were running on reused agents instead of a fresh one each time, then regardless of the DOTNET_MULTILEVEL_LOOKUP env var, the 'local' agent tool location for .NET SDKs/runtimes may be full of installs from other workflow runs. So the only way to guarantee you use the SDK you intended is to either blow away that location before installing your SDK and make sure that DOTNET_MULTILEVEL_LOOKUP=0, _or_ use a global.json file.

@AArnott thanks for looking into it. BTW the links are broken. Perhaps it's a private repo?

...it depends on how the agent is set up, whether it's clean, etc. If you were running on reused agents instead of a fresh one each time, then regardless of the DOTNET_MULTILEVEL_LOOKUP env var, the 'local' agent tool location for .NET SDKs/runtimes may be full of installs from other workflow runs. So the only way to guarantee you use the SDK you intended is to either blow away that location before installing your SDK and make sure that DOTNET_MULTILEVEL_LOOKUP=0, or use a global.json file.

I exclusively use GH actions, so it will be a fresh image each time and DOTNET_MULTILEVEL_LOOKUP will be 0. So I guess I don't need to change anything?

Just thought I'd highlight, there is a related issue (https://github.com/dependabot/dependabot-core/issues/1697) to also add Dependabot support for updating Global tools installed to dotnet-tools.json manifest files:

{
  "version": 1,
  "isRoot": true,
  "tools": {
    "dotnet-format": {
      "version": "3.1.37601",
      "commands": [
        "dotnet-format"
      ]
    }
  }
}

Upvotes appreciated. This post is now near the top in terms of upvotes, so hopefully it'll be implemented soon.

Perhaps it's a private repo?

yes, it was private. That's how gh repo create did it by default, interestingly. I've made it public.

I exclusively use GH actions, so it will be a fresh image each time and DOTNET_MULTILEVEL_LOOKUP will be 0. So I guess I don't need to change anything?

It's not about GH actions vs. something else. It's about agent reuse. So if you're exclusively using a fresh agent from the public pool, then ya, you probably have nothing to worry about for nearly any repo.

Note: the way to find potentially appropriate SDK versions would be to use

https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/releases-index.json

This links through to other JSON files and describes ALL SDKs released for .NET Core (and now the unified .NET).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kiprasmel picture kiprasmel  路  3Comments

tjwallace picture tjwallace  路  3Comments

cscherrer picture cscherrer  路  4Comments

rafaelrocha-hotmart picture rafaelrocha-hotmart  路  4Comments

LankyLou picture LankyLou  路  4Comments