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 design for simplifying workload versioning #294

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
153 changes: 153 additions & 0 deletions accepted/2023/simplify-workload-versioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Simplify Workload Versioning

.NET SDK Workloads are optional components of the .NET SDK. When designing workloads, one of the goals was to be able to update workloads separately from the .NET SDK. As a result, workloads version and update separately from the .NET SDK. This has made it hard to understand what versions of workloads are installed or available. Complicating this is the fact that workloads themselves don't directly have a version. Workload manifests, which can have a many-to-many relationship with workloads, have versions. These versions can be displayed with the `dotnet workload update --print-rollback` command, which displays output in the following format:

```
==workloadRollbackDefinitionJsonOutputStart==
{
"microsoft.net.sdk.android": "33.0.26/7.0.100",
"microsoft.net.sdk.ios": "16.2.1030/7.0.100",
"microsoft.net.sdk.maccatalyst": "16.2.1030/7.0.100",
"microsoft.net.sdk.macos": "13.1.1030/7.0.100",
"microsoft.net.sdk.maui": "7.0.59/7.0.100",
"microsoft.net.sdk.tvos": "16.1.1527/7.0.100",
"microsoft.net.workload.mono.toolchain.net6": "7.0.3/7.0.100",
"microsoft.net.workload.mono.toolchain.net7": "7.0.3/7.0.100",
"microsoft.net.workload.emscripten.net6": "7.0.3/7.0.100",
"microsoft.net.workload.emscripten.net7": "7.0.3/7.0.100"
}
==workloadRollbackDefinitionJsonOutputEnd==
```

This is very complicated and doesn't make it easy to understand workload versions.

## Goals

- Introduce a single version number that represents all workload versions for the the .NET SDK
- Workloads can continue to release outside the normal .NET SDK release schedule
- Deadlines for workload completion and insertion do not change from current processes
- Workload updates should require minimal (or no) additional validation of non-workload scenarios

## Workloads versions

Currently, we use a 3 part version number for the .NET SDK, for example 8.0.100. Typically we release a patch each month, for example 8.0.101, 8.0.102, etc.

We will create a workloads version number that encapsulates all the workload manifest versions that are released together. A workloads version is essentially a mapping from the workloads version to the different versions for each workload manifest. For public releases, the workloads version will use the same version number as the .NET SDK. For example, when .NET SDK 8.0.101 releases, there will be a corresponding workloads version 8.0.101 that corresponds to the versions of the workloads that were released together with that .NET SDK.

## Baseline workload versions

.NET SDK workloads have similar shipping deadlines as the .NET SDK. That means that the .NET SDK itself can't include final versions of workloads which are built outside of the .NET build, as building and signing off on those workloads happens in parallel with .NET build and signoff. So the .NET SDK ships with *baseline* workload manifests, which are mainly used to enable the .NET SDK to list which workloads are available, and to know if a project requires a workload which isn't installed. By default, when a workload is installed via the .NET CLI, the .NET SDK will first look for and update to the latest workload manifests for the current feature band before installing workloads.

When we build the .NET SDK, we will assign a workloads version to the baseline workload manifest versions that are included in that SDK. For releases with stabilized version numbers, we need a different workloads version number for the baseline workload manifest versions than for the stabilized version number for the .NET SDK. In that case, we will use `baseline` as a the first semantic version pre-release identifier, followed by build number information. For example, the baseline workloads version for 8.0.201 could be `8.0.201-baseline.23919.2`.

For non-stabilized builds of the .NET SDK, we will use the same (prerelease) version number for the workloads version as we do for the .NET SDK version.

QUESTION: Will it be OK for the `baseline` versions to be semantically less than `preview` and `rc` versions?

## Workload versions in Visual Studio

Visual Studio includes the .NET SDK, but rather than including baseline manifest versions for the .NET SDK workloads, it includes the final workload manifests for a given release. These manifests are inserted into Visual Studio together with the workloads.

For public releases of Visual Studio, the workloads version information should be created and inserted into Visual Studio together with the workloads. However, internal builds of Visual Studio may not have an assigned workloads version. In that case, the .NET SDK will use the same basic logic as it currently does to select manifests. This involves finding the latest installed manifest for the current feature band, and if an expected manifest isn't found for the current feature band, then falling back to previous feature bands. In this case, when a workloads version is needed, the .NET SDK will create a version using the .NET SDK feature band, the pre-release specifier `vs`, and a hash of the manifest IDs and versions. For example, `8.0.200-vs.9e7f4b93`.

NOTE: This means that these `vs` prerelase workloads versions would be semantically greater than any `baseline`, `preview`, or `rc` workloads versions.

