mirror of
https://github.com/actions/setup-dotnet.git
synced 2025-08-14 20:55:11 +00:00
Add installer
This commit is contained in:
parent
32b4c69676
commit
d10bcaa370
192
externals/get-os-distro.sh
vendored
Normal file
192
externals/get-os-distro.sh
vendored
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||||
|
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Stop script on NZEC
|
||||||
|
set -e
|
||||||
|
# Stop script if unbound variable found (use ${var:-} if intentional)
|
||||||
|
set -u
|
||||||
|
# By default cmd1 | cmd2 returns exit code of cmd2 regardless of cmd1 success
|
||||||
|
# This is causing it to fail
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# Use in the the functions: eval $invocation
|
||||||
|
invocation='say_verbose "Calling: ${yellow:-}${FUNCNAME[0]} ${green:-}$*${normal:-}"'
|
||||||
|
|
||||||
|
# standard output may be used as a return value in the functions
|
||||||
|
# we need a way to write text on the screen in the functions so that
|
||||||
|
# it won't interfere with the return value.
|
||||||
|
# Exposing stream 3 as a pipe to standard output of the script itself
|
||||||
|
exec 3>&1
|
||||||
|
|
||||||
|
say_err() {
|
||||||
|
printf "%b\n" "get-os-distro: Error: $1" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# This platform list is finite - if the SDK/Runtime has supported Linux distribution-specific assets,
|
||||||
|
# then and only then should the Linux distribution appear in this list.
|
||||||
|
# Adding a Linux distribution to this list does not imply distribution-specific support.
|
||||||
|
get_legacy_os_name_from_platform() {
|
||||||
|
|
||||||
|
platform="$1"
|
||||||
|
case "$platform" in
|
||||||
|
"centos.7")
|
||||||
|
echo "centos"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"debian.8")
|
||||||
|
echo "debian"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"fedora.23")
|
||||||
|
echo "fedora.23"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"fedora.27")
|
||||||
|
echo "fedora.27"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"fedora.24")
|
||||||
|
echo "fedora.24"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"opensuse.13.2")
|
||||||
|
echo "opensuse.13.2"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"opensuse.42.1")
|
||||||
|
echo "opensuse.42.1"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"opensuse.42.3")
|
||||||
|
echo "opensuse.42.3"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"rhel.7"*)
|
||||||
|
echo "rhel"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"ubuntu.14.04")
|
||||||
|
echo "ubuntu"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"ubuntu.16.04")
|
||||||
|
echo "ubuntu.16.04"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"ubuntu.16.10")
|
||||||
|
echo "ubuntu.16.10"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"ubuntu.18.04")
|
||||||
|
echo "ubuntu.18.04"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"alpine.3.4.3")
|
||||||
|
echo "alpine"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_linux_platform_name() {
|
||||||
|
|
||||||
|
if [ -e /etc/os-release ]; then
|
||||||
|
. /etc/os-release
|
||||||
|
echo "$ID.$VERSION_ID"
|
||||||
|
return 0
|
||||||
|
elif [ -e /etc/redhat-release ]; then
|
||||||
|
local redhatRelease=$(</etc/redhat-release)
|
||||||
|
if [[ $redhatRelease == "CentOS release 6."* || $redhatRelease == "Red Hat Enterprise Linux Server release 6."* ]]; then
|
||||||
|
echo "rhel.6"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
say_err "Linux specific platform name and version could not be detected: UName = $uname"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_current_os_name() {
|
||||||
|
|
||||||
|
local uname=$(uname)
|
||||||
|
if [ "$uname" = "Darwin" ]; then
|
||||||
|
echo "mac"
|
||||||
|
return 0
|
||||||
|
elif [ "$uname" = "Linux" ]; then
|
||||||
|
local linux_platform_name
|
||||||
|
linux_platform_name="$(get_linux_platform_name)" || { echo "linux" && return 0 ; }
|
||||||
|
|
||||||
|
if [[ $linux_platform_name == "rhel.6" ]]; then
|
||||||
|
echo "$linux_platform_name"
|
||||||
|
return 0
|
||||||
|
elif [[ $linux_platform_name == alpine* ]]; then
|
||||||
|
echo "linux-musl"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "linux"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
say_err "OS name could not be detected: UName = $uname"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_legacy_os_name() {
|
||||||
|
|
||||||
|
local uname=$(uname)
|
||||||
|
if [ "$uname" = "Darwin" ]; then
|
||||||
|
echo "mac"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
if [ -e /etc/os-release ]; then
|
||||||
|
. /etc/os-release
|
||||||
|
os=$(get_legacy_os_name_from_platform "$ID.$VERSION_ID" || echo "")
|
||||||
|
if [ -n "$os" ]; then
|
||||||
|
echo "$os"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
say_err "Distribution specific OS name and version could not be detected: UName = $uname"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_machine_architecture() {
|
||||||
|
|
||||||
|
if command -v uname > /dev/null; then
|
||||||
|
CPUName=$(uname -m)
|
||||||
|
case $CPUName in
|
||||||
|
armv7l)
|
||||||
|
echo "arm"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
aarch64)
|
||||||
|
echo "arm64"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Always default to 'x64'
|
||||||
|
echo "x64"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
osName=$(get_current_os_name || echo "")
|
||||||
|
legacyOsName=$(get_legacy_os_name || echo "")
|
||||||
|
arch=$(get_machine_architecture || echo "")
|
||||||
|
|
||||||
|
primaryName="$osName-$arch"
|
||||||
|
legacyName="$legacyOsName"
|
||||||
|
|
||||||
|
echo "Primary:$primaryName"
|
||||||
|
echo "Legacy:$legacyName"
|
||||||
|
|
||||||
|
if [ -z "$osName" ] && [ -z "$legacyOsName" ];then
|
||||||
|
exit 1
|
||||||
|
fi
|
18
externals/get-os-platform.ps1
vendored
Normal file
18
externals/get-os-platform.ps1
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
function Get-Machine-Architecture()
|
||||||
|
{
|
||||||
|
# possible values: AMD64, IA64, x86
|
||||||
|
return $ENV:PROCESSOR_ARCHITECTURE
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-CLIArchitecture-From-Architecture([string]$Architecture)
|
||||||
|
{
|
||||||
|
switch ($Architecture.ToLower())
|
||||||
|
{
|
||||||
|
{ ($_ -eq "amd64") -or ($_ -eq "x64") } { return "x64" }
|
||||||
|
{ $_ -eq "x86" } { return "x86" }
|
||||||
|
default { throw "Architecture not supported. If you think this is a bug, please report it at https://github.com/dotnet/cli/issues" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$CLIArchitecture = Get-CLIArchitecture-From-Architecture $(Get-Machine-Architecture)
|
||||||
|
Write-Output "Primary:win-$CLIArchitecture"
|
503
externals/install-dotnet.ps1
vendored
Normal file
503
externals/install-dotnet.ps1
vendored
Normal file
@ -0,0 +1,503 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||||
|
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
#
|
||||||
|
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Installs dotnet cli
|
||||||
|
.DESCRIPTION
|
||||||
|
Installs dotnet cli. If dotnet installation already exists in the given directory
|
||||||
|
it will update it only if the requested version differs from the one already installed.
|
||||||
|
.PARAMETER Channel
|
||||||
|
Default: LTS
|
||||||
|
Download from the Channel specified. Possible values:
|
||||||
|
- Current - most current release
|
||||||
|
- 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
|
||||||
|
.PARAMETER Version
|
||||||
|
Default: latest
|
||||||
|
Represents a build version on specific channel. Possible values:
|
||||||
|
- latest - most latest build on specific channel
|
||||||
|
- coherent - most latest coherent build on specific channel
|
||||||
|
coherent applies only to SDK downloads
|
||||||
|
- 3-part version in a format A.B.C - represents specific version of build
|
||||||
|
examples: 2.0.0-preview2-006120; 1.1.0
|
||||||
|
.PARAMETER InstallDir
|
||||||
|
Default: %LocalAppData%\Microsoft\dotnet
|
||||||
|
Path to where to install dotnet. Note that binaries will be placed directly in a given directory.
|
||||||
|
.PARAMETER Architecture
|
||||||
|
Default: <auto> - this value represents currently running OS architecture
|
||||||
|
Architecture of dotnet binaries to be installed.
|
||||||
|
Possible values are: <auto>, x64 and x86
|
||||||
|
.PARAMETER SharedRuntime
|
||||||
|
Default: false
|
||||||
|
Installs just the shared runtime bits, not the entire SDK
|
||||||
|
.PARAMETER DryRun
|
||||||
|
If set it will not perform installation but instead display what command line to use to consistently install
|
||||||
|
currently requested version of dotnet cli. In example if you specify version 'latest' it will display a link
|
||||||
|
with specific version so that this command can be used deterministicly in a build script.
|
||||||
|
It also displays binaries location if you prefer to install or download it yourself.
|
||||||
|
.PARAMETER NoPath
|
||||||
|
By default this script will set environment variable PATH for the current process to the binaries folder inside installation folder.
|
||||||
|
If set it will display binaries location but not set any environment variable.
|
||||||
|
.PARAMETER Verbose
|
||||||
|
Displays diagnostics information.
|
||||||
|
.PARAMETER AzureFeed
|
||||||
|
Default: https://dotnetcli.azureedge.net/dotnet
|
||||||
|
This parameter typically is not changed by the user.
|
||||||
|
It allows to change URL for the Azure feed used by this installer.
|
||||||
|
.PARAMETER UncachedFeed
|
||||||
|
This parameter typically is not changed by the user.
|
||||||
|
It allows to change URL for the Uncached feed used by this installer.
|
||||||
|
.PARAMETER ProxyAddress
|
||||||
|
If set, the installer will use the proxy when making web requests
|
||||||
|
.PARAMETER ProxyUseDefaultCredentials
|
||||||
|
Default: false
|
||||||
|
Use default credentials, when using proxy address.
|
||||||
|
#>
|
||||||
|
[cmdletbinding()]
|
||||||
|
param(
|
||||||
|
[string]$Channel="LTS",
|
||||||
|
[string]$Version="Latest",
|
||||||
|
[string]$InstallDir="<auto>",
|
||||||
|
[string]$Architecture="<auto>",
|
||||||
|
[switch]$SharedRuntime,
|
||||||
|
[switch]$DryRun,
|
||||||
|
[switch]$NoPath,
|
||||||
|
[string]$AzureFeed="https://dotnetcli.azureedge.net/dotnet",
|
||||||
|
[string]$UncachedFeed="https://dotnetcli.blob.core.windows.net/dotnet",
|
||||||
|
[string]$ProxyAddress,
|
||||||
|
[switch]$ProxyUseDefaultCredentials
|
||||||
|
)
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference="Stop"
|
||||||
|
$ProgressPreference="SilentlyContinue"
|
||||||
|
|
||||||
|
$BinFolderRelativePath=""
|
||||||
|
|
||||||
|
# example path with regex: shared/1.0.0-beta-12345/somepath
|
||||||
|
$VersionRegEx="/\d+\.\d+[^/]+/"
|
||||||
|
$OverrideNonVersionedFiles=$true
|
||||||
|
|
||||||
|
function Say($str) {
|
||||||
|
Write-Output "dotnet-install: $str"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Say-Verbose($str) {
|
||||||
|
Write-Verbose "dotnet-install: $str"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Say-Invocation($Invocation) {
|
||||||
|
$command = $Invocation.MyCommand;
|
||||||
|
$args = (($Invocation.BoundParameters.Keys | foreach { "-$_ `"$($Invocation.BoundParameters[$_])`"" }) -join " ")
|
||||||
|
Say-Verbose "$command $args"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Invoke-With-Retry([ScriptBlock]$ScriptBlock, [int]$MaxAttempts = 3, [int]$SecondsBetweenAttempts = 1) {
|
||||||
|
$Attempts = 0
|
||||||
|
|
||||||
|
while ($true) {
|
||||||
|
try {
|
||||||
|
return $ScriptBlock.Invoke()
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$Attempts++
|
||||||
|
if ($Attempts -lt $MaxAttempts) {
|
||||||
|
Start-Sleep $SecondsBetweenAttempts
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-Machine-Architecture() {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
# possible values: AMD64, IA64, x86
|
||||||
|
return $ENV:PROCESSOR_ARCHITECTURE
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: Architecture and CLIArchitecture should be unified
|
||||||
|
function Get-CLIArchitecture-From-Architecture([string]$Architecture) {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
switch ($Architecture.ToLower()) {
|
||||||
|
{ $_ -eq "<auto>" } { return Get-CLIArchitecture-From-Architecture $(Get-Machine-Architecture) }
|
||||||
|
{ ($_ -eq "amd64") -or ($_ -eq "x64") } { return "x64" }
|
||||||
|
{ $_ -eq "x86" } { return "x86" }
|
||||||
|
default { throw "Architecture not supported. If you think this is a bug, please report it at https://github.com/dotnet/cli/issues" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-Version-Info-From-Version-Text([string]$VersionText) {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
$Data = @($VersionText.Split([char[]]@(), [StringSplitOptions]::RemoveEmptyEntries));
|
||||||
|
|
||||||
|
$VersionInfo = @{}
|
||||||
|
$VersionInfo.CommitHash = $Data[0].Trim()
|
||||||
|
$VersionInfo.Version = $Data[1].Trim()
|
||||||
|
return $VersionInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
function Load-Assembly([string] $Assembly) {
|
||||||
|
try {
|
||||||
|
Add-Type -Assembly $Assembly | Out-Null
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
# On Nano Server, Powershell Core Edition is used. Add-Type is unable to resolve base class assemblies because they are not GAC'd.
|
||||||
|
# Loading the base class assemblies is not unnecessary as the types will automatically get resolved.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetHTTPResponse([Uri] $Uri)
|
||||||
|
{
|
||||||
|
Invoke-With-Retry(
|
||||||
|
{
|
||||||
|
|
||||||
|
$HttpClient = $null
|
||||||
|
|
||||||
|
try {
|
||||||
|
# HttpClient is used vs Invoke-WebRequest in order to support Nano Server which doesn't support the Invoke-WebRequest cmdlet.
|
||||||
|
Load-Assembly -Assembly System.Net.Http
|
||||||
|
|
||||||
|
if(-not $ProxyAddress)
|
||||||
|
{
|
||||||
|
# Despite no proxy being explicitly specified, we may still be behind a default proxy
|
||||||
|
$DefaultProxy = [System.Net.WebRequest]::DefaultWebProxy;
|
||||||
|
if($DefaultProxy -and (-not $DefaultProxy.IsBypassed($Uri))){
|
||||||
|
$ProxyAddress = $DefaultProxy.GetProxy($Uri).OriginalString
|
||||||
|
$ProxyUseDefaultCredentials = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($ProxyAddress){
|
||||||
|
$HttpClientHandler = New-Object System.Net.Http.HttpClientHandler
|
||||||
|
$HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{Address=$ProxyAddress;UseDefaultCredentials=$ProxyUseDefaultCredentials}
|
||||||
|
$HttpClient = New-Object System.Net.Http.HttpClient -ArgumentList $HttpClientHandler
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$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
|
||||||
|
# 10 minutes allows it to work over much slower connections.
|
||||||
|
$HttpClient.Timeout = New-TimeSpan -Minutes 10
|
||||||
|
$Response = $HttpClient.GetAsync($Uri).Result
|
||||||
|
if (($Response -eq $null) -or (-not ($Response.IsSuccessStatusCode)))
|
||||||
|
{
|
||||||
|
$ErrorMsg = "Failed to download $Uri."
|
||||||
|
if ($Response -ne $null)
|
||||||
|
{
|
||||||
|
$ErrorMsg += " $Response"
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $ErrorMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
return $Response
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ($HttpClient -ne $null) {
|
||||||
|
$HttpClient.Dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel, [bool]$Coherent) {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
$VersionFileUrl = $null
|
||||||
|
if ($SharedRuntime) {
|
||||||
|
$VersionFileUrl = "$UncachedFeed/Runtime/$Channel/latest.version"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($Coherent) {
|
||||||
|
$VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.coherent.version"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$Response = GetHTTPResponse -Uri $VersionFileUrl
|
||||||
|
$StringContent = $Response.Content.ReadAsStringAsync().Result
|
||||||
|
|
||||||
|
switch ($Response.Content.Headers.ContentType) {
|
||||||
|
{ ($_ -eq "application/octet-stream") } { $VersionText = [Text.Encoding]::UTF8.GetString($StringContent) }
|
||||||
|
{ ($_ -eq "text/plain") } { $VersionText = $StringContent }
|
||||||
|
{ ($_ -eq "text/plain; charset=UTF-8") } { $VersionText = $StringContent }
|
||||||
|
default { throw "``$Response.Content.Headers.ContentType`` is an unknown .version file content type." }
|
||||||
|
}
|
||||||
|
|
||||||
|
$VersionInfo = Get-Version-Info-From-Version-Text $VersionText
|
||||||
|
|
||||||
|
return $VersionInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$Channel, [string]$Version) {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
switch ($Version.ToLower()) {
|
||||||
|
{ $_ -eq "latest" } {
|
||||||
|
$LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $False
|
||||||
|
return $LatestVersionInfo.Version
|
||||||
|
}
|
||||||
|
{ $_ -eq "coherent" } {
|
||||||
|
$LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $True
|
||||||
|
return $LatestVersionInfo.Version
|
||||||
|
}
|
||||||
|
default { return $Version }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-Download-Link([string]$AzureFeed, [string]$Channel, [string]$SpecificVersion, [string]$CLIArchitecture) {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
if ($SharedRuntime) {
|
||||||
|
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-runtime-$SpecificVersion-win-$CLIArchitecture.zip"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificVersion-win-$CLIArchitecture.zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
Say-Verbose "Constructed primary payload URL: $PayloadURL"
|
||||||
|
|
||||||
|
return $PayloadURL
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-LegacyDownload-Link([string]$AzureFeed, [string]$Channel, [string]$SpecificVersion, [string]$CLIArchitecture) {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
if ($SharedRuntime) {
|
||||||
|
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-win-$CLIArchitecture.$SpecificVersion.zip"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-dev-win-$CLIArchitecture.$SpecificVersion.zip"
|
||||||
|
}
|
||||||
|
|
||||||
|
Say-Verbose "Constructed legacy payload URL: $PayloadURL"
|
||||||
|
|
||||||
|
return $PayloadURL
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-User-Share-Path() {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
$InstallRoot = $env:DOTNET_INSTALL_DIR
|
||||||
|
if (!$InstallRoot) {
|
||||||
|
$InstallRoot = "$env:LocalAppData\Microsoft\dotnet"
|
||||||
|
}
|
||||||
|
return $InstallRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
function Resolve-Installation-Path([string]$InstallDir) {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
if ($InstallDir -eq "<auto>") {
|
||||||
|
return Get-User-Share-Path
|
||||||
|
}
|
||||||
|
return $InstallDir
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-Version-Info-From-Version-File([string]$InstallRoot, [string]$RelativePathToVersionFile) {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
$VersionFile = Join-Path -Path $InstallRoot -ChildPath $RelativePathToVersionFile
|
||||||
|
Say-Verbose "Local version file: $VersionFile"
|
||||||
|
|
||||||
|
if (Test-Path $VersionFile) {
|
||||||
|
$VersionText = cat $VersionFile
|
||||||
|
Say-Verbose "Local version file text: $VersionText"
|
||||||
|
return Get-Version-Info-From-Version-Text $VersionText
|
||||||
|
}
|
||||||
|
|
||||||
|
Say-Verbose "Local version file not found."
|
||||||
|
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Is-Dotnet-Package-Installed([string]$InstallRoot, [string]$RelativePathToPackage, [string]$SpecificVersion) {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
$DotnetPackagePath = Join-Path -Path $InstallRoot -ChildPath $RelativePathToPackage | Join-Path -ChildPath $SpecificVersion
|
||||||
|
Say-Verbose "Is-Dotnet-Package-Installed: Path to a package: $DotnetPackagePath"
|
||||||
|
return Test-Path $DotnetPackagePath -PathType Container
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-Absolute-Path([string]$RelativeOrAbsolutePath) {
|
||||||
|
# Too much spam
|
||||||
|
# Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($RelativeOrAbsolutePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-Path-Prefix-With-Version($path) {
|
||||||
|
$match = [regex]::match($path, $VersionRegEx)
|
||||||
|
if ($match.Success) {
|
||||||
|
return $entry.FullName.Substring(0, $match.Index + $match.Length)
|
||||||
|
}
|
||||||
|
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package([System.IO.Compression.ZipArchive]$Zip, [string]$OutPath) {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
$ret = @()
|
||||||
|
foreach ($entry in $Zip.Entries) {
|
||||||
|
$dir = Get-Path-Prefix-With-Version $entry.FullName
|
||||||
|
if ($dir -ne $null) {
|
||||||
|
$path = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $dir)
|
||||||
|
if (-Not (Test-Path $path -PathType Container)) {
|
||||||
|
$ret += $dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = $ret | Sort-Object | Get-Unique
|
||||||
|
|
||||||
|
$values = ($ret | foreach { "$_" }) -join ";"
|
||||||
|
Say-Verbose "Directories to unpack: $values"
|
||||||
|
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
|
# Example zip content and extraction algorithm:
|
||||||
|
# Rule: files if extracted are always being extracted to the same relative path locally
|
||||||
|
# .\
|
||||||
|
# a.exe # file does not exist locally, extract
|
||||||
|
# b.dll # file exists locally, override only if $OverrideFiles set
|
||||||
|
# aaa\ # same rules as for files
|
||||||
|
# ...
|
||||||
|
# abc\1.0.0\ # directory contains version and exists locally
|
||||||
|
# ... # do not extract content under versioned part
|
||||||
|
# abc\asd\ # same rules as for files
|
||||||
|
# ...
|
||||||
|
# def\ghi\1.0.1\ # directory contains version and does not exist locally
|
||||||
|
# ... # extract content
|
||||||
|
function Extract-Dotnet-Package([string]$ZipPath, [string]$OutPath) {
|
||||||
|
Say-Invocation $MyInvocation
|
||||||
|
|
||||||
|
Load-Assembly -Assembly System.IO.Compression.FileSystem
|
||||||
|
Set-Variable -Name Zip
|
||||||
|
try {
|
||||||
|
$Zip = [System.IO.Compression.ZipFile]::OpenRead($ZipPath)
|
||||||
|
|
||||||
|
$DirectoriesToUnpack = Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package -Zip $Zip -OutPath $OutPath
|
||||||
|
|
||||||
|
foreach ($entry in $Zip.Entries) {
|
||||||
|
$PathWithVersion = Get-Path-Prefix-With-Version $entry.FullName
|
||||||
|
if (($PathWithVersion -eq $null) -Or ($DirectoriesToUnpack -contains $PathWithVersion)) {
|
||||||
|
$DestinationPath = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $entry.FullName)
|
||||||
|
$DestinationDir = Split-Path -Parent $DestinationPath
|
||||||
|
$OverrideFiles=$OverrideNonVersionedFiles -Or (-Not (Test-Path $DestinationPath))
|
||||||
|
if ((-Not $DestinationPath.EndsWith("\")) -And $OverrideFiles) {
|
||||||
|
New-Item -ItemType Directory -Force -Path $DestinationDir | Out-Null
|
||||||
|
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $DestinationPath, $OverrideNonVersionedFiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ($Zip -ne $null) {
|
||||||
|
$Zip.Dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function DownloadFile([Uri]$Uri, [string]$OutPath) {
|
||||||
|
$Stream = $null
|
||||||
|
|
||||||
|
try {
|
||||||
|
$Response = GetHTTPResponse -Uri $Uri
|
||||||
|
$Stream = $Response.Content.ReadAsStreamAsync().Result
|
||||||
|
$File = [System.IO.File]::Create($OutPath)
|
||||||
|
$Stream.CopyTo($File)
|
||||||
|
$File.Close()
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if ($Stream -ne $null) {
|
||||||
|
$Stream.Dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot, [string]$BinFolderRelativePath) {
|
||||||
|
$BinPath = Get-Absolute-Path $(Join-Path -Path $InstallRoot -ChildPath $BinFolderRelativePath)
|
||||||
|
if (-Not $NoPath) {
|
||||||
|
Say "Adding to current process PATH: `"$BinPath`". Note: This change will not be visible if PowerShell was run as a child process."
|
||||||
|
$env:path = "$BinPath;" + $env:path
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Say "Binaries of dotnet can be found in $BinPath"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture
|
||||||
|
$SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version
|
||||||
|
$DownloadLink = Get-Download-Link -AzureFeed $AzureFeed -Channel $Channel -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
|
||||||
|
$LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -Channel $Channel -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
|
||||||
|
|
||||||
|
if ($DryRun) {
|
||||||
|
Say "Payload URLs:"
|
||||||
|
Say "Primary - $DownloadLink"
|
||||||
|
Say "Legacy - $LegacyDownloadLink"
|
||||||
|
Say "Repeatable invocation: .\$($MyInvocation.MyCommand) -Version $SpecificVersion -Channel $Channel -Architecture $CLIArchitecture -InstallDir $InstallDir"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
$InstallRoot = Resolve-Installation-Path $InstallDir
|
||||||
|
Say-Verbose "InstallRoot: $InstallRoot"
|
||||||
|
|
||||||
|
$IsSdkInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage "sdk" -SpecificVersion $SpecificVersion
|
||||||
|
Say-Verbose ".NET SDK installed? $IsSdkInstalled"
|
||||||
|
if ($IsSdkInstalled) {
|
||||||
|
Say ".NET SDK version $SpecificVersion is already installed."
|
||||||
|
Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath $BinFolderRelativePath
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null
|
||||||
|
|
||||||
|
$installDrive = $((Get-Item $InstallRoot).PSDrive.Name);
|
||||||
|
Write-Output "${installDrive}:";
|
||||||
|
$free = Get-CimInstance -Class win32_logicaldisk | where Deviceid -eq "${installDrive}:"
|
||||||
|
if ($free.Freespace / 1MB -le 100 ) {
|
||||||
|
Say "There is not enough disk space on drive ${installDrive}:"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
$ZipPath = [System.IO.Path]::GetTempFileName()
|
||||||
|
Say-Verbose "Zip path: $ZipPath"
|
||||||
|
Say "Downloading link: $DownloadLink"
|
||||||
|
try {
|
||||||
|
DownloadFile -Uri $DownloadLink -OutPath $ZipPath
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Say "Cannot download: $DownloadLink"
|
||||||
|
$DownloadLink = $LegacyDownloadLink
|
||||||
|
$ZipPath = [System.IO.Path]::GetTempFileName()
|
||||||
|
Say-Verbose "Legacy zip path: $ZipPath"
|
||||||
|
Say "Downloading legacy link: $DownloadLink"
|
||||||
|
DownloadFile -Uri $DownloadLink -OutPath $ZipPath
|
||||||
|
}
|
||||||
|
|
||||||
|
Say "Extracting zip from $DownloadLink"
|
||||||
|
Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot
|
||||||
|
|
||||||
|
Remove-Item $ZipPath
|
||||||
|
|
||||||
|
Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath $BinFolderRelativePath
|
||||||
|
|
||||||
|
Say "Installation finished"
|
||||||
|
exit 0
|
317
lib/installer.js
Normal file
317
lib/installer.js
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||||
|
result["default"] = mod;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
// Load tempDirectory before it gets wiped by tool-cache
|
||||||
|
let tempDirectory = process.env['RUNNER_TEMPDIRECTORY'] || '';
|
||||||
|
const core = __importStar(require("@actions/core"));
|
||||||
|
const exec = __importStar(require("@actions/exec"));
|
||||||
|
const io = __importStar(require("@actions/io"));
|
||||||
|
const tc = __importStar(require("@actions/tool-cache"));
|
||||||
|
const httpClient = require("typed-rest-client/HttpClient");
|
||||||
|
const fs_1 = require("fs");
|
||||||
|
const os = __importStar(require("os"));
|
||||||
|
const path = __importStar(require("path"));
|
||||||
|
const semver = __importStar(require("semver"));
|
||||||
|
const util = __importStar(require("util"));
|
||||||
|
const IS_WINDOWS = process.platform === 'win32';
|
||||||
|
if (!tempDirectory) {
|
||||||
|
let baseLocation;
|
||||||
|
if (IS_WINDOWS) {
|
||||||
|
// On windows use the USERPROFILE env variable
|
||||||
|
baseLocation = process.env['USERPROFILE'] || 'C:\\';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
baseLocation = '/Users';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
baseLocation = '/home';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tempDirectory = path.join(baseLocation, 'actions', 'temp');
|
||||||
|
}
|
||||||
|
class DotnetCoreInstaller {
|
||||||
|
constructor(version) {
|
||||||
|
if (semver.valid(semver.clean(version) || '') == null) {
|
||||||
|
throw 'Implicit version not permitted';
|
||||||
|
}
|
||||||
|
this.version = version;
|
||||||
|
this.cachedToolName = 'dncs';
|
||||||
|
this.arch = 'x64';
|
||||||
|
}
|
||||||
|
installDotnet() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
// Check cache
|
||||||
|
let toolPath;
|
||||||
|
let osSuffixes = yield this.detectMachineOS();
|
||||||
|
let parts = osSuffixes[0].split('-');
|
||||||
|
if (parts.length > 1) {
|
||||||
|
this.arch = parts[1];
|
||||||
|
}
|
||||||
|
toolPath = this.getLocalTool();
|
||||||
|
if (!toolPath) {
|
||||||
|
// download, extract, cache
|
||||||
|
console.log('Getting a download url', this.version);
|
||||||
|
let downloadUrls = yield this.getDownloadUrls(osSuffixes, this.version);
|
||||||
|
toolPath = yield this.downloadAndInstall(downloadUrls);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('Using cached tool');
|
||||||
|
}
|
||||||
|
// Prepend the tools path. instructs the agent to prepend for future tasks
|
||||||
|
core.addPath(toolPath);
|
||||||
|
try {
|
||||||
|
let globalToolPath = '';
|
||||||
|
if (IS_WINDOWS) {
|
||||||
|
globalToolPath = path.join(process.env.USERPROFILE || '', '.dotnet\\tools');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
globalToolPath = path.join(process.env.HOME || '', '.dotnet/tools');
|
||||||
|
}
|
||||||
|
yield io.mkdirP(globalToolPath);
|
||||||
|
core.addPath(globalToolPath);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
//nop
|
||||||
|
}
|
||||||
|
// Set DOTNET_ROOT for dotnet core Apphost to find runtime since it is installed to a non well-known location.
|
||||||
|
core.exportVariable('DOTNET_ROOT', toolPath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getLocalTool() {
|
||||||
|
console.log('Checking tool cache');
|
||||||
|
return tc.find(this.cachedToolName, this.version, this.arch);
|
||||||
|
}
|
||||||
|
detectMachineOS() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let osSuffix = [];
|
||||||
|
let output = '';
|
||||||
|
let resultCode = 0;
|
||||||
|
if (IS_WINDOWS) {
|
||||||
|
let escapedScript = path
|
||||||
|
.join(__dirname, '..', 'externals', 'get-os-platform.ps1')
|
||||||
|
.replace(/'/g, "''");
|
||||||
|
let command = `& '${escapedScript}'`;
|
||||||
|
const powershellPath = yield io.which('powershell', true);
|
||||||
|
resultCode = yield exec.exec(`"${powershellPath}"`, [
|
||||||
|
'-NoLogo',
|
||||||
|
'-Sta',
|
||||||
|
'-NoProfile',
|
||||||
|
'-NonInteractive',
|
||||||
|
'-ExecutionPolicy',
|
||||||
|
'Unrestricted',
|
||||||
|
'-Command',
|
||||||
|
command
|
||||||
|
], {
|
||||||
|
listeners: {
|
||||||
|
stdout: (data) => {
|
||||||
|
output += data.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let scriptPath = path.join(__dirname, '..', 'externals', 'get-os-distro.sh');
|
||||||
|
fs_1.chmodSync(scriptPath, '777');
|
||||||
|
const toolPath = yield io.which(scriptPath, true);
|
||||||
|
resultCode = yield exec.exec(`"${toolPath}"`, [], {
|
||||||
|
listeners: {
|
||||||
|
stdout: (data) => {
|
||||||
|
output += data.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (resultCode != 0) {
|
||||||
|
throw `Failed to detect os with result code ${resultCode}`;
|
||||||
|
}
|
||||||
|
let index;
|
||||||
|
if ((index = output.indexOf('Primary:')) >= 0) {
|
||||||
|
let primary = output.substr(index + 'Primary:'.length).split(os.EOL)[0];
|
||||||
|
osSuffix.push(primary);
|
||||||
|
}
|
||||||
|
if ((index = output.indexOf('Legacy:')) >= 0) {
|
||||||
|
let legacy = output.substr(index + 'Legacy:'.length).split(os.EOL)[0];
|
||||||
|
osSuffix.push(legacy);
|
||||||
|
}
|
||||||
|
if (osSuffix.length == 0) {
|
||||||
|
throw 'Could not detect platform';
|
||||||
|
}
|
||||||
|
return osSuffix;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
downloadAndInstall(downloadUrls) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let downloaded = false;
|
||||||
|
let downloadPath = '';
|
||||||
|
for (const url of downloadUrls) {
|
||||||
|
try {
|
||||||
|
downloadPath = yield tc.downloadTool(url);
|
||||||
|
downloaded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.log('Could Not Download', url, JSON.stringify(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!downloaded) {
|
||||||
|
throw 'Failed to download package';
|
||||||
|
}
|
||||||
|
// extract
|
||||||
|
console.log('Extracting Package', downloadPath);
|
||||||
|
let extPath = IS_WINDOWS
|
||||||
|
? yield tc.extractZip(downloadPath)
|
||||||
|
: yield tc.extractTar(downloadPath);
|
||||||
|
// cache tool
|
||||||
|
console.log('Caching tool');
|
||||||
|
let cachedDir = yield tc.cacheDir(extPath, this.cachedToolName, this.version, this.arch);
|
||||||
|
console.log('Successfully installed', this.version);
|
||||||
|
return cachedDir;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// OsSuffixes - The suffix which is a part of the file name ex- linux-x64, windows-x86
|
||||||
|
// Type - SDK / Runtime
|
||||||
|
// Version - Version of the SDK/Runtime
|
||||||
|
getDownloadUrls(osSuffixes, version) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let downloadUrls = [];
|
||||||
|
let releasesJSON = yield this.getReleasesJson();
|
||||||
|
let releasesInfo = JSON.parse(yield releasesJSON.readBody());
|
||||||
|
releasesInfo = releasesInfo.filter((releaseInfo) => {
|
||||||
|
return (releaseInfo['version-sdk'] === version ||
|
||||||
|
releaseInfo['version-sdk-display'] === version);
|
||||||
|
});
|
||||||
|
if (releasesInfo.length != 0) {
|
||||||
|
let release = releasesInfo[0];
|
||||||
|
let blobUrl = release['blob-sdk'];
|
||||||
|
let dlcUrl = release['dlc--sdk'];
|
||||||
|
let fileName = release['sdk-' + osSuffixes[0]]
|
||||||
|
? release['sdk-' + osSuffixes[0]]
|
||||||
|
: release['sdk-' + osSuffixes[1]];
|
||||||
|
if (!!fileName) {
|
||||||
|
fileName = fileName.trim();
|
||||||
|
// For some latest version, the filename itself can be full download url.
|
||||||
|
// Do a very basic check for url(instead of regex) as the url is only for downloading and
|
||||||
|
// is coming from .net core releases json and not some ransom user input
|
||||||
|
if (fileName.toLowerCase().startsWith('https://')) {
|
||||||
|
downloadUrls.push(fileName);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!!blobUrl) {
|
||||||
|
downloadUrls.push(util.format('%s%s', blobUrl.trim(), fileName));
|
||||||
|
}
|
||||||
|
if (!!dlcUrl) {
|
||||||
|
downloadUrls.push(util.format('%s%s', dlcUrl.trim(), fileName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw `The specified version's download links are not correctly formed in the supported versions document => ${DotNetCoreReleasesUrl}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log(`Could not fetch download information for version ${version}`);
|
||||||
|
downloadUrls = yield this.getFallbackDownloadUrls(version);
|
||||||
|
}
|
||||||
|
if (downloadUrls.length == 0) {
|
||||||
|
throw `Could not construct download URL. Please ensure that specified version ${version} is valid.`;
|
||||||
|
}
|
||||||
|
return downloadUrls;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getReleasesJson() {
|
||||||
|
var httpCallbackClient = new httpClient.HttpClient('setup-dotnet', [], {});
|
||||||
|
return httpCallbackClient.get(DotNetCoreReleasesUrl);
|
||||||
|
}
|
||||||
|
getFallbackDownloadUrls(version) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let primaryUrlSearchString;
|
||||||
|
let legacyUrlSearchString;
|
||||||
|
let output = '';
|
||||||
|
let resultCode = 0;
|
||||||
|
if (IS_WINDOWS) {
|
||||||
|
let escapedScript = path
|
||||||
|
.join(__dirname, '..', 'externals', 'install-dotnet.ps1')
|
||||||
|
.replace(/'/g, "''");
|
||||||
|
let command = `& '${escapedScript}' -Version ${version} -DryRun`;
|
||||||
|
const powershellPath = yield io.which('powershell', true);
|
||||||
|
resultCode = yield exec.exec(`"${powershellPath}"`, [
|
||||||
|
'-NoLogo',
|
||||||
|
'-Sta',
|
||||||
|
'-NoProfile',
|
||||||
|
'-NonInteractive',
|
||||||
|
'-ExecutionPolicy',
|
||||||
|
'Unrestricted',
|
||||||
|
'-Command',
|
||||||
|
command
|
||||||
|
], {
|
||||||
|
listeners: {
|
||||||
|
stdout: (data) => {
|
||||||
|
output += data.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
primaryUrlSearchString = 'dotnet-install: Primary - ';
|
||||||
|
legacyUrlSearchString = 'dotnet-install: Legacy - ';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let escapedScript = path
|
||||||
|
.join(__dirname, '..', 'externals', 'install-dotnet.sh')
|
||||||
|
.replace(/'/g, "''");
|
||||||
|
fs_1.chmodSync(escapedScript, '777');
|
||||||
|
const scriptPath = yield io.which(escapedScript, true);
|
||||||
|
resultCode = yield exec.exec(`"${scriptPath}"`, ['--version', version, '--dry-run'], {
|
||||||
|
listeners: {
|
||||||
|
stdout: (data) => {
|
||||||
|
output += data.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
primaryUrlSearchString = 'dotnet-install: Payload URL: ';
|
||||||
|
legacyUrlSearchString = 'dotnet-install: Legacy payload URL: ';
|
||||||
|
}
|
||||||
|
if (resultCode != 0) {
|
||||||
|
throw `Failed to get download urls with result code ${resultCode}`;
|
||||||
|
}
|
||||||
|
let primaryUrl = '';
|
||||||
|
let legacyUrl = '';
|
||||||
|
if (!!output && output.length > 0) {
|
||||||
|
let lines = output.split(os.EOL);
|
||||||
|
if (!!lines && lines.length > 0) {
|
||||||
|
lines.forEach((line) => {
|
||||||
|
if (!line) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var primarySearchStringIndex = line.indexOf(primaryUrlSearchString);
|
||||||
|
if (primarySearchStringIndex > -1) {
|
||||||
|
primaryUrl = line.substring(primarySearchStringIndex + primaryUrlSearchString.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var legacySearchStringIndex = line.indexOf(legacyUrlSearchString);
|
||||||
|
if (legacySearchStringIndex > -1) {
|
||||||
|
legacyUrl = line.substring(legacySearchStringIndex + legacyUrlSearchString.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [primaryUrl, legacyUrl];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.DotnetCoreInstaller = DotnetCoreInstaller;
|
||||||
|
const DotNetCoreReleasesUrl = 'https://raw.githubusercontent.com/dotnet/core/master/release-notes/releases.json';
|
40
lib/setup-dotnet.js
Normal file
40
lib/setup-dotnet.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
|
||||||
|
result["default"] = mod;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const core = __importStar(require("@actions/core"));
|
||||||
|
const installer = __importStar(require("./installer"));
|
||||||
|
function run() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
try {
|
||||||
|
//
|
||||||
|
// Version is optional. If supplied, install / use from the tool cache
|
||||||
|
// If not supplied then task is still used to setup proxy, auth, etc...
|
||||||
|
//
|
||||||
|
const version = core.getInput('version');
|
||||||
|
if (version) {
|
||||||
|
const dotnetInstaller = new installer.DotnetCoreInstaller(version);
|
||||||
|
yield dotnetInstaller.installDotnet();
|
||||||
|
}
|
||||||
|
// TODO: setup proxy from runner proxy config
|
||||||
|
// TODO: problem matchers registered
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
core.setFailed(error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
run();
|
815
src/install-dotnet.sh
Normal file
815
src/install-dotnet.sh
Normal file
@ -0,0 +1,815 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||||
|
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Stop script on NZEC
|
||||||
|
set -e
|
||||||
|
# Stop script if unbound variable found (use ${var:-} if intentional)
|
||||||
|
set -u
|
||||||
|
# By default cmd1 | cmd2 returns exit code of cmd2 regardless of cmd1 success
|
||||||
|
# This is causing it to fail
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# Use in the the functions: eval $invocation
|
||||||
|
invocation='say_verbose "Calling: ${yellow:-}${FUNCNAME[0]} ${green:-}$*${normal:-}"'
|
||||||
|
|
||||||
|
# standard output may be used as a return value in the functions
|
||||||
|
# we need a way to write text on the screen in the functions so that
|
||||||
|
# it won't interfere with the return value.
|
||||||
|
# Exposing stream 3 as a pipe to standard output of the script itself
|
||||||
|
exec 3>&1
|
||||||
|
|
||||||
|
# Setup some colors to use. These need to work in fairly limited shells, like the Ubuntu Docker container where there are only 8 colors.
|
||||||
|
# See if stdout is a terminal
|
||||||
|
if [ -t 1 ]; then
|
||||||
|
# see if it supports colors
|
||||||
|
ncolors=$(tput colors)
|
||||||
|
if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then
|
||||||
|
bold="$(tput bold || echo)"
|
||||||
|
normal="$(tput sgr0 || echo)"
|
||||||
|
black="$(tput setaf 0 || echo)"
|
||||||
|
red="$(tput setaf 1 || echo)"
|
||||||
|
green="$(tput setaf 2 || echo)"
|
||||||
|
yellow="$(tput setaf 3 || echo)"
|
||||||
|
blue="$(tput setaf 4 || echo)"
|
||||||
|
magenta="$(tput setaf 5 || echo)"
|
||||||
|
cyan="$(tput setaf 6 || echo)"
|
||||||
|
white="$(tput setaf 7 || echo)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
say_err() {
|
||||||
|
printf "%b\n" "${red:-}dotnet_install: Error: $1${normal:-}" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
say() {
|
||||||
|
# using stream 3 (defined in the beginning) to not interfere with stdout of functions
|
||||||
|
# which may be used as return value
|
||||||
|
printf "%b\n" "${cyan:-}dotnet-install:${normal:-} $1" >&3
|
||||||
|
}
|
||||||
|
|
||||||
|
say_verbose() {
|
||||||
|
if [ "$verbose" = true ]; then
|
||||||
|
say "$1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_os_download_name_from_platform() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
platform="$1"
|
||||||
|
case "$platform" in
|
||||||
|
"centos.7")
|
||||||
|
echo "centos"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"debian.8")
|
||||||
|
echo "debian"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"fedora.23")
|
||||||
|
echo "fedora.23"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"fedora.24")
|
||||||
|
echo "fedora.24"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"opensuse.13.2")
|
||||||
|
echo "opensuse.13.2"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"opensuse.42.1")
|
||||||
|
echo "opensuse.42.1"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"rhel.7"*)
|
||||||
|
echo "rhel"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"ubuntu.14.04")
|
||||||
|
echo "ubuntu"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"ubuntu.16.04")
|
||||||
|
echo "ubuntu.16.04"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"ubuntu.16.10")
|
||||||
|
echo "ubuntu.16.10"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"alpine.3.4.3")
|
||||||
|
echo "alpine"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_current_os_name() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local uname=$(uname)
|
||||||
|
if [ "$uname" = "Darwin" ]; then
|
||||||
|
echo "osx"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
if [ "$uname" = "Linux" ]; then
|
||||||
|
echo "linux"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
say_err "OS name could not be detected: $ID.$VERSION_ID"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get_distro_specific_os_name() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local uname=$(uname)
|
||||||
|
if [ "$uname" = "Darwin" ]; then
|
||||||
|
echo "osx"
|
||||||
|
return 0
|
||||||
|
elif [ -n "$runtime_id" ]; then
|
||||||
|
echo $(get_os_download_name_from_platform "${runtime_id%-*}" || echo "${runtime_id%-*}")
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
if [ -e /etc/os-release ]; then
|
||||||
|
. /etc/os-release
|
||||||
|
os=$(get_os_download_name_from_platform "$ID.$VERSION_ID" || echo "")
|
||||||
|
if [ -n "$os" ]; then
|
||||||
|
echo "$os"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
say_verbose "Distribution specific OS name and version could not be detected: $ID.$VERSION_ID"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
machine_has() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
hash "$1" > /dev/null 2>&1
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
check_min_reqs() {
|
||||||
|
local hasMinimum=false
|
||||||
|
if machine_has "curl"; then
|
||||||
|
hasMinimum=true
|
||||||
|
elif machine_has "wget"; then
|
||||||
|
hasMinimum=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$hasMinimum" = "false" ]; then
|
||||||
|
say_err "curl (recommended) or wget are required to download dotnet. Install missing prerequisite to proceed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
check_pre_reqs() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local failing=false;
|
||||||
|
|
||||||
|
if [ "${DOTNET_INSTALL_SKIP_PREREQS:-}" = "1" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(uname)" = "Linux" ]; then
|
||||||
|
if ! [ -x "$(command -v ldconfig)" ]; then
|
||||||
|
echo "ldconfig is not in PATH, trying /sbin/ldconfig."
|
||||||
|
LDCONFIG_COMMAND="/sbin/ldconfig"
|
||||||
|
else
|
||||||
|
LDCONFIG_COMMAND="ldconfig"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -z "$($LDCONFIG_COMMAND -p | grep libunwind)" ] && say_err "Unable to locate libunwind. Install libunwind to continue" && failing=true
|
||||||
|
[ -z "$($LDCONFIG_COMMAND -p | grep libssl)" ] && say_err "Unable to locate libssl. Install libssl to continue" && failing=true
|
||||||
|
[ -z "$($LDCONFIG_COMMAND -p | grep libicu)" ] && say_err "Unable to locate libicu. Install libicu to continue" && failing=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$failing" = true ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# input - $1
|
||||||
|
to_lowercase() {
|
||||||
|
#eval $invocation
|
||||||
|
|
||||||
|
echo "$1" | tr '[:upper:]' '[:lower:]'
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# input - $1
|
||||||
|
remove_trailing_slash() {
|
||||||
|
#eval $invocation
|
||||||
|
|
||||||
|
local input=${1:-}
|
||||||
|
echo "${input%/}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# input - $1
|
||||||
|
remove_beginning_slash() {
|
||||||
|
#eval $invocation
|
||||||
|
|
||||||
|
local input=${1:-}
|
||||||
|
echo "${input#/}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# root_path - $1
|
||||||
|
# child_path - $2 - this parameter can be empty
|
||||||
|
combine_paths() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
# TODO: Consider making it work with any number of paths. For now:
|
||||||
|
if [ ! -z "${3:-}" ]; then
|
||||||
|
say_err "combine_paths: Function takes two parameters."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local root_path=$(remove_trailing_slash $1)
|
||||||
|
local child_path=$(remove_beginning_slash ${2:-})
|
||||||
|
say_verbose "combine_paths: root_path=$root_path"
|
||||||
|
say_verbose "combine_paths: child_path=$child_path"
|
||||||
|
echo "$root_path/$child_path"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
get_machine_architecture() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
# Currently the only one supported
|
||||||
|
echo "x64"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# architecture - $1
|
||||||
|
get_normalized_architecture_from_architecture() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local architecture=$(to_lowercase $1)
|
||||||
|
case $architecture in
|
||||||
|
\<auto\>)
|
||||||
|
echo "$(get_normalized_architecture_from_architecture $(get_machine_architecture))"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
amd64|x64)
|
||||||
|
echo "x64"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
x86)
|
||||||
|
say_err "Architecture \`x86\` currently not supported"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
say_err "Architecture \`$architecture\` not supported. If you think this is a bug, please report it at https://github.com/dotnet/cli/issues"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# version_info is a conceptual two line string representing commit hash and 4-part version
|
||||||
|
# format:
|
||||||
|
# Line 1: # commit_hash
|
||||||
|
# Line 2: # 4-part version
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# version_text - stdin
|
||||||
|
get_version_from_version_info() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
cat | tail -n 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# version_text - stdin
|
||||||
|
get_commit_hash_from_version_info() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
cat | head -n 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# install_root - $1
|
||||||
|
# relative_path_to_package - $2
|
||||||
|
# specific_version - $3
|
||||||
|
is_dotnet_package_installed() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local install_root=$1
|
||||||
|
local relative_path_to_package=$2
|
||||||
|
local specific_version=${3//[$'\t\r\n']}
|
||||||
|
|
||||||
|
local dotnet_package_path=$(combine_paths $(combine_paths $install_root $relative_path_to_package) $specific_version)
|
||||||
|
say_verbose "is_dotnet_package_installed: dotnet_package_path=$dotnet_package_path"
|
||||||
|
|
||||||
|
if [ -d "$dotnet_package_path" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# azure_feed - $1
|
||||||
|
# channel - $2
|
||||||
|
# normalized_architecture - $3
|
||||||
|
get_latest_version_info() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local azure_feed=$1
|
||||||
|
local channel=$2
|
||||||
|
local normalized_architecture=$3
|
||||||
|
local coherent=$4
|
||||||
|
|
||||||
|
local version_file_url=null
|
||||||
|
if [ "$shared_runtime" = true ]; then
|
||||||
|
version_file_url="$uncached_feed/Runtime/$channel/latest.version"
|
||||||
|
else
|
||||||
|
if [ "$coherent" = true ]; then
|
||||||
|
version_file_url="$uncached_feed/Sdk/$channel/latest.coherent.version"
|
||||||
|
else
|
||||||
|
version_file_url="$uncached_feed/Sdk/$channel/latest.version"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
say_verbose "get_latest_version_info: latest url: $version_file_url"
|
||||||
|
|
||||||
|
download $version_file_url
|
||||||
|
return $?
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# azure_feed - $1
|
||||||
|
# channel - $2
|
||||||
|
# normalized_architecture - $3
|
||||||
|
# version - $4
|
||||||
|
get_specific_version_from_version() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local azure_feed=$1
|
||||||
|
local channel=$2
|
||||||
|
local normalized_architecture=$3
|
||||||
|
local version=$(to_lowercase $4)
|
||||||
|
|
||||||
|
case $version in
|
||||||
|
latest)
|
||||||
|
local version_info
|
||||||
|
version_info="$(get_latest_version_info $azure_feed $channel $normalized_architecture false)" || return 1
|
||||||
|
say_verbose "get_specific_version_from_version: version_info=$version_info"
|
||||||
|
echo "$version_info" | get_version_from_version_info
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
coherent)
|
||||||
|
local version_info
|
||||||
|
version_info="$(get_latest_version_info $azure_feed $channel $normalized_architecture true)" || return 1
|
||||||
|
say_verbose "get_specific_version_from_version: version_info=$version_info"
|
||||||
|
echo "$version_info" | get_version_from_version_info
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo $version
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# azure_feed - $1
|
||||||
|
# channel - $2
|
||||||
|
# normalized_architecture - $3
|
||||||
|
# specific_version - $4
|
||||||
|
construct_download_link() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local azure_feed=$1
|
||||||
|
local channel=$2
|
||||||
|
local normalized_architecture=$3
|
||||||
|
local specific_version=${4//[$'\t\r\n']}
|
||||||
|
|
||||||
|
local osname
|
||||||
|
osname=$(get_current_os_name) || return 1
|
||||||
|
|
||||||
|
local download_link=null
|
||||||
|
if [ "$shared_runtime" = true ]; then
|
||||||
|
download_link="$azure_feed/Runtime/$specific_version/dotnet-runtime-$specific_version-$osname-$normalized_architecture.tar.gz"
|
||||||
|
else
|
||||||
|
download_link="$azure_feed/Sdk/$specific_version/dotnet-sdk-$specific_version-$osname-$normalized_architecture.tar.gz"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$download_link"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# azure_feed - $1
|
||||||
|
# channel - $2
|
||||||
|
# normalized_architecture - $3
|
||||||
|
# specific_version - $4
|
||||||
|
construct_legacy_download_link() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local azure_feed=$1
|
||||||
|
local channel=$2
|
||||||
|
local normalized_architecture=$3
|
||||||
|
local specific_version=${4//[$'\t\r\n']}
|
||||||
|
|
||||||
|
local distro_specific_osname
|
||||||
|
distro_specific_osname=$(get_distro_specific_os_name) || return 1
|
||||||
|
|
||||||
|
local legacy_download_link=null
|
||||||
|
if [ "$shared_runtime" = true ]; then
|
||||||
|
legacy_download_link="$azure_feed/Runtime/$specific_version/dotnet-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz"
|
||||||
|
else
|
||||||
|
legacy_download_link="$azure_feed/Sdk/$specific_version/dotnet-dev-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$legacy_download_link"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
get_user_install_path() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
if [ ! -z "${DOTNET_INSTALL_DIR:-}" ]; then
|
||||||
|
echo $DOTNET_INSTALL_DIR
|
||||||
|
else
|
||||||
|
echo "$HOME/.dotnet"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# install_dir - $1
|
||||||
|
resolve_installation_path() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local install_dir=$1
|
||||||
|
if [ "$install_dir" = "<auto>" ]; then
|
||||||
|
local user_install_path=$(get_user_install_path)
|
||||||
|
say_verbose "resolve_installation_path: user_install_path=$user_install_path"
|
||||||
|
echo "$user_install_path"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$install_dir"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# install_root - $1
|
||||||
|
get_installed_version_info() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local install_root=$1
|
||||||
|
local version_file=$(combine_paths "$install_root" "$local_version_file_relative_path")
|
||||||
|
say_verbose "Local version file: $version_file"
|
||||||
|
if [ ! -z "$version_file" ] | [ -r "$version_file" ]; then
|
||||||
|
local version_info="$(cat $version_file)"
|
||||||
|
echo "$version_info"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
say_verbose "Local version file not found."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# relative_or_absolute_path - $1
|
||||||
|
get_absolute_path() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local relative_or_absolute_path=$1
|
||||||
|
echo $(cd $(dirname "$1") && pwd -P)/$(basename "$1")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# input_files - stdin
|
||||||
|
# root_path - $1
|
||||||
|
# out_path - $2
|
||||||
|
# override - $3
|
||||||
|
copy_files_or_dirs_from_list() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local root_path=$(remove_trailing_slash $1)
|
||||||
|
local out_path=$(remove_trailing_slash $2)
|
||||||
|
local override=$3
|
||||||
|
local override_switch=$(if [ "$override" = false ]; then printf -- "-n"; fi)
|
||||||
|
|
||||||
|
cat | uniq | while read -r file_path; do
|
||||||
|
local path=$(remove_beginning_slash ${file_path#$root_path})
|
||||||
|
local target=$out_path/$path
|
||||||
|
if [ "$override" = true ] || (! ([ -d "$target" ] || [ -e "$target" ])); then
|
||||||
|
mkdir -p $out_path/$(dirname $path)
|
||||||
|
cp -R $override_switch $root_path/$path $target
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# zip_path - $1
|
||||||
|
# out_path - $2
|
||||||
|
extract_dotnet_package() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local zip_path=$1
|
||||||
|
local out_path=$2
|
||||||
|
|
||||||
|
local temp_out_path=$(mktemp -d $temporary_file_template)
|
||||||
|
|
||||||
|
local failed=false
|
||||||
|
tar -xzf "$zip_path" -C "$temp_out_path" > /dev/null || failed=true
|
||||||
|
|
||||||
|
local folders_with_version_regex='^.*/[0-9]+\.[0-9]+[^/]+/'
|
||||||
|
find $temp_out_path -type f | grep -Eo $folders_with_version_regex | copy_files_or_dirs_from_list $temp_out_path $out_path false
|
||||||
|
find $temp_out_path -type f | grep -Ev $folders_with_version_regex | copy_files_or_dirs_from_list $temp_out_path $out_path true
|
||||||
|
|
||||||
|
rm -rf $temp_out_path
|
||||||
|
|
||||||
|
if [ "$failed" = true ]; then
|
||||||
|
say_err "Extraction failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# args:
|
||||||
|
# remote_path - $1
|
||||||
|
# [out_path] - $2 - stdout if not provided
|
||||||
|
download() {
|
||||||
|
eval $invocation
|
||||||
|
|
||||||
|
local remote_path=$1
|
||||||
|
local out_path=${2:-}
|
||||||
|
|
||||||
|
local failed=false
|
||||||
|
if machine_has "curl"; then
|
||||||
|
downloadcurl $remote_path $out_path || failed=true
|
||||||
|
elif machine_has "wget"; then
|
||||||
|
downloadwget $remote_path $out_path || failed=true
|
||||||
|
else
|
||||||
|
failed=true
|
||||||
|
fi
|
||||||
|
if [ "$failed" = true ]; then
|
||||||
|
say_verbose "Download failed: $remote_path"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadcurl() {
|
||||||
|
eval $invocation
|
||||||
|
local remote_path=$1
|
||||||
|
local out_path=${2:-}
|
||||||
|
|
||||||
|
local failed=false
|
||||||
|
if [ -z "$out_path" ]; then
|
||||||
|
curl --retry 10 -sSL -f --create-dirs $remote_path || failed=true
|
||||||
|
else
|
||||||
|
curl --retry 10 -sSL -f --create-dirs -o $out_path $remote_path || failed=true
|
||||||
|
fi
|
||||||
|
if [ "$failed" = true ]; then
|
||||||
|
say_verbose "Curl download failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadwget() {
|
||||||
|
eval $invocation
|
||||||
|
local remote_path=$1
|
||||||
|
local out_path=${2:-}
|
||||||
|
|
||||||
|
local failed=false
|
||||||
|
if [ -z "$out_path" ]; then
|
||||||
|
wget -q --tries 10 $remote_path || failed=true
|
||||||
|
else
|
||||||
|
wget -v --tries 10 -O $out_path $remote_path || failed=true
|
||||||
|
fi
|
||||||
|
if [ "$failed" = true ]; then
|
||||||
|
say_verbose "Wget download failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
calculate_vars() {
|
||||||
|
eval $invocation
|
||||||
|
valid_legacy_download_link=true
|
||||||
|
|
||||||
|
normalized_architecture=$(get_normalized_architecture_from_architecture "$architecture")
|
||||||
|
say_verbose "normalized_architecture=$normalized_architecture"
|
||||||
|
|
||||||
|
specific_version=$(get_specific_version_from_version $azure_feed $channel $normalized_architecture $version)
|
||||||
|
say_verbose "specific_version=$specific_version"
|
||||||
|
if [ -z "$specific_version" ]; then
|
||||||
|
say_err "Could not get version information."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
download_link=$(construct_download_link $azure_feed $channel $normalized_architecture $specific_version)
|
||||||
|
say_verbose "download_link=$download_link"
|
||||||
|
|
||||||
|
legacy_download_link=$(construct_legacy_download_link $azure_feed $channel $normalized_architecture $specific_version) || valid_legacy_download_link=false
|
||||||
|
|
||||||
|
if [ "$valid_legacy_download_link" = true ]; then
|
||||||
|
say_verbose "legacy_download_link=$legacy_download_link"
|
||||||
|
else
|
||||||
|
say_verbose "Cound not construct a legacy_download_link; omitting..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
install_root=$(resolve_installation_path $install_dir)
|
||||||
|
say_verbose "install_root=$install_root"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_dotnet() {
|
||||||
|
eval $invocation
|
||||||
|
local download_failed=false
|
||||||
|
|
||||||
|
if is_dotnet_package_installed $install_root "sdk" $specific_version; then
|
||||||
|
say ".NET SDK version $specific_version is already installed."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p $install_root
|
||||||
|
zip_path=$(mktemp $temporary_file_template)
|
||||||
|
say_verbose "Zip path: $zip_path"
|
||||||
|
|
||||||
|
say "Downloading link: $download_link"
|
||||||
|
download "$download_link" $zip_path || download_failed=true
|
||||||
|
|
||||||
|
# if the download fails, download the legacy_download_link
|
||||||
|
if [ "$download_failed" = true ] && [ "$valid_legacy_download_link" = true ]; then
|
||||||
|
say "Cannot download: $download_link"
|
||||||
|
download_link=$legacy_download_link
|
||||||
|
zip_path=$(mktemp $temporary_file_template)
|
||||||
|
say_verbose "Legacy zip path: $zip_path"
|
||||||
|
say "Downloading legacy link: $download_link"
|
||||||
|
download "$download_link" $zip_path
|
||||||
|
fi
|
||||||
|
|
||||||
|
say "Extracting zip from $download_link"
|
||||||
|
extract_dotnet_package $zip_path $install_root
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
local_version_file_relative_path="/.version"
|
||||||
|
bin_folder_relative_path=""
|
||||||
|
temporary_file_template="${TMPDIR:-/tmp}/dotnet.XXXXXXXXX"
|
||||||
|
|
||||||
|
channel="LTS"
|
||||||
|
version="Latest"
|
||||||
|
install_dir="<auto>"
|
||||||
|
architecture="<auto>"
|
||||||
|
dry_run=false
|
||||||
|
no_path=false
|
||||||
|
azure_feed="https://dotnetcli.azureedge.net/dotnet"
|
||||||
|
uncached_feed="https://dotnetcli.blob.core.windows.net/dotnet"
|
||||||
|
verbose=false
|
||||||
|
shared_runtime=false
|
||||||
|
runtime_id=""
|
||||||
|
|
||||||
|
while [ $# -ne 0 ]
|
||||||
|
do
|
||||||
|
name=$1
|
||||||
|
case $name in
|
||||||
|
-c|--channel|-[Cc]hannel)
|
||||||
|
shift
|
||||||
|
channel=$1
|
||||||
|
;;
|
||||||
|
-v|--version|-[Vv]ersion)
|
||||||
|
shift
|
||||||
|
version="$1"
|
||||||
|
;;
|
||||||
|
-i|--install-dir|-[Ii]nstall[Dd]ir)
|
||||||
|
shift
|
||||||
|
install_dir="$1"
|
||||||
|
;;
|
||||||
|
--arch|--architecture|-[Aa]rch|-[Aa]rchitecture)
|
||||||
|
shift
|
||||||
|
architecture="$1"
|
||||||
|
;;
|
||||||
|
--shared-runtime|-[Ss]hared[Rr]untime)
|
||||||
|
shared_runtime=true
|
||||||
|
;;
|
||||||
|
--dry-run|-[Dd]ry[Rr]un)
|
||||||
|
dry_run=true
|
||||||
|
;;
|
||||||
|
--no-path|-[Nn]o[Pp]ath)
|
||||||
|
no_path=true
|
||||||
|
;;
|
||||||
|
--verbose|-[Vv]erbose)
|
||||||
|
verbose=true
|
||||||
|
;;
|
||||||
|
--azure-feed|-[Aa]zure[Ff]eed)
|
||||||
|
shift
|
||||||
|
azure_feed="$1"
|
||||||
|
;;
|
||||||
|
--uncached-feed|-[Uu]ncached[Ff]eed)
|
||||||
|
shift
|
||||||
|
uncached_feed="$1"
|
||||||
|
;;
|
||||||
|
--runtime-id|-[Rr]untime[Ii]d)
|
||||||
|
shift
|
||||||
|
runtime_id="$1"
|
||||||
|
;;
|
||||||
|
-?|--?|-h|--help|-[Hh]elp)
|
||||||
|
script_name="$(basename $0)"
|
||||||
|
echo ".NET Tools Installer"
|
||||||
|
echo "Usage: $script_name [-c|--channel <CHANNEL>] [-v|--version <VERSION>] [-p|--prefix <DESTINATION>]"
|
||||||
|
echo " $script_name -h|-?|--help"
|
||||||
|
echo ""
|
||||||
|
echo "$script_name is a simple command line interface for obtaining dotnet cli."
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " -c,--channel <CHANNEL> Download from the CHANNEL specified, Defaults to \`$channel\`."
|
||||||
|
echo " -Channel"
|
||||||
|
echo " Possible values:"
|
||||||
|
echo " - Current - most current release"
|
||||||
|
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 " -v,--version <VERSION> Use specific VERSION, Defaults to \`$version\`."
|
||||||
|
echo " -Version"
|
||||||
|
echo " Possible values:"
|
||||||
|
echo " - latest - most latest build on specific channel"
|
||||||
|
echo " - coherent - most latest coherent build on specific channel"
|
||||||
|
echo " coherent applies only to SDK downloads"
|
||||||
|
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 " -i,--install-dir <DIR> Install under specified location (see Install Location below)"
|
||||||
|
echo " -InstallDir"
|
||||||
|
echo " --architecture <ARCHITECTURE> Architecture of .NET Tools. Currently only x64 is supported."
|
||||||
|
echo " --arch,-Architecture,-Arch"
|
||||||
|
echo " --shared-runtime Installs just the shared runtime bits, not the entire SDK."
|
||||||
|
echo " -SharedRuntime"
|
||||||
|
echo " --dry-run,-DryRun Do not perform installation. Display download link."
|
||||||
|
echo " --no-path, -NoPath Do not set PATH for the current process."
|
||||||
|
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 " --runtime-id Installs the .NET Tools for the given platform (use linux-x64 for portable linux)."
|
||||||
|
echo " -RuntimeId"
|
||||||
|
echo " -?,--?,-h,--help,-Help Shows this help message"
|
||||||
|
echo ""
|
||||||
|
echo "Install Location:"
|
||||||
|
echo " Location is chosen in following order:"
|
||||||
|
echo " - --install-dir option"
|
||||||
|
echo " - Environmental variable DOTNET_INSTALL_DIR"
|
||||||
|
echo " - $HOME/.dotnet"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
say_err "Unknown argument \`$name\`"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
check_min_reqs
|
||||||
|
calculate_vars
|
||||||
|
|
||||||
|
if [ "$dry_run" = true ]; then
|
||||||
|
say "Payload URL: $download_link"
|
||||||
|
if [ "$valid_legacy_download_link" = true ]; then
|
||||||
|
say "Legacy payload URL: $legacy_download_link"
|
||||||
|
fi
|
||||||
|
say "Repeatable invocation: ./$(basename $0) --version $specific_version --channel $channel --install-dir $install_dir"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
check_pre_reqs
|
||||||
|
install_dotnet
|
||||||
|
|
||||||
|
bin_path=$(get_absolute_path $(combine_paths $install_root $bin_folder_relative_path))
|
||||||
|
if [ "$no_path" = false ]; then
|
||||||
|
say "Adding to current process PATH: \`$bin_path\`. Note: This change will be visible only when sourcing script."
|
||||||
|
export PATH=$bin_path:$PATH
|
||||||
|
else
|
||||||
|
say "Binaries of dotnet can be found in $bin_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
say "Installation finished successfully."
|
363
src/installer.ts
Normal file
363
src/installer.ts
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
// Load tempDirectory before it gets wiped by tool-cache
|
||||||
|
let tempDirectory = process.env['RUNNER_TEMPDIRECTORY'] || '';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as io from '@actions/io';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
import httpClient = require('typed-rest-client/HttpClient');
|
||||||
|
import httpInterfaces = require('typed-rest-client/Interfaces');
|
||||||
|
import {HttpClientResponse} from 'typed-rest-client/HttpClient';
|
||||||
|
import {chmodSync} from 'fs';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as semver from 'semver';
|
||||||
|
import * as util from 'util';
|
||||||
|
|
||||||
|
const IS_WINDOWS = process.platform === 'win32';
|
||||||
|
|
||||||
|
if (!tempDirectory) {
|
||||||
|
let baseLocation;
|
||||||
|
if (IS_WINDOWS) {
|
||||||
|
// On windows use the USERPROFILE env variable
|
||||||
|
baseLocation = process.env['USERPROFILE'] || 'C:\\';
|
||||||
|
} else {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
baseLocation = '/Users';
|
||||||
|
} else {
|
||||||
|
baseLocation = '/home';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tempDirectory = path.join(baseLocation, 'actions', 'temp');
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DotnetCoreInstaller {
|
||||||
|
constructor(version: string) {
|
||||||
|
if (semver.valid(semver.clean(version) || '') == null) {
|
||||||
|
throw 'Implicit version not permitted';
|
||||||
|
}
|
||||||
|
this.version = version;
|
||||||
|
this.cachedToolName = 'dncs';
|
||||||
|
this.arch = 'x64';
|
||||||
|
}
|
||||||
|
|
||||||
|
public async installDotnet() {
|
||||||
|
// Check cache
|
||||||
|
let toolPath: string;
|
||||||
|
let osSuffixes = await this.detectMachineOS();
|
||||||
|
let parts = osSuffixes[0].split('-');
|
||||||
|
if (parts.length > 1) {
|
||||||
|
this.arch = parts[1];
|
||||||
|
}
|
||||||
|
toolPath = this.getLocalTool();
|
||||||
|
|
||||||
|
if (!toolPath) {
|
||||||
|
// download, extract, cache
|
||||||
|
console.log('Getting a download url', this.version);
|
||||||
|
let downloadUrls = await this.getDownloadUrls(osSuffixes, this.version);
|
||||||
|
toolPath = await this.downloadAndInstall(downloadUrls);
|
||||||
|
} else {
|
||||||
|
console.log('Using cached tool');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepend the tools path. instructs the agent to prepend for future tasks
|
||||||
|
core.addPath(toolPath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
let globalToolPath: string = '';
|
||||||
|
if (IS_WINDOWS) {
|
||||||
|
globalToolPath = path.join(
|
||||||
|
process.env.USERPROFILE || '',
|
||||||
|
'.dotnet\\tools'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
globalToolPath = path.join(process.env.HOME || '', '.dotnet/tools');
|
||||||
|
}
|
||||||
|
|
||||||
|
await io.mkdirP(globalToolPath);
|
||||||
|
core.addPath(globalToolPath);
|
||||||
|
} catch (error) {
|
||||||
|
//nop
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set DOTNET_ROOT for dotnet core Apphost to find runtime since it is installed to a non well-known location.
|
||||||
|
core.exportVariable('DOTNET_ROOT', toolPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getLocalTool(): string {
|
||||||
|
console.log('Checking tool cache');
|
||||||
|
return tc.find(this.cachedToolName, this.version, this.arch);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async detectMachineOS(): Promise<string[]> {
|
||||||
|
let osSuffix = [];
|
||||||
|
let output = '';
|
||||||
|
|
||||||
|
let resultCode = 0;
|
||||||
|
if (IS_WINDOWS) {
|
||||||
|
let escapedScript = path
|
||||||
|
.join(__dirname, '..', 'externals', 'get-os-platform.ps1')
|
||||||
|
.replace(/'/g, "''");
|
||||||
|
let command = `& '${escapedScript}'`;
|
||||||
|
|
||||||
|
const powershellPath = await io.which('powershell', true);
|
||||||
|
resultCode = await exec.exec(
|
||||||
|
`"${powershellPath}"`,
|
||||||
|
[
|
||||||
|
'-NoLogo',
|
||||||
|
'-Sta',
|
||||||
|
'-NoProfile',
|
||||||
|
'-NonInteractive',
|
||||||
|
'-ExecutionPolicy',
|
||||||
|
'Unrestricted',
|
||||||
|
'-Command',
|
||||||
|
command
|
||||||
|
],
|
||||||
|
{
|
||||||
|
listeners: {
|
||||||
|
stdout: (data: Buffer) => {
|
||||||
|
output += data.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let scriptPath = path.join(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'externals',
|
||||||
|
'get-os-distro.sh'
|
||||||
|
);
|
||||||
|
chmodSync(scriptPath, '777');
|
||||||
|
|
||||||
|
const toolPath = await io.which(scriptPath, true);
|
||||||
|
resultCode = await exec.exec(`"${toolPath}"`, [], {
|
||||||
|
listeners: {
|
||||||
|
stdout: (data: Buffer) => {
|
||||||
|
output += data.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode != 0) {
|
||||||
|
throw `Failed to detect os with result code ${resultCode}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let index;
|
||||||
|
if ((index = output.indexOf('Primary:')) >= 0) {
|
||||||
|
let primary = output.substr(index + 'Primary:'.length).split(os.EOL)[0];
|
||||||
|
osSuffix.push(primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((index = output.indexOf('Legacy:')) >= 0) {
|
||||||
|
let legacy = output.substr(index + 'Legacy:'.length).split(os.EOL)[0];
|
||||||
|
osSuffix.push(legacy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (osSuffix.length == 0) {
|
||||||
|
throw 'Could not detect platform';
|
||||||
|
}
|
||||||
|
|
||||||
|
return osSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async downloadAndInstall(downloadUrls: string[]) {
|
||||||
|
let downloaded = false;
|
||||||
|
let downloadPath = '';
|
||||||
|
for (const url of downloadUrls) {
|
||||||
|
try {
|
||||||
|
downloadPath = await tc.downloadTool(url);
|
||||||
|
downloaded = true;
|
||||||
|
break;
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Could Not Download', url, JSON.stringify(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!downloaded) {
|
||||||
|
throw 'Failed to download package';
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract
|
||||||
|
console.log('Extracting Package', downloadPath);
|
||||||
|
let extPath: string = IS_WINDOWS
|
||||||
|
? await tc.extractZip(downloadPath)
|
||||||
|
: await tc.extractTar(downloadPath);
|
||||||
|
|
||||||
|
// cache tool
|
||||||
|
console.log('Caching tool');
|
||||||
|
let cachedDir = await tc.cacheDir(
|
||||||
|
extPath,
|
||||||
|
this.cachedToolName,
|
||||||
|
this.version,
|
||||||
|
this.arch
|
||||||
|
);
|
||||||
|
console.log('Successfully installed', this.version);
|
||||||
|
return cachedDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OsSuffixes - The suffix which is a part of the file name ex- linux-x64, windows-x86
|
||||||
|
// Type - SDK / Runtime
|
||||||
|
// Version - Version of the SDK/Runtime
|
||||||
|
private async getDownloadUrls(
|
||||||
|
osSuffixes: string[],
|
||||||
|
version: string
|
||||||
|
): Promise<string[]> {
|
||||||
|
let downloadUrls = [];
|
||||||
|
let releasesJSON = await this.getReleasesJson();
|
||||||
|
|
||||||
|
let releasesInfo = JSON.parse(await releasesJSON.readBody());
|
||||||
|
releasesInfo = releasesInfo.filter((releaseInfo: any) => {
|
||||||
|
return (
|
||||||
|
releaseInfo['version-sdk'] === version ||
|
||||||
|
releaseInfo['version-sdk-display'] === version
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (releasesInfo.length != 0) {
|
||||||
|
let release = releasesInfo[0];
|
||||||
|
let blobUrl: string = release['blob-sdk'];
|
||||||
|
let dlcUrl: string = release['dlc--sdk'];
|
||||||
|
let fileName: string = release['sdk-' + osSuffixes[0]]
|
||||||
|
? release['sdk-' + osSuffixes[0]]
|
||||||
|
: release['sdk-' + osSuffixes[1]];
|
||||||
|
|
||||||
|
if (!!fileName) {
|
||||||
|
fileName = fileName.trim();
|
||||||
|
// For some latest version, the filename itself can be full download url.
|
||||||
|
// Do a very basic check for url(instead of regex) as the url is only for downloading and
|
||||||
|
// is coming from .net core releases json and not some ransom user input
|
||||||
|
if (fileName.toLowerCase().startsWith('https://')) {
|
||||||
|
downloadUrls.push(fileName);
|
||||||
|
} else {
|
||||||
|
if (!!blobUrl) {
|
||||||
|
downloadUrls.push(util.format('%s%s', blobUrl.trim(), fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!dlcUrl) {
|
||||||
|
downloadUrls.push(util.format('%s%s', dlcUrl.trim(), fileName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw `The specified version's download links are not correctly formed in the supported versions document => ${DotNetCoreReleasesUrl}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`Could not fetch download information for version ${version}`
|
||||||
|
);
|
||||||
|
downloadUrls = await this.getFallbackDownloadUrls(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downloadUrls.length == 0) {
|
||||||
|
throw `Could not construct download URL. Please ensure that specified version ${version} is valid.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return downloadUrls;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getReleasesJson(): Promise<HttpClientResponse> {
|
||||||
|
var httpCallbackClient = new httpClient.HttpClient('setup-dotnet', [], {});
|
||||||
|
return httpCallbackClient.get(DotNetCoreReleasesUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getFallbackDownloadUrls(version: string): Promise<string[]> {
|
||||||
|
let primaryUrlSearchString: string;
|
||||||
|
let legacyUrlSearchString: string;
|
||||||
|
let output = '';
|
||||||
|
let resultCode = 0;
|
||||||
|
|
||||||
|
if (IS_WINDOWS) {
|
||||||
|
let escapedScript = path
|
||||||
|
.join(__dirname, '..', 'externals', 'install-dotnet.ps1')
|
||||||
|
.replace(/'/g, "''");
|
||||||
|
let command = `& '${escapedScript}' -Version ${version} -DryRun`;
|
||||||
|
|
||||||
|
const powershellPath = await io.which('powershell', true);
|
||||||
|
resultCode = await exec.exec(
|
||||||
|
`"${powershellPath}"`,
|
||||||
|
[
|
||||||
|
'-NoLogo',
|
||||||
|
'-Sta',
|
||||||
|
'-NoProfile',
|
||||||
|
'-NonInteractive',
|
||||||
|
'-ExecutionPolicy',
|
||||||
|
'Unrestricted',
|
||||||
|
'-Command',
|
||||||
|
command
|
||||||
|
],
|
||||||
|
{
|
||||||
|
listeners: {
|
||||||
|
stdout: (data: Buffer) => {
|
||||||
|
output += data.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
primaryUrlSearchString = 'dotnet-install: Primary - ';
|
||||||
|
legacyUrlSearchString = 'dotnet-install: Legacy - ';
|
||||||
|
} else {
|
||||||
|
let escapedScript = path
|
||||||
|
.join(__dirname, '..', 'externals', 'install-dotnet.sh')
|
||||||
|
.replace(/'/g, "''");
|
||||||
|
chmodSync(escapedScript, '777');
|
||||||
|
|
||||||
|
const scriptPath = await io.which(escapedScript, true);
|
||||||
|
resultCode = await exec.exec(
|
||||||
|
`"${scriptPath}"`,
|
||||||
|
['--version', version, '--dry-run'],
|
||||||
|
{
|
||||||
|
listeners: {
|
||||||
|
stdout: (data: Buffer) => {
|
||||||
|
output += data.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
primaryUrlSearchString = 'dotnet-install: Payload URL: ';
|
||||||
|
legacyUrlSearchString = 'dotnet-install: Legacy payload URL: ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode != 0) {
|
||||||
|
throw `Failed to get download urls with result code ${resultCode}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let primaryUrl: string = '';
|
||||||
|
let legacyUrl: string = '';
|
||||||
|
if (!!output && output.length > 0) {
|
||||||
|
let lines: string[] = output.split(os.EOL);
|
||||||
|
if (!!lines && lines.length > 0) {
|
||||||
|
lines.forEach((line: string) => {
|
||||||
|
if (!line) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var primarySearchStringIndex = line.indexOf(primaryUrlSearchString);
|
||||||
|
if (primarySearchStringIndex > -1) {
|
||||||
|
primaryUrl = line.substring(
|
||||||
|
primarySearchStringIndex + primaryUrlSearchString.length
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var legacySearchStringIndex = line.indexOf(legacyUrlSearchString);
|
||||||
|
if (legacySearchStringIndex > -1) {
|
||||||
|
legacyUrl = line.substring(
|
||||||
|
legacySearchStringIndex + legacyUrlSearchString.length
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [primaryUrl, legacyUrl];
|
||||||
|
}
|
||||||
|
|
||||||
|
private version: string;
|
||||||
|
private cachedToolName: string;
|
||||||
|
private arch: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DotNetCoreReleasesUrl: string =
|
||||||
|
'https://raw.githubusercontent.com/dotnet/core/master/release-notes/releases.json';
|
23
src/setup-dotnet.ts
Normal file
23
src/setup-dotnet.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as installer from './installer';
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
try {
|
||||||
|
//
|
||||||
|
// Version is optional. If supplied, install / use from the tool cache
|
||||||
|
// If not supplied then task is still used to setup proxy, auth, etc...
|
||||||
|
//
|
||||||
|
const version = core.getInput('version');
|
||||||
|
if (version) {
|
||||||
|
const dotnetInstaller = new installer.DotnetCoreInstaller(version);
|
||||||
|
await dotnetInstaller.installDotnet();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: setup proxy from runner proxy config
|
||||||
|
// TODO: problem matchers registered
|
||||||
|
} catch (error) {
|
||||||
|
core.setFailed(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
Loading…
x
Reference in New Issue
Block a user