Compare commits

...

61 Commits

Author SHA1 Message Date
5306333434 Merge pull request #213 from al-cheb/al-cheb/move-dotnet-dir
Move toolcache folder
2021-06-28 12:09:16 +03:00
5070486392 Move toolcache folder 2021-06-28 11:44:40 +03:00
9647326bcf Merge pull request #212 from actions/dependabot/npm_and_yarn/ws-7.5.0
Bump ws from 7.3.1 to 7.5.0
2021-06-28 11:40:18 +03:00
f720d14d91 Merge pull request #198 from actions/dependabot/npm_and_yarn/hosted-git-info-2.8.9
Bump hosted-git-info from 2.8.8 to 2.8.9
2021-06-28 11:14:17 +03:00
7f3ad75595 Merge pull request #197 from actions/dependabot/npm_and_yarn/lodash-4.17.21
Bump lodash from 4.17.20 to 4.17.21
2021-06-28 11:01:16 +03:00
634914df32 Bump lodash from 4.17.20 to 4.17.21
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-28 07:44:45 +00:00
4fc55d8fdb Bump ws from 7.3.1 to 7.5.0
Bumps [ws](https://github.com/websockets/ws) from 7.3.1 to 7.5.0.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.3.1...7.5.0)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-28 07:44:32 +00:00
c98cd238db Merge pull request #158 from actions/dependabot/npm_and_yarn/node-notifier-8.0.1
Bump node-notifier from 8.0.0 to 8.0.1
2021-06-28 10:43:59 +03:00
b25fa305a3 Merge remote-tracking branch 'upstream/main' into dependabot/npm_and_yarn/node-notifier-8.0.1 2021-06-28 10:29:43 +03:00
181f10df92 Merge pull request #210 from timheuer/patch-3
Update description for action
2021-06-28 10:21:17 +03:00
c65a26fe6d Merge pull request #204 from vsafonkin/v-vlsafo/update-installers
Update installers scripts
2021-06-28 10:21:03 +03:00
dd9ada314c Update description for action
Fixes #209 by adding a few more terms for search discoverability
2021-06-22 10:57:55 -07:00
12194cb2cd Update installers scripts 2021-06-08 12:27:57 +03:00
b8ab602c41 Merge pull request #191 from MaksimZhukov/v-mazhuk/automate-releasing-new-versions
Automate releasing new versions of the setup-dotnet action
2021-05-27 17:28:39 +03:00
95f03216f8 Use permissions as a top-level key 2021-05-27 16:20:43 +03:00
5529ce4143 Use the "publish-action" action 2021-05-27 14:39:14 +03:00
5b0ff19756 Bump hosted-git-info from 2.8.8 to 2.8.9
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-11 01:09:33 +00:00
bf3c3eb1fd Merge pull request #196 from Darleev/patch-1
Update README.md
2021-05-06 18:53:19 +03:00
450b569534 Update README.md 2021-05-06 21:38:47 +07:00
e1cf912aaf Update README.md 2021-05-06 20:10:13 +07:00
03d6eb5402 Update README.md 2021-05-06 15:10:43 +07:00
effa3e26d2 Update README.md 2021-05-06 02:55:59 +07:00
a71d1eb2c8 Merge pull request #195 from actions/malob/update-readme
Add preview example to README
2021-05-05 16:49:32 +03:00
b50d70502b Update README.md 2021-05-05 16:47:58 +03:00
d8cff32ae5 Update README.md 2021-05-05 16:45:32 +03:00
7b2009794e Merge pull request #110 from GGG-KILLER/feature/include-prerelease
Include include-prerelease option to be able to match latest prerelease versions
2021-05-05 15:53:38 +03:00
53d6483af8 Merge pull request #192 from dsame/fix/json-bom
Add workaround to fix BOM-related error  during parsing global.json
2021-05-04 18:29:42 +03:00
34c59b7d0b build dist/index.js 2021-04-28 17:29:56 +05:00
bd485b6bd1 Add workaround to fix BOM-related error during parsing global.json 2021-04-28 13:43:02 +05:00
0cfae9b3a5 Automate releasing new versions of the setup-dotnet action 2021-04-26 16:02:03 +03:00
23fa2c1473 Improve key names validation from user' nuget config #187
Improve key names validation from user' nuget config
2021-04-22 18:28:20 +03:00
291588df6a Rerun build 2021-04-22 12:22:10 -03:00
87189a16c7 Merge branch 'main' of https://github.com/actions/setup-dotnet into feature/include-prerelease 2021-04-22 12:21:25 -03:00
cde58ab948 Resolve conflicts 2021-04-22 14:19:03 +03:00
20e68089c2 Merge pull request #178 from actions/dependabot/npm_and_yarn/y18n-4.0.1
Bump y18n from 4.0.0 to 4.0.1
2021-04-22 14:07:23 +03:00
7790315509 Fix for 5.x notation #175 2021-04-22 14:06:54 +03:00
c47e26f841 Support various nuget.config name formats #188 2021-04-22 14:06:13 +03:00
3edf2e9363 Minor fix 2021-04-19 16:16:44 +03:00
a8c97016ef Minor fix 2021-04-19 16:11:14 +03:00
07b4a80293 Build index.js 2021-04-19 14:26:52 +03:00
d08d2193f9 Prettier 2021-04-19 14:26:05 +03:00
632ccbdfb9 Support nuget.config name formats 2021-04-19 14:25:42 +03:00
b81836a248 Fix index.js 2021-04-15 13:57:21 +03:00
3b539379a8 Minor fix 2021-04-15 13:44:48 +03:00
596753d72c Minor fix 2021-04-15 13:43:20 +03:00
f9d7f16c00 Improve key names validation for keys from user nuget config 2021-04-15 13:30:04 +03:00
56d96e897e Improve key names validation for keys from user nuget config 2021-04-15 13:28:24 +03:00
5a201f5e82 Merge pull request #177 from GGG-KILLER/patch-1
Remove unsupported versions of .NET from README
2021-04-08 09:40:20 +03:00
13ccc115f4 Merge pull request #183 from anoff/patch-1
Update link to pre-installed runner Software
2021-04-08 09:39:48 +03:00
f2eb44dadd Update link to pre-installed runner Software 2021-04-05 21:35:43 +09:00
8dcf1ee9bc Bump y18n from 4.0.0 to 4.0.1
Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/yargs/y18n/releases)
- [Changelog](https://github.com/yargs/y18n/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yargs/y18n/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-30 22:18:10 +00:00
3707a44789 Remove unsupported versions of .NET from README.
.NET Core 2.2 and 3.0 are no longer supported and should not be in the examples.

All versions were changed to pull in the latest revision as people might copy this without
checking all accepted version formats and getting the latest revision is good security practice.
2021-03-25 20:07:28 -03:00
385512c5e5 fix test 2021-03-18 11:07:32 +03:00
083d63a2ff add yesy cases 2021-03-18 10:18:52 +03:00
6930747f7f create release 2021-03-17 14:58:04 +03:00
20afff9c90 work on fixing test 2021-03-17 14:56:08 +03:00
36fa9877a9 try fixing 5.x issue 2021-03-17 12:38:57 +03:00
77a48bbb85 Merge pull request #174 from actions/v-malob/update-codeowners
Add @actions/virtual-environments-owners to CODEOWNERS file
2021-03-05 09:36:43 +03:00
768b4eb7db Implement passing includePrerelase to semver 2021-02-19 13:33:36 -03:00
ba7e1e2155 Include include-prerelease input in action.yml 2021-02-19 13:29:15 -03:00
9fdd50a4bb Bump node-notifier from 8.0.0 to 8.0.1
Bumps [node-notifier](https://github.com/mikaelbr/node-notifier) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/mikaelbr/node-notifier/releases)
- [Changelog](https://github.com/mikaelbr/node-notifier/blob/v8.0.1/CHANGELOG.md)
- [Commits](https://github.com/mikaelbr/node-notifier/compare/v8.0.0...v8.0.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-22 08:02:59 +00:00
12 changed files with 1040 additions and 276 deletions

View File

@ -0,0 +1,28 @@
name: Release new action version
on:
release:
types: [released]
workflow_dispatch:
inputs:
TAG_NAME:
description: 'Tag name that the major tag will point to'
required: true
env:
TAG_NAME: ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }}
permissions:
contents: write
jobs:
update_tag:
name: Update the major tag to include the ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} changes
environment:
name: releaseNewActionVersion
runs-on: ubuntu-latest
steps:
- name: Update the ${{ env.TAG_NAME }} tag
id: update-major-tag
uses: actions/publish-action@v0.1.0
with:
source-tag: ${{ env.TAG_NAME }}
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}

View File