## Experience

`dotnet --version` would continue to print the SDK version, as it does today:

```
> dotnet --version
8.0.201
```

We will add a new `dotnet workload --version` command to print the workloads version:

```
> dotnet workload --version
8.0.201
```

We will also update `dotnet --info` and `dotnet workload --info` to display the workloads version. For example:

```
> dotnet --info
.NET SDK:
Version: 8.0.201
Commit: <commit>
Workloads version: 8.0.201

<further information>

> dotnet workload --info
Workloads version: 8.0.201
[wasm-tools]
Installation Source: SDK 8.0.100-preview.4
Manifest Version: 8.0.0-preview.4.23181.9/8.0.100-preview.4
Manifest Path: C:\git\dotnet-sdk-main\.dotnet\sdk-manifests\8.0.100-preview.4\microsoft.net.workload.mono.toolchain.current\WorkloadManifest.json
Install Type: FileBased
```

When updating workloads, console output will include the old and new workloads versions:

```
> dotnet workload install wasm-tools
Checking for updated workloads version...
Updating workloads version from 8.0.201-baseline.23919.2 to 8.0.201...
Installing workload manifest microsoft.net.sdk.android version 34.0.0-preview.4.230...
<Further workload manifest update messages>
Installing pack Microsoft.NET.Runtime.WebAssembly.Sdk version 8.0.0-preview.4.23181.9...
<Further workload pack installation manifests>
Garbage collecting for SDK feature band(s) 8.0.200...

Successfully updated workloads version from 8.0.201-baseline.23919.2 to 8.0.201.2.
Successfully installed workload(s) wasm-tools.
```

The proposed [workload history](https://github.com/dotnet/sdk/pull/30486) command will also display the workloads versions.

The .NET SDK will periodically (once a day, by default) check for updated workload versions in order to notify users if there is an update available. Currently, commands such as `dotnet build` print out the following message:

```
Workload updates are available. Run `dotnet workload list` for more information.
```

The `dotnet workload list` command, in turn, prints out the following message:

```
Updates are available for the following workload(s): wasm-tools. Run `dotnet workload update` to get the latest.
```

We will modify both commands to print the same message:

```
A workload update (version 8.0.202) is available. Run `dotnet workload update` to install the latest workloads.
```

We will add a new `--version` option to `dotnet workload update` to allow installing (or downgrading to) a specific workloads version:
Copy link
Member

Choose a reason for hiding this comment

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

Would downgrading remove the previous workload version? What happens if a user installs VS and they get 8.0.203.6 of the workloads, but then drop to the CLI and try to downgrade. Would we block this scenario?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it would remove the previous version if possible, but if it was installed by VS it would leave it and print a message saying you'd need to use global.json to select the earlier version.


```
> dotnet workload update --version 8.0.202
Updating workloads version from 8.0.201 to 8.0.202...
<Manifest and pack installation / garbage collection messages>
Successfully updated workloads version from 8.0.201 to 8.0.202.
```

## Specifying workload versions with global.json

We will add support for specifying the workloads version in global.json. For example:
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't this break global.json? It would look for an SDK with that version, which would never exist, or is the plan to look for an SDK version of 8.0.201 and then a workload patch version of 8.0.201.0?

How would someone specify a patch version when the SDK version is prerelease? We cannot have a 4-part version number as that breaks semver 2.0

Should we consider adding a separate entry instead, e.g. `"workload-patch": "0"? That still won't solve the problem with preview SDKs I think

Copy link
Member Author

Choose a reason for hiding this comment

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

The idea was that only the first three parts of the version would be used for SDK selection. You're right though, it's probably better to have a separate value for the workloads version though.


```json
{
"sdk": {
"workloadsVersion": "8.0.201"
}
Copy link
Member

Choose a reason for hiding this comment

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

Right now, AutoImport.props is imported into every project for .NET 6, 7, 8, etc. If you have 8.0.201 and 8.0.202 workloads installed, is AutoImport.props only imported for the pinned version?

If so, we might want to add some more robust logging to understand why the workload resolver imported one file over another.

}
```

This would force the SDK to use workloads version `8.0.201`, and would error if that version was not installed.

We will support side-by-side workload version installations. If 8.0.202 is installed, we would support running `dotnet workload install --version 8.0.201` to install that version of the workloads. After installing the earlier version, the .NET SDK would still by default use the latest installed workloads version. An earlier workloads version would only be used if it was specified in a global.json file.

NOTE: We may not implement the global.json workloads version support in the same release as the rest of the changes described in this design.