From 0e683b87754ed626f093cab58d4c57a6961ea6e6 Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Sun, 5 May 2019 19:35:25 -0700 Subject: [PATCH 01/14] Add proposal for global.json updates --- accepted/global-json-updates.md | 790 ++++++++++++++++++++++++++++++++ 1 file changed, 790 insertions(+) create mode 100644 accepted/global-json-updates.md diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md new file mode 100644 index 000000000..67d751237 --- /dev/null +++ b/accepted/global-json-updates.md @@ -0,0 +1,790 @@ +# Proposal for global.json updates for preview and roll-forward + +--- +**NOTE:** + +SDK version numbers do not use semantic versioning. [Docs has an article on SDK versioning](https://docs.microsoft.com/en-us/dotnet/core/versions/). + +--- + +`global.json` allows users to pin the version of the SDK they are using. This was added to .NET Core as a way for users to roll-back to a previous version per project until a fix could be provided if there a problem occurred. While our guidance remains to use it only for special circumstances, users now use it for numerous reasons. + +This document describes changes to `global.json` to provide more flexibility to reflect the wider usage. + +## Definitions + +The term *SDK patch* is used to describe the last two positions of the SDK major/minor/feature/patch (n in x.y.znn). + +The term *feature band* is used to describe the SDK major/minor/feature/patch (x.y.z in x.y.znn). + +Given x.y.znn + +* x is the major version +* y is the minor version +* z is the feature band +* nn is the patch + +## Current behavior + +The SDK resolvers (in both the CLI and VS) search from the current location (project in VS) up the directory hierarchy until they encounter a `global.json` file. When the resolver finds one, _it stop looking_ and attempts to resolve the SDK version specified in that file. This happens even if this `global.json` does not specify an SDK version. + +When a `global.json` is encountered and it specifies an SDK version, the SDK resolvers attempt to locate that specific SDK patch version: + +1. When the resolver finds no `global.json` or finds one that does not specify SDK version, the latest SDK is used, regardless of whether it is preview or stable. +2. When the resolver finds a `global.json`, the specific SDK patch version it specifies is used if it is found. +3. When the resolver finds a `global.json`, and the SDK it specifies can't be found, the resolver looks for a close match: + * Prior to .NET Core 2.1 + * The highest patch version for the SDK feature band is used _regardless of whether it is higher than what is specified_. For example, if `global.json` specifies 2.2.103 is specified and is not available, and 2.2.101 is the highest SDK on the machine, it will be used. + * In .NET Core 2.1 and higher + * The highest patch version for the SDK feature band is used _assuming it is higher than what is specified_. For example, if `global.json` specifies 2.2.103 is specified, and 2.2.101 is the highest SDK on the machine, an error will occur. + +Due to the rules above, the following occurs: + +* Due to #2, simply installing a patch on the machine will not update the machine to use the safer version. + * The impact of this is mitigated by work we did in .NET Core 3.0 Preview 3 to replace earlier patch versions. +* Due to #3, users can not guarantee they are pinned to a specific version. + +## Customer problems to address + +* User wants to install a preview but only use it with a subset of projects. + * Due to Rule #1, a `global.json` is the only way to opt out of a preview in the common case of the preview being the highest version on the machine. + * The `global.json` the user addss must include a specific version. + * If no matching SDK version in the feature band (xyz in x.y.z.nn) is found, the command being called fails. + * Each `global.json` must later be updated to use the new stable SDK. + * Due to Rule #2, there is no roll forward from preview to stable if they are both on the machine. + * The impact of this is mitigated by work we did in .NET Core 3.0 Preview 3 to replace earlier patch versions. +* User wants to always use a specific SDK patch version, and receive an error if it is not on the machine. + * The user cannot currently do this. +* User wants to always run the latest patch of a particular SDK feature band. + * Due to Rule #2, the only way to do this is to remove the previous version. + * The impact of this is mitigated by work we did in .NET Core 3.0 Preview 3 to replace earlier patch versions. +* User includes a feature that requires an SDK that corresponds to a SDK feature band or higher, and wants to run the best available tools. + * There is no mechanism to require at least a specific version, but otherwise use the latest, or the latest in a range. + * Upgrading to a newer SDK will provide no benefit to projects tied to an earlier version. +* User wants to know what version of the SDK is being used. + * `global.json` is a subtle, hidden and easily forgotten marker – programmers may not understand the downsides of leaving a global.json that was suggested in a blog post in place – or they may forget it. +* User rarely wants the risk of running the unsupported scenario of multiple SDK versions in a solution build. + * There is nothing that encourages users to place `global.json` in the repository root or the same level as the solution. Using different versions of the SDK for different projects in the solution is not a supported scenario. +* Hosted CI, including Azure DevOps and Kudu, need to provide a small number of SDKs. + * If there is no patch of the major/minor/feature version (xyz in `x.y.znn`) present, the build fails, causing friction between user and host needs. + +## Proposal C + +This proposal parallels [runtime roll-forward behavior as initially described](https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/roll-forward-on-no-candidate-fx.md)} and [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md). All of the features proposed here may not be included in Phase 1. It will include at least `ignorePreview` and `"rollforward": "disable"` + +This is called Proposal C to differentiate it from two earlier proposals that were discarded but are included at the end of this document for reference. + +### Details + +The runtime options allow for three basic approaches: find nearest, find latest and find exact. + +To avoid a backward compatibility issue, the default recovery strategy when a match is not found needs to match the old `dotnet` behavior. This defines the defaults, and the default behavior differs between runtime. + +* Runtime: From the [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md) document "_In all cases except `disable` the highest available patch version is selected._" +* SDK: For backwards compatibility, the additonal option `patch` is offered and is the default. + * If the specified patch version is found, it is used. + * In all other cases (except `disable`), the highest patch within the appropriate feature band is used. + +The `global.json` schema to offers support for all of the identified scenarios that can be fixed without altering how we find `global.json`: + +* `sdk` + * `version` + * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. + * Identical to today - wildcards are not supported. + * Default: empty + * `ignorePreview` + * `true` or `false` + * Default: `false` to more closely match today's behavior. + * Alternate names: `releaseOnly` or `useReleaseOnly` + * `rollForward` + * To avoid some of the repetitiveness, I do not restate in these rules that version numbers below that specified are always ignored. + * `patch` + * If the requested major, minor, feature band, and patch if found, use it. + * Otherwise, use the highest patch within the specified major, minor and feature band. + * `feature` + * If the requested major, minor, feature band, and patch is found, use it. + * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. + * Otherwise, use the lowest higher feature band, and use the highest patch within that feature band. + * `minor` + * If the requested major, minor, feature band, and patch is found, use it. + * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. + * Otherwise, if the requested major and minor is found, use the highest patch in the _lowest_ feature band within that major and minor version. + * Otherwise, use the lowest higher minor, it's lowest feature band, and the highest patch within that feature band. + * `major` + * If the requested major, minor, feature band, and patch is found, use it. + * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. + * Otherwise, if the requested major and minor is found, use the highest patch in the _lowest_ feature band within that major and minor version. + * Otherwise, if the requested major is found, use the _lowest_ minor within that major version, and it's lowest feature band, and it's highest patch. + * Otherwise, use the lowest higher major, it's lowest minor, and it's lowest feature band, and the highest patch within that feature band. + * `latestPatch` + * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. + * Select the highest available patch version that matches the specified major, minor and feature band versions. + * `latestFeature` + * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. + * Select the highest available feature band that matches the specified major and minor version, and select it's highest patch. + * `latestMinor` + * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. + * Select the highest available minor that matches the specified major version, and select it's highest feature band, and that feature band's highest patch. + * `latestMajor` + * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. + * Select the highest available major version, and select it's highest minor version, and that minor's highest feature band, and that feature band's highest patch. + * `disable` + * Do not roll forward. Only bind to specified version. This policy means the latest patches, including security patches will not be used. + * Defaults: + * If a version is specified in the `global.json`, the default is `patch` to match today's behavior. + * If there is no `global.json` or a version is not specified, the default is`latestMajor`, again to match today's behavior. + +### Examples to illustrate roll-forward options + +The following table considers various SDK version combinations that may be available on the machine, and the outcome of the following `global.json`: + +```json +{ + "sdk": { + "version": "2.1.501", + "rollForward": [entry in table header] + } +} +``` + +| Available SDKs | patch | feature | minor | major | latestPatch | latestFeature | latestMinor | latestMajor | disable | +|------------------------------------------------------|-----------|---------|---------|---------|-------------|---------------|-------------|-------------|---------| +| 2.1.500 | fail | fail | fail | fail | fail | fail | fail | fail | fail | +| 2.1.501, 2.1.503 | _2.1.501_ | 2.1.501 | 2.1.501 | 2.1.501 | _2.1.503_ | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.501 | +| 2.1.503, 2.1.505, 2.1.601, 2.2.101, 3.0.100 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.505 | 2.1.601 | 2.2.101 | 3.0.100 | fail | +| 2.1.601, 2.1.604, 2.1.702, 2.2.101, 2.2.203, 3.0.100 | fail | 2.1.601 | 2.1.601 | 2.1.601 | fail | 2.1.702 | 2.2.203 | 3.0.100 | fail | +| 2.2.101, 2.2.203, 3.0.100 | fail | fail | 2.2.101 | 2.2.101 | fail | fail | 2.2.203 | 3.0.100 | fail | +| 3.0.100, 3.1.102 | fail | fail | fail | 3.0.100 | fail | fail | fail | 3.1.102 | fail | + +Or, if it is easier to understand with logic (bail out of logic on fail or select): + +1. If not `latest`, and there is a match, select it. +1. If we get here for `disable`, then fail. +1. Exclude any lower versions. +1. If `minor` or `latestMinor`, exclude any that don't match major version. +1. If `feature` or `latestFeature`, exclude any that don't match major and minor version. +1. If `patch` or `latestPatch`, exclude any that don't match major, minor and feature band version. +1. If there is one left, select it. +1. If any of the `latest` options are used, select the highest. +1. Otherwise, select the lowest major, and within that lowest minor, and within that lowest feature band. Then select the highest patch in that feature band. + +## Customer problems addressed + +* User wants to install a preview but only use it with a subset of projects. + * User opts out in projects where preview should not be used by adding `ignorePreview: true` to those projects, or + * User adds `ignorePreview: true` in a `global.json` higher in their file system, and adds `ignorePreview: false` in a `global.json` in specific projects. +* User wants to always use a specific SDK patch version, and receive an error if it is not on the machine. + * User adds `rollForward: disable` to a `global.json` +* User wants to always run the latest patch of a particular SDK feature band. + * User adds `rollForward: latestPatch` and the lowest version (with patch) they want to a `global.json` +* User includes a feature that requires an SDK that corresponds to a SDK feature band or higher, and wants to run the best available tools. + * User adds `rollForward: latestMajor` and the lowest version (with patch) they want to a `global.json` +* User wants to know what version of the SDK is being used. + * Not addressed in this proposal: `global.json` will still be a subtle, hidden and easily forgotten marker +* User rarely wants the risk of running the unsupported scenario of multiple SDK versions in a solution build. + * Not addressed in this proposal: There is nothing that encourages users to place `global.json` in a logical location. +* Hosted CI, including Azure DevOps and Kudu, need to provide a small number of SDKs. + * It will now be possible for users to specify `global.json` files in a way that can work with Azure DevOps (roll-forward across at least feature bands) + * We will work with Azure DevOps to determine the recommended `global.json` for this scenario. + +## Examples of usage + +This section lists a series of scenarios and following the corresponding `global.json`. Following this is a table that shows the SDK that would be selected with specific combinations of SDKs present on the machine. + +### 1. Explicitly state today's behavior when a `global.json` specifies a version + +The following `global.json` defines a specific version number, and to use a higher version if it is available. + +```json +{ + "sdk": { + "version": "2.2.100", + "ignorePreview": false, + "rollForward": "patch" + } +} +``` + +The common `global.json` for this case is likely to be: + +```json +{ + "sdk": { + "version": "2.2.100" + } +} +``` + +### 2. Explicitly state today's behavior when there is no `global.json` + +Today's behavior is that an exact match is used if found, and if not found, the highest patch above the current in the feature band is used. + +If there is no `global.json` that would logically be the same as the following. + +```json +{ + "sdk": { + "ignorePreview": false, + "rollForward": "latestMajor" + } +} +``` + +### 3. Skip previews, otherwise as though there was no `global.json` + +If there is no `global.json` but the user does not want to use previews, that would logically be the same as the following: + +```json +{ + "sdk": { + "ignorePreview": true, + "rollForward": "latestMajor" + } +} +``` + +The common `global.json` for this case is likely to be: + +```json +{ + "sdk": { + "ignorePreview": true, + } +} +``` + +The user may include their preference for previews high in their directory structure, and then individual project can elect different behavior with a new `global.json`. This will continue to follow today's rules. In particular: if there is a `global.json` file for a different reason, like a custom SDK, the higher `global.json` will not be used. + +### 4 Select highest available, higher than specified + +If the user wants to specify the lowest SDK they want to use, they could use the following: + +```json +{ + "sdk": { + "version": "2.2.100", + "ignorePreview": false, + "rollForward": "latestMajor" + } +} +``` + +The common `global.json` for this case is likely to be: + +```json +{ + "sdk": { + "version": "2.2.100", + "rollForward": "latestMajor" + } +} +``` + +### 5. Select highest available, higher than specified, previews ignored + +If the user wanted to use any SDK higher than the one listed but ignore previews, they could use the following: + +```json +{ + "sdk": { + "version": "2.2.100", + "ignorePreview": true, + "rollForward": "latestMajor" + } +} +``` + +### 6. Select highest available with unusual combination + +If the user wants to use any SDK as high or higher than the one listed but ignore previews, they could use the following: + +```json +{ + "sdk": { + "version": "3.0.100-Pre", + "ignorePreview": true, + "rollForward": "highestMajor" + } +} +``` + +This will always fail. The error will be: + +``` +No SDK could be selected due to global.json issue: Specifying a preview version with exactMatch and skipPreview set to true will always fail. +``` + +### 7. Fail if exact match is not found + +If the user wants an exact match, they could use the following. + +```json +{ + "sdk": { + "version": "2.2.100", + "ignorePreview": false, + "rollForward": "disable" + } +} +``` + +The common `global.json` for this case is likely to be: + +```json +{ + "sdk": { + "version": "2.2.100", + "rollForward": "disable" + } +} +``` + +When this fails, the error is: + +``` +The requested SDK version was not available, and SDK version roll-forward was disabled. +``` + +### 8. Exact match or highest within runtime major.minor + +This proposal does not support arbitrary ranges or arbitrary upper bounds. + +### 9. Highest within runtime major.minor + +If the user wants the highest within a runtime major/minor, they could use the following. + + +```json +{ + "sdk": { + "version": "2.2.100", + "ignorePreview": false, + "rollForward": "latestMinor" + } +} +``` + +### 10. Highest within arbitrary upper and lower bound + +If the user wanted to use any SDK higher than the one listed but ignore previews, they could use the following. + +Proposal C + +There is no concept of an arbitrary upper bound (as there isn't with the runtime) + +### Selections made + +This table lists the SDKs selected in each of the above scenarios. + +| Available SDKs | #1 | #2 | #3 | #4 | #5 | #6 | #7 | #8 | #9 | #10 | +|-------------------------------|---------|-------------|---------|-------------|---------|---------|---------|---------|---------|---------| +| 2.1.700 | fail | 2.1.700 | 2.1.700 | fail | fail | fail | fail | fail | fail | 2.1.700 | +| 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | fail | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | +| 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | fail | 2.2.103 | 2.2.103 | 2.2.103 | +| 2.1.700, 2.2.100, 2.2.103 | 2.2.100 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | 2.2.100 | 2.2.100 | 2.2.103 | 2.2.103 | +| 2.1.700, 2.2.103, 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | fail | fail | 2.2.100 | 2.2.103 | 2.2.103 | +| 2.1.700, 2.2.103, 3.1.100 | 2.2.103 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | fail | 2.2.103 | 2.2.103 | 2.2.103 | + +## Other proposals considered + +Prior to arriving at this proposal, two others were considered. The first, Proposal A is an independent versioning scheme. It's largest downside is that it would be _another_ way users would have to think about specifying versioning behavior. The second, Proposal B was an attempt to parallel NuGet. Definining [NuGet version ranges](https://docs.microsoft.com/en-us/nuget/reference/package-versioning) is relatively complex, and it didn't feel like a good fit. The recommended proposal (above), Proposal C, is based on runtime roll-forward behavior. + +### Proposal A + +This proposal is independently created, not considering other versioning definitions like NuGet. + +The proposal for Phase 1 is limited to allow us to complete it as quickly as possible - specifically for .NET Core 3.0. Phase 1 would support only `ignorePreview` and possibly `exactMatch`. + +This Phase 2 proposal extends the `global.json` schema to offer support for all of the identified scenarios: + +* `sdk` + * `version` + * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. + * Identical to today - wildcards are not supported. + * Default: empty + * `ignorePreview` + * `true` or `false` + * (Proposed) Default: `false` to more closely match today's behavior. + * Alternative text to consider: `releaseOnly`, `allowPrerelease` + * `matchRule` + * `exactMatch` + * `exactMatchOrHighest` - exact match to version or highest below upper limit. If no upper limit is declared, highest on box. If no version is specified, the highest considering upper limit or on box. + * `highest` - highest below upper limit. If no upper limit is declared, highest on box. + * Default: `exactMatchOrHighest` to more closely match today's behavior if a version is specified. + * `upperLimit` + * An upper limit that supports wildcards. + * Asterisk are the only character valid as a wildcard + * All asterisk must be right of all numbers. 2.2.2** is legal. 2.*.2** is illegal. + * Default: + * If exactMatchOrHighest and the version is specified, the feature band of the version. If the feature band is `2.2.203`, the default is `2.2.1**`. + * If no version is specified, `*.*.***`. + * Alternative to consider: NuGet's approach of exclusive upper limit. + +#### Examples + +Example 1 + +```json +{ + "sdk": { + "version": "2.2.100", + "skipPreview": false, + "matchRule": "exactMatchOrHighest", + "upperLimit": "2.2.1**" + } +} +``` + +Example 2 + +```json +{ + "sdk": { + "skipPreview": false, + "matchRule": "highest", + "upperLimit": "*.*.***" + } +} +``` + +Example 3 + +```json +{ + "sdk": { + "skipPreview": true, + "matchRule": "highest", + "upperLimit": "*.*.***" + } +} +``` + +In this case, `whenNotFound` has no meaning. + +Example 4 + +```json +{ + "sdk": { + "version": "2.2.100", + "skipPreview": false, + "matchRule": "highest", + "upperLimit": "*.*.***" + } +} +``` + +Example 5 + +```json +{ + "sdk": { + "version": "2.2.100", + "matchRule": "highest", + "skipPreview": true, + "upperLimit": "*.*.***" + } +} +``` + +Example 6 + +```json +{ + "sdk": { + "version": "3.0.100-Pre", + "matchRule": "highest", + "skipPreview": true, + "upperLimit": "*.*.***" + } +} +``` + +Example 7 + +```json +{ + "sdk": { + "version": "2.2.100", + "matchRule": "exactMatch", + "skipPreview": false, + "upperLimit": "*.*.***" + } +} +``` + +Example 8 + +```json +{ + "sdk": { + "version": "2.2.100", + "matchRule": "exactMatchOrHighest", + "skipPreview": false, + "upperLimit": "2.2.***" + } +} +``` + +Example 9 + +```json +{ + "sdk": { + "version": "2.2.100", + "matchRule": "highest", + "skipPreview": true, + "upperLimit": "2.2.***" + } +} +``` + +Example 10 + +```json +{ + "sdk": { + "version": "2.1.203", + "matchRule": "highest", + "skipPreview": true, + "upperLimit": "2.2.***" + } +} +``` + +#### Proposal A implementation thought exercise + +From a user perspective, considering what the user may want to achieve and looking at the results with different SDKs available is helpful. However, this makes it look like a bit of a nightmare to code. Here is one sequence that is not crazy hard: + +1. Assign defaults (use high numbers for stars). +1. Look for the `global.json` rules that produce errors and fail if that occurs. +1. If `matchRule == exactMatch` see if there is a match and fail if not. +1. If `matchRule == exactMatchOrHighest` and there is one exact match, use it. +1. Find all available SDKs. +1. Remove any previews if `skipPreview == true`. +1. Remove any that are below the `version`. +1. Remove any that are above the `upperLimit`. +1. If none are left, fail. +1. Since either `matchRule == highest` or `matchRule == exactMatchOrHighest` without a match must now be true, use the highest. + +#### Error: Special error on ExactMatch + +Proposal A + +```json +{ + "sdk": { + "matchRule": "exactMatch", + "skipPreview": true, + "upperLimit": "*.*.***" + } +} +``` + +The same error appears for `exactMatch` or `exactMatchOrHighest` match rules. + +The error is: + +``` +No SDK could be selected due to global.json issue: exactMatch and exactMatchOrHighest matchRules can't be used without a version. +``` + +#### Error: Special error when could never succeed due to `version`, `exactMatch` and `skipPreview` flags + +Proposal A + +```json +{ + "sdk": { + "version": "3.0.100-Pre", + "matchRule": "exactMatch", + "skipPreview": true, + "upperLimit": "*.*.***" + } +} +``` + +The error is: + +``` +No SDK could be selected due to global.json issue: Specifying a preview version with exactMatch and skipPreview set to true will always fail. +``` + +#### Error: Special error when could never succeed because `upperLimit` is below `version` + +Proposal A + +```json +{ + "sdk": { + "version": "3.0.100", + "matchRule": "exactMatch", // any value + "skipPreview": false, // any value + "upperLimit": "2.*.***" + } +} +``` + +The error is: + +``` +No SDK could be selected due to global.json issue: Version is higher than upperLimit. +``` + +### Proposal B + +_NOTE: On reflection, this version as stated would have a backwards compatibility problem. `global.json` with unexpected characters in the version would probably fail. This could be managed with a different name, but this proposal seems overly compex anyway._ + +This proposal parallels NuGet version selection. Initially only the portions of the [NuGet versioning spec](https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards) listed here would be supplied. + +To avoid a backward compatibility issue, the default recovery strategy when a match is not found needs to match the old 'dotnet' behavior, for specifications below 3.0. This approach could solve that by retaining current behavior for version definitions below 3.0 that do not use any NuGet formatting. + +The proposal for Phase 1 is limited to allow us to complete it as quickly as possible - specifically for .NET Core 3.0. Phase 1 would support only `allowPrerelease`. + +This Phase 2 proposal extends the `global.json` schema to offer support for all of the identified scenarios: + +* `sdk` + * `version` + * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. + * [NuGet versioning](https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards) + * Default: empty + * `allowPrerelease` + * `true` or `false` + * (Proposed) Default: `true` to more closely match today's behavior. + * `matchRule` + * `highest` - Use the version listed is a lower bound and the highest is selected, even if the stated version exists. + * `legacy` - Use the exact patch version if it is available. If it is not available, use the highest patch version in the current feature band. + * Default: `legacy` to match today's behavior if a version is specified. `fail` may be a better long term default, but changing at 3.0 means that `version: 2.0.0` and `version: 3.0.0` behave differently. + +Example 1 + +```json +{ + "sdk": { + "version": "2.2.100", + "allowPrerelease": true, + "matchRule": "legacy" + } +} +``` + +Example 2 + +```json +{ + "sdk": { + "version": "*", + "allowPrerelease": true, + "matchRule": "legacy" + } +} +``` + +Example 3 + +```json +{ + "sdk": { + "version": "*", + "allowPrerelease": false, + "matchRule": "legacy" + } +} +``` + +In this case, `matchRule` has no meaning. + +Example 4 + +```json +{ + "sdk": { + "version": "2.2.100", + "allowPrerelease": true, + "matchRule": "highest" + } +} +``` + +Example 5 + +```json +{ + "sdk": { + "version": "2.2.100", + "allowPrerelease": false, + "matchRule": "highest" + } +} +``` + +Example 6 + +```json +{ + "sdk": { + "version": "3.0.100-Pre", + "matchRule": "highest", + "allowPrerelease": false, + } +} +``` + +Example 7 + +```json +{ + "sdk": { + "version": "[2.2.100]", + "allowPrerelease": true, + "matchRule": "highest" + } +} +``` + +Example 8 + +```json +{ + "sdk": { + "version": "[2.2.100,2.2.*]", + "allowPrerelease": true, + "matchRule": "legacy" + } +} +``` + +Example 9 + +```json +{ + "sdk": { + "version": "2.2.*", + "allowPrerelease": true, + "matchRule": "highest" + } +} +``` + +Example 10 + +```json +{ + "sdk": { + "version": "[2.1.203,2.2.*]", + "allowPrerelease": false, + "matchRule": "highest" + } +} +``` + +## Additional suggestions + +These suggestions have been received via Twitter, and I want to acknowledge that we've seen them and why we aren't acting on them at present. + +### [Have Visual Studio offer to download the requested SDK](https://twitter.com/Nick_Craver/status/1124649778078527488) + +This idea is for Visual Studio, so it is not covered in this proposal. + +The roll-forward within feature band is a feature, so this probably makes sense only when the presence of global.json would fail. + From 27753476563e318dce10a506504e720cd4cc05a3 Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Sun, 12 May 2019 17:18:59 -0700 Subject: [PATCH 02/14] Simplified proposal --- accepted/global-json-updates.md | 619 ++++---------------------------- 1 file changed, 72 insertions(+), 547 deletions(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index 67d751237..2b11d6291 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -1,4 +1,4 @@ -# Proposal for global.json updates for preview and roll-forward +# Proposal for global.json updates for preview and limited roll-forward --- **NOTE:** @@ -7,9 +7,9 @@ SDK version numbers do not use semantic versioning. [Docs has an article on SDK --- -`global.json` allows users to pin the version of the SDK they are using. This was added to .NET Core as a way for users to roll-back to a previous version per project until a fix could be provided if there a problem occurred. While our guidance remains to use it only for special circumstances, users now use it for numerous reasons. +`global.json` allows users to pin the version of the SDK they are using. This was added to .NET Core in case we introduced an issue to allow roll-back per project until a fix could be provided. While our guidance remains to use it only for special circumstances, users now use it for numerous reasons. -This document describes changes to `global.json` to provide more flexibility to reflect the wider usage. +This document describes changes to `global.json` to provide more flexibility. ## Definitions @@ -44,96 +44,50 @@ Due to the rules above, the following occurs: * The impact of this is mitigated by work we did in .NET Core 3.0 Preview 3 to replace earlier patch versions. * Due to #3, users can not guarantee they are pinned to a specific version. -## Customer problems to address - -* User wants to install a preview but only use it with a subset of projects. - * Due to Rule #1, a `global.json` is the only way to opt out of a preview in the common case of the preview being the highest version on the machine. - * The `global.json` the user addss must include a specific version. - * If no matching SDK version in the feature band (xyz in x.y.z.nn) is found, the command being called fails. - * Each `global.json` must later be updated to use the new stable SDK. - * Due to Rule #2, there is no roll forward from preview to stable if they are both on the machine. - * The impact of this is mitigated by work we did in .NET Core 3.0 Preview 3 to replace earlier patch versions. -* User wants to always use a specific SDK patch version, and receive an error if it is not on the machine. - * The user cannot currently do this. -* User wants to always run the latest patch of a particular SDK feature band. - * Due to Rule #2, the only way to do this is to remove the previous version. - * The impact of this is mitigated by work we did in .NET Core 3.0 Preview 3 to replace earlier patch versions. -* User includes a feature that requires an SDK that corresponds to a SDK feature band or higher, and wants to run the best available tools. - * There is no mechanism to require at least a specific version, but otherwise use the latest, or the latest in a range. - * Upgrading to a newer SDK will provide no benefit to projects tied to an earlier version. -* User wants to know what version of the SDK is being used. - * `global.json` is a subtle, hidden and easily forgotten marker – programmers may not understand the downsides of leaving a global.json that was suggested in a blog post in place – or they may forget it. -* User rarely wants the risk of running the unsupported scenario of multiple SDK versions in a solution build. - * There is nothing that encourages users to place `global.json` in the repository root or the same level as the solution. Using different versions of the SDK for different projects in the solution is not a supported scenario. -* Hosted CI, including Azure DevOps and Kudu, need to provide a small number of SDKs. - * If there is no patch of the major/minor/feature version (xyz in `x.y.znn`) present, the build fails, causing friction between user and host needs. - -## Proposal C - -This proposal parallels [runtime roll-forward behavior as initially described](https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/roll-forward-on-no-candidate-fx.md)} and [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md). All of the features proposed here may not be included in Phase 1. It will include at least `ignorePreview` and `"rollforward": "disable"` - -This is called Proposal C to differentiate it from two earlier proposals that were discarded but are included at the end of this document for reference. +## Proposal -### Details +### Roll-forward -The runtime options allow for three basic approaches: find nearest, find latest and find exact. +.NET Core SDK "roll-forward" is problematic because version numbers are not linear. For example: 2.1.600 contains MSBuild 16 while 2.2.100 contains MSBuild 15.9. This is not a new problem, since it already occurs when there is no `global.json`. However, we do not want to roll-forward in scenarios that might surprise the user. Because of this, roll-forward will be allowed only in limited scenarios. -To avoid a backward compatibility issue, the default recovery strategy when a match is not found needs to match the old `dotnet` behavior. This defines the defaults, and the default behavior differs between runtime. - -* Runtime: From the [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md) document "_In all cases except `disable` the highest available patch version is selected._" -* SDK: For backwards compatibility, the additonal option `patch` is offered and is the default. - * If the specified patch version is found, it is used. - * In all other cases (except `disable`), the highest patch within the appropriate feature band is used. +### Details The `global.json` schema to offers support for all of the identified scenarios that can be fixed without altering how we find `global.json`: * `sdk` * `version` * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. - * Identical to today - wildcards are not supported. + * Wildcards are not supported. * Default: empty * `ignorePreview` * `true` or `false` - * Default: `false` to more closely match today's behavior. + * Default: `false` to match previous behavior. * Alternate names: `releaseOnly` or `useReleaseOnly` + * This is new behavior. * `rollForward` - * To avoid some of the repetitiveness, I do not restate in these rules that version numbers below that specified are always ignored. + * Lower version numbers are always ignored. * `patch` - * If the requested major, minor, feature band, and patch if found, use it. - * Otherwise, use the highest patch within the specified major, minor and feature band. - * `feature` - * If the requested major, minor, feature band, and patch is found, use it. - * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. - * Otherwise, use the lowest higher feature band, and use the highest patch within that feature band. - * `minor` * If the requested major, minor, feature band, and patch is found, use it. - * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. - * Otherwise, if the requested major and minor is found, use the highest patch in the _lowest_ feature band within that major and minor version. - * Otherwise, use the lowest higher minor, it's lowest feature band, and the highest patch within that feature band. - * `major` - * If the requested major, minor, feature band, and patch is found, use it. - * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. - * Otherwise, if the requested major and minor is found, use the highest patch in the _lowest_ feature band within that major and minor version. - * Otherwise, if the requested major is found, use the _lowest_ minor within that major version, and it's lowest feature band, and it's highest patch. - * Otherwise, use the lowest higher major, it's lowest minor, and it's lowest feature band, and the highest patch within that feature band. + * Otherwise, use the highest patch within the specified major, minor and feature band. + * This policy means the latest patches, including security patches will not be used unless the specified version is remove from the machine. + * This matches previous behavior when a `global.json` file contains a version. * `latestPatch` * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. * Select the highest available patch version that matches the specified major, minor and feature band versions. - * `latestFeature` - * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. - * Select the highest available feature band that matches the specified major and minor version, and select it's highest patch. - * `latestMinor` - * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. - * Select the highest available minor that matches the specified major version, and select it's highest feature band, and that feature band's highest patch. - * `latestMajor` + * This is new behavior. + * `disable` + * Do not roll forward. Only bind to specified version. + * This policy means the latest patches, including security patches will not be used. + * This is new behavior. + * `latest` * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. * Select the highest available major version, and select it's highest minor version, and that minor's highest feature band, and that feature band's highest patch. - * `disable` - * Do not roll forward. Only bind to specified version. This policy means the latest patches, including security patches will not be used. + * Using latest with a version is new behavior. Otherwise this matches previous behavior when there is no `global.json` or no version is specified. * Defaults: - * If a version is specified in the `global.json`, the default is `patch` to match today's behavior. - * If there is no `global.json` or a version is not specified, the default is`latestMajor`, again to match today's behavior. - + * Defaults are defined to support backward compatibility issues. + * If a version is specified in the `global.json`, the default is `patch` to match previous behavior. + * If there is no `global.json` or a version is not specified, the default is`latest`, again to match previous behavior. + ### Examples to illustrate roll-forward options The following table considers various SDK version combinations that may be available on the machine, and the outcome of the following `global.json`: @@ -147,51 +101,43 @@ The following table considers various SDK version combinations that may be avail } ``` -| Available SDKs | patch | feature | minor | major | latestPatch | latestFeature | latestMinor | latestMajor | disable | -|------------------------------------------------------|-----------|---------|---------|---------|-------------|---------------|-------------|-------------|---------| -| 2.1.500 | fail | fail | fail | fail | fail | fail | fail | fail | fail | -| 2.1.501, 2.1.503 | _2.1.501_ | 2.1.501 | 2.1.501 | 2.1.501 | _2.1.503_ | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.501 | -| 2.1.503, 2.1.505, 2.1.601, 2.2.101, 3.0.100 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.505 | 2.1.601 | 2.2.101 | 3.0.100 | fail | -| 2.1.601, 2.1.604, 2.1.702, 2.2.101, 2.2.203, 3.0.100 | fail | 2.1.601 | 2.1.601 | 2.1.601 | fail | 2.1.702 | 2.2.203 | 3.0.100 | fail | -| 2.2.101, 2.2.203, 3.0.100 | fail | fail | 2.2.101 | 2.2.101 | fail | fail | 2.2.203 | 3.0.100 | fail | -| 3.0.100, 3.1.102 | fail | fail | fail | 3.0.100 | fail | fail | fail | 3.1.102 | fail | +| Available SDKs | patch | latestPatch | latest | disable | +|---------------------------------------------|---------|-------------|---------|---------| +| 2.1.500 | fail | fail | fail | fail | +| 2.1.501, 2.1.503 | 2.1.501 | 2.1.503 | 2.1.503 | 2.1.501 | +| 2.1.503, 2.1.505, 2.1.601, 2.2.101, 3.0.100 | 2.1.503 | 2.1.505 | 3.0.100 | fail | +| 2.2.101, 2.2.203, 3.0.100 | fail | fail | 3.0.100 | fail | +| 3.0.100, 3.1.102 | fail | fail | 3.1.102 | fail | -Or, if it is easier to understand with logic (bail out of logic on fail or select): +Or, if it is easier to understand possible logic: -1. If not `latest`, and there is a match, select it. +1. If `ignorePreview` is `false`, exclude all preview versions. +1. If `patch` or `disable`, and there is a match, select it. 1. If we get here for `disable`, then fail. 1. Exclude any lower versions. -1. If `minor` or `latestMinor`, exclude any that don't match major version. -1. If `feature` or `latestFeature`, exclude any that don't match major and minor version. 1. If `patch` or `latestPatch`, exclude any that don't match major, minor and feature band version. +1. If there are none left, then fail. 1. If there is one left, select it. -1. If any of the `latest` options are used, select the highest. -1. Otherwise, select the lowest major, and within that lowest minor, and within that lowest feature band. Then select the highest patch in that feature band. - -## Customer problems addressed - -* User wants to install a preview but only use it with a subset of projects. - * User opts out in projects where preview should not be used by adding `ignorePreview: true` to those projects, or - * User adds `ignorePreview: true` in a `global.json` higher in their file system, and adds `ignorePreview: false` in a `global.json` in specific projects. -* User wants to always use a specific SDK patch version, and receive an error if it is not on the machine. - * User adds `rollForward: disable` to a `global.json` -* User wants to always run the latest patch of a particular SDK feature band. - * User adds `rollForward: latestPatch` and the lowest version (with patch) they want to a `global.json` -* User includes a feature that requires an SDK that corresponds to a SDK feature band or higher, and wants to run the best available tools. - * User adds `rollForward: latestMajor` and the lowest version (with patch) they want to a `global.json` -* User wants to know what version of the SDK is being used. - * Not addressed in this proposal: `global.json` will still be a subtle, hidden and easily forgotten marker -* User rarely wants the risk of running the unsupported scenario of multiple SDK versions in a solution build. - * Not addressed in this proposal: There is nothing that encourages users to place `global.json` in a logical location. -* Hosted CI, including Azure DevOps and Kudu, need to provide a small number of SDKs. - * It will now be possible for users to specify `global.json` files in a way that can work with Azure DevOps (roll-forward across at least feature bands) - * We will work with Azure DevOps to determine the recommended `global.json` for this scenario. +1. Select the highest remaining. + +## Customer problems to address + +| Problem | Current | Proposed | +|---------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------| +| Install a preview but use it with only a subset of projects. | `version: ` or `version: -pre` or similar (*)| `ignorePreview: true`, or `ignorePreview: true` higher in the file system and `ignorePreview: false`. | +| Fail if specific version is not available. | Not possible | `rollForward: disable` in `global.json` | +| Always run the latest patch of a particular SDK feature band. | Not possible | `rollForward: latestPatch` and lowest acceptable `version` (**) | +| Use at least a specific version, otherwise latest | Not possible | `rollForward: latest` and lowest acceptable `version` (**) | +| Hosted CI | Friction matching SDKs for users + | _This proposal needs review by DevOps | +(*) One pain point of this is that these `global.json` file must be removed or later be updated to use the new stable SDK, which is almost never the desired behavior. +(**) Because the SDK version numbers are not linear, this will not always result in the highest SDK on the machine being used. ## Examples of usage This section lists a series of scenarios and following the corresponding `global.json`. Following this is a table that shows the SDK that would be selected with specific combinations of SDKs present on the machine. -### 1. Explicitly state today's behavior when a `global.json` specifies a version +### 1. Explicitly state previous behavior when a `global.json` specifies a version The following `global.json` defines a specific version number, and to use a higher version if it is available. @@ -215,9 +161,9 @@ The common `global.json` for this case is likely to be: } ``` -### 2. Explicitly state today's behavior when there is no `global.json` +### 2. Explicitly state previous behavior when there is no `global.json` -Today's behavior is that an exact match is used if found, and if not found, the highest patch above the current in the feature band is used. +previous behavior is that an exact match is used if found, and if not found, the highest patch above the current in the feature band is used. If there is no `global.json` that would logically be the same as the following. @@ -225,7 +171,7 @@ If there is no `global.json` that would logically be the same as the following. { "sdk": { "ignorePreview": false, - "rollForward": "latestMajor" + "rollForward": "latest" } } ``` @@ -238,7 +184,7 @@ If there is no `global.json` but the user does not want to use previews, that wo { "sdk": { "ignorePreview": true, - "rollForward": "latestMajor" + "rollForward": "latest" } } ``` @@ -253,7 +199,7 @@ The common `global.json` for this case is likely to be: } ``` -The user may include their preference for previews high in their directory structure, and then individual project can elect different behavior with a new `global.json`. This will continue to follow today's rules. In particular: if there is a `global.json` file for a different reason, like a custom SDK, the higher `global.json` will not be used. +The user may include their preference for previews high in their directory structure, and then individual project can elect different behavior with a new `global.json`. This will continue to follow previous rules. In particular: if there is a `global.json` file for a different reason, like a custom SDK, the higher `global.json` will not be used. ### 4 Select highest available, higher than specified @@ -264,7 +210,7 @@ If the user wants to specify the lowest SDK they want to use, they could use the "sdk": { "version": "2.2.100", "ignorePreview": false, - "rollForward": "latestMajor" + "rollForward": "latest" } } ``` @@ -275,7 +221,7 @@ The common `global.json` for this case is likely to be: { "sdk": { "version": "2.2.100", - "rollForward": "latestMajor" + "rollForward": "latest" } } ``` @@ -289,7 +235,7 @@ If the user wanted to use any SDK higher than the one listed but ignore previews "sdk": { "version": "2.2.100", "ignorePreview": true, - "rollForward": "latestMajor" + "rollForward": "latest" } } ``` @@ -303,7 +249,7 @@ If the user wants to use any SDK as high or higher than the one listed but ignor "sdk": { "version": "3.0.100-Pre", "ignorePreview": true, - "rollForward": "highestMajor" + "rollForward": "highest" } } ``` @@ -345,446 +291,25 @@ When this fails, the error is: The requested SDK version was not available, and SDK version roll-forward was disabled. ``` -### 8. Exact match or highest within runtime major.minor - -This proposal does not support arbitrary ranges or arbitrary upper bounds. - -### 9. Highest within runtime major.minor - -If the user wants the highest within a runtime major/minor, they could use the following. - - -```json -{ - "sdk": { - "version": "2.2.100", - "ignorePreview": false, - "rollForward": "latestMinor" - } -} -``` - -### 10. Highest within arbitrary upper and lower bound - -If the user wanted to use any SDK higher than the one listed but ignore previews, they could use the following. - -Proposal C - -There is no concept of an arbitrary upper bound (as there isn't with the runtime) - ### Selections made This table lists the SDKs selected in each of the above scenarios. -| Available SDKs | #1 | #2 | #3 | #4 | #5 | #6 | #7 | #8 | #9 | #10 | -|-------------------------------|---------|-------------|---------|-------------|---------|---------|---------|---------|---------|---------| -| 2.1.700 | fail | 2.1.700 | 2.1.700 | fail | fail | fail | fail | fail | fail | 2.1.700 | -| 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | fail | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | -| 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | fail | 2.2.103 | 2.2.103 | 2.2.103 | -| 2.1.700, 2.2.100, 2.2.103 | 2.2.100 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | 2.2.100 | 2.2.100 | 2.2.103 | 2.2.103 | -| 2.1.700, 2.2.103, 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | fail | fail | 2.2.100 | 2.2.103 | 2.2.103 | -| 2.1.700, 2.2.103, 3.1.100 | 2.2.103 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | fail | 2.2.103 | 2.2.103 | 2.2.103 | +| Available SDKs | #1 | #2 | #3 | #4 | #5 | #6 | #7 | +|-------------------------------|---------|-------------|---------|-------------|---------|---------|---------| +| 2.1.700 | fail | 2.1.700 | 2.1.700 | fail | fail | fail | fail | +| 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | fail | 2.2.100 | +| 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | fail | +| 2.1.700, 2.2.100, 2.2.103 | 2.2.100 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | 2.2.100 | +| 2.1.700, 2.2.103, 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | fail | fail | +| 2.1.700, 2.2.103, 3.1.100 | 2.2.103 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | fail | ## Other proposals considered -Prior to arriving at this proposal, two others were considered. The first, Proposal A is an independent versioning scheme. It's largest downside is that it would be _another_ way users would have to think about specifying versioning behavior. The second, Proposal B was an attempt to parallel NuGet. Definining [NuGet version ranges](https://docs.microsoft.com/en-us/nuget/reference/package-versioning) is relatively complex, and it didn't feel like a good fit. The recommended proposal (above), Proposal C, is based on runtime roll-forward behavior. - -### Proposal A - -This proposal is independently created, not considering other versioning definitions like NuGet. - -The proposal for Phase 1 is limited to allow us to complete it as quickly as possible - specifically for .NET Core 3.0. Phase 1 would support only `ignorePreview` and possibly `exactMatch`. - -This Phase 2 proposal extends the `global.json` schema to offer support for all of the identified scenarios: - -* `sdk` - * `version` - * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. - * Identical to today - wildcards are not supported. - * Default: empty - * `ignorePreview` - * `true` or `false` - * (Proposed) Default: `false` to more closely match today's behavior. - * Alternative text to consider: `releaseOnly`, `allowPrerelease` - * `matchRule` - * `exactMatch` - * `exactMatchOrHighest` - exact match to version or highest below upper limit. If no upper limit is declared, highest on box. If no version is specified, the highest considering upper limit or on box. - * `highest` - highest below upper limit. If no upper limit is declared, highest on box. - * Default: `exactMatchOrHighest` to more closely match today's behavior if a version is specified. - * `upperLimit` - * An upper limit that supports wildcards. - * Asterisk are the only character valid as a wildcard - * All asterisk must be right of all numbers. 2.2.2** is legal. 2.*.2** is illegal. - * Default: - * If exactMatchOrHighest and the version is specified, the feature band of the version. If the feature band is `2.2.203`, the default is `2.2.1**`. - * If no version is specified, `*.*.***`. - * Alternative to consider: NuGet's approach of exclusive upper limit. - -#### Examples - -Example 1 - -```json -{ - "sdk": { - "version": "2.2.100", - "skipPreview": false, - "matchRule": "exactMatchOrHighest", - "upperLimit": "2.2.1**" - } -} -``` - -Example 2 - -```json -{ - "sdk": { - "skipPreview": false, - "matchRule": "highest", - "upperLimit": "*.*.***" - } -} -``` - -Example 3 - -```json -{ - "sdk": { - "skipPreview": true, - "matchRule": "highest", - "upperLimit": "*.*.***" - } -} -``` - -In this case, `whenNotFound` has no meaning. - -Example 4 - -```json -{ - "sdk": { - "version": "2.2.100", - "skipPreview": false, - "matchRule": "highest", - "upperLimit": "*.*.***" - } -} -``` - -Example 5 - -```json -{ - "sdk": { - "version": "2.2.100", - "matchRule": "highest", - "skipPreview": true, - "upperLimit": "*.*.***" - } -} -``` - -Example 6 - -```json -{ - "sdk": { - "version": "3.0.100-Pre", - "matchRule": "highest", - "skipPreview": true, - "upperLimit": "*.*.***" - } -} -``` - -Example 7 - -```json -{ - "sdk": { - "version": "2.2.100", - "matchRule": "exactMatch", - "skipPreview": false, - "upperLimit": "*.*.***" - } -} -``` - -Example 8 - -```json -{ - "sdk": { - "version": "2.2.100", - "matchRule": "exactMatchOrHighest", - "skipPreview": false, - "upperLimit": "2.2.***" - } -} -``` - -Example 9 - -```json -{ - "sdk": { - "version": "2.2.100", - "matchRule": "highest", - "skipPreview": true, - "upperLimit": "2.2.***" - } -} -``` - -Example 10 - -```json -{ - "sdk": { - "version": "2.1.203", - "matchRule": "highest", - "skipPreview": true, - "upperLimit": "2.2.***" - } -} -``` - -#### Proposal A implementation thought exercise - -From a user perspective, considering what the user may want to achieve and looking at the results with different SDKs available is helpful. However, this makes it look like a bit of a nightmare to code. Here is one sequence that is not crazy hard: - -1. Assign defaults (use high numbers for stars). -1. Look for the `global.json` rules that produce errors and fail if that occurs. -1. If `matchRule == exactMatch` see if there is a match and fail if not. -1. If `matchRule == exactMatchOrHighest` and there is one exact match, use it. -1. Find all available SDKs. -1. Remove any previews if `skipPreview == true`. -1. Remove any that are below the `version`. -1. Remove any that are above the `upperLimit`. -1. If none are left, fail. -1. Since either `matchRule == highest` or `matchRule == exactMatchOrHighest` without a match must now be true, use the highest. - -#### Error: Special error on ExactMatch - -Proposal A - -```json -{ - "sdk": { - "matchRule": "exactMatch", - "skipPreview": true, - "upperLimit": "*.*.***" - } -} -``` - -The same error appears for `exactMatch` or `exactMatchOrHighest` match rules. - -The error is: - -``` -No SDK could be selected due to global.json issue: exactMatch and exactMatchOrHighest matchRules can't be used without a version. -``` - -#### Error: Special error when could never succeed due to `version`, `exactMatch` and `skipPreview` flags - -Proposal A - -```json -{ - "sdk": { - "version": "3.0.100-Pre", - "matchRule": "exactMatch", - "skipPreview": true, - "upperLimit": "*.*.***" - } -} -``` - -The error is: - -``` -No SDK could be selected due to global.json issue: Specifying a preview version with exactMatch and skipPreview set to true will always fail. -``` - -#### Error: Special error when could never succeed because `upperLimit` is below `version` - -Proposal A - -```json -{ - "sdk": { - "version": "3.0.100", - "matchRule": "exactMatch", // any value - "skipPreview": false, // any value - "upperLimit": "2.*.***" - } -} -``` - -The error is: - -``` -No SDK could be selected due to global.json issue: Version is higher than upperLimit. -``` - -### Proposal B - -_NOTE: On reflection, this version as stated would have a backwards compatibility problem. `global.json` with unexpected characters in the version would probably fail. This could be managed with a different name, but this proposal seems overly compex anyway._ - -This proposal parallels NuGet version selection. Initially only the portions of the [NuGet versioning spec](https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards) listed here would be supplied. - -To avoid a backward compatibility issue, the default recovery strategy when a match is not found needs to match the old 'dotnet' behavior, for specifications below 3.0. This approach could solve that by retaining current behavior for version definitions below 3.0 that do not use any NuGet formatting. - -The proposal for Phase 1 is limited to allow us to complete it as quickly as possible - specifically for .NET Core 3.0. Phase 1 would support only `allowPrerelease`. - -This Phase 2 proposal extends the `global.json` schema to offer support for all of the identified scenarios: - -* `sdk` - * `version` - * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. - * [NuGet versioning](https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards) - * Default: empty - * `allowPrerelease` - * `true` or `false` - * (Proposed) Default: `true` to more closely match today's behavior. - * `matchRule` - * `highest` - Use the version listed is a lower bound and the highest is selected, even if the stated version exists. - * `legacy` - Use the exact patch version if it is available. If it is not available, use the highest patch version in the current feature band. - * Default: `legacy` to match today's behavior if a version is specified. `fail` may be a better long term default, but changing at 3.0 means that `version: 2.0.0` and `version: 3.0.0` behave differently. - -Example 1 - -```json -{ - "sdk": { - "version": "2.2.100", - "allowPrerelease": true, - "matchRule": "legacy" - } -} -``` - -Example 2 - -```json -{ - "sdk": { - "version": "*", - "allowPrerelease": true, - "matchRule": "legacy" - } -} -``` - -Example 3 - -```json -{ - "sdk": { - "version": "*", - "allowPrerelease": false, - "matchRule": "legacy" - } -} -``` - -In this case, `matchRule` has no meaning. - -Example 4 - -```json -{ - "sdk": { - "version": "2.2.100", - "allowPrerelease": true, - "matchRule": "highest" - } -} -``` - -Example 5 - -```json -{ - "sdk": { - "version": "2.2.100", - "allowPrerelease": false, - "matchRule": "highest" - } -} -``` - -Example 6 - -```json -{ - "sdk": { - "version": "3.0.100-Pre", - "matchRule": "highest", - "allowPrerelease": false, - } -} -``` - -Example 7 - -```json -{ - "sdk": { - "version": "[2.2.100]", - "allowPrerelease": true, - "matchRule": "highest" - } -} -``` - -Example 8 - -```json -{ - "sdk": { - "version": "[2.2.100,2.2.*]", - "allowPrerelease": true, - "matchRule": "legacy" - } -} -``` - -Example 9 - -```json -{ - "sdk": { - "version": "2.2.*", - "allowPrerelease": true, - "matchRule": "highest" - } -} -``` - -Example 10 - -```json -{ - "sdk": { - "version": "[2.1.203,2.2.*]", - "allowPrerelease": false, - "matchRule": "highest" - } -} -``` - -## Additional suggestions - -These suggestions have been received via Twitter, and I want to acknowledge that we've seen them and why we aren't acting on them at present. - -### [Have Visual Studio offer to download the requested SDK](https://twitter.com/Nick_Craver/status/1124649778078527488) +Prior to arriving at this proposal, two others were considered. -This idea is for Visual Studio, so it is not covered in this proposal. +The first, was an independent versioning scheme. It's largest downside is that it would be _another_ way users would have to think about specifying versioning behavior. -The roll-forward within feature band is a feature, so this probably makes sense only when the presence of global.json would fail. +The second was an attempt to parallel NuGet. Definining [NuGet version ranges](https://docs.microsoft.com/en-us/nuget/reference/package-versioning) is relatively complex, and it didn't feel like a good fit. +The recommended proposal (above), aligns closely to existing behavior and parallels runtime roll-forward behavior in other cases. From fe947bf07e4ecb15bd567bebb4a9ea46f2f45bc6 Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Mon, 3 Jun 2019 14:20:24 +0200 Subject: [PATCH 03/14] Update accepted/global-json-updates.md Co-Authored-By: Jared Parsons --- accepted/global-json-updates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index 2b11d6291..128e534ea 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -26,7 +26,7 @@ Given x.y.znn ## Current behavior -The SDK resolvers (in both the CLI and VS) search from the current location (project in VS) up the directory hierarchy until they encounter a `global.json` file. When the resolver finds one, _it stop looking_ and attempts to resolve the SDK version specified in that file. This happens even if this `global.json` does not specify an SDK version. +The SDK resolvers (in both the CLI and VS) search from the current location (project in VS) up the directory hierarchy until they encounter a `global.json` file. When the resolver finds one, _it stops looking_ and attempts to resolve the SDK version specified in that file. This happens even if this `global.json` does not specify an SDK version. When a `global.json` is encountered and it specifies an SDK version, the SDK resolvers attempt to locate that specific SDK patch version: From 14d8ce6a335c16f82c488ccb951697ddba7abfad Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Sun, 23 Jun 2019 14:26:53 -0700 Subject: [PATCH 04/14] Updated strategy based on runtime roll forward --- accepted/global-json-updates.md | 624 ++++++++++++++++++++++++++++---- 1 file changed, 551 insertions(+), 73 deletions(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index 2b11d6291..eca884f4b 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -1,4 +1,4 @@ -# Proposal for global.json updates for preview and limited roll-forward +# Proposal for global.json --- **NOTE:** @@ -7,9 +7,9 @@ SDK version numbers do not use semantic versioning. [Docs has an article on SDK --- -`global.json` allows users to pin the version of the SDK they are using. This was added to .NET Core in case we introduced an issue to allow roll-back per project until a fix could be provided. While our guidance remains to use it only for special circumstances, users now use it for numerous reasons. +`global.json` allows users to pin the version of the SDK they are using. This was added to .NET Core as a way for users to roll-back to a previous version per project until a fix could be provided if there a problem occurred. While our guidance remains to use it only for special circumstances, users now use it for numerous reasons. -This document describes changes to `global.json` to provide more flexibility. +This document describes changes to `global.json` to provide more flexibility to reflect the wider usage. ## Definitions @@ -30,7 +30,11 @@ The SDK resolvers (in both the CLI and VS) search from the current location (pro When a `global.json` is encountered and it specifies an SDK version, the SDK resolvers attempt to locate that specific SDK patch version: -1. When the resolver finds no `global.json` or finds one that does not specify SDK version, the latest SDK is used, regardless of whether it is preview or stable. +1. When the resolver finds no `global.json` or finds one that does not specify SDK version,the latest SDK is used. Whether previews are considered depends on how `dotnet` is called: + - If called from the CLI, previews are considered. + - If called Visual Studio a flag is passed indicating whether to consider previews: + - If Visual Studio is a Preview, or the "Use Previews" options is set by the user, previews are considered. + - Otherwise, previews are not considered when called from Visual Studio. 2. When the resolver finds a `global.json`, the specific SDK patch version it specifies is used if it is found. 3. When the resolver finds a `global.json`, and the SDK it specifies can't be found, the resolver looks for a close match: * Prior to .NET Core 2.1 @@ -44,50 +48,96 @@ Due to the rules above, the following occurs: * The impact of this is mitigated by work we did in .NET Core 3.0 Preview 3 to replace earlier patch versions. * Due to #3, users can not guarantee they are pinned to a specific version. -## Proposal - -### Roll-forward +## Customer problems to address -.NET Core SDK "roll-forward" is problematic because version numbers are not linear. For example: 2.1.600 contains MSBuild 16 while 2.2.100 contains MSBuild 15.9. This is not a new problem, since it already occurs when there is no `global.json`. However, we do not want to roll-forward in scenarios that might surprise the user. Because of this, roll-forward will be allowed only in limited scenarios. +* User wants to install a preview but only use it with a subset of projects. + * Due to Rule #1, a `global.json` is the only way to opt out of a preview in the common case of the preview being the highest version on the machine. + * The `global.json` the user addss must include a specific version. + * If no matching SDK version in the feature band (xyz in x.y.z.nn) is found, the command being called fails. + * Each `global.json` must later be updated to use the new stable SDK. + * Due to Rule #2, there is no roll forward from preview to stable if they are both on the machine. + * The impact of this is mitigated by work we did in .NET Core 3.0 Preview 3 to replace earlier patch versions. +* User wants to always use a specific SDK patch version, and receive an error if it is not on the machine. + * The user cannot currently do this. +* User wants to always run the latest patch of a particular SDK feature band. + * Due to Rule #2, the only way to do this is to remove the previous version. + * The impact of this is mitigated by work we did in .NET Core 3.0 Preview 3 to replace earlier patch versions. +* User includes a feature that requires an SDK that corresponds to a SDK feature band or higher, and wants to run the best available tools. + * There is no mechanism to require at least a specific version, but otherwise use the latest, or the latest in a range. + * Upgrading to a newer SDK will provide no benefit to projects tied to an earlier version. +* User wants to know what version of the SDK is being used. + * `global.json` is a subtle, hidden and easily forgotten marker – programmers may not understand the downsides of leaving a global.json that was suggested in a blog post in place – or they may forget it. +* User rarely wants the risk of running the unsupported scenario of multiple SDK versions in a solution build. + * There is nothing that encourages users to place `global.json` in the repository root or the same level as the solution. Using different versions of the SDK for different projects in the solution is not a supported scenario. +* Hosted CI, including Azure DevOps and Kudu, need to provide a small number of SDKs. + * If there is no patch of the major/minor/feature version (xyz in `x.y.znn`) present, the build fails, causing friction between user and host needs. + +## Proposal C + +This proposal parallels [runtime roll-forward behavior as initially described](https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/roll-forward-on-no-candidate-fx.md)} and [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md). All of the features proposed here may not be included in Phase 1. It will include at least `ignorePreview` and `"rollforward": "disable"` + +This is called Proposal C to differentiate it from two earlier proposals that were discarded but are included at the end of this document for reference. ### Details +The runtime options allow for three basic approaches: find nearest, find latest and find exact. + +To avoid a backward compatibility issue, the default recovery strategy when a match is not found needs to match the old `dotnet` behavior. This defines the defaults, and the default behavior differs between runtime. + +* Runtime: From the [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md) document "_In all cases except `disable` the highest available patch version is selected._" +* SDK: For backwards compatibility, the additonal option `patch` is offered and is the default. + * If the specified patch version is found, it is used. + * In all other cases (except `disable`), the highest patch within the appropriate feature band is used. + The `global.json` schema to offers support for all of the identified scenarios that can be fixed without altering how we find `global.json`: * `sdk` * `version` * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. - * Wildcards are not supported. + * Identical to today - wildcards are not supported. * Default: empty * `ignorePreview` * `true` or `false` - * Default: `false` to match previous behavior. + * Default: `false` to more closely match today's behavior. * Alternate names: `releaseOnly` or `useReleaseOnly` - * This is new behavior. * `rollForward` - * Lower version numbers are always ignored. + * To avoid some of the repetitiveness, I do not restate in these rules that version numbers below that specified are always ignored. * `patch` - * If the requested major, minor, feature band, and patch is found, use it. + * If the requested major, minor, feature band, and patch if found, use it. * Otherwise, use the highest patch within the specified major, minor and feature band. - * This policy means the latest patches, including security patches will not be used unless the specified version is remove from the machine. - * This matches previous behavior when a `global.json` file contains a version. + * `feature` + * If the requested major, minor, feature band, and patch is found, use it. + * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. + * Otherwise, use the lowest higher feature band, and use the highest patch within that feature band. + * `minor` + * If the requested major, minor, feature band, and patch is found, use it. + * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. + * Otherwise, if the requested major and minor is found, use the highest patch in the _lowest_ feature band within that major and minor version. + * Otherwise, use the lowest higher minor, it's lowest feature band, and the highest patch within that feature band. + * `major` + * If the requested major, minor, feature band, and patch is found, use it. + * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. + * Otherwise, if the requested major and minor is found, use the highest patch in the _lowest_ feature band within that major and minor version. + * Otherwise, if the requested major is found, use the _lowest_ minor within that major version, and it's lowest feature band, and it's highest patch. + * Otherwise, use the lowest higher major, it's lowest minor, and it's lowest feature band, and the highest patch within that feature band. * `latestPatch` * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. * Select the highest available patch version that matches the specified major, minor and feature band versions. - * This is new behavior. - * `disable` - * Do not roll forward. Only bind to specified version. - * This policy means the latest patches, including security patches will not be used. - * This is new behavior. - * `latest` + * `latestFeature` + * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. + * Select the highest available feature band that matches the specified major and minor version, and select it's highest patch. + * `latestMinor` + * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. + * Select the highest available minor that matches the specified major version, and select it's highest feature band, and that feature band's highest patch. + * `latestMajor` * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. * Select the highest available major version, and select it's highest minor version, and that minor's highest feature band, and that feature band's highest patch. - * Using latest with a version is new behavior. Otherwise this matches previous behavior when there is no `global.json` or no version is specified. + * `disable` + * Do not roll forward. Only bind to specified version. This policy means the latest patches, including security patches will not be used. * Defaults: - * Defaults are defined to support backward compatibility issues. - * If a version is specified in the `global.json`, the default is `patch` to match previous behavior. - * If there is no `global.json` or a version is not specified, the default is`latest`, again to match previous behavior. - + * If a version is specified in the `global.json`, the default is `patch` to match today's behavior. + * If there is no `global.json` or a version is not specified, the default is`latestMajor`, again to match today's behavior. + ### Examples to illustrate roll-forward options The following table considers various SDK version combinations that may be available on the machine, and the outcome of the following `global.json`: @@ -101,43 +151,51 @@ The following table considers various SDK version combinations that may be avail } ``` -| Available SDKs | patch | latestPatch | latest | disable | -|---------------------------------------------|---------|-------------|---------|---------| -| 2.1.500 | fail | fail | fail | fail | -| 2.1.501, 2.1.503 | 2.1.501 | 2.1.503 | 2.1.503 | 2.1.501 | -| 2.1.503, 2.1.505, 2.1.601, 2.2.101, 3.0.100 | 2.1.503 | 2.1.505 | 3.0.100 | fail | -| 2.2.101, 2.2.203, 3.0.100 | fail | fail | 3.0.100 | fail | -| 3.0.100, 3.1.102 | fail | fail | 3.1.102 | fail | +| Available SDKs | patch | feature | minor | major | latestPatch | latestFeature | latestMinor | latestMajor | disable | +|------------------------------------------------------|-----------|---------|---------|---------|-------------|---------------|-------------|-------------|---------| +| 2.1.500 | fail | fail | fail | fail | fail | fail | fail | fail | fail | +| 2.1.501, 2.1.503 | _2.1.501_ | 2.1.501 | 2.1.501 | 2.1.501 | _2.1.503_ | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.501 | +| 2.1.503, 2.1.505, 2.1.601, 2.2.101, 3.0.100 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.505 | 2.1.601 | 2.2.101 | 3.0.100 | fail | +| 2.1.601, 2.1.604, 2.1.702, 2.2.101, 2.2.203, 3.0.100 | fail | 2.1.601 | 2.1.601 | 2.1.601 | fail | 2.1.702 | 2.2.203 | 3.0.100 | fail | +| 2.2.101, 2.2.203, 3.0.100 | fail | fail | 2.2.101 | 2.2.101 | fail | fail | 2.2.203 | 3.0.100 | fail | +| 3.0.100, 3.1.102 | fail | fail | fail | 3.0.100 | fail | fail | fail | 3.1.102 | fail | -Or, if it is easier to understand possible logic: +Or, if it is easier to understand with logic (bail out of logic on fail or select): -1. If `ignorePreview` is `false`, exclude all preview versions. -1. If `patch` or `disable`, and there is a match, select it. +1. If not `latest`, and there is a match, select it. 1. If we get here for `disable`, then fail. 1. Exclude any lower versions. +1. If `minor` or `latestMinor`, exclude any that don't match major version. +1. If `feature` or `latestFeature`, exclude any that don't match major and minor version. 1. If `patch` or `latestPatch`, exclude any that don't match major, minor and feature band version. -1. If there are none left, then fail. 1. If there is one left, select it. -1. Select the highest remaining. - -## Customer problems to address - -| Problem | Current | Proposed | -|---------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------| -| Install a preview but use it with only a subset of projects. | `version: ` or `version: -pre` or similar (*)| `ignorePreview: true`, or `ignorePreview: true` higher in the file system and `ignorePreview: false`. | -| Fail if specific version is not available. | Not possible | `rollForward: disable` in `global.json` | -| Always run the latest patch of a particular SDK feature band. | Not possible | `rollForward: latestPatch` and lowest acceptable `version` (**) | -| Use at least a specific version, otherwise latest | Not possible | `rollForward: latest` and lowest acceptable `version` (**) | -| Hosted CI | Friction matching SDKs for users - | _This proposal needs review by DevOps | -(*) One pain point of this is that these `global.json` file must be removed or later be updated to use the new stable SDK, which is almost never the desired behavior. -(**) Because the SDK version numbers are not linear, this will not always result in the highest SDK on the machine being used. +1. If any of the `latest` options are used, select the highest. +1. Otherwise, select the lowest major, and within that lowest minor, and within that lowest feature band. Then select the highest patch in that feature band. + +## Customer problems addressed + +* User wants to install a preview but only use it with a subset of projects. + * User opts out in projects where preview should not be used by adding `ignorePreview: true` to those projects, or + * User adds `ignorePreview: true` in a `global.json` higher in their file system, and adds `ignorePreview: false` in a `global.json` in specific projects. +* User wants to always use a specific SDK patch version, and receive an error if it is not on the machine. + * User adds `rollForward: disable` to a `global.json` +* User wants to always run the latest patch of a particular SDK feature band. + * User adds `rollForward: latestPatch` and the lowest version (with patch) they want to a `global.json` +* User includes a feature that requires an SDK that corresponds to a SDK feature band or higher, and wants to run the best available tools. + * User adds `rollForward: latestMajor` and the lowest version (with patch) they want to a `global.json` +* User wants to know what version of the SDK is being used. + * Not addressed in this proposal: `global.json` will still be a subtle, hidden and easily forgotten marker +* User rarely wants the risk of running the unsupported scenario of multiple SDK versions in a solution build. + * Not addressed in this proposal: There is nothing that encourages users to place `global.json` in a logical location. +* Hosted CI, including Azure DevOps and Kudu, need to provide a small number of SDKs. + * It will now be possible for users to specify `global.json` files in a way that can work with Azure DevOps (roll-forward across at least feature bands) + * We will work with Azure DevOps to determine the recommended `global.json` for this scenario. ## Examples of usage This section lists a series of scenarios and following the corresponding `global.json`. Following this is a table that shows the SDK that would be selected with specific combinations of SDKs present on the machine. -### 1. Explicitly state previous behavior when a `global.json` specifies a version +### 1. Explicitly state today's behavior when a `global.json` specifies a version The following `global.json` defines a specific version number, and to use a higher version if it is available. @@ -161,9 +219,9 @@ The common `global.json` for this case is likely to be: } ``` -### 2. Explicitly state previous behavior when there is no `global.json` +### 2. Explicitly state today's behavior when there is no `global.json` -previous behavior is that an exact match is used if found, and if not found, the highest patch above the current in the feature band is used. +Today's behavior is that an exact match is used if found, and if not found, the highest patch above the current in the feature band is used. If there is no `global.json` that would logically be the same as the following. @@ -171,7 +229,7 @@ If there is no `global.json` that would logically be the same as the following. { "sdk": { "ignorePreview": false, - "rollForward": "latest" + "rollForward": "latestMajor" } } ``` @@ -184,7 +242,7 @@ If there is no `global.json` but the user does not want to use previews, that wo { "sdk": { "ignorePreview": true, - "rollForward": "latest" + "rollForward": "latestMajor" } } ``` @@ -199,7 +257,7 @@ The common `global.json` for this case is likely to be: } ``` -The user may include their preference for previews high in their directory structure, and then individual project can elect different behavior with a new `global.json`. This will continue to follow previous rules. In particular: if there is a `global.json` file for a different reason, like a custom SDK, the higher `global.json` will not be used. +The user may include their preference for previews high in their directory structure, and then individual project can elect different behavior with a new `global.json`. This will continue to follow today's rules. In particular: if there is a `global.json` file for a different reason, like a custom SDK, the higher `global.json` will not be used. ### 4 Select highest available, higher than specified @@ -210,7 +268,7 @@ If the user wants to specify the lowest SDK they want to use, they could use the "sdk": { "version": "2.2.100", "ignorePreview": false, - "rollForward": "latest" + "rollForward": "latestMajor" } } ``` @@ -221,7 +279,7 @@ The common `global.json` for this case is likely to be: { "sdk": { "version": "2.2.100", - "rollForward": "latest" + "rollForward": "latestMajor" } } ``` @@ -235,7 +293,7 @@ If the user wanted to use any SDK higher than the one listed but ignore previews "sdk": { "version": "2.2.100", "ignorePreview": true, - "rollForward": "latest" + "rollForward": "latestMajor" } } ``` @@ -249,7 +307,7 @@ If the user wants to use any SDK as high or higher than the one listed but ignor "sdk": { "version": "3.0.100-Pre", "ignorePreview": true, - "rollForward": "highest" + "rollForward": "highestMajor" } } ``` @@ -291,25 +349,445 @@ When this fails, the error is: The requested SDK version was not available, and SDK version roll-forward was disabled. ``` +### 8. Exact match or highest within runtime major.minor + +This proposal does not support arbitrary ranges or arbitrary upper bounds. + +### 9. Highest within runtime major.minor + +If the user wants the highest within a runtime major/minor, they could use the following. + + +```json +{ + "sdk": { + "version": "2.2.100", + "ignorePreview": false, + "rollForward": "latestMinor" + } +} +``` + +### 10. Highest within arbitrary upper and lower bound + +If the user wanted to use any SDK higher than the one listed but ignore previews, they could use the following. + +Proposal C + +There is no concept of an arbitrary upper bound (as there isn't with the runtime) + ### Selections made This table lists the SDKs selected in each of the above scenarios. -| Available SDKs | #1 | #2 | #3 | #4 | #5 | #6 | #7 | -|-------------------------------|---------|-------------|---------|-------------|---------|---------|---------| -| 2.1.700 | fail | 2.1.700 | 2.1.700 | fail | fail | fail | fail | -| 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | fail | 2.2.100 | -| 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | fail | -| 2.1.700, 2.2.100, 2.2.103 | 2.2.100 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | 2.2.100 | -| 2.1.700, 2.2.103, 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | fail | fail | -| 2.1.700, 2.2.103, 3.1.100 | 2.2.103 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | fail | +| Available SDKs | #1 | #2 | #3 | #4 | #5 | #6 | #7 | #8 | #9 | #10 | +|-------------------------------|---------|-------------|---------|-------------|---------|---------|---------|---------|---------|---------| +| 2.1.700 | fail | 2.1.700 | 2.1.700 | fail | fail | fail | fail | fail | fail | 2.1.700 | +| 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | fail | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | +| 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | fail | 2.2.103 | 2.2.103 | 2.2.103 | +| 2.1.700, 2.2.100, 2.2.103 | 2.2.100 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | 2.2.100 | 2.2.100 | 2.2.103 | 2.2.103 | +| 2.1.700, 2.2.103, 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | fail | fail | 2.2.100 | 2.2.103 | 2.2.103 | +| 2.1.700, 2.2.103, 3.1.100 | 2.2.103 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | fail | 2.2.103 | 2.2.103 | 2.2.103 | ## Other proposals considered -Prior to arriving at this proposal, two others were considered. +Prior to arriving at this proposal, two others were considered. The first, Proposal A is an independent versioning scheme. It's largest downside is that it would be _another_ way users would have to think about specifying versioning behavior. The second, Proposal B was an attempt to parallel NuGet. Definining [NuGet version ranges](https://docs.microsoft.com/en-us/nuget/reference/package-versioning) is relatively complex, and it didn't feel like a good fit. The recommended proposal (above), Proposal C, is based on runtime roll-forward behavior. + +### Proposal A + +This proposal is independently created, not considering other versioning definitions like NuGet. + +The proposal for Phase 1 is limited to allow us to complete it as quickly as possible - specifically for .NET Core 3.0. Phase 1 would support only `ignorePreview` and possibly `exactMatch`. + +This Phase 2 proposal extends the `global.json` schema to offer support for all of the identified scenarios: + +* `sdk` + * `version` + * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. + * Identical to today - wildcards are not supported. + * Default: empty + * `ignorePreview` + * `true` or `false` + * (Proposed) Default: `false` to more closely match today's behavior. + * Alternative text to consider: `releaseOnly`, `allowPrerelease` + * `matchRule` + * `exactMatch` + * `exactMatchOrHighest` - exact match to version or highest below upper limit. If no upper limit is declared, highest on box. If no version is specified, the highest considering upper limit or on box. + * `highest` - highest below upper limit. If no upper limit is declared, highest on box. + * Default: `exactMatchOrHighest` to more closely match today's behavior if a version is specified. + * `upperLimit` + * An upper limit that supports wildcards. + * Asterisk are the only character valid as a wildcard + * All asterisk must be right of all numbers. 2.2.2** is legal. 2.*.2** is illegal. + * Default: + * If exactMatchOrHighest and the version is specified, the feature band of the version. If the feature band is `2.2.203`, the default is `2.2.1**`. + * If no version is specified, `*.*.***`. + * Alternative to consider: NuGet's approach of exclusive upper limit. + +#### Examples + +Example 1 + +```json +{ + "sdk": { + "version": "2.2.100", + "skipPreview": false, + "matchRule": "exactMatchOrHighest", + "upperLimit": "2.2.1**" + } +} +``` + +Example 2 + +```json +{ + "sdk": { + "skipPreview": false, + "matchRule": "highest", + "upperLimit": "*.*.***" + } +} +``` + +Example 3 + +```json +{ + "sdk": { + "skipPreview": true, + "matchRule": "highest", + "upperLimit": "*.*.***" + } +} +``` + +In this case, `whenNotFound` has no meaning. + +Example 4 + +```json +{ + "sdk": { + "version": "2.2.100", + "skipPreview": false, + "matchRule": "highest", + "upperLimit": "*.*.***" + } +} +``` + +Example 5 + +```json +{ + "sdk": { + "version": "2.2.100", + "matchRule": "highest", + "skipPreview": true, + "upperLimit": "*.*.***" + } +} +``` + +Example 6 + +```json +{ + "sdk": { + "version": "3.0.100-Pre", + "matchRule": "highest", + "skipPreview": true, + "upperLimit": "*.*.***" + } +} +``` + +Example 7 + +```json +{ + "sdk": { + "version": "2.2.100", + "matchRule": "exactMatch", + "skipPreview": false, + "upperLimit": "*.*.***" + } +} +``` + +Example 8 + +```json +{ + "sdk": { + "version": "2.2.100", + "matchRule": "exactMatchOrHighest", + "skipPreview": false, + "upperLimit": "2.2.***" + } +} +``` + +Example 9 + +```json +{ + "sdk": { + "version": "2.2.100", + "matchRule": "highest", + "skipPreview": true, + "upperLimit": "2.2.***" + } +} +``` + +Example 10 + +```json +{ + "sdk": { + "version": "2.1.203", + "matchRule": "highest", + "skipPreview": true, + "upperLimit": "2.2.***" + } +} +``` + +#### Proposal A implementation thought exercise + +From a user perspective, considering what the user may want to achieve and looking at the results with different SDKs available is helpful. However, this makes it look like a bit of a nightmare to code. Here is one sequence that is not crazy hard: + +1. Assign defaults (use high numbers for stars). +1. Look for the `global.json` rules that produce errors and fail if that occurs. +1. If `matchRule == exactMatch` see if there is a match and fail if not. +1. If `matchRule == exactMatchOrHighest` and there is one exact match, use it. +1. Find all available SDKs. +1. Remove any previews if `skipPreview == true`. +1. Remove any that are below the `version`. +1. Remove any that are above the `upperLimit`. +1. If none are left, fail. +1. Since either `matchRule == highest` or `matchRule == exactMatchOrHighest` without a match must now be true, use the highest. + +#### Error: Special error on ExactMatch + +Proposal A + +```json +{ + "sdk": { + "matchRule": "exactMatch", + "skipPreview": true, + "upperLimit": "*.*.***" + } +} +``` + +The same error appears for `exactMatch` or `exactMatchOrHighest` match rules. + +The error is: + +``` +No SDK could be selected due to global.json issue: exactMatch and exactMatchOrHighest matchRules can't be used without a version. +``` + +#### Error: Special error when could never succeed due to `version`, `exactMatch` and `skipPreview` flags + +Proposal A + +```json +{ + "sdk": { + "version": "3.0.100-Pre", + "matchRule": "exactMatch", + "skipPreview": true, + "upperLimit": "*.*.***" + } +} +``` + +The error is: + +``` +No SDK could be selected due to global.json issue: Specifying a preview version with exactMatch and skipPreview set to true will always fail. +``` + +#### Error: Special error when could never succeed because `upperLimit` is below `version` + +Proposal A + +```json +{ + "sdk": { + "version": "3.0.100", + "matchRule": "exactMatch", // any value + "skipPreview": false, // any value + "upperLimit": "2.*.***" + } +} +``` + +The error is: + +``` +No SDK could be selected due to global.json issue: Version is higher than upperLimit. +``` + +### Proposal B + +_NOTE: On reflection, this version as stated would have a backwards compatibility problem. `global.json` with unexpected characters in the version would probably fail. This could be managed with a different name, but this proposal seems overly compex anyway._ + +This proposal parallels NuGet version selection. Initially only the portions of the [NuGet versioning spec](https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards) listed here would be supplied. + +To avoid a backward compatibility issue, the default recovery strategy when a match is not found needs to match the old 'dotnet' behavior, for specifications below 3.0. This approach could solve that by retaining current behavior for version definitions below 3.0 that do not use any NuGet formatting. + +The proposal for Phase 1 is limited to allow us to complete it as quickly as possible - specifically for .NET Core 3.0. Phase 1 would support only `allowPrerelease`. + +This Phase 2 proposal extends the `global.json` schema to offer support for all of the identified scenarios: + +* `sdk` + * `version` + * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. + * [NuGet versioning](https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards) + * Default: empty + * `allowPrerelease` + * `true` or `false` + * (Proposed) Default: `true` to more closely match today's behavior. + * `matchRule` + * `highest` - Use the version listed is a lower bound and the highest is selected, even if the stated version exists. + * `legacy` - Use the exact patch version if it is available. If it is not available, use the highest patch version in the current feature band. + * Default: `legacy` to match today's behavior if a version is specified. `fail` may be a better long term default, but changing at 3.0 means that `version: 2.0.0` and `version: 3.0.0` behave differently. + +Example 1 + +```json +{ + "sdk": { + "version": "2.2.100", + "allowPrerelease": true, + "matchRule": "legacy" + } +} +``` + +Example 2 + +```json +{ + "sdk": { + "version": "*", + "allowPrerelease": true, + "matchRule": "legacy" + } +} +``` + +Example 3 + +```json +{ + "sdk": { + "version": "*", + "allowPrerelease": false, + "matchRule": "legacy" + } +} +``` + +In this case, `matchRule` has no meaning. + +Example 4 + +```json +{ + "sdk": { + "version": "2.2.100", + "allowPrerelease": true, + "matchRule": "highest" + } +} +``` + +Example 5 + +```json +{ + "sdk": { + "version": "2.2.100", + "allowPrerelease": false, + "matchRule": "highest" + } +} +``` + +Example 6 + +```json +{ + "sdk": { + "version": "3.0.100-Pre", + "matchRule": "highest", + "allowPrerelease": false, + } +} +``` + +Example 7 + +```json +{ + "sdk": { + "version": "[2.2.100]", + "allowPrerelease": true, + "matchRule": "highest" + } +} +``` + +Example 8 + +```json +{ + "sdk": { + "version": "[2.2.100,2.2.*]", + "allowPrerelease": true, + "matchRule": "legacy" + } +} +``` + +Example 9 + +```json +{ + "sdk": { + "version": "2.2.*", + "allowPrerelease": true, + "matchRule": "highest" + } +} +``` + +Example 10 + +```json +{ + "sdk": { + "version": "[2.1.203,2.2.*]", + "allowPrerelease": false, + "matchRule": "highest" + } +} +``` + +## Additional suggestions + +These suggestions have been received via Twitter, and I want to acknowledge that we've seen them and why we aren't acting on them at present. -The first, was an independent versioning scheme. It's largest downside is that it would be _another_ way users would have to think about specifying versioning behavior. +### [Have Visual Studio offer to download the requested SDK](https://twitter.com/Nick_Craver/status/1124649778078527488) -The second was an attempt to parallel NuGet. Definining [NuGet version ranges](https://docs.microsoft.com/en-us/nuget/reference/package-versioning) is relatively complex, and it didn't feel like a good fit. +This idea is for Visual Studio, so it is not covered in this proposal. -The recommended proposal (above), aligns closely to existing behavior and parallels runtime roll-forward behavior in other cases. +The roll-forward within feature band is a feature, so this probably makes sense only when the presence of global.json would fail. From 8704917b92ff57cf47ed21db2395db70a1c78362 Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Sun, 23 Jun 2019 14:38:57 -0700 Subject: [PATCH 05/14] Removed old proposals, updated usePreview --- accepted/global-json-updates.md | 397 +------------------------------- 1 file changed, 5 insertions(+), 392 deletions(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index 82c7a04d3..ab471e7d6 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -96,10 +96,9 @@ The `global.json` schema to offers support for all of the identified scenarios t * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. * Identical to today - wildcards are not supported. * Default: empty - * `ignorePreview` + * `usePreview` * `true` or `false` - * Default: `false` to more closely match today's behavior. - * Alternate names: `releaseOnly` or `useReleaseOnly` + * Default: `true` to match today's behavior. * `rollForward` * To avoid some of the repetitiveness, I do not restate in these rules that version numbers below that specified are always ignored. * `patch` @@ -391,396 +390,10 @@ This table lists the SDKs selected in each of the above scenarios. ## Other proposals considered -Prior to arriving at this proposal, two others were considered. The first, Proposal A is an independent versioning scheme. It's largest downside is that it would be _another_ way users would have to think about specifying versioning behavior. The second, Proposal B was an attempt to parallel NuGet. Definining [NuGet version ranges](https://docs.microsoft.com/en-us/nuget/reference/package-versioning) is relatively complex, and it didn't feel like a good fit. The recommended proposal (above), Proposal C, is based on runtime roll-forward behavior. +Prior to arriving at this proposal, two others were considered. Neither of these align well with roll-forward on the .NET Core Runtime. -### Proposal A - -This proposal is independently created, not considering other versioning definitions like NuGet. - -The proposal for Phase 1 is limited to allow us to complete it as quickly as possible - specifically for .NET Core 3.0. Phase 1 would support only `ignorePreview` and possibly `exactMatch`. - -This Phase 2 proposal extends the `global.json` schema to offer support for all of the identified scenarios: - -* `sdk` - * `version` - * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. - * Identical to today - wildcards are not supported. - * Default: empty - * `ignorePreview` - * `true` or `false` - * (Proposed) Default: `false` to more closely match today's behavior. - * Alternative text to consider: `releaseOnly`, `allowPrerelease` - * `matchRule` - * `exactMatch` - * `exactMatchOrHighest` - exact match to version or highest below upper limit. If no upper limit is declared, highest on box. If no version is specified, the highest considering upper limit or on box. - * `highest` - highest below upper limit. If no upper limit is declared, highest on box. - * Default: `exactMatchOrHighest` to more closely match today's behavior if a version is specified. - * `upperLimit` - * An upper limit that supports wildcards. - * Asterisk are the only character valid as a wildcard - * All asterisk must be right of all numbers. 2.2.2** is legal. 2.*.2** is illegal. - * Default: - * If exactMatchOrHighest and the version is specified, the feature band of the version. If the feature band is `2.2.203`, the default is `2.2.1**`. - * If no version is specified, `*.*.***`. - * Alternative to consider: NuGet's approach of exclusive upper limit. - -#### Examples - -Example 1 - -```json -{ - "sdk": { - "version": "2.2.100", - "skipPreview": false, - "matchRule": "exactMatchOrHighest", - "upperLimit": "2.2.1**" - } -} -``` - -Example 2 - -```json -{ - "sdk": { - "skipPreview": false, - "matchRule": "highest", - "upperLimit": "*.*.***" - } -} -``` - -Example 3 - -```json -{ - "sdk": { - "skipPreview": true, - "matchRule": "highest", - "upperLimit": "*.*.***" - } -} -``` - -In this case, `whenNotFound` has no meaning. - -Example 4 - -```json -{ - "sdk": { - "version": "2.2.100", - "skipPreview": false, - "matchRule": "highest", - "upperLimit": "*.*.***" - } -} -``` - -Example 5 - -```json -{ - "sdk": { - "version": "2.2.100", - "matchRule": "highest", - "skipPreview": true, - "upperLimit": "*.*.***" - } -} -``` - -Example 6 - -```json -{ - "sdk": { - "version": "3.0.100-Pre", - "matchRule": "highest", - "skipPreview": true, - "upperLimit": "*.*.***" - } -} -``` - -Example 7 - -```json -{ - "sdk": { - "version": "2.2.100", - "matchRule": "exactMatch", - "skipPreview": false, - "upperLimit": "*.*.***" - } -} -``` - -Example 8 - -```json -{ - "sdk": { - "version": "2.2.100", - "matchRule": "exactMatchOrHighest", - "skipPreview": false, - "upperLimit": "2.2.***" - } -} -``` - -Example 9 - -```json -{ - "sdk": { - "version": "2.2.100", - "matchRule": "highest", - "skipPreview": true, - "upperLimit": "2.2.***" - } -} -``` - -Example 10 - -```json -{ - "sdk": { - "version": "2.1.203", - "matchRule": "highest", - "skipPreview": true, - "upperLimit": "2.2.***" - } -} -``` - -#### Proposal A implementation thought exercise - -From a user perspective, considering what the user may want to achieve and looking at the results with different SDKs available is helpful. However, this makes it look like a bit of a nightmare to code. Here is one sequence that is not crazy hard: - -1. Assign defaults (use high numbers for stars). -1. Look for the `global.json` rules that produce errors and fail if that occurs. -1. If `matchRule == exactMatch` see if there is a match and fail if not. -1. If `matchRule == exactMatchOrHighest` and there is one exact match, use it. -1. Find all available SDKs. -1. Remove any previews if `skipPreview == true`. -1. Remove any that are below the `version`. -1. Remove any that are above the `upperLimit`. -1. If none are left, fail. -1. Since either `matchRule == highest` or `matchRule == exactMatchOrHighest` without a match must now be true, use the highest. - -#### Error: Special error on ExactMatch - -Proposal A - -```json -{ - "sdk": { - "matchRule": "exactMatch", - "skipPreview": true, - "upperLimit": "*.*.***" - } -} -``` - -The same error appears for `exactMatch` or `exactMatchOrHighest` match rules. - -The error is: - -``` -No SDK could be selected due to global.json issue: exactMatch and exactMatchOrHighest matchRules can't be used without a version. -``` - -#### Error: Special error when could never succeed due to `version`, `exactMatch` and `skipPreview` flags - -Proposal A - -```json -{ - "sdk": { - "version": "3.0.100-Pre", - "matchRule": "exactMatch", - "skipPreview": true, - "upperLimit": "*.*.***" - } -} -``` - -The error is: - -``` -No SDK could be selected due to global.json issue: Specifying a preview version with exactMatch and skipPreview set to true will always fail. -``` - -#### Error: Special error when could never succeed because `upperLimit` is below `version` - -Proposal A - -```json -{ - "sdk": { - "version": "3.0.100", - "matchRule": "exactMatch", // any value - "skipPreview": false, // any value - "upperLimit": "2.*.***" - } -} -``` - -The error is: - -``` -No SDK could be selected due to global.json issue: Version is higher than upperLimit. -``` - -### Proposal B - -_NOTE: On reflection, this version as stated would have a backwards compatibility problem. `global.json` with unexpected characters in the version would probably fail. This could be managed with a different name, but this proposal seems overly compex anyway._ - -This proposal parallels NuGet version selection. Initially only the portions of the [NuGet versioning spec](https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards) listed here would be supplied. - -To avoid a backward compatibility issue, the default recovery strategy when a match is not found needs to match the old 'dotnet' behavior, for specifications below 3.0. This approach could solve that by retaining current behavior for version definitions below 3.0 that do not use any NuGet formatting. - -The proposal for Phase 1 is limited to allow us to complete it as quickly as possible - specifically for .NET Core 3.0. Phase 1 would support only `allowPrerelease`. - -This Phase 2 proposal extends the `global.json` schema to offer support for all of the identified scenarios: - -* `sdk` - * `version` - * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. - * [NuGet versioning](https://docs.microsoft.com/en-us/nuget/reference/package-versioning#version-ranges-and-wildcards) - * Default: empty - * `allowPrerelease` - * `true` or `false` - * (Proposed) Default: `true` to more closely match today's behavior. - * `matchRule` - * `highest` - Use the version listed is a lower bound and the highest is selected, even if the stated version exists. - * `legacy` - Use the exact patch version if it is available. If it is not available, use the highest patch version in the current feature band. - * Default: `legacy` to match today's behavior if a version is specified. `fail` may be a better long term default, but changing at 3.0 means that `version: 2.0.0` and `version: 3.0.0` behave differently. - -Example 1 - -```json -{ - "sdk": { - "version": "2.2.100", - "allowPrerelease": true, - "matchRule": "legacy" - } -} -``` - -Example 2 - -```json -{ - "sdk": { - "version": "*", - "allowPrerelease": true, - "matchRule": "legacy" - } -} -``` - -Example 3 - -```json -{ - "sdk": { - "version": "*", - "allowPrerelease": false, - "matchRule": "legacy" - } -} -``` - -In this case, `matchRule` has no meaning. - -Example 4 - -```json -{ - "sdk": { - "version": "2.2.100", - "allowPrerelease": true, - "matchRule": "highest" - } -} -``` - -Example 5 - -```json -{ - "sdk": { - "version": "2.2.100", - "allowPrerelease": false, - "matchRule": "highest" - } -} -``` - -Example 6 - -```json -{ - "sdk": { - "version": "3.0.100-Pre", - "matchRule": "highest", - "allowPrerelease": false, - } -} -``` - -Example 7 - -```json -{ - "sdk": { - "version": "[2.2.100]", - "allowPrerelease": true, - "matchRule": "highest" - } -} -``` - -Example 8 - -```json -{ - "sdk": { - "version": "[2.2.100,2.2.*]", - "allowPrerelease": true, - "matchRule": "legacy" - } -} -``` - -Example 9 - -```json -{ - "sdk": { - "version": "2.2.*", - "allowPrerelease": true, - "matchRule": "highest" - } -} -``` - -Example 10 - -```json -{ - "sdk": { - "version": "[2.1.203,2.2.*]", - "allowPrerelease": false, - "matchRule": "highest" - } -} -``` +* An independent versioning scheme. It's largest downside is that it would be _another_ way users would have to think about specifying versioning behavior. +* An attempt to parallel NuGet. Definining [NuGet version ranges](https://docs.microsoft.com/en-us/nuget/reference/package-versioning) is relatively complex, and it didn't feel like a good fit. ## Additional suggestions From 04fc796e931586ea8431d5f2d2aebcdd1c8b31fe Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Sun, 23 Jun 2019 14:44:44 -0700 Subject: [PATCH 06/14] Updated more usePreview --- accepted/global-json-updates.md | 38 +++++++++++---------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index ab471e7d6..c2fb0f205 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -72,11 +72,9 @@ Due to the rules above, the following occurs: * Hosted CI, including Azure DevOps and Kudu, need to provide a small number of SDKs. * If there is no patch of the major/minor/feature version (xyz in `x.y.znn`) present, the build fails, causing friction between user and host needs. -## Proposal C +## Proposal -This proposal parallels [runtime roll-forward behavior as initially described](https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/roll-forward-on-no-candidate-fx.md)} and [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md). All of the features proposed here may not be included in Phase 1. It will include at least `ignorePreview` and `"rollforward": "disable"` - -This is called Proposal C to differentiate it from two earlier proposals that were discarded but are included at the end of this document for reference. +This proposal parallels [runtime roll-forward behavior as initially described](https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/roll-forward-on-no-candidate-fx.md)} and [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md). All of the features proposed here may not be included in Phase 1. It will include at least `usePreview` and `"rollforward": "disable"` ### Details @@ -174,8 +172,8 @@ Or, if it is easier to understand with logic (bail out of logic on fail or selec ## Customer problems addressed * User wants to install a preview but only use it with a subset of projects. - * User opts out in projects where preview should not be used by adding `ignorePreview: true` to those projects, or - * User adds `ignorePreview: true` in a `global.json` higher in their file system, and adds `ignorePreview: false` in a `global.json` in specific projects. + * User opts out in projects where preview should not be used by adding `usePreview: false` to those projects, or + * User adds `usePreview: false` in a `global.json` higher in their file system, and adds `usePreview: true` in a `global.json` in specific projects. * User wants to always use a specific SDK patch version, and receive an error if it is not on the machine. * User adds `rollForward: disable` to a `global.json` * User wants to always run the latest patch of a particular SDK feature band. @@ -202,7 +200,7 @@ The following `global.json` defines a specific version number, and to use a high { "sdk": { "version": "2.2.100", - "ignorePreview": false, + "usePreview": true, "rollForward": "patch" } } @@ -227,7 +225,7 @@ If there is no `global.json` that would logically be the same as the following. ```json { "sdk": { - "ignorePreview": false, + "usePreview": true, "rollForward": "latestMajor" } } @@ -240,7 +238,7 @@ If there is no `global.json` but the user does not want to use previews, that wo ```json { "sdk": { - "ignorePreview": true, + "usePreview": false, "rollForward": "latestMajor" } } @@ -251,7 +249,7 @@ The common `global.json` for this case is likely to be: ```json { "sdk": { - "ignorePreview": true, + "usePreview": false, } } ``` @@ -266,7 +264,7 @@ If the user wants to specify the lowest SDK they want to use, they could use the { "sdk": { "version": "2.2.100", - "ignorePreview": false, + "usePreview": true, "rollForward": "latestMajor" } } @@ -291,7 +289,7 @@ If the user wanted to use any SDK higher than the one listed but ignore previews { "sdk": { "version": "2.2.100", - "ignorePreview": true, + "usePreview": false, "rollForward": "latestMajor" } } @@ -305,7 +303,7 @@ If the user wants to use any SDK as high or higher than the one listed but ignor { "sdk": { "version": "3.0.100-Pre", - "ignorePreview": true, + "usePreview": false, "rollForward": "highestMajor" } } @@ -325,7 +323,7 @@ If the user wants an exact match, they could use the following. { "sdk": { "version": "2.2.100", - "ignorePreview": false, + "usePreview": true, "rollForward": "disable" } } @@ -361,7 +359,7 @@ If the user wants the highest within a runtime major/minor, they could use the f { "sdk": { "version": "2.2.100", - "ignorePreview": false, + "usePreview": true, "rollForward": "latestMinor" } } @@ -394,13 +392,3 @@ Prior to arriving at this proposal, two others were considered. Neither of these * An independent versioning scheme. It's largest downside is that it would be _another_ way users would have to think about specifying versioning behavior. * An attempt to parallel NuGet. Definining [NuGet version ranges](https://docs.microsoft.com/en-us/nuget/reference/package-versioning) is relatively complex, and it didn't feel like a good fit. - -## Additional suggestions - -These suggestions have been received via Twitter, and I want to acknowledge that we've seen them and why we aren't acting on them at present. - -### [Have Visual Studio offer to download the requested SDK](https://twitter.com/Nick_Craver/status/1124649778078527488) - -This idea is for Visual Studio, so it is not covered in this proposal. - -The roll-forward within feature band is a feature, so this probably makes sense only when the presence of global.json would fail. From da302f7df06adf833bd55fa0d6f188b9c8ce3a6f Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Sun, 23 Jun 2019 14:52:18 -0700 Subject: [PATCH 07/14] Respond to some comments --- accepted/global-json-updates.md | 1 + 1 file changed, 1 insertion(+) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index c2fb0f205..59ea34362 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -98,6 +98,7 @@ The `global.json` schema to offers support for all of the identified scenarios t * `true` or `false` * Default: `true` to match today's behavior. * `rollForward` + * The `version` must be specified if `rollForward` is specified. * To avoid some of the repetitiveness, I do not restate in these rules that version numbers below that specified are always ignored. * `patch` * If the requested major, minor, feature band, and patch if found, use it. From c79e2195a358c02c893a4b1d5ea0402aec530dfa Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Mon, 24 Jun 2019 14:11:59 -0700 Subject: [PATCH 08/14] Changed to high patch, changed default --- accepted/global-json-updates.md | 173 +++++++++++++------------------- 1 file changed, 67 insertions(+), 106 deletions(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index 59ea34362..1848c9c63 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -24,7 +24,7 @@ Given x.y.znn * z is the feature band * nn is the patch -## Current behavior +## Previous behavior The SDK resolvers (in both the CLI and VS) search from the current location (project in VS) up the directory hierarchy until they encounter a `global.json` file. When the resolver finds one, _it stops looking_ and attempts to resolve the SDK version specified in that file. This happens even if this `global.json` does not specify an SDK version. @@ -76,16 +76,11 @@ Due to the rules above, the following occurs: This proposal parallels [runtime roll-forward behavior as initially described](https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/roll-forward-on-no-candidate-fx.md)} and [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md). All of the features proposed here may not be included in Phase 1. It will include at least `usePreview` and `"rollforward": "disable"` -### Details - The runtime options allow for three basic approaches: find nearest, find latest and find exact. -To avoid a backward compatibility issue, the default recovery strategy when a match is not found needs to match the old `dotnet` behavior. This defines the defaults, and the default behavior differs between runtime. +## Details -* Runtime: From the [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md) document "_In all cases except `disable` the highest available patch version is selected._" -* SDK: For backwards compatibility, the additonal option `patch` is offered and is the default. - * If the specified patch version is found, it is used. - * In all other cases (except `disable`), the highest patch within the appropriate feature band is used. +* Runtime: From the [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md) document" _In all cases except `disable` the highest available patch version is selected._" The `global.json` schema to offers support for all of the identified scenarios that can be fixed without altering how we find `global.json`: @@ -97,46 +92,55 @@ The `global.json` schema to offers support for all of the identified scenarios t * `usePreview` * `true` or `false` * Default: `true` to match today's behavior. - * `rollForward` - * The `version` must be specified if `rollForward` is specified. - * To avoid some of the repetitiveness, I do not restate in these rules that version numbers below that specified are always ignored. - * `patch` + * `rollForward` (The `version` must be specified if `rollForward` is specified.) + * `patch` **[[(I've gone back and forth on "patch" or "legacy" here)]]** * If the requested major, minor, feature band, and patch if found, use it. * Otherwise, use the highest patch within the specified major, minor and feature band. + * This recreates the previous SDK selection criteria. * `feature` - * If the requested major, minor, feature band, and patch is found, use it. - * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. - * Otherwise, use the lowest higher feature band, and use the highest patch within that feature band. + * If the requested major, minor, feature band, is found, use the highest available patch in that band. + * Otherwise, use the lowest higher major/minor/feature band, use the highest patch within that feature band. * `minor` - * If the requested major, minor, feature band, and patch is found, use it. - * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. - * Otherwise, if the requested major and minor is found, use the highest patch in the _lowest_ feature band within that major and minor version. - * Otherwise, use the lowest higher minor, it's lowest feature band, and the highest patch within that feature band. - * `major` - * If the requested major, minor, feature band, and patch is found, use it. - * Otherwise, if the requested major, minor and feature band is found, use the highest patch in that band. - * Otherwise, if the requested major and minor is found, use the highest patch in the _lowest_ feature band within that major and minor version. - * Otherwise, if the requested major is found, use the _lowest_ minor within that major version, and it's lowest feature band, and it's highest patch. + * If the requested major, minor, feature band, is found, use the highest available patch in that band. + * If there is higher major/minor/feature band, use the highest patch within that feature band. + * Otherwise, use the lowest higher major/minor, use it's lowest feature band, and the highest patch within that feature band. + * `major` + * If the requested major, minor, feature band, is found, use the highest available patch in that band. + * If there is a higher major/minor/feature band, use the highest patch within that feature band. + * If there is a higher major/minor, use it's lowest feature band, and the highest patch within that feature band. * Otherwise, use the lowest higher major, it's lowest minor, and it's lowest feature band, and the highest patch within that feature band. * `latestPatch` * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. - * Select the highest available patch version that matches the specified major, minor and feature band versions. + * Select the highest available patch version that matches the specified major, minor and feature band. * `latestFeature` - * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. + * Even if the requested major, minor, feature band is found, do not use it unless it matches rules below. * Select the highest available feature band that matches the specified major and minor version, and select it's highest patch. * `latestMinor` - * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. + * Even if the requested major, minor is found, do not use it unless it matches rules below. * Select the highest available minor that matches the specified major version, and select it's highest feature band, and that feature band's highest patch. - * `latestMajor` - * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. + * `latestMajor` (latest) + * Even if the requested major is found, do not use it unless it matches rules below. * Select the highest available major version, and select it's highest minor version, and that minor's highest feature band, and that feature band's highest patch. * `disable` * Do not roll forward. Only bind to specified version. This policy means the latest patches, including security patches will not be used. - * Defaults: - * If a version is specified in the `global.json`, the default is `patch` to match today's behavior. - * If there is no `global.json` or a version is not specified, the default is`latestMajor`, again to match today's behavior. -### Examples to illustrate roll-forward options +## Default + +### No `global.json` + +If there is no `global.json` or a version is not specified, the default is`latestMajor`, again to match today's behavior. + +### `global.json` containing a version number + +**Breaking Change** This default represents a breaking change. We took this change because we believe the new behavior will cause no change or be an improvement. + +Previously, if the specified patch version was present, it was used even if a higher patch number was installed. In discussing this with users, ignoring the updated patch was not the anticipated behavior. Also, the previous default did not roll forward across feature, minor or major bands. This could result in failure when a usable SDK existed on the machine. + +The new behavior is that the highest patch within the feature band will be used even if the requested version is available, unless the `rollForward` is set to `disable`. + +Also, the nearest higher SDK will be used if there is no version that matches the requested major, minor and feature. This behavior can be changed by specifying other options for the `rollForward` property. + +## Examples to illustrate roll-forward options The following table considers various SDK version combinations that may be available on the machine, and the outcome of the following `global.json`: @@ -149,26 +153,16 @@ The following table considers various SDK version combinations that may be avail } ``` -| Available SDKs | patch | feature | minor | major | latestPatch | latestFeature | latestMinor | latestMajor | disable | -|------------------------------------------------------|-----------|---------|---------|---------|-------------|---------------|-------------|-------------|---------| -| 2.1.500 | fail | fail | fail | fail | fail | fail | fail | fail | fail | -| 2.1.501, 2.1.503 | _2.1.501_ | 2.1.501 | 2.1.501 | 2.1.501 | _2.1.503_ | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.501 | -| 2.1.503, 2.1.505, 2.1.601, 2.2.101, 3.0.100 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.505 | 2.1.601 | 2.2.101 | 3.0.100 | fail | -| 2.1.601, 2.1.604, 2.1.702, 2.2.101, 2.2.203, 3.0.100 | fail | 2.1.601 | 2.1.601 | 2.1.601 | fail | 2.1.702 | 2.2.203 | 3.0.100 | fail | -| 2.2.101, 2.2.203, 3.0.100 | fail | fail | 2.2.101 | 2.2.101 | fail | fail | 2.2.203 | 3.0.100 | fail | -| 3.0.100, 3.1.102 | fail | fail | fail | 3.0.100 | fail | fail | fail | 3.1.102 | fail | - -Or, if it is easier to understand with logic (bail out of logic on fail or select): - -1. If not `latest`, and there is a match, select it. -1. If we get here for `disable`, then fail. -1. Exclude any lower versions. -1. If `minor` or `latestMinor`, exclude any that don't match major version. -1. If `feature` or `latestFeature`, exclude any that don't match major and minor version. -1. If `patch` or `latestPatch`, exclude any that don't match major, minor and feature band version. -1. If there is one left, select it. -1. If any of the `latest` options are used, select the highest. -1. Otherwise, select the lowest major, and within that lowest minor, and within that lowest feature band. Then select the highest patch in that feature band. +| Available SDKs | patch | feature | minor | major(*) | latestPatch | latestFeature | latestMinor | latestMajor | disable | +|------------------------------------------------------|---------|---------|---------|----------|-------------|---------------|-------------|-------------|---------| +| 2.1.500 | fail | fail | fail | fail | fail | fail | fail | fail | fail | +| 2.1.501, 2.1.503 | 2.1.501 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.503 | 2.1.501 | +| 2.1.503, 2.1.505, 2.1.601, 2.2.101, 3.0.100 | 2.1.505 | 2.1.505 | 2.1.505 | 2.1.505 | 2.1.505 | 2.1.601 | 2.2.101 | 3.0.100 | fail | +| 2.1.601, 2.1.604, 2.1.702, 2.2.101, 2.2.203, 3.0.100 | fail | 2.1.604 | 2.1.604 | 2.1.604 | fail | 2.1.702 | 2.2.203 | 3.0.100 | fail | +| 2.2.101, 2.2.203, 3.0.100 | fail | fail | 2.2.101 | 2.2.101 | fail | fail | 2.2.203 | 3.0.100 | fail | +| 3.0.100, 3.1.102 | fail | fail | fail | 3.0.102 | fail | fail | fail | 3.1.102 | fail | + +(*) Default ## Customer problems addressed @@ -176,15 +170,11 @@ Or, if it is easier to understand with logic (bail out of logic on fail or selec * User opts out in projects where preview should not be used by adding `usePreview: false` to those projects, or * User adds `usePreview: false` in a `global.json` higher in their file system, and adds `usePreview: true` in a `global.json` in specific projects. * User wants to always use a specific SDK patch version, and receive an error if it is not on the machine. - * User adds `rollForward: disable` to a `global.json` + * User adds `version` and `rollForward: disable` to a `global.json` +* User wants to set a floor or lowest version and use the best installed version above that (common for public repos). + * User adds `rollForward: latestMajor` and the lowest version (with patch) they want to a `global.json` * User wants to always run the latest patch of a particular SDK feature band. * User adds `rollForward: latestPatch` and the lowest version (with patch) they want to a `global.json` -* User includes a feature that requires an SDK that corresponds to a SDK feature band or higher, and wants to run the best available tools. - * User adds `rollForward: latestMajor` and the lowest version (with patch) they want to a `global.json` -* User wants to know what version of the SDK is being used. - * Not addressed in this proposal: `global.json` will still be a subtle, hidden and easily forgotten marker -* User rarely wants the risk of running the unsupported scenario of multiple SDK versions in a solution build. - * Not addressed in this proposal: There is nothing that encourages users to place `global.json` in a logical location. * Hosted CI, including Azure DevOps and Kudu, need to provide a small number of SDKs. * It will now be possible for users to specify `global.json` files in a way that can work with Azure DevOps (roll-forward across at least feature bands) * We will work with Azure DevOps to determine the recommended `global.json` for this scenario. @@ -193,33 +183,22 @@ Or, if it is easier to understand with logic (bail out of logic on fail or selec This section lists a series of scenarios and following the corresponding `global.json`. Following this is a table that shows the SDK that would be selected with specific combinations of SDKs present on the machine. -### 1. Explicitly state today's behavior when a `global.json` specifies a version +### 1. Previous behavior when a `global.json` specifies a version -The following `global.json` defines a specific version number, and to use a higher version if it is available. +The following `global.json` defines a specific version number, and to use a higher patch only when this version is not available. Note: this is not the default. ```json { "sdk": { "version": "2.2.100", - "usePreview": true, "rollForward": "patch" } } ``` -The common `global.json` for this case is likely to be: - -```json -{ - "sdk": { - "version": "2.2.100" - } -} -``` - -### 2. Explicitly state today's behavior when there is no `global.json` +### 2. Explicitly state previous behavior when there is no `global.json` by excluding the version number and stating behavior -Today's behavior is that an exact match is used if found, and if not found, the highest patch above the current in the feature band is used. +Today's behavior is that an exact match is used if found, and if not found, the highest patch above the requested in the feature band is used. If there is no `global.json` that would logically be the same as the following. @@ -255,7 +234,7 @@ The common `global.json` for this case is likely to be: } ``` -The user may include their preference for previews high in their directory structure, and then individual project can elect different behavior with a new `global.json`. This will continue to follow today's rules. In particular: if there is a `global.json` file for a different reason, like a custom SDK, the higher `global.json` will not be used. +The user may include their preference for previews high in their directory structure, and then individual project can elect different behavior with a new `global.json`. This will continue to follow today's rules. In particular: if there is a `global.json` file for a different reason, like a custom SDK, the higher `global.json` in the directory tree will not be used. ### 4 Select highest available, higher than specified @@ -271,17 +250,6 @@ If the user wants to specify the lowest SDK they want to use, they could use the } ``` -The common `global.json` for this case is likely to be: - -```json -{ - "sdk": { - "version": "2.2.100", - "rollForward": "latestMajor" - } -} -``` - ### 5. Select highest available, higher than specified, previews ignored If the user wanted to use any SDK higher than the one listed but ignore previews, they could use the following: @@ -347,11 +315,7 @@ When this fails, the error is: The requested SDK version was not available, and SDK version roll-forward was disabled. ``` -### 8. Exact match or highest within runtime major.minor - -This proposal does not support arbitrary ranges or arbitrary upper bounds. - -### 9. Highest within runtime major.minor +### 8. Highest within runtime major.minor If the user wants the highest within a runtime major/minor, they could use the following. @@ -361,31 +325,28 @@ If the user wants the highest within a runtime major/minor, they could use the f "sdk": { "version": "2.2.100", "usePreview": true, - "rollForward": "latestMinor" + "rollForward": "latestFeature" } } ``` -### 10. Highest within arbitrary upper and lower bound +### 9. Exact match or highest within runtime major.minor -If the user wanted to use any SDK higher than the one listed but ignore previews, they could use the following. - -Proposal C +This proposal does not support arbitrary ranges or arbitrary upper bounds. -There is no concept of an arbitrary upper bound (as there isn't with the runtime) ### Selections made This table lists the SDKs selected in each of the above scenarios. -| Available SDKs | #1 | #2 | #3 | #4 | #5 | #6 | #7 | #8 | #9 | #10 | -|-------------------------------|---------|-------------|---------|-------------|---------|---------|---------|---------|---------|---------| -| 2.1.700 | fail | 2.1.700 | 2.1.700 | fail | fail | fail | fail | fail | fail | 2.1.700 | -| 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | fail | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | -| 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | fail | 2.2.103 | 2.2.103 | 2.2.103 | -| 2.1.700, 2.2.100, 2.2.103 | 2.2.100 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | 2.2.100 | 2.2.100 | 2.2.103 | 2.2.103 | -| 2.1.700, 2.2.103, 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | fail | fail | 2.2.100 | 2.2.103 | 2.2.103 | -| 2.1.700, 2.2.103, 3.1.100 | 2.2.103 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | fail | 2.2.103 | 2.2.103 | 2.2.103 | +| Available SDKs | #1 | #2 | #3 | #4 | #5 | #6 | #7 | #8 | +|-------------------------------|---------|-------------|---------|-------------|---------|---------|---------|---------| +| 2.1.700 | fail | 2.1.700 | 2.1.700 | fail | fail | fail | fail | fail | +| 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | 2.2.100 | fail | 2.2.100 | 2.2.100 | +| 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | fail | 2.2.103 | +| 2.1.700, 2.2.100, 2.2.103 | 2.2.100 | 2.2.103 | 2.2.103 | 2.2.103 | 2.2.103 | fail | 2.2.100 | 2.2.100 | +| 2.1.700, 2.2.103, 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | 3.1.100-Pre | 2.2.103 | fail | fail | 2.2.100 | +| 2.1.700, 2.2.103, 3.1.100 | 2.2.103 | 3.1.100 | 3.1.100 | 3.1.100 | 3.1.100 | fail | fail | 2.2.103 | ## Other proposals considered From 8d8eb31a9bf3cc65d05eff3ecefdff17f2ad73bd Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Wed, 26 Jun 2019 14:21:11 -0700 Subject: [PATCH 09/14] Updated roll-forward options --- accepted/global-json-updates.md | 51 +++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index 1848c9c63..a61ca2f24 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -92,35 +92,48 @@ The `global.json` schema to offers support for all of the identified scenarios t * `usePreview` * `true` or `false` * Default: `true` to match today's behavior. - * `rollForward` (The `version` must be specified if `rollForward` is specified.) + * `rollForward` (The `version` must be specified if `rollForward` is specified, except `latestMajor`.) + * There are four basic intents for roll-forward: + * `patch`: legacy behavior. + * `feature`, `minor`, `major`: highest patch of the nearest version within specified limits. + * `latestPatch`, `latestFeature`, `latestMinor`, `latestMajor`: highest patch of the highest available version within specified limits. + * `disable`: use exactly the version specified. * `patch` **[[(I've gone back and forth on "patch" or "legacy" here)]]** - * If the requested major, minor, feature band, and patch if found, use it. - * Otherwise, use the highest patch within the specified major, minor and feature band. + * If the requested major/minor/feature band/patch is found, use it. + * Otherwise, if there are higher patches within the major/minor/feature band, use the highest patch of that set. + * Otherwise, fail. * This recreates the previous SDK selection criteria. * `feature` - * If the requested major, minor, feature band, is found, use the highest available patch in that band. - * Otherwise, use the lowest higher major/minor/feature band, use the highest patch within that feature band. + * If the requested major/minor/feature band is found, use the highest patch within that set. + * Otherwise, if there are higher feature band versions within the major/minor, use the highest patch in the lowest higher feature band of that set. + * Otherwise, fail. * `minor` - * If the requested major, minor, feature band, is found, use the highest available patch in that band. - * If there is higher major/minor/feature band, use the highest patch within that feature band. - * Otherwise, use the lowest higher major/minor, use it's lowest feature band, and the highest patch within that feature band. + * If the requested major/minor/feature band is found, use the highest patch within that set. + * Otherwise, if there are other feature band versions within the major/minor, use the highest patch in the lowest higher feature band of that set. + * Otherwise, if there are higher minor versions within the major, use the lowest higher minor version in that set, it's lowest feature band, and the highest patch within that feature band. + * Otherwise, fail. * `major` - * If the requested major, minor, feature band, is found, use the highest available patch in that band. - * If there is a higher major/minor/feature band, use the highest patch within that feature band. - * If there is a higher major/minor, use it's lowest feature band, and the highest patch within that feature band. - * Otherwise, use the lowest higher major, it's lowest minor, and it's lowest feature band, and the highest patch within that feature band. + * If the requested major/minor/feature band is found, use the highest patch within that set. + * Otherwise, if there are other feature band versions within the major/minor, use the highest patch in the lowest higher feature band of that set. + * Otherwise, if there are other minor versions within the major, use the lowest higher minor version in that set, its lowest feature band, and the highest patch within that feature band. + * Otherwise, if there higher major versions, use the lowest higher major version in that set, its lowest minor, its lowest feature band, and the highest patch within that feature band. + * Otherwise, fail. * `latestPatch` - * Even if the requested major, minor, feature band and patch is found, do not use it unless it matches rules below. - * Select the highest available patch version that matches the specified major, minor and feature band. + * Even if the requested major/minor/feature band/patch is found, do not use it unless it matches rules below. + * Considering only the equal and higher patches in the major/minor/feature band, select the highest available patch. + * If there are no equal or higher patches in the major/minor/feature band, fail. * `latestFeature` - * Even if the requested major, minor, feature band is found, do not use it unless it matches rules below. - * Select the highest available feature band that matches the specified major and minor version, and select it's highest patch. + * Even if the requested major/minor/feature band is found, do not use it unless it matches rules below. + * Considering only the equal and higher feature bands in the major/minor, select the highest available feature band and its highest patch. + * If there are no equal or higher feature bands in the major/minor, fail. * `latestMinor` - * Even if the requested major, minor is found, do not use it unless it matches rules below. - * Select the highest available minor that matches the specified major version, and select it's highest feature band, and that feature band's highest patch. + * Even if the requested major/minor is found, do not use it unless it matches rules below. + * Considering only the equal and higher minor versions in the major version, select the highest available minor, its highest feature band and its highest patch. + * If there are no equal or higher minor versions in the major, fail. * `latestMajor` (latest) * Even if the requested major is found, do not use it unless it matches rules below. - * Select the highest available major version, and select it's highest minor version, and that minor's highest feature band, and that feature band's highest patch. + * Considering only the equal and higher major versions, select the highest available major, its highest minor, its highest feature band and its highest patch. + * If there are no equal or higher major versions, fail. * `disable` * Do not roll forward. Only bind to specified version. This policy means the latest patches, including security patches will not be used. From 117d7eb7dc60bc7904af835e8aca1ae17259f41a Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Thu, 27 Jun 2019 09:07:34 -0700 Subject: [PATCH 10/14] Update accepted/global-json-updates.md Co-Authored-By: Nick Guerrera --- accepted/global-json-updates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index a61ca2f24..3531d5ad6 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -32,7 +32,7 @@ When a `global.json` is encountered and it specifies an SDK version, the SDK res 1. When the resolver finds no `global.json` or finds one that does not specify SDK version,the latest SDK is used. Whether previews are considered depends on how `dotnet` is called: - If called from the CLI, previews are considered. - - If called Visual Studio a flag is passed indicating whether to consider previews: + - If called from Visual Studio, a flag is passed indicating whether to consider previews: - If Visual Studio is a Preview, or the "Use Previews" options is set by the user, previews are considered. - Otherwise, previews are not considered when called from Visual Studio. 2. When the resolver finds a `global.json`, the specific SDK patch version it specifies is used if it is found. From eb73096a9211f1343d8355b3fd3817f5230c1a44 Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Thu, 27 Jun 2019 09:08:41 -0700 Subject: [PATCH 11/14] Update accepted/global-json-updates.md Co-Authored-By: Nick Guerrera --- accepted/global-json-updates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index 3531d5ad6..4b9a99e25 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -286,7 +286,7 @@ If the user wants to use any SDK as high or higher than the one listed but ignor "sdk": { "version": "3.0.100-Pre", "usePreview": false, - "rollForward": "highestMajor" + "rollForward": "latestMajor" } } ``` From 95aacbb3a5de05ea7278f8e09e37bc62cad936ca Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Thu, 27 Jun 2019 09:36:59 -0700 Subject: [PATCH 12/14] Update based on comments --- accepted/global-json-updates.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index 4b9a99e25..9cf7003db 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -74,7 +74,7 @@ Due to the rules above, the following occurs: ## Proposal -This proposal parallels [runtime roll-forward behavior as initially described](https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/roll-forward-on-no-candidate-fx.md)} and [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md). All of the features proposed here may not be included in Phase 1. It will include at least `usePreview` and `"rollforward": "disable"` +This proposal parallels [runtime roll-forward behavior as initially described](https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/roll-forward-on-no-candidate-fx.md)} and [recent improvements](https://github.com/dotnet/designs/blob/master/accepted/runtime-binding.md). The runtime options allow for three basic approaches: find nearest, find latest and find exact. @@ -91,14 +91,16 @@ The `global.json` schema to offers support for all of the identified scenarios t * Default: empty * `usePreview` * `true` or `false` - * Default: `true` to match today's behavior. + * Default + * If called from Visual Studio, use the preview status requested + * Otherwise,`true` to match today's behavior. * `rollForward` (The `version` must be specified if `rollForward` is specified, except `latestMajor`.) * There are four basic intents for roll-forward: * `patch`: legacy behavior. * `feature`, `minor`, `major`: highest patch of the nearest version within specified limits. * `latestPatch`, `latestFeature`, `latestMinor`, `latestMajor`: highest patch of the highest available version within specified limits. * `disable`: use exactly the version specified. - * `patch` **[[(I've gone back and forth on "patch" or "legacy" here)]]** + * `patch` * If the requested major/minor/feature band/patch is found, use it. * Otherwise, if there are higher patches within the major/minor/feature band, use the highest patch of that set. * Otherwise, fail. @@ -145,13 +147,16 @@ If there is no `global.json` or a version is not specified, the default is`lates ### `global.json` containing a version number -**Breaking Change** This default represents a breaking change. We took this change because we believe the new behavior will cause no change or be an improvement. +**Change** The default behavior is changed. We took this change because we believe the new behavior will cause no change or be an improvement. -Previously, if the specified patch version was present, it was used even if a higher patch number was installed. In discussing this with users, ignoring the updated patch was not the anticipated behavior. Also, the previous default did not roll forward across feature, minor or major bands. This could result in failure when a usable SDK existed on the machine. +The new behavior is `major` as described above. The previous behavior is `patch` as described above. -The new behavior is that the highest patch within the feature band will be used even if the requested version is available, unless the `rollForward` is set to `disable`. +This changes behavior in two scenarios: -Also, the nearest higher SDK will be used if there is no version that matches the requested major, minor and feature. This behavior can be changed by specifying other options for the `rollForward` property. +* Previously, if the specified patch version was present, it was used even if a higher patch number was installed. In discussing this with users, ignoring the updated patch was not the anticipated behavior. +* If there was no patch version of the specified major/minor/feature band, SDK resolution previously failed. SDK resolution will now succeed. + +This will change behavior if the specified SDK is not present, but a higher version outside the current feature band is present. Previously SDK selection failed in this scenario, and now it will succeed. ## Examples to illustrate roll-forward options From 3de53e36d27a975c545ffb617ea39f503723c1f4 Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Thu, 27 Jun 2019 11:28:38 -0700 Subject: [PATCH 13/14] Updated usePreview to allowPrerelease --- accepted/global-json-updates.md | 60 ++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index 9cf7003db..e19be96cc 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -30,11 +30,11 @@ The SDK resolvers (in both the CLI and VS) search from the current location (pro When a `global.json` is encountered and it specifies an SDK version, the SDK resolvers attempt to locate that specific SDK patch version: -1. When the resolver finds no `global.json` or finds one that does not specify SDK version,the latest SDK is used. Whether previews are considered depends on how `dotnet` is called: - - If called from the CLI, previews are considered. - - If called from Visual Studio, a flag is passed indicating whether to consider previews: - - If Visual Studio is a Preview, or the "Use Previews" options is set by the user, previews are considered. - - Otherwise, previews are not considered when called from Visual Studio. +1. When the resolver finds no `global.json` or finds one that does not specify SDK version,the latest SDK is used. Whether prerelease versions are considered depends on how `dotnet` is called: + - If called from the CLI, prerelease versions are considered. + - If called from Visual Studio, a flag is passed indicating whether to consider prerelease versions: + - If Visual Studio is a Preview, or the "Use Previews" options is set by the user, prerelease versions are considered. + - Otherwise, prerelease versions are not considered when called from Visual Studio. 2. When the resolver finds a `global.json`, the specific SDK patch version it specifies is used if it is found. 3. When the resolver finds a `global.json`, and the SDK it specifies can't be found, the resolver looks for a close match: * Prior to .NET Core 2.1 @@ -45,17 +45,17 @@ When a `global.json` is encountered and it specifies an SDK version, the SDK res Due to the rules above, the following occurs: * Due to #2, simply installing a patch on the machine will not update the machine to use the safer version. - * The impact of this is mitigated by work we did in .NET Core 3.0 Preview 3 to replace earlier patch versions. + * The impact of this is mitigated by work we did in .NET Core 3.0 prerelease version 3 to replace earlier patch versions. * Due to #3, users can not guarantee they are pinned to a specific version. ## Customer problems to address -* User wants to install a preview but only use it with a subset of projects. - * Due to Rule #1, a `global.json` is the only way to opt out of a preview in the common case of the preview being the highest version on the machine. - * The `global.json` the user addss must include a specific version. +* User wants to install a prerelease version but only use it with a subset of projects. + * Due to Rule #1, a `global.json` is the only way to opt out of a prerelease version in the common case of the prerelease version being the highest version on the machine. + * The `global.json` the user adds must include a specific version. * If no matching SDK version in the feature band (xyz in x.y.z.nn) is found, the command being called fails. * Each `global.json` must later be updated to use the new stable SDK. - * Due to Rule #2, there is no roll forward from preview to stable if they are both on the machine. + * Due to Rule #2, there is no roll forward from prerelease version to stable if they are both on the machine. * The impact of this is mitigated by work we did in .NET Core 3.0 Preview 3 to replace earlier patch versions. * User wants to always use a specific SDK patch version, and receive an error if it is not on the machine. * The user cannot currently do this. @@ -89,10 +89,10 @@ The `global.json` schema to offers support for all of the identified scenarios t * The lowest SDK the resolver would select - any SDKs with lower versions are ignored. * Identical to today - wildcards are not supported. * Default: empty - * `usePreview` + * `allowPrerelease` * `true` or `false` * Default - * If called from Visual Studio, use the preview status requested + * If called from Visual Studio, use the prerelease status requested * Otherwise,`true` to match today's behavior. * `rollForward` (The `version` must be specified if `rollForward` is specified, except `latestMajor`.) * There are four basic intents for roll-forward: @@ -184,9 +184,9 @@ The following table considers various SDK version combinations that may be avail ## Customer problems addressed -* User wants to install a preview but only use it with a subset of projects. - * User opts out in projects where preview should not be used by adding `usePreview: false` to those projects, or - * User adds `usePreview: false` in a `global.json` higher in their file system, and adds `usePreview: true` in a `global.json` in specific projects. +* User wants to install a prerelease version but only use it with a subset of projects. + * User opts out in projects where prerelease version should not be used by adding `allowPrerelease: false` to those projects, or + * User adds `allowPrerelease: false` in a `global.json` higher in their file system, and adds `allowPrerelease: true` in a `global.json` in specific projects. * User wants to always use a specific SDK patch version, and receive an error if it is not on the machine. * User adds `version` and `rollForward: disable` to a `global.json` * User wants to set a floor or lowest version and use the best installed version above that (common for public repos). @@ -223,20 +223,20 @@ If there is no `global.json` that would logically be the same as the following. ```json { "sdk": { - "usePreview": true, + "allowPrerelease": true, "rollForward": "latestMajor" } } ``` -### 3. Skip previews, otherwise as though there was no `global.json` +### 3. Skip prerelease versions, otherwise as though there was no `global.json` -If there is no `global.json` but the user does not want to use previews, that would logically be the same as the following: +If there is no `global.json` but the user does not want to use prerelease versions, that would logically be the same as the following: ```json { "sdk": { - "usePreview": false, + "allowPrerelease": false, "rollForward": "latestMajor" } } @@ -247,12 +247,12 @@ The common `global.json` for this case is likely to be: ```json { "sdk": { - "usePreview": false, + "allowPrerelease": false, } } ``` -The user may include their preference for previews high in their directory structure, and then individual project can elect different behavior with a new `global.json`. This will continue to follow today's rules. In particular: if there is a `global.json` file for a different reason, like a custom SDK, the higher `global.json` in the directory tree will not be used. +The user may include their preference for prerelease versions high in their directory structure, and then individual project can elect different behavior with a new `global.json`. This will continue to follow today's rules. In particular: if there is a `global.json` file for a different reason, like a custom SDK, the higher `global.json` in the directory tree will not be used. ### 4 Select highest available, higher than specified @@ -262,21 +262,21 @@ If the user wants to specify the lowest SDK they want to use, they could use the { "sdk": { "version": "2.2.100", - "usePreview": true, + "allowPrerelease": true, "rollForward": "latestMajor" } } ``` -### 5. Select highest available, higher than specified, previews ignored +### 5. Select highest available, higher than specified, prerelease versions ignored -If the user wanted to use any SDK higher than the one listed but ignore previews, they could use the following: +If the user wanted to use any SDK higher than the one listed but ignore prerelease versions, they could use the following: ```json { "sdk": { "version": "2.2.100", - "usePreview": false, + "allowPrerelease": false, "rollForward": "latestMajor" } } @@ -284,13 +284,13 @@ If the user wanted to use any SDK higher than the one listed but ignore previews ### 6. Select highest available with unusual combination -If the user wants to use any SDK as high or higher than the one listed but ignore previews, they could use the following: +If the user wants to use any SDK as high or higher than the one listed but ignore prerelease versions, they could use the following: ```json { "sdk": { "version": "3.0.100-Pre", - "usePreview": false, + "allowPrerelease": false, "rollForward": "latestMajor" } } @@ -299,7 +299,7 @@ If the user wants to use any SDK as high or higher than the one listed but ignor This will always fail. The error will be: ``` -No SDK could be selected due to global.json issue: Specifying a preview version with exactMatch and skipPreview set to true will always fail. +No SDK could be selected due to global.json issue: Specifying a prerelease version version with exactMatch and allowPrerelease is set to false will always fail. ``` ### 7. Fail if exact match is not found @@ -310,7 +310,7 @@ If the user wants an exact match, they could use the following. { "sdk": { "version": "2.2.100", - "usePreview": true, + "allowPrerelease": true, "rollForward": "disable" } } @@ -342,7 +342,7 @@ If the user wants the highest within a runtime major/minor, they could use the f { "sdk": { "version": "2.2.100", - "usePreview": true, + "allowPrerelease": true, "rollForward": "latestFeature" } } From 56b7fcd077b9223db2dca26045a3807be00462b9 Mon Sep 17 00:00:00 2001 From: Kathleen Dollard Date: Sun, 30 Jun 2019 15:52:14 -0700 Subject: [PATCH 14/14] Update accepted/global-json-updates.md Co-Authored-By: Steve MacLean --- accepted/global-json-updates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accepted/global-json-updates.md b/accepted/global-json-updates.md index e19be96cc..1cc0cbfc3 100644 --- a/accepted/global-json-updates.md +++ b/accepted/global-json-updates.md @@ -166,7 +166,7 @@ The following table considers various SDK version combinations that may be avail { "sdk": { "version": "2.1.501", - "rollForward": [entry in table header] + "rollForward": "" } } ```