@ -13,7 +13,7 @@ This action sets up a [dotnet core cli](https://github.com/dotnet/cli) environme
Please Note: GitHub hosted runners have some versions of the .NET SDK
preinstalled. Installed versions are subject to change. Please refer to the
documentation
[software installed on github hosted runners](https://help.github.com/en/actions/reference/software-installed-on-github-hosted-runners)
[software installed on github hosted runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-software)
for .NET SDK versions that are currently available.
# Usage
@ -23,13 +23,24 @@ See [action.yml](action.yml)
Basic:
```yaml
steps:
- uses: actions/checkout@main
- uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '3.1.x' # SDK Version to use; x will use the latest version of the 3.1 channel
- run: dotnet build <my project>
```
Preview version:
```yml
steps:
- uses: actions@checkout@v2
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
include-prerelease: true
- run: dotnet build <my project>
```
Matrix Testing:
```yaml
jobs:
@ -37,7 +48,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet: [ '2.2.103', '3.0', '3.1.x' ]
dotnet: [ '2.1.x', '3.1.x', '5.0.x' ]
name: Dotnet ${{ matrix.dotnet }} sample
steps:
- uses: actions/checkout@v2
@ -59,7 +70,7 @@ jobs:
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with:
dotnet-version: '2.2.103'
dotnet-version: '2.1.x'
- name: Setup dotnet
uses: actions/setup-dotnet@v1
with:
@ -82,10 +93,10 @@ steps:
- run: dotnet build <my project>
- name: Create the package
run: dotnet pack --configuration Release <my project>
- name: Publish the package to GPR
- name: Publish the package to GPR
run: dotnet nuget push <my project>/bin/Release/*.nupkg
# Authticates packages to push to Azure Artifacts
# Authenticates packages to push to Azure Artifacts
- uses: actions/setup-dotnet@v1
with:
source-url: https://pkgs.dev.azure.com/<your-organization>/_packaging/<your-feed-name>/nuget/v3/index.json
@ -93,6 +104,16 @@ steps:
NUGET_AUTH_TOKEN: ${{secrets.AZURE_DEVOPS_PAT}} # Note, create a secret with this name in Settings
- name: Publish the package to Azure Artifacts
run: dotnet nuget push <my project>/bin/Release/*.nupkg
# Authenticates packages to push to nuget.org.
# It's only the way to push a package to nuget.org feed for macOS/Linux machines due to API key config store limitations.
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 3.1.x
- name: Publish the package to nuget.org
run: dotnet nuget push */bin/Release/*.nupkg -k $NUGET_AUTH_TOKEN -s https://api.nuget.org/v3/index.json
env:
NUGET_AUTH_TOKEN: ${{ secrets.NUGET_TOKEN }}
```
## Environment Variables to use with dotnet
@ -113,7 +134,7 @@ build:
- uses: actions/checkout@main
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '3.1.100' # SDK Version to use.
dotnet-version: '3.1.x' # SDK Version to use.
```
# License

View File

@ -1,13 +1,13 @@
$dotnetPaths = @{
Linux = @("/usr/share/dotnet")
macOS = @("$env:HOME/.dotnet")
Windows = @("$env:ProgramFiles\dotnet/*",
"$env:LocalAppData\Microsoft\dotnet/*")
Linux = "/usr/share/dotnet"
macOS = "$env:HOME/.dotnet"
Windows = "$env:ProgramFiles\dotnet", "$env:LocalAppData\Microsoft\dotnet"
}
foreach ($path in $dotnetPaths[$args[0]]) {
if (Test-Path $path) {
Write-Host "Clear $path path"
Remove-Item $path -Recurse -Force
foreach ($srcPath in $dotnetPaths[$args[0]]) {
if (Test-Path $srcPath) {
Write-Host "Move $srcPath path"
$dstPath = Join-Path ([IO.Path]::GetTempPath()) ([IO.Path]::GetRandomFileName())
Move-Item -Path $srcPath -Destination $dstPath
}
}

View File

@ -13,6 +13,8 @@ describe('version tests', () => {
);
each([
['3.x', '3.x'],
['3.*', '3.*'],
['3.1.x', '3.1'],
['1.1.*', '1.1'],
['2.0', '2.0']
@ -42,7 +44,6 @@ describe('version tests', () => {
'.2.3',
'.2.x',
'1',
'2.x',
'*.*.1',
'*.1',
'*.',

View File

@ -1,5 +1,5 @@
name: 'Setup .NET Core SDK'
description: 'Set up a specific version of the .NET Core CLI in the PATH and set up authentication to a private NuGet repository'
description: 'Used to build and publish .NET source. Set up a specific version of the .NET Core CLI in the PATH and set up authentication to a private NuGet repository'
author: 'GitHub'
branding:
icon: play
@ -13,6 +13,9 @@ inputs:
description: 'Optional OWNER for using packages from GitHub Package Registry organizations/users other than the current repository''s owner. Only used if a GPR URL is also provided in source-url'
config-file:
description: 'Optional NuGet.config location, if your NuGet.config isn''t located in the root of the repo.'
include-prerelease:
description: 'Whether prerelease versions should be matched with non-exact versions (for example 5.0.0-preview.6 being matched by 5, 5.0, 5.x or 5.0.x). Defaults to false if not provided.'
required: False
runs:
using: 'node12'
main: 'dist/index.js'

65
dist/index.js vendored
View File

@ -4840,11 +4840,26 @@ const github = __importStar(__webpack_require__(469));
const xmlbuilder = __importStar(__webpack_require__(312));
const xmlParser = __importStar(__webpack_require__(989));
function configAuthentication(feedUrl, existingFileLocation = '', processRoot = process.cwd()) {
const existingNuGetConfig = path.resolve(processRoot, existingFileLocation == '' ? 'nuget.config' : existingFileLocation);
const existingNuGetConfig = path.resolve(processRoot, existingFileLocation === ''
? getExistingNugetConfig(processRoot)
: existingFileLocation);
const tempNuGetConfig = path.resolve(processRoot, '../', 'nuget.config');
writeFeedToFile(feedUrl, existingNuGetConfig, tempNuGetConfig);
}
exports.configAuthentication = configAuthentication;
function isValidKey(key) {
return /^[\w\-\.]+$/i.test(key);
}
function getExistingNugetConfig(processRoot) {
const defaultConfigName = 'nuget.config';
const configFileNames = fs
.readdirSync(processRoot)
.filter(filename => filename.toLowerCase() === defaultConfigName);
if (configFileNames.length) {
return configFileNames[0];
}
return defaultConfigName;
}
function writeFeedToFile(feedUrl, existingFileLocation, tempFileLocation) {
console.log(`dotnet-auth: Finding any source references in ${existingFileLocation}, writing a new temporary configuration file with credentials to ${tempFileLocation}`);
let xml;
@ -4910,8 +4925,8 @@ function writeFeedToFile(feedUrl, existingFileLocation, tempFileLocation) {
}
xml = xml.ele('packageSourceCredentials');
sourceKeys.forEach(key => {
if (key.indexOf(' ') > -1) {
throw new Error("This action currently can't handle source names with spaces. Remove the space from your repo's NuGet.config and try again.");
if (!isValidKey(key)) {
throw new Error("Source name can contain letters, numbers, and '-', '_', '.' symbols only. Please, fix source name in NuGet.config and try again.");
}
xml = xml
.ele(key)
@ -7814,14 +7829,18 @@ function run() {
core.debug('No version found, trying to find version from global.json');
const globalJsonPath = path.join(process.cwd(), 'global.json');
if (fs.existsSync(globalJsonPath)) {
const globalJson = JSON.parse(fs.readFileSync(globalJsonPath, { encoding: 'utf8' }));
const globalJson = JSON.parse(
// .trim() is necessary to strip BOM https://github.com/nodejs/node/issues/20649
fs.readFileSync(globalJsonPath, { encoding: 'utf8' }).trim());
if (globalJson.sdk && globalJson.sdk.version) {
version = globalJson.sdk.version;
}
}
}
if (version) {
const dotnetInstaller = new installer.DotnetCoreInstaller(version);
const includePrerelease = (core.getInput('include-prerelease') || 'false').toLowerCase() ===
'true';
const dotnetInstaller = new installer.DotnetCoreInstaller(version, includePrerelease);
yield dotnetInstaller.installDotnet();
}
const sourceUrl = core.getInput('source-url');
@ -16868,15 +16887,16 @@ class DotNetVersionInfo {
this.isExactVersionSet = true;
return;
}
//Note: No support for previews when using generic
let parts = version.split('.');
const parts = version.split('.');
if (parts.length < 2 || parts.length > 3)
this.throwInvalidVersionFormat();
if (parts.length == 3 && parts[2] !== 'x' && parts[2] !== '*') {
this.throwInvalidVersionFormat();
}
let major = this.getVersionNumberOrThrow(parts[0]);
let minor = this.getVersionNumberOrThrow(parts[1]);
const major = this.getVersionNumberOrThrow(parts[0]);
const minor = ['x', '*'].includes(parts[1])
? parts[1]
: this.getVersionNumberOrThrow(parts[1]);
this.fullversion = major + '.' + minor;
}
getVersionNumberOrThrow(input) {
@ -16894,7 +16914,7 @@ class DotNetVersionInfo {
}
}
throwInvalidVersionFormat() {
throw 'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*';
throw new Error('Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*');
}
/**
* If true exacatly one version should be resolved
@ -16908,8 +16928,9 @@ class DotNetVersionInfo {
}
exports.DotNetVersionInfo = DotNetVersionInfo;
class DotnetCoreInstaller {
constructor(version) {
constructor(version, includePrerelease = false) {
this.version = version;
this.includePrerelease = includePrerelease;
}
installDotnet() {
return __awaiter(this, void 0, void 0, function* () {
@ -16997,7 +17018,7 @@ class DotnetCoreInstaller {
}
console.log(process.env['PATH']);
if (resultCode != 0) {
throw `Failed to install dotnet ${resultCode}. ${output}`;
throw new Error(`Failed to install dotnet ${resultCode}. ${output}`);
}
});
}
@ -17016,16 +17037,24 @@ class DotnetCoreInstaller {
const releasesResult = releasesResponse.result || {};
let releasesInfo = releasesResult['releases'];
releasesInfo = releasesInfo.filter((releaseInfo) => {
return (semver.satisfies(releaseInfo['sdk']['version'], versionInfo.version()) ||
semver.satisfies(releaseInfo['sdk']['version-display'], versionInfo.version()));
return (semver.satisfies(releaseInfo['sdk']['version'], versionInfo.version(), {
includePrerelease: this.includePrerelease
}) ||
semver.satisfies(releaseInfo['sdk']['version-display'], versionInfo.version(), {
includePrerelease: this.includePrerelease
}));
});
// Exclude versions that are newer than the latest if using not exact
let latestSdk = releasesResult['latest-sdk'];
releasesInfo = releasesInfo.filter((releaseInfo) => semver.lte(releaseInfo['sdk']['version'], latestSdk));
releasesInfo = releasesInfo.filter((releaseInfo) => semver.lte(releaseInfo['sdk']['version'], latestSdk, {
includePrerelease: this.includePrerelease
}));
// Sort for latest version
releasesInfo = releasesInfo.sort((a, b) => semver.rcompare(a['sdk']['version'], b['sdk']['version']));
releasesInfo = releasesInfo.sort((a, b) => semver.rcompare(a['sdk']['version'], b['sdk']['version'], {
includePrerelease: this.includePrerelease
}));
if (releasesInfo.length == 0) {
throw `Could not find dotnet core version. Please ensure that specified version ${versionInfo.inputVersion} is valid.`;
throw new Error(`Could not find dotnet core version. Please ensure that specified version ${versionInfo.inputVersion} is valid.`);
}
let release = releasesInfo[0];
return release['sdk']['version'];
@ -17046,7 +17075,7 @@ class DotnetCoreInstaller {
return versionParts[0] == sdkParts[0];
});
if (releasesInfo.length === 0) {
throw `Could not find info for version ${versionParts.join('.')} at ${DotNetCoreIndexUrl}`;
throw new Error(`Could not find info for version ${versionParts.join('.')} at ${DotNetCoreIndexUrl}`);
}
return releasesInfo[0]['releases.json'];
});

View File

@ -16,15 +16,27 @@
- LTS - most current supported release
- 2-part version in a format A.B - represents a specific release
examples: 2.0, 1.0
- Branch name
examples: release/2.0.0, Master
Note: The version parameter overrides the channel parameter.
- 3-part version in a format A.B.Cxx - represents a specific SDK release
examples: 5.0.1xx, 5.0.2xx
Supported since 5.0 release
Note: The version parameter overrides the channel parameter when any version other than 'latest' is used.
.PARAMETER Quality
Download the latest build of specified quality in the channel. The possible values are: daily, signed, validated, preview, GA.
Works only in combination with channel. Not applicable for current and LTS channels and will be ignored if those channels are used.
For SDK use channel in A.B.Cxx format: using quality together with channel in A.B format is not supported.
Supported since 5.0 release.
Note: The version parameter overrides the channel parameter when any version other than 'latest' is used, and therefore overrides the quality.
.PARAMETER Version
Default: latest
Represents a build version on specific channel. Possible values:
- latest - most latest build on specific channel
- 3-part version in a format A.B.C - represents specific version of build
examples: 2.0.0-preview2-006120, 1.1.0
.PARAMETER Internal
Download internal builds. Requires providing credentials via -FeedCredential parameter.
.PARAMETER FeedCredential
Token to access Azure feed. Used as a query string to append to the Azure feed.
This parameter typically is not specified.
.PARAMETER InstallDir
Default: %LocalAppData%\Microsoft\dotnet
Path to where to install dotnet. Note that binaries will be placed directly in a given directory.
@ -59,9 +71,6 @@
.PARAMETER UncachedFeed
This parameter typically is not changed by the user.
It allows changing the URL for the Uncached feed used by this installer.
.PARAMETER FeedCredential
Used as a query string to append to the Azure feed.
It allows changing the URL to use non-public blob storage accounts.
.PARAMETER ProxyAddress
If set, the installer will use the proxy when making web requests
.PARAMETER ProxyUseDefaultCredentials
@ -81,11 +90,12 @@
[cmdletbinding()]
param(
[string]$Channel="LTS",
[string]$Quality,
[string]$Version="Latest",
[switch]$Internal,
[string]$JSonFile,
[string]$InstallDir="<auto>",
[string]$Architecture="<auto>",
[ValidateSet("dotnet", "aspnetcore", "windowsdesktop", IgnoreCase = $false)]
[string]$Runtime,
[Obsolete("This parameter may be removed in a future version of this script. The recommended alternative is '-Runtime dotnet'.")]
[switch]$SharedRuntime,
@ -205,7 +215,7 @@ function Get-Machine-Architecture() {
function Get-CLIArchitecture-From-Architecture([string]$Architecture) {
Say-Invocation $MyInvocation
switch ($Architecture.ToLower()) {
switch ($Architecture.ToLowerInvariant()) {
{ $_ -eq "<auto>" } { return Get-CLIArchitecture-From-Architecture $(Get-Machine-Architecture) }
{ ($_ -eq "amd64") -or ($_ -eq "x64") } { return "x64" }
{ $_ -eq "x86" } { return "x86" }
@ -215,6 +225,53 @@ function Get-CLIArchitecture-From-Architecture([string]$Architecture) {
}
}
function Get-NormalizedQuality([string]$Quality) {
Say-Invocation $MyInvocation
if ([string]::IsNullOrEmpty($Quality)) {
return ""
}
switch ($Quality) {
{ @("daily", "signed", "validated", "preview") -contains $_ } { return $Quality.ToLowerInvariant() }
#ga quality is available without specifying quality, so normalizing it to empty
{ $_ -eq "ga" } { return "" }
default { throw "'$Quality' is not a supported value for -Quality option. Supported values are: daily, signed, validated, preview, ga. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues." }
}
}
function Get-NormalizedChannel([string]$Channel) {
Say-Invocation $MyInvocation
if ([string]::IsNullOrEmpty($Channel)) {
return ""
}
if ($Channel.StartsWith('release/')) {
Say-Warning 'Using branch name with -Channel option is no longer supported with newer releases. Use -Quality option with a channel in X.Y format instead, such as "-Channel 5.0 -Quality Daily."'
}
switch ($Channel) {
{ $_ -eq "lts" } { return "LTS" }
{ $_ -eq "current" } { return "current" }
default { return $Channel.ToLowerInvariant() }
}
}
function Get-NormalizedProduct([string]$Runtime) {
Say-Invocation $MyInvocation
switch ($Runtime) {
{ $_ -eq "dotnet" } { return "dotnet-runtime" }
{ $_ -eq "aspnetcore" } { return "aspnetcore-runtime" }
{ $_ -eq "windowsdesktop" } { return "windowsdesktop-runtime" }
{ [string]::IsNullOrEmpty($_) } { return "dotnet-sdk" }
default { throw "'$Runtime' is not a supported value for -Runtime option, supported values are: dotnet, aspnetcore, windowsdesktop. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues." }
}
}
# The version text returned from the feeds is a 1-line or 2-line string:
# For the SDK and the dotnet runtime (2 lines):
# Line 1: # commit_hash
@ -243,7 +300,7 @@ function Load-Assembly([string] $Assembly) {
}
}
function GetHTTPResponse([Uri] $Uri)
function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect, [bool]$DisableFeedCredential)
{
Invoke-With-Retry(
{
@ -270,26 +327,42 @@ function GetHTTPResponse([Uri] $Uri)
}
}
$HttpClientHandler = New-Object System.Net.Http.HttpClientHandler
if($ProxyAddress) {
$HttpClientHandler = New-Object System.Net.Http.HttpClientHandler
$HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{
Address=$ProxyAddress;
UseDefaultCredentials=$ProxyUseDefaultCredentials;
BypassList = $ProxyBypassList;
}
$HttpClient = New-Object System.Net.Http.HttpClient -ArgumentList $HttpClientHandler
}
if ($DisableRedirect)
{
$HttpClientHandler.AllowAutoRedirect = $false
}
else {
$HttpClient = New-Object System.Net.Http.HttpClient -ArgumentList $HttpClientHandler
$HttpClient = New-Object System.Net.Http.HttpClient
}
# Default timeout for HttpClient is 100s. For a 50 MB download this assumes 500 KB/s average, any less will time out
# 20 minutes allows it to work over much slower connections.
$HttpClient.Timeout = New-TimeSpan -Minutes 20
$Task = $HttpClient.GetAsync("${Uri}${FeedCredential}").ConfigureAwait("false");
if ($HeaderOnly){
$completionOption = [System.Net.Http.HttpCompletionOption]::ResponseHeadersRead
}
else {
$completionOption = [System.Net.Http.HttpCompletionOption]::ResponseContentRead
}
if ($DisableFeedCredential) {
$UriWithCredential = $Uri
}
else {
$UriWithCredential = "${Uri}${FeedCredential}"
}
$Task = $HttpClient.GetAsync("$UriWithCredential", $completionOption).ConfigureAwait("false");
$Response = $Task.GetAwaiter().GetResult();
if (($null -eq $Response) -or (-not ($Response.IsSuccessStatusCode))) {
if (($null -eq $Response) -or ((-not $HeaderOnly) -and (-not ($Response.IsSuccessStatusCode)))) {
# The feed credential is potentially sensitive info. Do not log FeedCredential to console output.
$DownloadException = [System.Exception] "Unable to download $Uri."
@ -349,6 +422,9 @@ function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel) {
else {
throw "Invalid value for `$Runtime"
}
Say-Verbose "Constructed latest.version URL: $VersionFileUrl"
try {
$Response = GetHTTPResponse -Uri $VersionFileUrl
}
@ -411,7 +487,7 @@ function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$Channel,
Say-Invocation $MyInvocation
if (-not $JSonFile) {
if ($Version.ToLower() -eq "latest") {
if ($Version.ToLowerInvariant() -eq "latest") {
$LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel
return $LatestVersionInfo.Version
}
@ -478,58 +554,116 @@ function Get-LegacyDownload-Link([string]$AzureFeed, [string]$SpecificVersion, [
return $PayloadURL
}
function Get-Product-Version([string]$AzureFeed, [string]$SpecificVersion) {
function Get-Product-Version([string]$AzureFeed, [string]$SpecificVersion, [string]$PackageDownloadLink) {
Say-Invocation $MyInvocation
if ($Runtime -eq "dotnet") {
$ProductVersionTxtURL = "$AzureFeed/Runtime/$SpecificVersion/productVersion.txt"
}
elseif ($Runtime -eq "aspnetcore") {
$ProductVersionTxtURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/productVersion.txt"
}
elseif ($Runtime -eq "windowsdesktop") {
# The windows desktop runtime is part of the core runtime layout prior to 5.0
$ProductVersionTxtURL = "$AzureFeed/Runtime/$SpecificVersion/productVersion.txt"
if ($SpecificVersion -match '^(\d+)\.(.*)')
{
$majorVersion = [int]$Matches[1]
if ($majorVersion -ge 5)
{
$ProductVersionTxtURL = "$AzureFeed/WindowsDesktop/$SpecificVersion/productVersion.txt"
# Try to get the version number, using the productVersion.txt file located next to the installer file.
$ProductVersionTxtURLs = (Get-Product-Version-Url $AzureFeed $SpecificVersion $PackageDownloadLink -Flattened $true),
(Get-Product-Version-Url $AzureFeed $SpecificVersion $PackageDownloadLink -Flattened $false)
Foreach ($ProductVersionTxtURL in $ProductVersionTxtURLs) {
Say-Verbose "Checking for the existence of $ProductVersionTxtURL"
try {
$productVersionResponse = GetHTTPResponse($productVersionTxtUrl)
if ($productVersionResponse.StatusCode -eq 200) {
$productVersion = $productVersionResponse.Content.ReadAsStringAsync().Result.Trim()
if ($productVersion -ne $SpecificVersion)
{
Say "Using alternate version $productVersion found in $ProductVersionTxtURL"
}
return $productVersion
}
else {
Say-Verbose "Got StatusCode $($productVersionResponse.StatusCode) when trying to get productVersion.txt at $productVersionTxtUrl."
}
}
catch {
Say-Verbose "Could not read productVersion.txt at $productVersionTxtUrl (Exception: '$($_.Exception.Message)'. )"
}
}
elseif (-not $Runtime) {
$ProductVersionTxtURL = "$AzureFeed/Sdk/$SpecificVersion/productVersion.txt"
}
else {
throw "Invalid value '$Runtime' specified for `$Runtime"
# Getting the version number with productVersion.txt has failed. Try parsing the download link for a version number.
if ([string]::IsNullOrEmpty($PackageDownloadLink))
{
Say-Verbose "Using the default value '$SpecificVersion' as the product version."
return $SpecificVersion
}
Say-Verbose "Checking for existence of $ProductVersionTxtURL"
$productVersion = Get-ProductVersionFromDownloadLink $PackageDownloadLink $SpecificVersion
return $productVersion
}
try {
$productVersionResponse = GetHTTPResponse($productVersionTxtUrl)
function Get-Product-Version-Url([string]$AzureFeed, [string]$SpecificVersion, [string]$PackageDownloadLink, [bool]$Flattened) {
Say-Invocation $MyInvocation
if ($productVersionResponse.StatusCode -eq 200) {
$productVersion = $productVersionResponse.Content.ReadAsStringAsync().Result.Trim()
if ($productVersion -ne $SpecificVersion)
{
Say "Using alternate version $productVersion found in $ProductVersionTxtURL"
}
$majorVersion=$null
if ($SpecificVersion -match '^(\d+)\.(.*)') {
$majorVersion = $Matches[1] -as[int]
}
return $productVersion
$pvFileName='productVersion.txt'
if($Flattened) {
if(-not $Runtime) {
$pvFileName='sdk-productVersion.txt'
}
elseif($Runtime -eq "dotnet") {
$pvFileName='runtime-productVersion.txt'
}
else {
Say-Verbose "Got StatusCode $($productVersionResponse.StatusCode) trying to get productVersion.txt at $productVersionTxtUrl, so using default value of $SpecificVersion"
$productVersion = $SpecificVersion
$pvFileName="$Runtime-productVersion.txt"
}
} catch {
Say-Verbose "Could not read productVersion.txt at $productVersionTxtUrl, so using default value of $SpecificVersion (Exception: '$($_.Exception.Message)' )"
$productVersion = $SpecificVersion
}
return $productVersion
if ([string]::IsNullOrEmpty($PackageDownloadLink)) {
if ($Runtime -eq "dotnet") {
$ProductVersionTxtURL = "$AzureFeed/Runtime/$SpecificVersion/$pvFileName"
}
elseif ($Runtime -eq "aspnetcore") {
$ProductVersionTxtURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/$pvFileName"
}
elseif ($Runtime -eq "windowsdesktop") {
# The windows desktop runtime is part of the core runtime layout prior to 5.0
$ProductVersionTxtURL = "$AzureFeed/Runtime/$SpecificVersion/$pvFileName"
if ($majorVersion -ne $null -and $majorVersion -ge 5) {
$ProductVersionTxtURL = "$AzureFeed/WindowsDesktop/$SpecificVersion/$pvFileName"
}
}
elseif (-not $Runtime) {
$ProductVersionTxtURL = "$AzureFeed/Sdk/$SpecificVersion/$pvFileName"
}
else {
throw "Invalid value '$Runtime' specified for `$Runtime"
}
}
else {
$ProductVersionTxtURL = $PackageDownloadLink.Substring(0, $PackageDownloadLink.LastIndexOf("/")) + "/$pvFileName"
}
Say-Verbose "Constructed productVersion link: $ProductVersionTxtURL"
return $ProductVersionTxtURL
}
function Get-ProductVersionFromDownloadLink([string]$PackageDownloadLink, [string]$SpecificVersion)
{
Say-Invocation $MyInvocation
#product specific version follows the product name
#for filename 'dotnet-sdk-3.1.404-win-x64.zip': the product version is 3.1.400
$filename = $PackageDownloadLink.Substring($PackageDownloadLink.LastIndexOf("/") + 1)
$filenameParts = $filename.Split('-')
if ($filenameParts.Length -gt 2)
{
$productVersion = $filenameParts[2]
Say-Verbose "Extracted product version '$productVersion' from download link '$PackageDownloadLink'."
}
else {
Say-Verbose "Using the default value '$SpecificVersion' as the product version."
$productVersion = $SpecificVersion
}
return $productVersion
}
function Get-User-Share-Path() {
@ -702,15 +836,150 @@ function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot, [string]$BinFolde
}
}
function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Internal, [string]$Product, [string]$Architecture) {
Say-Invocation $MyInvocation
#quality is not supported for LTS or current channel
if (![string]::IsNullOrEmpty($Quality) -and (@("LTS", "current") -contains $Channel)) {
$Quality = ""
Say-Warning "Specifying quality for current or LTS channel is not supported, the quality will be ignored."
}
Say-Verbose "Retrieving primary payload URL from aka.ms link for channel: '$Channel', quality: '$Quality' product: '$Product', os: 'win', architecture: '$Architecture'."
#construct aka.ms link
$akaMsLink = "https://aka.ms/dotnet"
if ($Internal) {
$akaMsLink += "/internal"
}
$akaMsLink += "/$Channel"
if (-not [string]::IsNullOrEmpty($Quality)) {
$akaMsLink +="/$Quality"
}
$akaMsLink +="/$Product-win-$Architecture.zip"
Say-Verbose "Constructed aka.ms link: '$akaMsLink'."
$akaMsDownloadLink=$null
for ($maxRedirections = 9; $maxRedirections -ge 0; $maxRedirections--)
{
#get HTTP response
#do not pass credentials as a part of the $akaMsLink and do not apply credentials in the GetHTTPResponse function
#otherwise the redirect link would have credentials as well
#it would result in applying credentials twice to the resulting link and thus breaking it, and in echoing credentials to the output as a part of redirect link
$Response= GetHTTPResponse -Uri $akaMsLink -HeaderOnly $true -DisableRedirect $true -DisableFeedCredential $true
Say-Verbose "Received response:`n$Response"
if ([string]::IsNullOrEmpty($Response)) {
Say-Verbose "The link '$akaMsLink' is not valid: failed to get redirect location. The resource is not available."
return $null
}
#if HTTP code is 301 (Moved Permanently), the redirect link exists
if ($Response.StatusCode -eq 301)
{
try {
$akaMsDownloadLink = $Response.Headers.GetValues("Location")[0]
if ([string]::IsNullOrEmpty($akaMsDownloadLink)) {
Say-Verbose "The link '$akaMsLink' is not valid: server returned 301 (Moved Permanently), but the headers do not contain the redirect location."
return $null
}
Say-Verbose "The redirect location retrieved: '$akaMsDownloadLink'."
# This may yet be a link to another redirection. Attempt to retrieve the page again.
$akaMsLink = $akaMsDownloadLink
continue
}
catch {
Say-Verbose "The link '$akaMsLink' is not valid: failed to get redirect location."
return $null
}
}
elseif ((($Response.StatusCode -lt 300) -or ($Response.StatusCode -ge 400)) -and (-not [string]::IsNullOrEmpty($akaMsDownloadLink)))
{
# Redirections have ended.
return $akaMsDownloadLink
}
Say-Verbose "The link '$akaMsLink' is not valid: failed to retrieve the redirection location."
return $null
}
Say-Verbose "Aka.ms links have redirected more than the maximum allowed redirections. This may be caused by a cyclic redirection of aka.ms links."
return $null
}
Say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:"
Say "- The SDK needs to be installed without user interaction and without admin rights."
Say "- The SDK installation doesn't need to persist across multiple CI runs."
Say "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.`r`n"
if ($Internal -and [string]::IsNullOrWhitespace($FeedCredential)) {
$message = "Provide credentials via -FeedCredential parameter."
if ($DryRun) {
Say-Warning "$message"
} else {
throw "$message"
}
}
#FeedCredential should start with "?", for it to be added to the end of the link.
#adding "?" at the beginning of the FeedCredential if needed.
if ((![string]::IsNullOrWhitespace($FeedCredential)) -and ($FeedCredential[0] -ne '?')) {
$FeedCredential = "?" + $FeedCredential
}
$CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture
$SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version -JSonFile $JSonFile
$DownloadLink, $EffectiveVersion = Get-Download-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
$LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
$NormalizedQuality = Get-NormalizedQuality $Quality
Say-Verbose "Normalized quality: '$NormalizedQuality'"
$NormalizedChannel = Get-NormalizedChannel $Channel
Say-Verbose "Normalized channel: '$NormalizedChannel'"
$NormalizedProduct = Get-NormalizedProduct $Runtime
Say-Verbose "Normalized product: '$NormalizedProduct'"
$DownloadLink = $null
#try to get download location from aka.ms link
#not applicable when exact version is specified via command or json file
if ([string]::IsNullOrEmpty($JSonFile) -and ($Version -eq "latest")) {
$AkaMsDownloadLink = Get-AkaMSDownloadLink -Channel $NormalizedChannel -Quality $NormalizedQuality -Internal $Internal -Product $NormalizedProduct -Architecture $CLIArchitecture
if ([string]::IsNullOrEmpty($AkaMsDownloadLink)){
if (-not [string]::IsNullOrEmpty($NormalizedQuality)) {
# if quality is specified - exit with error - there is no fallback approach
Say-Error "Failed to locate the latest version in the channel '$NormalizedChannel' with '$NormalizedQuality' quality for '$NormalizedProduct', os: 'win', architecture: '$CLIArchitecture'."
Say-Error "Refer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support."
throw "aka.ms link resolution failure"
}
Say-Verbose "Falling back to latest.version file approach."
}
else {
Say-Verbose "Retrieved primary named payload URL from aka.ms link: '$AkaMsDownloadLink'."
$DownloadLink = $AkaMsDownloadLink
Say-Verbose "Downloading using legacy url will not be attempted."
$LegacyDownloadLink = $null
#get version from the path
$pathParts = $DownloadLink.Split('/')
if ($pathParts.Length -ge 2) {
$SpecificVersion = $pathParts[$pathParts.Length - 2]
Say-Verbose "Version: '$SpecificVersion'."
}
else {
Say-Error "Failed to extract the version from download link '$DownloadLink'."
}
#retrieve effective (product) version
$EffectiveVersion = Get-Product-Version -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -PackageDownloadLink $DownloadLink
Say-Verbose "Product version: '$EffectiveVersion'."
}
}
if ([string]::IsNullOrEmpty($DownloadLink)) {
$SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version -JSonFile $JSonFile
$DownloadLink, $EffectiveVersion = Get-Download-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
$LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
}
$InstallRoot = Resolve-Installation-Path $InstallDir
Say-Verbose "InstallRoot: $InstallRoot"
@ -718,9 +987,9 @@ $ScriptName = $MyInvocation.MyCommand.Name
if ($DryRun) {
Say "Payload URLs:"
Say "Primary named payload URL: $DownloadLink"
Say "Primary named payload URL: ${DownloadLink}"
if ($LegacyDownloadLink) {
Say "Legacy named payload URL: $LegacyDownloadLink"
Say "Legacy named payload URL: ${LegacyDownloadLink}"
}
$RepeatableCommand = ".\$ScriptName -Version `"$SpecificVersion`" -InstallDir `"$InstallRoot`" -Architecture `"$CLIArchitecture`""
if ($Runtime -eq "dotnet") {
@ -729,11 +998,20 @@ if ($DryRun) {
elseif ($Runtime -eq "aspnetcore") {
$RepeatableCommand+=" -Runtime `"aspnetcore`""
}
if (-not [string]::IsNullOrEmpty($NormalizedQuality))
{
$RepeatableCommand+=" -Quality `"$NormalizedQuality`""
}
foreach ($key in $MyInvocation.BoundParameters.Keys) {
if (-not (@("Architecture","Channel","DryRun","InstallDir","Runtime","SharedRuntime","Version") -contains $key)) {
if (-not (@("Architecture","Channel","DryRun","InstallDir","Runtime","SharedRuntime","Version","Quality","FeedCredential") -contains $key)) {
$RepeatableCommand+=" -$key `"$($MyInvocation.BoundParameters[$key])`""
}
}
if ($MyInvocation.BoundParameters.Keys -contains "FeedCredential") {
$RepeatableCommand+=" -FeedCredential `"<feedCredential>`""
}
Say "Repeatable invocation: $RepeatableCommand"
if ($SpecificVersion -ne $EffectiveVersion)
{
@ -779,9 +1057,16 @@ if ($isAssetInstalled) {
New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null
$installDrive = $((Get-Item $InstallRoot).PSDrive.Name);
$diskInfo = Get-PSDrive -Name $installDrive
if ($diskInfo.Free / 1MB -le 100) {
$installDrive = $((Get-Item $InstallRoot -Force).PSDrive.Name);
$diskInfo = $null
try{
$diskInfo = Get-PSDrive -Name $installDrive
}
catch{
Say-Warning "Failed to check the disk space. Installation will continue, but it may fail if you do not have enough disk space."
}
if ( ($diskInfo -ne $null) -and ($diskInfo.Free / 1MB -le 100)) {
throw "There is not enough disk space on drive ${installDrive}:"
}
@ -902,42 +1187,42 @@ Say "Note that the script does not resolve dependencies during installation."
Say "To check the list of dependencies, go to https://docs.microsoft.com/dotnet/core/install/windows#dependencies"
Say "Installation finished"
# SIG # Begin signature block
# MIIjjwYJKoZIhvcNAQcCoIIjgDCCI3wCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCNsnhcJvx/hXmM
# w8KjuvvIMDBFonhg9XJFc1QwfTyH4aCCDYEwggX/MIID56ADAgECAhMzAAABh3IX
# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAGF/0aIv/nK8pL
# ube4cwR/nwps7FSM0D5vjXxuQe3LXaCCDYEwggX/MIID56ADAgECAhMzAAAB32vw
# LpKnSrTQAAAAAAHfMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw
# bmcgUENBIDIwMTEwHhcNMjAxMjE1MjEzMTQ1WhcNMjExMjAyMjEzMTQ1WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB
# znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH
# sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d
# weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ
# itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV
# Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw
# AQC2uxlZEACjqfHkuFyoCwfL25ofI9DZWKt4wEj3JBQ48GPt1UsDv834CcoUUPMn
# s/6CtPoaQ4Thy/kbOOg/zJAnrJeiMQqRe2Lsdb/NSI2gXXX9lad1/yPUDOXo4GNw
# PjXq1JZi+HZV91bUr6ZjzePj1g+bepsqd/HC1XScj0fT3aAxLRykJSzExEBmU9eS
# yuOwUuq+CriudQtWGMdJU650v/KmzfM46Y6lo/MCnnpvz3zEL7PMdUdwqj/nYhGG
# 3UVILxX7tAdMbz7LN+6WOIpT1A41rwaoOVnv+8Ua94HwhjZmu1S73yeV7RZZNxoh
# EegJi9YYssXa7UZUUkCCA+KnAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUOPbML8IdkNGtCfMmVPtvI6VZ8+Mw
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDYzMDA5MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy
# S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K
# NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV
# BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr
# qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx
# zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe
# yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g
# yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf
# AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI
# 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5
# GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea
# jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAnnqH
# tDyYUFaVAkvAK0eqq6nhoL95SZQu3RnpZ7tdQ89QR3++7A+4hrr7V4xxmkB5BObS
# 0YK+MALE02atjwWgPdpYQ68WdLGroJZHkbZdgERG+7tETFl3aKF4KpoSaGOskZXp
# TPnCaMo2PXoAMVMGpsQEQswimZq3IQ3nRQfBlJ0PoMMcN/+Pks8ZTL1BoPYsJpok
# t6cql59q6CypZYIwgyJ892HpttybHKg1ZtQLUlSXccRMlugPgEcNZJagPEgPYni4
# b11snjRAgf0dyQ0zI9aLXqTxWUU5pCIFiPT0b2wsxzRqCtyGqpkGM8P9GazO8eao
# mVItCYBcJSByBx/pS0cSYwBBHAZxJODUqxSXoSGDvmTfqUJXntnWkL4okok1FiCD
# Z4jpyXOQunb6egIXvkgQ7jb2uO26Ow0m8RwleDvhOMrnHsupiOPbozKroSa6paFt
# VSh89abUSooR8QdZciemmoFhcWkEwFg4spzvYNP4nIs193261WyTaRMZoceGun7G
# CT2Rl653uUj+F+g94c63AhzSq4khdL4HlFIP2ePv29smfUnHtGq6yYFDLnT0q/Y+
# Di3jwloF8EWkkHRtSuXlFUbTmwr/lDDgbpZiKhLS7CBTDj32I0L5i532+uHczw82
# oZDmYmYmIUSMbZOgS65h797rj5JJ6OkeEUJoAVwwggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
@ -977,55 +1262,55 @@ Say "Installation finished"
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZDCCFWACAQEwgZUwfjELMAkG
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAd9r8C6Sp0q00AAAAAAB3zAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgpT/bxWwe
# aW0EinKMWCAzDXUjwXkIHldYzR6lw4/1Pc0wQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgJROF4V78
# d/qQulLV5Z+ncgFZgk9r0UoE37o5jTCDpSowQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQCHd7sSQVq0YDg8QDx6/kLWn3s6jtvvIDCCgsO9spHM
# quPd4FPbG67DCsKDClekQs52qrtRO3Zo+JMnCw4j3bS+gZHzeJr2shbftOrpsFoD
# l7OPcUmtrqul9dkQCOp8t0MP3ls0n96/YyNy6lz4BAlTdkdDx957uAxalKaCIBzb
# R9QyppOKIfNFvwD4EI5KI6tpmSy/uH8SrRg7ZExAYZl6J6R18WkL7KHn649lPoAQ
# ujwrIXH10xOJops45ILGzKWQcHmCzLJGYapL4VHUuK+73nT+9ZROGHdk/PyvIcdw
# iERa+C06v305t3DA+CuHFy1tvyw7IFF6RVbLZPwxrJjToYIS7jCCEuoGCisGAQQB
# gjcDAwExghLaMIIS1gYJKoZIhvcNAQcCoIISxzCCEsMCAQMxDzANBglghkgBZQME
# BgkqhkiG9w0BAQEFAASCAQB74lkh+M6W7qWNERtX5/xMFrSgaVCptxuBlBl93FN3
# aQJiCaToMeDXG3Nsf/GhIFCBz4fReYrMmZg5pmjs+d8s1dgXz2oOWVwx6bhnzdXS
# ce5dMqkUCzvN4QeJxTIhpeXsHwupWVbuqeTo5DdZHouTi4UFzgM0/K+sSbRI/FEe
# rS/vxtMbrtMz57kKjB/Z86dKJeDxQydKSapL/YIamfmYx26rjpXJqEs7W+FVRJwK
# nNNT0OGt7tdkL6k+7XQtK3f5yUqKMdTlmPyZ73z69tAtJclkIFC3liFVTMAf4MK+
# nheQal7OPNth86h4Lek3E19n2hrABTUeW2cLZ2WWEspFoYIS8TCCEu0GCisGAQQB
# gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME
# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEIOCaTmvM1AP0WaEVqzKaaCu/R+bTlR4kCrM/ZXsb
# /eNOAgZgGeLsMwsYEzIwMjEwMjAzMjExNzQ5LjU5MVowBIACAfSggdSkgdEwgc4x
# MDEwDQYJYIZIAWUDBAIBBQAEIEIDAx5adVXZRJeNOj9JW7M03Sl8XvjtY9M98TvW
# cUwSAgZgieWmNfwYEzIwMjEwNTE5MTcwMDI4LjY0OVowBIACAfSggdSkgdEwgc4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p
# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjo4OTdBLUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZaCCDkEwggT1MIID3aADAgECAhMzAAABLCKvRZd1+RvuAAAA
# AAEsMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# VFNTIEVTTjpEOURFLUUzOUEtNDNGRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABYfWiM16gKiRpAAAA
# AAFhMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# MB4XDTE5MTIxOTAxMTUwM1oXDTIxMDMxNzAxMTUwM1owgc4xCzAJBgNVBAYTAlVT
# MB4XDTIxMDExNDE5MDIyMVoXDTIyMDQxMTE5MDIyMVowgc4xCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy
# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4OTdB
# LUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj
# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPK1zgSSq+MxAYo3qpCt
# QDxSMPPJy6mm/wfEJNjNUnYtLFBwl1BUS5trEk/t41ldxITKehs+ABxYqo4Qxsg3
# Gy1ugKiwHAnYiiekfC+ZhptNFgtnDZIn45zC0AlVr/6UfLtsLcHCh1XElLUHfEC0
# nBuQcM/SpYo9e3l1qY5NdMgDGxCsmCKdiZfYXIu+U0UYIBhdzmSHnB3fxZOBVcr5
# htFHEBBNt/rFJlm/A4yb8oBsp+Uf0p5QwmO/bCcdqB15JpylOhZmWs0sUfJKlK9E
# rAhBwGki2eIRFKsQBdkXS9PWpF1w2gIJRvSkDEaCf+lbGTPdSzHSbfREWOF9wY3i
# Yj8CAwEAAaOCARswggEXMB0GA1UdDgQWBBRRahZSGfrCQhCyIyGH9DkiaW7L0zAf
# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpEOURF
# LUUzOUEtNDNGRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj
# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJeInahBrU//GzTqhxUy
# AC8UXct6UJCkb2xEZKV3gjggmLAheBrxJk7tH+Pw2tTcyarLRfmV2xo5oBk5pW/O
# cDc/n/TcTeQU6JIN5PlTcn0C9RlKQ6t9OuU/WAyAxGTjKE4ENnUjXtxiNlD/K2ZG
# MLvjpROBKh7TtkUJK6ZGWw/uTRabNBxRg13TvjkGHXEUEDJ8imacw9BCeR9L6und
# r32tj4duOFIHD8m1es3SNN98Zq4IDBP3Ccb+HQgxpbeHIUlK0y6zmzIkvfN73Zxw
# fGvFv0/Max79WJY0cD8poCnZFijciWrf0eD1T2/+7HgewzrdxPdSFockUQ8QovID
# IYkCAwEAAaOCARswggEXMB0GA1UdDgQWBBRWHpqd1hv71SVj5LAdPfNE7PhLLzAf
# BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH
# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU
# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF
# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0
# YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG
# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQBPFxHIwi4vAH49w9Svmz6K3tM55RlW
# 5pPeULXdut2Rqy6Ys0+VpZsbuaEoxs6Z1C3hMbkiqZFxxyltxJpuHTyGTg61zfNI
# F5n6RsYF3s7IElDXNfZznF1/2iWc6uRPZK8rxxUJ/7emYXZCYwuUY0XjsCpP9pbR
# RKeJi6r5arSyI+NfKxvgoM21JNt1BcdlXuAecdd/k8UjxCscffanoK2n6LFw1PcZ
# lEO7NId7o+soM2C0QY5BYdghpn7uqopB6ixyFIIkDXFub+1E7GmAEwfU6VwEHL7y
# 9rNE8bd+JrQs+yAtkkHy9FmXg/PsGq1daVzX1So7CJ6nyphpuHSN3VfTMIIGcTCC
# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQAQTA9bqVBmx5TTMhzj+Q8zWkPQXgCc
# SQiqy2YYWF0hWr5GEiN2LtA+EWdu1y8oysZau4CP7SzM8VTSq31CLJiOy39Z4RvE
# q2mr0EftFvmX2CxQ7ZyzrkhWMZaZQLkYbH5oabIFwndW34nh980BOY395tfnNS/Y
# 6N0f+jXdoFn7fI2c43TFYsUqIPWjOHJloMektlD6/uS6Zn4xse/lItFm+fWOcB2A
# xyXEB3ZREeSg9j7+GoEl1xT/iJuV/So7TlWdwyacQu4lv3MBsvxzRIbKhZwrDYog
# moyJ+rwgQB8mKS4/M1SDRtIptamoTFJ56Tk6DuUXx1JudToelgjEZPa5MIIGcTCC
# BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv
@ -1060,36 +1345,36 @@ Say "Installation finished"
# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a
# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ
# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+
# NR4Iuto229Nfj950iEkSoYICzzCCAjgCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
# NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP
# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4
# OTdBLUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUADE5OKSMoNx/mYxYWap1RTOohbJ2ggYMwgYCk
# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpE
# OURFLUUzOUEtNDNGRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUAFW5ShAw5ekTEXvL/4V1s0rbDz3mggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOPFChkwIhgPMjAyMTAyMDMxNTQwMDlaGA8yMDIxMDIwNDE1NDAwOVowdDA6Bgor
# BgEEAYRZCgQBMSwwKjAKAgUA48UKGQIBADAHAgEAAgIXmDAHAgEAAgIRyTAKAgUA
# 48ZbmQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID
# B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAHeeznL2n6HWCjHH94Fl
# hcdW6TEXzq4XNgp1Gx1W9F8gJ4x+SwoV7elJZkwgGffcpHomLvIY/VSuzsl1NgtJ
# TWM2UxoqSv58BBOrl4eGhH6kkg8Ucy2tdeK5T8cHa8pMkq2j9pFd2mRG/6VMk0dl
# Xz7Uy3Z6bZqkcABMyAfuAaGbMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgUENBIDIwMTACEzMAAAEsIq9Fl3X5G+4AAAAAASwwDQYJYIZIAWUDBAIB
# BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx
# IgQg/QYv7yp+354WTjWUIsXWndTEzXjaYjqwYjcBxCJKjdUwgfoGCyqGSIb3DQEJ
# EAIvMYHqMIHnMIHkMIG9BCBbn/0uFFh42hTM5XOoKdXevBaiSxmYK9Ilcn9nu5ZH
# 4TCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABLCKv
# RZd1+RvuAAAAAAEsMCIEIIfIM3YbzHswb/Kj/qq1l1cHA6QBl+gEXYanUNJomrpT
# MA0GCSqGSIb3DQEBCwUABIIBAAwdcXssUZGO7ho5+NHLjIxLtQk543aKGo+lrRMY
# Q9abE1h/AaaNJl0iGxX4IihNWyfovSfYL3L4eODUBAu68tWSxeceRfWNsb/ZZfUi
# v89hpLssI/Gf1BEgNMA4zCuIGQiC8okusVumEpAhhvCEbSiTTTtBdolTnU/CAKui
# oxaU3R9XkKh1F4oAM26+dJ1J2BLQXPs5afNvvedDsZWNQUPK1sFF3JRfzxiTrwBW
# EJRyflev9gyDoqCHzippgb+6+eti1WTkcA9Q49GIT11S6LOAVqkSC9N7Nqf8ksh8
# ARdwT8jigpsm+mj7lrVU9upDkhVYhKeO8oiZq95Q53Zkteo=
# AORPamMwIhgPMjAyMTA1MTkxNDQzNDdaGA8yMDIxMDUyMDE0NDM0N1owdzA9Bgor
# BgEEAYRZCgQBMS8wLTAKAgUA5E9qYwIBADAKAgEAAgIYCAIB/zAHAgEAAgIRJjAK
# AgUA5FC74wIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAEP3zVlXXAn4O1Ms
# 68s11f0LWnsKutmWLnJls7YN9alFuA+jJcq/S8x2VoJAWAZTdWtdT5N7Gf/BeTAe
# 8D0krUvAYkKN9sIFxBsV9Zum88+HNP6X3yn+CD+ZzOlhtqvgG+fuuRXPutLZfLc/
# BMw0pVKop8ty4t9o1C5cghIH1n8uMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAFh9aIzXqAqJGkAAAAAAWEwDQYJYIZIAWUD
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
# CQQxIgQgYzAzV5OthXnPmIpD3kPIVmwz+sGvwSbjTEW5rOqHhyUwgfoGCyqGSIb3
# DQEJEAIvMYHqMIHnMIHkMIG9BCBhz4un6mkSLd/zA+0N5YLDGp4vW/VBtNW/lpmh
# tAk4bzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
# YfWiM16gKiRpAAAAAAFhMCIEIArbvJb5j55rDAQa7Rx5K7mjnxUY6RgiRY1AROOp
# m2rLMA0GCSqGSIb3DQEBCwUABIIBAFS544uO7FOimzTwMDkFNhMT7GmZBc9L2wDF
# EjcDlHvcdELdWpqa8Au6rCdhW6618btwdFqR7fkNMH7F0TrY1ktOae5fZUaybIRV
# EMWAAqXLkVyePlssQamjt1+BYvxzYFh3T5JBr2R/rzaGJIck49r1cEsLBV0JaSQT
# QKiOsR+QEbSCNWXQaq/koJrKR5Ugro0y4SmSejnOj5+1/4PlTolFoJAM4pIiT2A1
# uMs5f219BvkwyRmF32z9EpYQvBdoGpT65DQDuZQ5F6wV1Ph4H7yOzarcdnwPohT+
# BeH2jrcR3BMAxC3umgRUmTxnzPsoq9FJuRTyHCNygGn+VMImNfk=
# SIG # End signature block

View File

@ -24,7 +24,7 @@ exec 3>&1
# See if stdout is a terminal
if [ -t 1 ] && command -v tput > /dev/null; then
# see if it supports colors
ncolors=$(tput colors)
ncolors=$(tput colors || echo 0)
if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then
bold="$(tput bold || echo)"
normal="$(tput sgr0 || echo)"
@ -368,6 +368,75 @@ get_normalized_os() {
return 0
}
# args:
# quality - $1
get_normalized_quality() {
eval $invocation
local quality="$(to_lowercase "$1")"
if [ ! -z "$quality" ]; then
case "$quality" in
daily | signed | validated | preview)
echo "$quality"
return 0
;;
ga)
#ga quality is available without specifying quality, so normalizing it to empty
return 0
;;
*)
say_err "'$quality' is not a supported value for --quality option. Supported values are: daily, signed, validated, preview, ga. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues."
return 1
;;
esac
fi
return 0
}
# args:
# channel - $1
get_normalized_channel() {
eval $invocation
local channel="$(to_lowercase "$1")"
if [[ $channel == release/* ]]; then
say_warning 'Using branch name with -Channel option is no longer supported with newer releases. Use -Quality option with a channel in X.Y format instead.';
fi
if [ ! -z "$channel" ]; then
case "$channel" in
lts)
echo "LTS"
return 0
;;
*)
echo "$channel"
return 0
;;
esac
fi
return 0
}
# args:
# runtime - $1
get_normalized_product() {
eval $invocation
local runtime="$(to_lowercase "$1")"
if [[ "$runtime" == "dotnet" ]]; then
product="dotnet-runtime"
elif [[ "$runtime" == "aspnetcore" ]]; then
product="aspnetcore-runtime"
elif [ -z "$runtime" ]; then
product="dotnet-sdk"
fi
echo "$product"
return 0
}
# The version text returned from the feeds is a 1-line or 2-line string:
# For the SDK and the dotnet runtime (2 lines):
# Line 1: # commit_hash
@ -541,43 +610,131 @@ construct_download_link() {
# args:
# azure_feed - $1
# specific_version - $2
# download link - $3 (optional)
get_specific_product_version() {
# If we find a 'productVersion.txt' at the root of any folder, we'll use its contents
# to resolve the version of what's in the folder, superseding the specified version.
# if 'productVersion.txt' is missing but download link is already available, product version will be taken from download link
eval $invocation
local azure_feed="$1"
local specific_version="${2//[$'\t\r\n']}"
local specific_product_version=$specific_version
local package_download_link=""
if [ $# -gt 2 ]; then
local package_download_link="$3"
fi
local specific_product_version=null
# Try to get the version number, using the productVersion.txt file located next to the installer file.
local download_links=($(get_specific_product_version_url "$azure_feed" "$specific_version" true "$package_download_link")
$(get_specific_product_version_url "$azure_feed" "$specific_version" false "$package_download_link"))
for download_link in "${download_links[@]}"
do
say_verbose "Checking for the existence of $download_link"
if machine_has "curl"
then
specific_product_version=$(curl -s --fail "${download_link}${feed_credential}")
if [ $? = 0 ]; then
echo "${specific_product_version//[$'\t\r\n']}"
return 0
fi
elif machine_has "wget"
then
specific_product_version=$(wget -qO- "${download_link}${feed_credential}")
if [ $? = 0 ]; then
echo "${specific_product_version//[$'\t\r\n']}"
return 0
fi
fi
done
# Getting the version number with productVersion.txt has failed. Try parsing the download link for a version number.
say_verbose "Failed to get the version using productVersion.txt file. Download link will be parsed instead."
specific_product_version="$(get_product_specific_version_from_download_link "$package_download_link" "$specific_version")"
echo "${specific_product_version//[$'\t\r\n']}"
return 0
}
# args:
# azure_feed - $1
# specific_version - $2
# is_flattened - $3
# download link - $4 (optional)
get_specific_product_version_url() {
eval $invocation
local azure_feed="$1"
local specific_version="$2"
local is_flattened="$3"
local package_download_link=""
if [ $# -gt 3 ]; then
local package_download_link="$4"
fi
local pvFileName="productVersion.txt"
if [ "$is_flattened" = true ]; then
if [ -z "$runtime" ]; then
pvFileName="sdk-productVersion.txt"
elif [[ "$runtime" == "dotnet" ]]; then
pvFileName="runtime-productVersion.txt"
else
pvFileName="$runtime-productVersion.txt"
fi
fi
local download_link=null
if [[ "$runtime" == "dotnet" ]]; then
download_link="$azure_feed/Runtime/$specific_version/productVersion.txt${feed_credential}"
elif [[ "$runtime" == "aspnetcore" ]]; then
download_link="$azure_feed/aspnetcore/Runtime/$specific_version/productVersion.txt${feed_credential}"
elif [ -z "$runtime" ]; then
download_link="$azure_feed/Sdk/$specific_version/productVersion.txt${feed_credential}"
if [ -z "$package_download_link" ]; then
if [[ "$runtime" == "dotnet" ]]; then
download_link="$azure_feed/Runtime/$specific_version/${pvFileName}"
elif [[ "$runtime" == "aspnetcore" ]]; then
download_link="$azure_feed/aspnetcore/Runtime/$specific_version/${pvFileName}"
elif [ -z "$runtime" ]; then
download_link="$azure_feed/Sdk/$specific_version/${pvFileName}"
else
return 1
fi
else
return 1
download_link="${package_download_link%/*}/${pvFileName}"
fi
if machine_has "curl"
then
specific_product_version=$(curl -s --fail "$download_link")
if [ $? -ne 0 ]
then
specific_product_version=$specific_version
fi
elif machine_has "wget"
then
specific_product_version=$(wget -qO- "$download_link")
if [ $? -ne 0 ]
then
specific_product_version=$specific_version
fi
fi
specific_product_version="${specific_product_version//[$'\t\r\n']}"
say_verbose "Constructed productVersion link: $download_link"
echo "$download_link"
return 0
}
# args:
# download link - $1
# specific version - $2
get_product_specific_version_from_download_link()
{
eval $invocation
local download_link="$1"
local specific_version="$2"
local specific_product_version=""
if [ -z "$download_link" ]; then
echo "$specific_version"
return 0
fi
#get filename
filename="${download_link##*/}"
#product specific version follows the product name
#for filename 'dotnet-sdk-3.1.404-linux-x64.tar.gz': the product version is 3.1.404
IFS='-'
read -ra filename_elems <<< "$filename"
count=${#filename_elems[@]}
if [[ "$count" -gt 2 ]]; then
specific_product_version="${filename_elems[2]}"
else
specific_product_version=$specific_version
fi
unset IFS;
echo "$specific_product_version"
return 0
}
@ -711,19 +868,62 @@ extract_dotnet_package() {
return 0
}
# args:
# remote_path - $1
# disable_feed_credential - $2
get_http_header()
{
eval $invocation
local remote_path="$1"
local disable_feed_credential="$2"
local failed=false
local response
if machine_has "curl"; then
get_http_header_curl $remote_path $disable_feed_credential || failed=true
elif machine_has "wget"; then
get_http_header_wget $remote_path $disable_feed_credential || failed=true
else
failed=true
fi
if [ "$failed" = true ]; then
say_verbose "Failed to get HTTP header: '$remote_path'."
return 1
fi
return 0
}
# args:
# remote_path - $1
# disable_feed_credential - $2
get_http_header_curl() {
eval $invocation
local remote_path="$1"
remote_path_with_credential="${remote_path}${feed_credential}"
local disable_feed_credential="$2"
remote_path_with_credential="$remote_path"
if [ "$disable_feed_credential" = false ]; then
remote_path_with_credential+="$feed_credential"
fi
curl_options="-I -sSL --retry 5 --retry-delay 2 --connect-timeout 15 "
curl $curl_options "$remote_path_with_credential" || return 1
return 0
}
# args:
# remote_path - $1
# disable_feed_credential - $2
get_http_header_wget() {
eval $invocation
local remote_path="$1"
remote_path_with_credential="${remote_path}${feed_credential}"
local disable_feed_credential="$2"
remote_path_with_credential="$remote_path"
if [ "$disable_feed_credential" = false ]; then
remote_path_with_credential+="$feed_credential"
fi
wget_options="-q -S --spider --tries 5 --waitretry 2 --connect-timeout 15 "
wget $wget_options "$remote_path_with_credential" 2>&1 || return 1
return 0
@ -763,7 +963,7 @@ download() {
say "Download attempt #$attempts has failed: $http_code $download_error_msg"
say "Attempt #$((attempts+1)) will start in $((attempts*10)) seconds."
sleep $((attempts*20))
sleep $((attempts*10))
done
@ -783,6 +983,7 @@ downloadcurl() {
local remote_path="$1"
local out_path="${2:-}"
# Append feed_credential as late as possible before calling curl to avoid logging feed_credential
# Avoid passing URI with credentials to functions: note, most of them echoing parameters of invocation in verbose output.
local remote_path_with_credential="${remote_path}${feed_credential}"
local curl_options="--retry 20 --retry-delay 2 --connect-timeout 15 -sSL -f --create-dirs "
local failed=false
@ -792,7 +993,8 @@ downloadcurl() {
curl $curl_options -o "$out_path" "$remote_path_with_credential" || failed=true
fi
if [ "$failed" = true ]; then
local response=$(get_http_header_curl $remote_path_with_credential)
local disable_feed_credential=false
local response=$(get_http_header_curl $remote_path $disable_feed_credential)
http_code=$( echo "$response" | awk '/^HTTP/{print $2}' | tail -1 )
download_error_msg="Unable to download $remote_path."
if [[ $http_code != 2* ]]; then
@ -822,7 +1024,8 @@ downloadwget() {
wget $wget_options -O "$out_path" "$remote_path_with_credential" || failed=true
fi
if [ "$failed" = true ]; then
local response=$(get_http_header_wget $remote_path_with_credential)
local disable_feed_credential=false
local response=$(get_http_header_wget $remote_path $disable_feed_credential)
http_code=$( echo "$response" | awk '/^ HTTP/{print $2}' | tail -1 )
download_error_msg="Unable to download $remote_path."
if [[ $http_code != 2* ]]; then
@ -834,15 +1037,115 @@ downloadwget() {
return 0
}
get_download_link_from_aka_ms() {
eval $invocation
#quality is not supported for LTS or current channel
if [[ ! -z "$normalized_quality" && ("$normalized_channel" == "LTS" || "$normalized_channel" == "current") ]]; then
normalized_quality=""
say_warning "Specifying quality for current or LTS channel is not supported, the quality will be ignored."
fi
say_verbose "Retrieving primary payload URL from aka.ms for channel: '$normalized_channel', quality: '$normalized_quality', product: '$normalized_product', os: '$normalized_os', architecture: '$normalized_architecture'."
#construct aka.ms link
aka_ms_link="https://aka.ms/dotnet"
if [ "$internal" = true ]; then
aka_ms_link="$aka_ms_link/internal"
fi
aka_ms_link="$aka_ms_link/$normalized_channel"
if [[ ! -z "$normalized_quality" ]]; then
aka_ms_link="$aka_ms_link/$normalized_quality"
fi
aka_ms_link="$aka_ms_link/$normalized_product-$normalized_os-$normalized_architecture.tar.gz"
say_verbose "Constructed aka.ms link: '$aka_ms_link'."
#get HTTP response
#do not pass credentials as a part of the $aka_ms_link and do not apply credentials in the get_http_header function
#otherwise the redirect link would have credentials as well
#it would result in applying credentials twice to the resulting link and thus breaking it, and in echoing credentials to the output as a part of redirect link
disable_feed_credential=true
response="$(get_http_header $aka_ms_link $disable_feed_credential)"
say_verbose "Received response: $response"
# Get results of all the redirects.
http_codes=$( echo "$response" | awk '$1 ~ /^HTTP/ {print $2}' )
# They all need to be 301, otherwise some links are broken (except for the last, which is not a redirect but 200 or 404).
broken_redirects=$( echo "$http_codes" | sed '$d' | grep -v '301' )
# All HTTP codes are 301 (Moved Permanently), the redirect link exists.
if [[ -z "$broken_redirects" ]]; then
aka_ms_download_link=$( echo "$response" | awk '$1 ~ /^Location/{print $2}' | tail -1 | tr -d '\r')
if [[ -z "$aka_ms_download_link" ]]; then
say_verbose "The aka.ms link '$aka_ms_link' is not valid: failed to get redirect location."
return 1
fi
say_verbose "The redirect location retrieved: '$aka_ms_download_link'."
return 0
else
say_verbose "The aka.ms link '$aka_ms_link' is not valid: received HTTP code: $(echo "$broken_redirects" | paste -sd "," -)."
return 1
fi
}
calculate_vars() {
eval $invocation
valid_legacy_download_link=true
#normalize input variables
normalized_architecture="$(get_normalized_architecture_from_architecture "$architecture")"
say_verbose "normalized_architecture=$normalized_architecture"
say_verbose "Normalized architecture: '$normalized_architecture'."
normalized_os="$(get_normalized_os "$user_defined_os")"
say_verbose "normalized_os=$normalized_os"
say_verbose "Normalized OS: '$normalized_os'."
normalized_quality="$(get_normalized_quality "$quality")"
say_verbose "Normalized quality: '$normalized_quality'."
normalized_channel="$(get_normalized_channel "$channel")"
say_verbose "Normalized channel: '$normalized_channel'."
normalized_product="$(get_normalized_product "$runtime")"
say_verbose "Normalized product: '$normalized_product'."
#try to get download location from aka.ms link
#not applicable when exact version is specified via command or json file
normalized_version="$(to_lowercase "$version")"
if [[ -z "$json_file" && "$normalized_version" == "latest" ]]; then
valid_aka_ms_link=true;
get_download_link_from_aka_ms || valid_aka_ms_link=false
if [ "$valid_aka_ms_link" == false ]; then
# if quality is specified - exit with error - there is no fallback approach
if [ ! -z "$normalized_quality" ]; then
say_err "Failed to locate the latest version in the channel '$normalized_channel' with '$normalized_quality' quality for '$normalized_product', os: '$normalized_os', architecture: '$normalized_architecture'."
say_err "Refer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support."
return 1
fi
say_verbose "Falling back to latest.version file approach."
else
say_verbose "Retrieved primary payload URL from aka.ms link: '$aka_ms_download_link'."
download_link=$aka_ms_download_link
say_verbose "Downloading using legacy url will not be attempted."
valid_legacy_download_link=false
#get version from the path
IFS='/'
read -ra pathElems <<< "$download_link"
count=${#pathElems[@]}
specific_version="${pathElems[count-2]}"
unset IFS;
say_verbose "Version: '$specific_version'."
#Retrieve product specific version
specific_product_version="$(get_specific_product_version "$azure_feed" "$specific_version" "$download_link")"
say_verbose "Product specific version: '$specific_product_version'."
install_root="$(resolve_installation_path "$install_dir")"
say_verbose "InstallRoot: '$install_root'."
return
fi
fi
specific_version="$(get_specific_version_from_version "$azure_feed" "$channel" "$normalized_architecture" "$version" "$json_file")"
specific_product_version="$(get_specific_product_version "$azure_feed" "$specific_version")"
@ -1011,6 +1314,8 @@ feed_credential=""
verbose=false
runtime=""
runtime_id=""
quality=""
internal=false
override_non_versioned_files=true
non_dynamic_parameters=""
user_defined_os=""
@ -1027,6 +1332,14 @@ do
shift
version="$1"
;;
-q|--quality|-[Qq]uality)
shift
quality="$1"
;;
--internal|-[Ii]nternal)
internal=true
non_dynamic_parameters+=" $name"
;;
-i|--install-dir|-[Ii]nstall[Dd]ir)
shift
install_dir="$1"
@ -1084,7 +1397,9 @@ do
--feed-credential|-[Ff]eed[Cc]redential)
shift
feed_credential="$1"
non_dynamic_parameters+=" $name "\""$1"\"""
#feed_credential should start with "?", for it to be added to the end of the link.
#adding "?" at the beginning of the feed_credential if needed.
[[ -z "$(echo $feed_credential)" ]] || [[ $feed_credential == \?* ]] || feed_credential="?$feed_credential"
;;
--runtime-id|-[Rr]untime[Ii]d)
shift
@ -1116,15 +1431,26 @@ do
echo " - LTS - most current supported release"
echo " - 2-part version in a format A.B - represents a specific release"
echo " examples: 2.0; 1.0"
echo " - Branch name"
echo " examples: release/2.0.0; Master"
echo " Note: The version parameter overrides the channel parameter."
echo " - 3-part version in a format A.B.Cxx - represents a specific SDK release"
echo " examples: 5.0.1xx, 5.0.2xx."
echo " Supported since 5.0 release"
echo " Note: The version parameter overrides the channel parameter when any version other than `latest` is used."
echo " -v,--version <VERSION> Use specific VERSION, Defaults to \`$version\`."
echo " -Version"
echo " Possible values:"
echo " - latest - most latest build on specific channel"
echo " - 3-part version in a format A.B.C - represents specific version of build"
echo " examples: 2.0.0-preview2-006120; 1.1.0"
echo " -q,--quality <quality> Download the latest build of specified quality in the channel."
echo " -Quality"
echo " The possible values are: daily, signed, validated, preview, GA."
echo " Works only in combination with channel. Not applicable for current and LTS channels and will be ignored if those channels are used."
echo " For SDK use channel in A.B.Cxx format. Using quality for SDK together with channel in A.B format is not supported."
echo " Supported since 5.0 release."
echo " Note: The version parameter overrides the channel parameter when any version other than `latest` is used, and therefore overrides the quality."
echo " --internal,-Internal Download internal builds. Requires providing credentials via --feed-credential parameter."
echo " --feed-credential <FEEDCREDENTIAL> Token to access Azure feed. Used as a query string to append to the Azure feed."
echo " -FeedCredential This parameter typically is not specified."
echo " -i,--install-dir <DIR> Install under specified location (see Install Location below)"
echo " -InstallDir"
echo " --architecture <ARCHITECTURE> Architecture of dotnet binaries to be installed, Defaults to \`$architecture\`."
@ -1145,7 +1471,6 @@ do
echo " --verbose,-Verbose Display diagnostics information."
echo " --azure-feed,-AzureFeed Azure feed location. Defaults to $azure_feed, This parameter typically is not changed by the user."
echo " --uncached-feed,-UncachedFeed Uncached feed location. This parameter typically is not changed by the user."
echo " --feed-credential,-FeedCredential Azure feed shared access token. This parameter typically is not specified."
echo " --skip-non-versioned-files Skips non-versioned files if they already exist, such as the dotnet executable."
echo " -SkipNonVersionedFiles"
echo " --no-cdn,-NoCdn Disable downloading from the Azure CDN, and use the uncached feed directly."
@ -1186,23 +1511,44 @@ say "- The SDK needs to be installed without user interaction and without admin
say "- The SDK installation doesn't need to persist across multiple CI runs."
say "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.\n"
if [ "$internal" = true ] && [ -z "$(echo $feed_credential)" ]; then
message="Provide credentials via --feed-credential parameter."
if [ "$dry_run" = true ]; then
say_warning "$message"
else
say_err "$message"
exit 1
fi
fi
check_min_reqs
calculate_vars
script_name=$(basename "$0")
if [ "$dry_run" = true ]; then
say "Payload URLs:"
say "Primary named payload URL: $download_link"
say "Primary named payload URL: ${download_link}"
if [ "$valid_legacy_download_link" = true ]; then
say "Legacy named payload URL: $legacy_download_link"
say "Legacy named payload URL: ${legacy_download_link}"
fi
repeatable_command="./$script_name --version "\""$specific_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\"" --os "\""$normalized_os"\"""
if [ ! -z "$normalized_quality" ]; then
repeatable_command+=" --quality "\""$normalized_quality"\"""
fi
if [[ "$runtime" == "dotnet" ]]; then
repeatable_command+=" --runtime "\""dotnet"\"""
elif [[ "$runtime" == "aspnetcore" ]]; then
repeatable_command+=" --runtime "\""aspnetcore"\"""
fi
repeatable_command+="$non_dynamic_parameters"
if [ -n "$feed_credential" ]; then
repeatable_command+=" --feed-credential "\""<feed_credential>"\"""
fi
say "Repeatable invocation: $repeatable_command"
exit 0
fi
@ -1219,4 +1565,4 @@ fi
say "Note that the script does not resolve dependencies during installation."
say "To check the list of dependencies, go to https://docs.microsoft.com/dotnet/core/install, select your operating system and check the \"Dependencies\" section."
say "Installation finished successfully."
say "Installation finished successfully."

71
package-lock.json generated
View File

@ -2254,8 +2254,7 @@
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
"dev": true,
"optional": true
"dev": true
},
"har-schema": {
"version": "2.0.0",
@ -2332,9 +2331,9 @@
}
},
"hosted-git-info": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true
},
"html-encoding-sniffer": {
@ -2600,7 +2599,6 @@
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"dev": true,
"optional": true,
"requires": {
"is-docker": "^2.0.0"
}
@ -3405,9 +3403,9 @@
}
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
"lodash.get": {
@ -3437,6 +3435,15 @@
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
}
},
"macos-release": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz",
@ -3614,11 +3621,10 @@
"dev": true
},
"node-notifier": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.0.tgz",
"integrity": "sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.1.tgz",
"integrity": "sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==",
"dev": true,
"optional": true,
"requires": {
"growly": "^1.3.0",
"is-wsl": "^2.2.0",
@ -3629,25 +3635,25 @@
},
"dependencies": {
"semver": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
"version": "7.3.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
"integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
"dev": true,
"optional": true
"requires": {
"lru-cache": "^6.0.0"
}
},
"uuid": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
"integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==",
"dev": true,
"optional": true
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"optional": true,
"requires": {
"isexe": "^2.0.0"
}
@ -4445,8 +4451,7 @@
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
"integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
"dev": true,
"optional": true
"dev": true
},
"signal-exit": {
"version": "3.0.2",
@ -5319,9 +5324,9 @@
}
},
"ws": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==",
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz",
"integrity": "sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw==",
"dev": true
},
"xml-name-validator": {
@ -5342,9 +5347,15 @@
"dev": true
},
"y18n": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
"dev": true
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"yaml": {

View File

@ -13,7 +13,9 @@ export function configAuthentication(
) {
const existingNuGetConfig: string = path.resolve(
processRoot,
existingFileLocation == '' ? 'nuget.config' : existingFileLocation
existingFileLocation === ''
? getExistingNugetConfig(processRoot)
: existingFileLocation
);
const tempNuGetConfig: string = path.resolve(
@ -25,6 +27,21 @@ export function configAuthentication(
writeFeedToFile(feedUrl, existingNuGetConfig, tempNuGetConfig);
}
function isValidKey(key: string): boolean {
return /^[\w\-\.]+$/i.test(key);
}
function getExistingNugetConfig(processRoot: string) {
const defaultConfigName = 'nuget.config';
const configFileNames = fs
.readdirSync(processRoot)
.filter(filename => filename.toLowerCase() === defaultConfigName);
if (configFileNames.length) {
return configFileNames[0];
}
return defaultConfigName;
}
function writeFeedToFile(
feedUrl: string,
existingFileLocation: string,
@ -109,9 +126,9 @@ function writeFeedToFile(
xml = xml.ele('packageSourceCredentials');
sourceKeys.forEach(key => {
if (key.indexOf(' ') > -1) {
if (!isValidKey(key)) {
throw new Error(
"This action currently can't handle source names with spaces. Remove the space from your repo's NuGet.config and try again."
"Source name can contain letters, numbers, and '-', '_', '.' symbols only. Please, fix source name in NuGet.config and try again."
);
}

View File

@ -29,8 +29,7 @@ export class DotNetVersionInfo {
return;
}
//Note: No support for previews when using generic
let parts: string[] = version.split('.');
const parts: string[] = version.split('.');
if (parts.length < 2 || parts.length > 3) this.throwInvalidVersionFormat();
@ -38,8 +37,10 @@ export class DotNetVersionInfo {
this.throwInvalidVersionFormat();
}
let major = this.getVersionNumberOrThrow(parts[0]);
let minor = this.getVersionNumberOrThrow(parts[1]);
const major = this.getVersionNumberOrThrow(parts[0]);
const minor = ['x', '*'].includes(parts[1])
? parts[1]
: this.getVersionNumberOrThrow(parts[1]);
this.fullversion = major + '.' + minor;
}
@ -60,7 +61,9 @@ export class DotNetVersionInfo {
}
private throwInvalidVersionFormat() {
throw 'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*';
throw new Error(
'Invalid version format! Supported: 1.2.3, 1.2, 1.2.x, 1.2.*'
);
}
/**
@ -76,8 +79,9 @@ export class DotNetVersionInfo {
}
export class DotnetCoreInstaller {
constructor(version: string) {
constructor(version: string, includePrerelease: boolean = false) {
this.version = version;
this.includePrerelease = includePrerelease;
}
public async installDotnet() {
@ -187,7 +191,7 @@ export class DotnetCoreInstaller {
console.log(process.env['PATH']);
if (resultCode != 0) {
throw `Failed to install dotnet ${resultCode}. ${output}`;
throw new Error(`Failed to install dotnet ${resultCode}. ${output}`);
}
}
@ -212,13 +216,15 @@ export class DotnetCoreInstaller {
let releasesInfo: any[] = releasesResult['releases'];
releasesInfo = releasesInfo.filter((releaseInfo: any) => {
return (
semver.satisfies(
releaseInfo['sdk']['version'],
versionInfo.version()
) ||
semver.satisfies(releaseInfo['sdk']['version'], versionInfo.version(), {
includePrerelease: this.includePrerelease
}) ||
semver.satisfies(
releaseInfo['sdk']['version-display'],
versionInfo.version()
versionInfo.version(),
{
includePrerelease: this.includePrerelease
}
)
);
});
@ -227,16 +233,22 @@ export class DotnetCoreInstaller {
let latestSdk: string = releasesResult['latest-sdk'];
releasesInfo = releasesInfo.filter((releaseInfo: any) =>
semver.lte(releaseInfo['sdk']['version'], latestSdk)
semver.lte(releaseInfo['sdk']['version'], latestSdk, {
includePrerelease: this.includePrerelease
})
);
// Sort for latest version
releasesInfo = releasesInfo.sort((a, b) =>
semver.rcompare(a['sdk']['version'], b['sdk']['version'])
semver.rcompare(a['sdk']['version'], b['sdk']['version'], {
includePrerelease: this.includePrerelease
})
);
if (releasesInfo.length == 0) {
throw `Could not find dotnet core version. Please ensure that specified version ${versionInfo.inputVersion} is valid.`;
throw new Error(
`Could not find dotnet core version. Please ensure that specified version ${versionInfo.inputVersion} is valid.`
);
}
let release = releasesInfo[0];
@ -264,15 +276,18 @@ export class DotnetCoreInstaller {
});
if (releasesInfo.length === 0) {
throw `Could not find info for version ${versionParts.join(
'.'
)} at ${DotNetCoreIndexUrl}`;
throw new Error(
`Could not find info for version ${versionParts.join(
'.'
)} at ${DotNetCoreIndexUrl}`
);
}
return releasesInfo[0]['releases.json'];
}
private version: string;
private includePrerelease: boolean;
}
const DotNetCoreIndexUrl: string =

View File

@ -20,7 +20,8 @@ export async function run() {
const globalJsonPath = path.join(process.cwd(), 'global.json');
if (fs.existsSync(globalJsonPath)) {
const globalJson = JSON.parse(
fs.readFileSync(globalJsonPath, {encoding: 'utf8'})
// .trim() is necessary to strip BOM https://github.com/nodejs/node/issues/20649
fs.readFileSync(globalJsonPath, {encoding: 'utf8'}).trim()
);
if (globalJson.sdk && globalJson.sdk.version) {
version = globalJson.sdk.version;
@ -29,7 +30,14 @@ export async function run() {
}
if (version) {
const dotnetInstaller = new installer.DotnetCoreInstaller(version);
const includePrerelease: boolean =
(core.getInput('include-prerelease') || 'false').toLowerCase() ===
'true';
const dotnetInstaller = new installer.DotnetCoreInstaller(
version,
includePrerelease
);
await dotnetInstaller.installDotnet();
}