GitHub Actions Runner

This commit is contained in:
Tingluo Huang
2019-10-10 00:52:42 -04:00
commit c8afc84840
1255 changed files with 198670 additions and 0 deletions

650
src/Misc/dotnet-install.ps1 vendored Normal file
View File

@@ -0,0 +1,650 @@
#
# 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
Note: The version parameter overrides the channel parameter.
.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>, amd64, x64, x86, arm64, arm
.PARAMETER SharedRuntime
This parameter is obsolete and may be removed in a future version of this script.
The recommended alternative is '-Runtime dotnet'.
Default: false
Installs just the shared runtime bits, not the entire SDK.
This is equivalent to specifying `-Runtime dotnet`.
.PARAMETER Runtime
Installs just a shared runtime, not the entire SDK.
Possible values:
- dotnet - the Microsoft.NETCore.App shared runtime
- aspnetcore - the Microsoft.AspNetCore.App shared runtime
- windowsdesktop - the Microsoft.WindowsDesktop.App shared runtime
.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 changing the URL for the Azure feed used by this installer.
.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
Default: false
Use default credentials, when using proxy address.
.PARAMETER SkipNonVersionedFiles
Default: false
Skips installing non-versioned files if they already exist, such as dotnet.exe.
.PARAMETER NoCdn
Disable downloading from the Azure CDN, and use the uncached feed directly.
#>
[cmdletbinding()]
param(
[string]$Channel="LTS",
[string]$Version="Latest",
[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,
[switch]$DryRun,
[switch]$NoPath,
[string]$AzureFeed="https://dotnetcli.azureedge.net/dotnet",
[string]$UncachedFeed="https://dotnetcli.blob.core.windows.net/dotnet",
[string]$FeedCredential,
[string]$ProxyAddress,
[switch]$ProxyUseDefaultCredentials,
[switch]$SkipNonVersionedFiles,
[switch]$NoCdn
)
Set-StrictMode -Version Latest
$ErrorActionPreference="Stop"
$ProgressPreference="SilentlyContinue"
if ($NoCdn) {
$AzureFeed = $UncachedFeed
}
$BinFolderRelativePath=""
if ($SharedRuntime -and (-not $Runtime)) {
$Runtime = "dotnet"
}
# example path with regex: shared/1.0.0-beta-12345/somepath
$VersionRegEx="/\d+\.\d+[^/]+/"
$OverrideNonVersionedFiles = !$SkipNonVersionedFiles
function Say($str) {
Write-Host "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, x64, x86, arm64, arm
return $ENV:PROCESSOR_ARCHITECTURE
}
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" }
{ $_ -eq "arm" } { return "arm" }
{ $_ -eq "arm64" } { return "arm64" }
default { throw "Architecture not supported. If you think this is a bug, report it at https://github.com/dotnet/cli/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
# Line 2: # 4-part version
# For the aspnetcore runtime (1 line):
# Line 1: # 4-part version
function Get-Version-Info-From-Version-Text([string]$VersionText) {
Say-Invocation $MyInvocation
$Data = -split $VersionText
$VersionInfo = @{
CommitHash = $(if ($Data.Count -gt 1) { $Data[0] })
Version = $Data[-1] # last line is always the version number.
}
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) {
try {
# 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
}
} catch {
# Eat the exception and move forward as the above code is an attempt
# at resolving the DefaultProxy that may not have been a problem.
$ProxyAddress = $null
Say-Verbose("Exception ignored: $_.Exception.Message - moving forward...")
}
}
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
# 20 minutes allows it to work over much slower connections.
$HttpClient.Timeout = New-TimeSpan -Minutes 20
$Response = $HttpClient.GetAsync("${Uri}${FeedCredential}").Result
if (($Response -eq $null) -or (-not ($Response.IsSuccessStatusCode))) {
# The feed credential is potentially sensitive info. Do not log FeedCredential to console output.
$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 ($Runtime -eq "dotnet") {
$VersionFileUrl = "$UncachedFeed/Runtime/$Channel/latest.version"
}
elseif ($Runtime -eq "aspnetcore") {
$VersionFileUrl = "$UncachedFeed/aspnetcore/Runtime/$Channel/latest.version"
}
# Currently, the WindowsDesktop runtime is manufactured with the .Net core runtime
elseif ($Runtime -eq "windowsdesktop") {
$VersionFileUrl = "$UncachedFeed/Runtime/$Channel/latest.version"
}
elseif (-not $Runtime) {
if ($Coherent) {
$VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.coherent.version"
}
else {
$VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.version"
}
}
else {
throw "Invalid value for `$Runtime"
}
try {
$Response = GetHTTPResponse -Uri $VersionFileUrl
}
catch {
throw "Could not resolve version information."
}
$StringContent = $Response.Content.ReadAsStringAsync().Result
switch ($Response.Content.Headers.ContentType) {
{ ($_ -eq "application/octet-stream") } { $VersionText = $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]$SpecificVersion, [string]$CLIArchitecture) {
Say-Invocation $MyInvocation
if ($Runtime -eq "dotnet") {
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-runtime-$SpecificVersion-win-$CLIArchitecture.zip"
}
elseif ($Runtime -eq "aspnetcore") {
$PayloadURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/aspnetcore-runtime-$SpecificVersion-win-$CLIArchitecture.zip"
}
elseif ($Runtime -eq "windowsdesktop") {
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificVersion-win-$CLIArchitecture.zip"
}
elseif (-not $Runtime) {
$PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificVersion-win-$CLIArchitecture.zip"
}
else {
throw "Invalid value for `$Runtime"
}
Say-Verbose "Constructed primary named payload URL: $PayloadURL"
return $PayloadURL
}
function Get-LegacyDownload-Link([string]$AzureFeed, [string]$SpecificVersion, [string]$CLIArchitecture) {
Say-Invocation $MyInvocation
if (-not $Runtime) {
$PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-dev-win-$CLIArchitecture.$SpecificVersion.zip"
}
elseif ($Runtime -eq "dotnet") {
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-win-$CLIArchitecture.$SpecificVersion.zip"
}
else {
return $null
}
Say-Verbose "Constructed legacy named 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($Source, [string]$OutPath) {
if ($Source -notlike "http*") {
# Using System.IO.Path.GetFullPath to get the current directory
# does not work in this context - $pwd gives the current directory
if (![System.IO.Path]::IsPathRooted($Source)) {
$Source = $(Join-Path -Path $pwd -ChildPath $Source)
}
$Source = Get-Absolute-Path $Source
Say "Copying file from $Source to $OutPath"
Copy-Item $Source $OutPath
return
}
$Stream = $null
try {
$Response = GetHTTPResponse -Uri $Source
$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) {
$SuffixedBinPath = "$BinPath;"
if (-Not $env:path.Contains($SuffixedBinPath)) {
Say "Adding to current process PATH: `"$BinPath`". Note: This change will not be visible if PowerShell was run as a child process."
$env:path = $SuffixedBinPath + $env:path
} else {
Say-Verbose "Current process PATH already contains `"$BinPath`""
}
}
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 -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
$LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
$InstallRoot = Resolve-Installation-Path $InstallDir
Say-Verbose "InstallRoot: $InstallRoot"
$ScriptName = $MyInvocation.MyCommand.Name
if ($DryRun) {
Say "Payload URLs:"
Say "Primary named payload URL: $DownloadLink"
if ($LegacyDownloadLink) {
Say "Legacy named payload URL: $LegacyDownloadLink"
}
$RepeatableCommand = ".\$ScriptName -Version `"$SpecificVersion`" -InstallDir `"$InstallRoot`" -Architecture `"$CLIArchitecture`""
if ($Runtime -eq "dotnet") {
$RepeatableCommand+=" -Runtime `"dotnet`""
}
elseif ($Runtime -eq "aspnetcore") {
$RepeatableCommand+=" -Runtime `"aspnetcore`""
}
foreach ($key in $MyInvocation.BoundParameters.Keys) {
if (-not (@("Architecture","Channel","DryRun","InstallDir","Runtime","SharedRuntime","Version") -contains $key)) {
$RepeatableCommand+=" -$key `"$($MyInvocation.BoundParameters[$key])`""
}
}
Say "Repeatable invocation: $RepeatableCommand"
exit 0
}
if ($Runtime -eq "dotnet") {
$assetName = ".NET Core Runtime"
$dotnetPackageRelativePath = "shared\Microsoft.NETCore.App"
}
elseif ($Runtime -eq "aspnetcore") {
$assetName = "ASP.NET Core Runtime"
$dotnetPackageRelativePath = "shared\Microsoft.AspNetCore.App"
}
elseif ($Runtime -eq "windowsdesktop") {
$assetName = ".NET Core Windows Desktop Runtime"
$dotnetPackageRelativePath = "shared\Microsoft.WindowsDesktop.App"
}
elseif (-not $Runtime) {
$assetName = ".NET Core SDK"
$dotnetPackageRelativePath = "sdk"
}
else {
throw "Invalid value for `$Runtime"
}
# Check if the SDK version is already installed.
$isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion
if ($isAssetInstalled) {
Say "$assetName 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);
$diskInfo = Get-PSDrive -Name $installDrive
if ($diskInfo.Free / 1MB -le 100) {
Say "There is not enough disk space on drive ${installDrive}:"
exit 0
}
$ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
Say-Verbose "Zip path: $ZipPath"
$DownloadFailed = $false
Say "Downloading link: $DownloadLink"
try {
DownloadFile -Source $DownloadLink -OutPath $ZipPath
}
catch {
Say "Cannot download: $DownloadLink"
if ($LegacyDownloadLink) {
$DownloadLink = $LegacyDownloadLink
$ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
Say-Verbose "Legacy zip path: $ZipPath"
Say "Downloading legacy link: $DownloadLink"
try {
DownloadFile -Source $DownloadLink -OutPath $ZipPath
}
catch {
Say "Cannot download: $DownloadLink"
$DownloadFailed = $true
}
}
else {
$DownloadFailed = $true
}
}
if ($DownloadFailed) {
throw "Could not find/download: `"$assetName`" with version = $SpecificVersion`nRefer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support"
}
Say "Extracting zip from $DownloadLink"
Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot
# Check if the SDK version is now installed; if not, fail the installation.
$isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion
if (!$isAssetInstalled) {
throw "`"$assetName`" with version = $SpecificVersion failed to install with an unknown error."
}
Remove-Item $ZipPath
Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath $BinFolderRelativePath
Say "Installation finished"
exit 0

1025
src/Misc/dotnet-install.sh vendored Executable file

File diff suppressed because it is too large Load Diff

148
src/Misc/externals.sh Executable file
View File

@@ -0,0 +1,148 @@
#!/bin/bash
PACKAGERUNTIME=$1
PRECACHE=$2
NODE_URL=https://nodejs.org/dist
NODE12_VERSION="12.4.0"
get_abs_path() {
# exploits the fact that pwd will print abs path when no args
echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
}
LAYOUT_DIR=$(get_abs_path "$(dirname $0)/../../_layout")
DOWNLOAD_DIR="$(get_abs_path "$(dirname $0)/../../_downloads")/netcore2x"
function failed() {
local error=${1:-Undefined error}
echo "Failed: $error" >&2
exit 1
}
function checkRC() {
local rc=$?
if [ $rc -ne 0 ]; then
failed "${1} failed with return code $rc"
fi
}
function acquireExternalTool() {
local download_source=$1 # E.g. https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe
local target_dir="$LAYOUT_DIR/externals/$2" # E.g. $LAYOUT_DIR/externals/vswhere
local fix_nested_dir=$3 # Flag that indicates whether to move nested contents up one directory.
# Extract the portion of the URL after the protocol. E.g. github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe
local relative_url="${download_source#*://}"
# Check if the download already exists.
local download_target="$DOWNLOAD_DIR/$relative_url"
local download_basename="$(basename "$download_target")"
local download_dir="$(dirname "$download_target")"
if [[ "$PRECACHE" != "" ]]; then
if [ -f "$download_target" ]; then
echo "Download exists: $download_basename"
else
# Delete any previous partial file.
local partial_target="$DOWNLOAD_DIR/partial/$download_basename"
mkdir -p "$(dirname "$partial_target")" || checkRC 'mkdir'
if [ -f "$partial_target" ]; then
rm "$partial_target" || checkRC 'rm'
fi
# Download from source to the partial file.
echo "Downloading $download_source"
mkdir -p "$(dirname "$download_target")" || checkRC 'mkdir'
# curl -f Fail silently (no output at all) on HTTP errors (H)
# -k Allow connections to SSL sites without certs (H)
# -S Show error. With -s, make curl show errors when they occur
# -L Follow redirects (H)
# -o FILE Write to FILE instead of stdout
curl -fkSL -o "$partial_target" "$download_source" 2>"${download_target}_download.log" || checkRC 'curl'
# Move the partial file to the download target.
mv "$partial_target" "$download_target" || checkRC 'mv'
# Extract to current directory
# Ensure we can extract those files
# We might use them during dev.sh
if [[ "$download_basename" == *.zip ]]; then
# Extract the zip.
echo "Testing zip"
unzip "$download_target" -d "$download_dir" > /dev/null
local rc=$?
if [[ $rc -ne 0 && $rc -ne 1 ]]; then
failed "unzip failed with return code $rc"
fi
elif [[ "$download_basename" == *.tar.gz ]]; then
# Extract the tar gz.
echo "Testing tar gz"
tar xzf "$download_target" -C "$download_dir" > /dev/null || checkRC 'tar'
fi
fi
else
# Extract to layout.
mkdir -p "$target_dir" || checkRC 'mkdir'
local nested_dir=""
if [[ "$download_basename" == *.zip ]]; then
# Extract the zip.
echo "Extracting zip to layout"
unzip "$download_target" -d "$target_dir" > /dev/null
local rc=$?
if [[ $rc -ne 0 && $rc -ne 1 ]]; then
failed "unzip failed with return code $rc"
fi
# Capture the nested directory path if the fix_nested_dir flag is set.
if [[ "$fix_nested_dir" == "fix_nested_dir" ]]; then
nested_dir="${download_basename%.zip}" # Remove the trailing ".zip".
fi
elif [[ "$download_basename" == *.tar.gz ]]; then
# Extract the tar gz.
echo "Extracting tar gz to layout"
tar xzf "$download_target" -C "$target_dir" > /dev/null || checkRC 'tar'
# Capture the nested directory path if the fix_nested_dir flag is set.
if [[ "$fix_nested_dir" == "fix_nested_dir" ]]; then
nested_dir="${download_basename%.tar.gz}" # Remove the trailing ".tar.gz".
fi
else
# Copy the file.
echo "Copying to layout"
cp "$download_target" "$target_dir/" || checkRC 'cp'
fi
# Fixup the nested directory.
if [[ "$nested_dir" != "" ]]; then
if [ -d "$target_dir/$nested_dir" ]; then
mv "$target_dir/$nested_dir"/* "$target_dir/" || checkRC 'mv'
rmdir "$target_dir/$nested_dir" || checkRC 'rmdir'
fi
fi
fi
}
# Download the external tools only for Windows.
if [[ "$PACKAGERUNTIME" == "win-x64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/win-x64/node.exe" node12/bin
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/win-x64/node.lib" node12/bin
if [[ "$PRECACHE" != "" ]]; then
acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere
fi
fi
# Download the external tools only for OSX.
if [[ "$PACKAGERUNTIME" == "osx-x64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-darwin-x64.tar.gz" node12 fix_nested_dir
fi
# Download the external tools common across Linux PACKAGERUNTIMEs (excluding OSX).
if [[ "$PACKAGERUNTIME" == "linux-x64" || "$PACKAGERUNTIME" == "rhel.6-x64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-x64.tar.gz" node12 fix_nested_dir
# TODO: Repath this blob to use a consistent version format (_ vs .)
acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/12_4_0/alpine/node-v${NODE12_VERSION}-alpine.tar.gz" node12_alpine
fi
if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-armv7l.tar.gz" node12 fix_nested_dir
fi

View File

@@ -0,0 +1,91 @@
#!/usr/bin/env node
// Copyright (c) GitHub. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
var childProcess = require("child_process");
var path = require("path")
var supported = ['linux', 'darwin']
if (supported.indexOf(process.platform) == -1) {
console.log('Unsupported platform: ' + process.platform);
console.log('Supported platforms are: ' + supported.toString());
process.exit(1);
}
var stopping = false;
var listener = null;
var runService = function() {
var listenerExePath = path.join(__dirname, '../bin/Runner.Listener');
var interactive = process.argv[2] === "interactive";
if(!stopping) {
try {
if (interactive) {
console.log('Starting Runner listener interactively');
listener = childProcess.spawn(listenerExePath, ['run'], { env: process.env });
} else {
console.log('Starting Runner listener with startup type: service');
listener = childProcess.spawn(listenerExePath, ['run', '--startuptype', 'service'], { env: process.env });
}
console.log('Started listener process');
listener.stdout.on('data', (data) => {
process.stdout.write(data.toString('utf8'));
});
listener.stderr.on('data', (data) => {
process.stdout.write(data.toString('utf8'));
});
listener.on('close', (code) => {
console.log(`Runner listener exited with error code ${code}`);
if (code === 0) {
console.log('Runner listener exit with 0 return code, stop the service, no retry needed.');
stopping = true;
} else if (code === 1) {
console.log('Runner listener exit with terminated error, stop the service, no retry needed.');
stopping = true;
} else if (code === 2) {
console.log('Runner listener exit with retryable error, re-launch runner in 5 seconds.');
} else if (code === 3) {
console.log('Runner listener exit because of updating, re-launch runner in 5 seconds.');
} else {
console.log('Runner listener exit with undefined return code, re-launch runner in 5 seconds.');
}
if(!stopping) {
setTimeout(runService, 5000);
}
});
} catch(ex) {
console.log(ex);
}
}
}
runService();
console.log('Started running service');
var gracefulShutdown = function(code) {
console.log('Shutting down runner listener');
stopping = true;
if (listener) {
console.log('Sending SIGINT to runner listener to stop');
listener.kill('SIGINT');
// TODO wait for 30 seconds and send a SIGKILL
}
}
process.on('SIGINT', () => {
gracefulShutdown(0);
});
process.on('SIGTERM', () => {
gracefulShutdown(0);
});

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>{{SvcName}}</string>
<key>ProgramArguments</key>
<array>
<string>{{RunnerRoot}}/runsvc.sh</string>
</array>
<key>UserName</key>
<string>{{User}}</string>
<key>WorkingDirectory</key>
<string>{{RunnerRoot}}</string>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>{{UserHome}}/Library/Logs/{{SvcName}}/stdout.log</string>
<key>StandardErrorPath</key>
<string>{{UserHome}}/Library/Logs/{{SvcName}}/stderr.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>ACTIONS_RUNNER_SVC</key>
<string>1</string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,14 @@
[Unit]
Description={{Description}}
After=network.target
[Service]
ExecStart={{RunnerRoot}}/runsvc.sh
User={{User}}
WorkingDirectory={{RunnerRoot}}
KillMode=process
KillSignal=SIGTERM
TimeoutStopSec=5min
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,135 @@
#!/bin/bash
SVC_NAME="{{SvcNameVar}}"
SVC_DESCRIPTION="{{SvcDescription}}"
user_id=`id -u`
# launchctl should not run as sudo for launch runners
if [ $user_id -eq 0 ]; then
echo "Must not run with sudo"
exit 1
fi
SVC_CMD=$1
RUNNER_ROOT=`pwd`
LAUNCH_PATH="${HOME}/Library/LaunchAgents"
PLIST_PATH="${LAUNCH_PATH}/${SVC_NAME}.plist"
TEMPLATE_PATH=./bin/actions.runner.plist.template
TEMP_PATH=./bin/actions.runner.plist.temp
CONFIG_PATH=.service
function failed()
{
local error=${1:-Undefined error}
echo "Failed: $error" >&2
exit 1
}
if [ ! -f "${TEMPLATE_PATH}" ]; then
failed "Must run from runner root or install is corrupt"
fi
function install()
{
echo "Creating launch runner in ${PLIST_PATH}"
if [ ! -d "${LAUNCH_PATH}" ]; then
mkdir ${LAUNCH_PATH}
fi
if [ -f "${PLIST_PATH}" ]; then
failed "error: exists ${PLIST_PATH}"
fi
if [ -f "${TEMP_PATH}" ]; then
rm "${TEMP_PATH}" || failed "failed to delete ${TEMP_PATH}"
fi
log_path="${HOME}/Library/Logs/${SVC_NAME}"
echo "Creating ${log_path}"
mkdir -p "${log_path}" || failed "failed to create ${log_path}"
echo Creating ${PLIST_PATH}
sed "s/{{User}}/${SUDO_USER:-$USER}/g; s/{{SvcName}}/$SVC_NAME/g; s@{{RunnerRoot}}@${RUNNER_ROOT}@g; s@{{UserHome}}@$HOME@g;" "${TEMPLATE_PATH}" > "${TEMP_PATH}" || failed "failed to create replacement temp file"
mv "${TEMP_PATH}" "${PLIST_PATH}" || failed "failed to copy plist"
# Since we started with sudo, runsvc.sh will be owned by root. Change this to current login user.
echo Creating runsvc.sh
cp ./bin/runsvc.sh ./runsvc.sh || failed "failed to copy runsvc.sh"
chmod u+x ./runsvc.sh || failed "failed to set permission for runsvc.sh"
echo Creating ${CONFIG_PATH}
echo "${PLIST_PATH}" > ${CONFIG_PATH} || failed "failed to create .Service file"
echo "svc install complete"
}
function start()
{
echo "starting ${SVC_NAME}"
launchctl load -w "${PLIST_PATH}" || failed "failed to load ${PLIST_PATH}"
status
}
function stop()
{
echo "stopping ${SVC_NAME}"
launchctl unload "${PLIST_PATH}" || failed "failed to unload ${PLIST_PATH}"
status
}
function uninstall()
{
echo "uninstalling ${SVC_NAME}"
stop
rm "${PLIST_PATH}" || failed "failed to delete ${PLIST_PATH}"
if [ -f "${CONFIG_PATH}" ]; then
rm "${CONFIG_PATH}" || failed "failed to delete ${CONFIG_PATH}"
fi
}
function status()
{
echo "status ${SVC_NAME}:"
if [ -f "${PLIST_PATH}" ]; then
echo
echo "${PLIST_PATH}"
else
echo
echo "not installed"
echo
return
fi
echo
status_out=`launchctl list | grep "${SVC_NAME}"`
if [ ! -z "$status_out" ]; then
echo Started:
echo $status_out
echo
else
echo Stopped
echo
fi
}
function usage()
{
echo
echo Usage:
echo "./svc.sh [install, start, stop, status, uninstall]"
echo
}
case $SVC_CMD in
"install") install;;
"status") status;;
"uninstall") uninstall;;
"start") start;;
"stop") stop;;
*) usage;;
esac
exit 0

View File

@@ -0,0 +1,298 @@
#!/bin/bash
user_id=`id -u`
if [ $user_id -ne 0 ]; then
echo "Need to run with sudo privilege"
exit 1
fi
# Determine OS type
# Debian based OS (Debian, Ubuntu, Linux Mint) has /etc/debian_version
# Fedora based OS (Fedora, Redhat, Centos, Oracle Linux 7) has /etc/redhat-release
# SUSE based OS (OpenSUSE, SUSE Enterprise) has ID_LIKE=suse in /etc/os-release
function print_errormessage()
{
echo "Can't install dotnet core dependencies."
echo "You can manually install all required dependencies based on following documentation"
echo "https://docs.microsoft.com/en-us/dotnet/core/linux-prerequisites?tabs=netcore2x"
}
function print_rhel6message()
{
echo "We did our best effort to install dotnet core dependencies"
echo "However, there are some dependencies which require manual installation"
echo "You can install all remaining required dependencies based on the following documentation"
echo "https://github.com/dotnet/core/blob/master/Documentation/build-and-install-rhel6-prerequisites.md"
}
function print_rhel6errormessage()
{
echo "We couldn't install dotnet core dependencies"
echo "You can manually install all required dependencies based on following documentation"
echo "https://docs.microsoft.com/en-us/dotnet/core/linux-prerequisites?tabs=netcore2x"
echo "In addition, there are some dependencies which require manual installation. Please follow this documentation"
echo "https://github.com/dotnet/core/blob/master/Documentation/build-and-install-rhel6-prerequisites.md"
}
if [ -e /etc/os-release ]
then
echo "--------OS Information--------"
cat /etc/os-release
echo "------------------------------"
if [ -e /etc/debian_version ]
then
echo "The current OS is Debian based"
echo "--------Debian Version--------"
cat /etc/debian_version
echo "------------------------------"
# prefer apt over apt-get
command -v apt
if [ $? -eq 0 ]
then
apt update && apt install -y liblttng-ust0 libkrb5-3 zlib1g
if [ $? -ne 0 ]
then
echo "'apt' failed with exit code '$?'"
print_errormessage
exit 1
fi
# ubuntu 18 uses libcurl4
# ubuntu 14, 16 and other linux use libcurl3
apt install -y libcurl3 || apt install -y libcurl4
if [ $? -ne 0 ]
then
echo "'apt' failed with exit code '$?'"
print_errormessage
exit 1
fi
# debian 9 use libssl1.0.2
# other debian linux use libssl1.0.0
apt install -y libssl1.0.0 || apt install -y libssl1.0.2
if [ $? -ne 0 ]
then
echo "'apt' failed with exit code '$?'"
print_errormessage
exit 1
fi
# libicu version prefer: libicu52 -> libicu55 -> libicu57 -> libicu60
apt install -y libicu52 || apt install -y libicu55 || apt install -y libicu57 || apt install -y libicu60
if [ $? -ne 0 ]
then
echo "'apt' failed with exit code '$?'"
print_errormessage
exit 1
fi
else
command -v apt-get
if [ $? -eq 0 ]
then
apt-get update && apt-get install -y liblttng-ust0 libkrb5-3 zlib1g
if [ $? -ne 0 ]
then
echo "'apt-get' failed with exit code '$?'"
print_errormessage
exit 1
fi
# ubuntu 18 uses libcurl4
# ubuntu 14, 16 and other linux use libcurl3
apt-get install -y libcurl3 || apt-get install -y libcurl4
if [ $? -ne 0 ]
then
echo "'apt-get' failed with exit code '$?'"
print_errormessage
exit 1
fi
# debian 9 use libssl1.0.2
# other debian linux use libssl1.0.0
apt-get install -y libssl1.0.0 || apt install -y libssl1.0.2
if [ $? -ne 0 ]
then
echo "'apt-get' failed with exit code '$?'"
print_errormessage
exit 1
fi
# libicu version prefer: libicu52 -> libicu55 -> libicu57 -> libicu60
apt-get install -y libicu52 || apt install -y libicu55 || apt install -y libicu57 || apt install -y libicu60
if [ $? -ne 0 ]
then
echo "'apt-get' failed with exit code '$?'"
print_errormessage
exit 1
fi
else
echo "Can not find 'apt' or 'apt-get'"
print_errormessage
exit 1
fi
fi
elif [ -e /etc/redhat-release ]
then
echo "The current OS is Fedora based"
echo "--------Redhat Version--------"
cat /etc/redhat-release
echo "------------------------------"
# use dnf on fedora
# use yum on centos and redhat
if [ -e /etc/fedora-release ]
then
command -v dnf
if [ $? -eq 0 ]
then
useCompatSsl=0
grep -i 'fedora release 28' /etc/fedora-release
if [ $? -eq 0 ]
then
useCompatSsl=1
else
grep -i 'fedora release 27' /etc/fedora-release
if [ $? -eq 0 ]
then
useCompatSsl=1
else
grep -i 'fedora release 26' /etc/fedora-release
if [ $? -eq 0 ]
then
useCompatSsl=1
fi
fi
fi
if [ $useCompatSsl -eq 1 ]
then
echo "Use compat-openssl10-devel instead of openssl-devel for Fedora 27/28 (dotnet core requires openssl 1.0.x)"
dnf install -y compat-openssl10
if [ $? -ne 0 ]
then
echo "'dnf' failed with exit code '$?'"
print_errormessage
exit 1
fi
else
dnf install -y openssl-libs
if [ $? -ne 0 ]
then
echo "'dnf' failed with exit code '$?'"
print_errormessage
exit 1
fi
fi
dnf install -y lttng-ust libcurl krb5-libs zlib libicu
if [ $? -ne 0 ]
then
echo "'dnf' failed with exit code '$?'"
print_errormessage
exit 1
fi
else
echo "Can not find 'dnf'"
print_errormessage
exit 1
fi
else
command -v yum
if [ $? -eq 0 ]
then
yum install -y openssl-libs libcurl krb5-libs zlib libicu
if [ $? -ne 0 ]
then
echo "'yum' failed with exit code '$?'"
print_errormessage
exit 1
fi
# install lttng-ust separately since it's not part of offical package repository
yum install -y wget && wget -P /etc/yum.repos.d/ https://packages.efficios.com/repo.files/EfficiOS-RHEL7-x86-64.repo && rpmkeys --import https://packages.efficios.com/rhel/repo.key && yum updateinfo && yum install -y lttng-ust
if [ $? -ne 0 ]
then
echo "'lttng-ust' installation failed with exit code '$?'"
print_errormessage
exit 1
fi
else
echo "Can not find 'yum'"
print_errormessage
exit 1
fi
fi
else
# we might on OpenSUSE
OSTYPE=$(grep ID_LIKE /etc/os-release | cut -f2 -d=)
echo $OSTYPE
if [ $OSTYPE == '"suse"' ]
then
echo "The current OS is SUSE based"
command -v zypper
if [ $? -eq 0 ]
then
zypper -n install lttng-ust libopenssl1_0_0 libcurl4 krb5 zlib libicu52_1
if [ $? -ne 0 ]
then
echo "'zypper' failed with exit code '$?'"
print_errormessage
exit 1
fi
else
echo "Can not find 'zypper'"
print_errormessage
exit 1
fi
else
echo "Can't detect current OS type based on /etc/os-release."
print_errormessage
exit 1
fi
fi
elif [ -e /etc/redhat-release ]
# RHEL6 doesn't have an os-release file defined, read redhat-release instead
then
redhatRelease=$(</etc/redhat-release)
if [[ $redhatRelease == "CentOS release 6."* || $redhatRelease == "Red Hat Enterprise Linux Server release 6."* ]]
then
echo "The current OS is Red Hat Enterprise Linux 6 or Centos 6"
# Install known dependencies, as a best effort.
# The remaining dependencies are covered by the GitHub doc that will be shown by `print_rhel6message`
command -v yum
if [ $? -eq 0 ]
then
yum install -y openssl krb5-libs zlib
if [ $? -ne 0 ]
then
echo "'yum' failed with exit code '$?'"
print_rhel6errormessage
exit 1
fi
else
echo "Can not find 'yum'"
print_rhel6errormessage
exit 1
fi
print_rhel6message
exit 1
else
echo "Unknown RHEL OS version"
print_errormessage
exit 1
fi
else
echo "Unknown OS version"
print_errormessage
exit 1
fi
echo "-----------------------------"
echo " Finish Install Dependencies"
echo "-----------------------------"

20
src/Misc/layoutbin/runsvc.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/bash
# convert SIGTERM signal to SIGINT
# for more info on how to propagate SIGTERM to a child process see: http://veithen.github.io/2014/11/16/sigterm-propagation.html
trap 'kill -INT $PID' TERM INT
if [ -f ".path" ]; then
# configure
export PATH=`cat .path`
echo ".path=${PATH}"
fi
# insert anything to setup env when running as a service
# run the host process which keep the listener alive
./externals/node12/bin/node ./bin/RunnerService.js &
PID=$!
wait $PID
trap - TERM INT
wait $PID

View File

@@ -0,0 +1,143 @@
#!/bin/bash
SVC_NAME="{{SvcNameVar}}"
SVC_DESCRIPTION="{{SvcDescription}}"
SVC_CMD=$1
arg_2=${2}
RUNNER_ROOT=`pwd`
UNIT_PATH=/etc/systemd/system/${SVC_NAME}
TEMPLATE_PATH=./bin/actions.runner.service.template
TEMP_PATH=./bin/actions.runner.service.temp
CONFIG_PATH=.service
user_id=`id -u`
# systemctl must run as sudo
# this script is a convenience wrapper around systemctl
if [ $user_id -ne 0 ]; then
echo "Must run as sudo"
exit 1
fi
function failed()
{
local error=${1:-Undefined error}
echo "Failed: $error" >&2
exit 1
}
if [ ! -f "${TEMPLATE_PATH}" ]; then
failed "Must run from runner root or install is corrupt"
fi
#check if we run as root
if [[ $(id -u) != "0" ]]; then
echo "Failed: This script requires to run with sudo." >&2
exit 1
fi
function install()
{
echo "Creating launch runner in ${UNIT_PATH}"
if [ -f "${UNIT_PATH}" ]; then
failed "error: exists ${UNIT_PATH}"
fi
if [ -f "${TEMP_PATH}" ]; then
rm "${TEMP_PATH}" || failed "failed to delete ${TEMP_PATH}"
fi
# can optionally use username supplied
run_as_user=${arg_2:-$SUDO_USER}
echo "Run as user: ${run_as_user}"
run_as_uid=$(id -u ${run_as_user}) || failed "User does not exist"
echo "Run as uid: ${run_as_uid}"
run_as_gid=$(id -g ${run_as_user}) || failed "Group not available"
echo "gid: ${run_as_gid}"
sed "s/{{User}}/${run_as_user}/g; s/{{Description}}/$(echo ${SVC_DESCRIPTION} | sed -e 's/[\/&]/\\&/g')/g; s/{{RunnerRoot}}/$(echo ${RUNNER_ROOT} | sed -e 's/[\/&]/\\&/g')/g;" "${TEMPLATE_PATH}" > "${TEMP_PATH}" || failed "failed to create replacement temp file"
mv "${TEMP_PATH}" "${UNIT_PATH}" || failed "failed to copy unit file"
# unit file should not be executable and world writable
chmod 664 ${UNIT_PATH} || failed "failed to set permissions on ${UNIT_PATH}"
systemctl daemon-reload || failed "failed to reload daemons"
# Since we started with sudo, runsvc.sh will be owned by root. Change this to current login user.
cp ./bin/runsvc.sh ./runsvc.sh || failed "failed to copy runsvc.sh"
chown ${run_as_uid}:${run_as_gid} ./runsvc.sh || failed "failed to set owner for runsvc.sh"
chmod 755 ./runsvc.sh || failed "failed to set permission for runsvc.sh"
systemctl enable ${SVC_NAME} || failed "failed to enable ${SVC_NAME}"
echo "${SVC_NAME}" > ${CONFIG_PATH} || failed "failed to create .service file"
chown ${run_as_uid}:${run_as_gid} ${CONFIG_PATH} || failed "failed to set permission for ${CONFIG_PATH}"
}
function start()
{
systemctl start ${SVC_NAME} || failed "failed to start ${SVC_NAME}"
status
}
function stop()
{
systemctl stop ${SVC_NAME} || failed "failed to stop ${SVC_NAME}"
status
}
function uninstall()
{
stop
systemctl disable ${SVC_NAME} || failed "failed to disable ${SVC_NAME}"
rm "${UNIT_PATH}" || failed "failed to delete ${UNIT_PATH}"
if [ -f "${CONFIG_PATH}" ]; then
rm "${CONFIG_PATH}" || failed "failed to delete ${CONFIG_PATH}"
fi
systemctl daemon-reload || failed "failed to reload daemons"
}
function status()
{
if [ -f "${UNIT_PATH}" ]; then
echo
echo "${UNIT_PATH}"
else
echo
echo "not installed"
echo
return
fi
systemctl --no-pager status ${SVC_NAME}
}
function usage()
{
echo
echo Usage:
echo "./svc.sh [install, start, stop, status, uninstall]"
echo "Commands:"
echo " install [user]: Install runner service as Root or specified user."
echo " start: Manually start the runner service."
echo " stop: Manually stop the runner service."
echo " status: Display status of runner service."
echo " uninstall: Uninstall runner service."
echo
}
case $SVC_CMD in
"install") install;;
"status") status;;
"uninstall") uninstall;;
"start") start;;
"stop") stop;;
"status") status;;
*) usage;;
esac
exit 0

View File

@@ -0,0 +1,143 @@
@echo off
rem runner will replace key words in the template and generate a batch script to run.
rem Keywords:
rem PROCESSID = pid
rem RUNNERPROCESSNAME = Runner.Listener[.exe]
rem ROOTFOLDER = ./
rem EXISTRUNNERVERSION = 2.100.0
rem DOWNLOADRUNNERVERSION = 2.101.0
rem UPDATELOG = _diag/SelfUpdate-UTC.log
rem RESTARTINTERACTIVERUNNER = 0/1
setlocal
set runnerpid=_PROCESS_ID_
set runnerprocessname=_RUNNER_PROCESS_NAME_
set rootfolder=_ROOT_FOLDER_
set existrunnerversion=_EXIST_RUNNER_VERSION_
set downloadrunnerversion=_DOWNLOAD_RUNNER_VERSION_
set logfile=_UPDATE_LOG_
set restartinteractiverunner=_RESTART_INTERACTIVE_RUNNER_
rem log user who run the script
echo [%date% %time%] --------whoami-------- >> "%logfile%" 2>&1
whoami >> "%logfile%" 2>&1
echo [%date% %time%] --------whoami-------- >> "%logfile%" 2>&1
rem wait for runner process to exit.
echo [%date% %time%] Waiting for %runnerprocessname% (%runnerpid%) to complete >> "%logfile%" 2>&1
:loop
tasklist /fi "pid eq %runnerpid%" | find /I "%runnerprocessname%" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
goto copy
)
echo [%date% %time%] Process %runnerpid% still running, check again after 1 second. >> "%logfile%" 2>&1
ping -n 2 127.0.0.1 >nul
goto loop
rem start re-organize folders
:copy
echo [%date% %time%] Process %runnerpid% finished running >> "%logfile%" 2>&1
echo [%date% %time%] Sleep 1 more second to make sure process exited >> "%logfile%" 2>&1
ping -n 2 127.0.0.1 >nul
echo [%date% %time%] Re-organize folders >> "%logfile%" 2>&1
rem the folder structure under runner root will be
rem ./bin -> bin.2.100.0 (junction folder)
rem ./externals -> externals.2.100.0 (junction folder)
rem ./bin.2.100.0
rem ./externals.2.100.0
rem ./bin.2.99.0
rem ./externals.2.99.0
rem by using the juction folder we can avoid file in use problem.
rem if the bin/externals junction point already exist, we just need to delete the juction point then re-create to point to new bin/externals folder.
rem if the bin/externals still are real folders, we need to rename the existing folder to bin.version format then create junction point to new bin/externals folder.
rem check bin folder
rem we do findstr /C:" bin" since in migration mode, we create a junction folder from runner to bin.
rem as result, dir /AL | findstr "bin" will return the runner folder. output looks like (07/27/2016 05:21 PM <JUNCTION> runner [E:\bin])
dir "%rootfolder%" /AL 2>&1 | findstr /C:" bin" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
rem return code 1 means it can't find a bin folder that is a junction folder
rem so we need to move the current bin folder to bin.2.99.0 folder.
echo [%date% %time%] move "%rootfolder%\bin" "%rootfolder%\bin.%existrunnerversion%" >> "%logfile%" 2>&1
move "%rootfolder%\bin" "%rootfolder%\bin.%existrunnerversion%" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't move "%rootfolder%\bin" to "%rootfolder%\bin.%existrunnerversion%" >> "%logfile%" 2>&1
goto fail
)
) else (
rem otherwise it find a bin folder that is a junction folder
rem we just need to delete the junction point.
echo [%date% %time%] Delete existing junction bin folder >> "%logfile%" 2>&1
rmdir "%rootfolder%\bin" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't delete existing junction bin folder >> "%logfile%" 2>&1
goto fail
)
)
rem check externals folder
dir "%rootfolder%" /AL 2>&1 | findstr "externals" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
rem return code 1 means it can't find a externals folder that is a junction folder
rem so we need to move the current externals folder to externals.2.99.0 folder.
echo [%date% %time%] move "%rootfolder%\externals" "%rootfolder%\externals.%existrunnerversion%" >> "%logfile%" 2>&1
move "%rootfolder%\externals" "%rootfolder%\externals.%existrunnerversion%" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't move "%rootfolder%\externals" to "%rootfolder%\externals.%existrunnerversion%" >> "%logfile%" 2>&1
goto fail
)
) else (
rem otherwise it find a externals folder that is a junction folder
rem we just need to delete the junction point.
echo [%date% %time%] Delete existing junction externals folder >> "%logfile%" 2>&1
rmdir "%rootfolder%\externals" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't delete existing junction externals folder >> "%logfile%" 2>&1
goto fail
)
)
rem create junction bin folder
echo [%date% %time%] Create junction bin folder >> "%logfile%" 2>&1
mklink /J "%rootfolder%\bin" "%rootfolder%\bin.%downloadrunnerversion%" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't create junction bin folder >> "%logfile%" 2>&1
goto fail
)
rem create junction externals folder
echo [%date% %time%] Create junction externals folder >> "%logfile%" 2>&1
mklink /J "%rootfolder%\externals" "%rootfolder%\externals.%downloadrunnerversion%" >> "%logfile%" 2>&1
if ERRORLEVEL 1 (
echo [%date% %time%] Can't create junction externals folder >> "%logfile%" 2>&1
goto fail
)
echo [%date% %time%] Update succeed >> "%logfile%" 2>&1
rem rename the update log file with %logfile%.succeed/.failed/succeedneedrestart
rem runner service host can base on the log file name determin the result of the runner update
echo [%date% %time%] Rename "%logfile%" to be "%logfile%.succeed" >> "%logfile%" 2>&1
move "%logfile%" "%logfile%.succeed" >nul
rem restart interactive runner if needed
if %restartinteractiverunner% equ 1 (
echo [%date% %time%] Restart interactive runner >> "%logfile%.succeed" 2>&1
endlocal
start "Actions Runner" cmd.exe /k "_ROOT_FOLDER_\run.cmd"
) else (
endlocal
)
goto :eof
:fail
echo [%date% %time%] Rename "%logfile%" to be "%logfile%.failed" >> "%logfile%" 2>&1
move "%logfile%" "%logfile%.failed" >nul
goto :eof

View File

@@ -0,0 +1,133 @@
#!/bin/bash
# runner will replace key words in the template and generate a batch script to run.
# Keywords:
# PROCESSID = pid
# RUNNERPROCESSNAME = Runner.Listener[.exe]
# ROOTFOLDER = ./
# EXISTRUNNERVERSION = 2.100.0
# DOWNLOADRUNNERVERSION = 2.101.0
# UPDATELOG = _diag/SelfUpdate-UTC.log
# RESTARTINTERACTIVERUNNER = 0/1
runnerpid=_PROCESS_ID_
runnerprocessname=_RUNNER_PROCESS_NAME_
rootfolder="_ROOT_FOLDER_"
existrunnerversion=_EXIST_RUNNER_VERSION_
downloadrunnerversion=_DOWNLOAD_RUNNER_VERSION_
logfile="_UPDATE_LOG_"
restartinteractiverunner=_RESTART_INTERACTIVE_RUNNER_
# log user who run the script
date "+[%F %T-%4N] --------whoami--------" >> "$logfile" 2>&1
whoami >> "$logfile" 2>&1
date "+[%F %T-%4N] --------whoami--------" >> "$logfile" 2>&1
# wait for runner process to exit.
date "+[%F %T-%4N] Waiting for $runnerprocessname ($runnerpid) to complete" >> "$logfile" 2>&1
while [ -e /proc/$runnerpid ]
do
date "+[%F %T-%4N] Process $runnerpid still running" >> "$logfile" 2>&1
ping -c 2 127.0.0.1 >nul
done
date "+[%F %T-%4N] Process $runnerpid finished running" >> "$logfile" 2>&1
# start re-organize folders
date "+[%F %T-%4N] Sleep 1 more second to make sure process exited" >> "$logfile" 2>&1
ping -c 2 127.0.0.1 >nul
# the folder structure under runner root will be
# ./bin -> bin.2.100.0 (junction folder)
# ./externals -> externals.2.100.0 (junction folder)
# ./bin.2.100.0
# ./externals.2.100.0
# ./bin.2.99.0
# ./externals.2.99.0
# by using the juction folder we can avoid file in use problem.
# if the bin/externals junction point already exist, we just need to delete the juction point then re-create to point to new bin/externals folder.
# if the bin/externals still are real folders, we need to rename the existing folder to bin.version format then create junction point to new bin/externals folder.
# check bin folder
if [[ -L "$rootfolder/bin" && -d "$rootfolder/bin" ]]
then
# return code 0 means it find a bin folder that is a junction folder
# we just need to delete the junction point.
date "+[%F %T-%4N] Delete existing junction bin folder" >> "$logfile"
rm "$rootfolder/bin" >> "$logfile"
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't delete existing junction bin folder" >> "$logfile"
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
else
# otherwise, we need to move the current bin folder to bin.2.99.0 folder.
date "+[%F %T-%4N] move $rootfolder/bin $rootfolder/bin.$existrunnerversion" >> "$logfile" 2>&1
mv -fv "$rootfolder/bin" "$rootfolder/bin.$existrunnerversion" >> "$logfile" 2>&1
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't move $rootfolder/bin to $rootfolder/bin.$existrunnerversion" >> "$logfile" 2>&1
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
fi
# check externals folder
if [[ -L "$rootfolder/externals" && -d "$rootfolder/externals" ]]
then
# the externals folder is already a junction folder
# we just need to delete the junction point.
date "+[%F %T-%4N] Delete existing junction externals folder" >> "$logfile"
rm "$rootfolder/externals" >> "$logfile"
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't delete existing junction externals folder" >> "$logfile"
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
else
# otherwise, we need to move the current externals folder to externals.2.99.0 folder.
date "+[%F %T-%4N] move $rootfolder/externals $rootfolder/externals.$existrunnerversion" >> "$logfile" 2>&1
mv -fv "$rootfolder/externals" "$rootfolder/externals.$existrunnerversion" >> "$logfile" 2>&1
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't move $rootfolder/externals to $rootfolder/externals.$existrunnerversion" >> "$logfile" 2>&1
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
fi
# create junction bin folder
date "+[%F %T-%4N] Create junction bin folder" >> "$logfile" 2>&1
ln -s "$rootfolder/bin.$downloadrunnerversion" "$rootfolder/bin" >> "$logfile" 2>&1
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't create junction bin folder" >> "$logfile" 2>&1
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
# create junction externals folder
date "+[%F %T-%4N] Create junction externals folder" >> "$logfile" 2>&1
ln -s "$rootfolder/externals.$downloadrunnerversion" "$rootfolder/externals" >> "$logfile" 2>&1
if [ $? -ne 0 ]
then
date "+[%F %T-%4N] Can't create junction externals folder" >> "$logfile" 2>&1
mv -fv "$logfile" "$logfile.failed"
exit 1
fi
date "+[%F %T-%4N] Update succeed" >> "$logfile"
# rename the update log file with %logfile%.succeed/.failed/succeedneedrestart
# runner service host can base on the log file name determin the result of the runner update
date "+[%F %T-%4N] Rename $logfile to be $logfile.succeed" >> "$logfile" 2>&1
mv -fv "$logfile" "$logfile.succeed" >> "$logfile" 2>&1
# restart interactive runner if needed
if [ $restartinteractiverunner -ne 0 ]
then
date "+[%F %T-%4N] Restarting interactive runner" >> "$logfile.succeed" 2>&1
"$rootfolder/run.sh" &
fi

View File

@@ -0,0 +1,26 @@
@echo off
rem ********************************************************************************
rem Unblock specific files.
rem ********************************************************************************
setlocal
if defined VERBOSE_ARG (
set VERBOSE_ARG='Continue'
) else (
set VERBOSE_ARG='SilentlyContinue'
)
rem Unblock files in the root of the layout folder. E.g. .cmd files.
powershell.exe -NoLogo -Sta -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command "$VerbosePreference = %VERBOSE_ARG% ; Get-ChildItem -LiteralPath '%~dp0' | ForEach-Object { Write-Verbose ('Unblock: {0}' -f $_.FullName) ; $_ } | Unblock-File | Out-Null"
if /i "%~1" equ "remove" (
rem ********************************************************************************
rem Unconfigure the runner.
rem ********************************************************************************
"%~dp0bin\Runner.Listener.exe" %*
) else (
rem ********************************************************************************
rem Configure the runner.
rem ********************************************************************************
"%~dp0bin\Runner.Listener.exe" configure %*
)

86
src/Misc/layoutroot/config.sh Executable file
View File

@@ -0,0 +1,86 @@
#!/bin/bash
user_id=`id -u`
# we want to snapshot the environment of the config user
if [ $user_id -eq 0 -a -z "$AGENT_ALLOW_RUNASROOT" ]; then
echo "Must not run with sudo"
exit 1
fi
# Check dotnet core 2.1 dependencies for Linux
if [[ (`uname` == "Linux") ]]
then
command -v ldd > /dev/null
if [ $? -ne 0 ]
then
echo "Can not find 'ldd'. Please install 'ldd' and try again."
exit 1
fi
ldd ./bin/libcoreclr.so | grep 'not found'
if [ $? -eq 0 ]; then
echo "Dependencies is missing for Dotnet Core 2.1"
echo "Execute ./bin/installdependencies.sh to install any missing Dotnet Core 2.1 dependencies."
exit 1
fi
ldd ./bin/System.Security.Cryptography.Native.OpenSsl.so | grep 'not found'
if [ $? -eq 0 ]; then
echo "Dependencies is missing for Dotnet Core 2.1"
echo "Execute ./bin/installdependencies.sh to install any missing Dotnet Core 2.1 dependencies."
exit 1
fi
ldd ./bin/System.IO.Compression.Native.so | grep 'not found'
if [ $? -eq 0 ]; then
echo "Dependencies is missing for Dotnet Core 2.1"
echo "Execute ./bin/installdependencies.sh to install any missing Dotnet Core 2.1 dependencies."
exit 1
fi
ldd ./bin/System.Net.Http.Native.so | grep 'not found'
if [ $? -eq 0 ]; then
echo "Dependencies is missing for Dotnet Core 2.1"
echo "Execute ./bin/installdependencies.sh to install any missing Dotnet Core 2.1 dependencies."
exit 1
fi
if ! [ -x "$(command -v ldconfig)" ]; then
LDCONFIG_COMMAND="/sbin/ldconfig"
if ! [ -x "$LDCONFIG_COMMAND" ]; then
echo "Can not find 'ldconfig' in PATH and '/sbin/ldconfig' doesn't exists either. Please install 'ldconfig' and try again."
exit 1
fi
else
LDCONFIG_COMMAND="ldconfig"
fi
libpath=${LD_LIBRARY_PATH:-}
$LDCONFIG_COMMAND -NXv ${libpath//:/} 2>&1 | grep libicu >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Libicu's dependencies is missing for Dotnet Core 2.1"
echo "Execute ./bin/installdependencies.sh to install any missing Dotnet Core 2.1 dependencies."
exit 1
fi
fi
# Change directory to the script root directory
# https://stackoverflow.com/questions/59895/getting-the-source-directory-of-a-bash-script-from-within
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
cd $DIR
source ./env.sh
shopt -s nocasematch
if [[ "$1" == "remove" ]]; then
./bin/Runner.Listener "$@"
else
./bin/Runner.Listener configure "$@"
fi

44
src/Misc/layoutroot/env.sh Executable file
View File

@@ -0,0 +1,44 @@
#!/bin/bash
varCheckList=(
'LANG'
'JAVA_HOME'
'ANT_HOME'
'M2_HOME'
'ANDROID_HOME'
'GRADLE_HOME'
'NVM_BIN'
'NVM_PATH'
'VSTS_HTTP_PROXY'
'VSTS_HTTP_PROXY_USERNAME'
'VSTS_HTTP_PROXY_PASSWORD'
'LD_LIBRARY_PATH'
'PERL5LIB'
)
envContents=""
if [ -f ".env" ]; then
envContents=`cat .env`
else
touch .env
fi
function writeVar()
{
checkVar="$1"
checkDelim="${1}="
if test "${envContents#*$checkDelim}" = "$envContents"
then
if [ ! -z "${!checkVar}" ]; then
echo "${checkVar}=${!checkVar}">>.env
fi
fi
}
echo $PATH>.path
for var_name in ${varCheckList[@]}
do
writeVar "${var_name}"
done

View File

@@ -0,0 +1,33 @@
@echo off
rem ********************************************************************************
rem Unblock specific files.
rem ********************************************************************************
setlocal
if defined VERBOSE_ARG (
set VERBOSE_ARG='Continue'
) else (
set VERBOSE_ARG='SilentlyContinue'
)
rem Unblock files in the root of the layout folder. E.g. .cmd files.
powershell.exe -NoLogo -Sta -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command "$VerbosePreference = %VERBOSE_ARG% ; Get-ChildItem -LiteralPath '%~dp0' | ForEach-Object { Write-Verbose ('Unblock: {0}' -f $_.FullName) ; $_ } | Unblock-File | Out-Null"
if /i "%~1" equ "localRun" (
rem ********************************************************************************
rem Local run.
rem ********************************************************************************
"%~dp0bin\Runner.Listener.exe" %*
) else (
rem ********************************************************************************
rem Run.
rem ********************************************************************************
"%~dp0bin\Runner.Listener.exe" run %*
rem Return code 4 means the run once runner received an update message.
rem Sleep 5 seconds to wait for the update process finish and run the runner again.
if ERRORLEVEL 4 (
timeout /t 5 /nobreak > NUL
"%~dp0bin\Runner.Listener.exe" run %*
)
)

51
src/Misc/layoutroot/run.sh Executable file
View File

@@ -0,0 +1,51 @@
#!/bin/bash
# Validate not sudo
user_id=`id -u`
if [ $user_id -eq 0 -a -z "$AGENT_ALLOW_RUNASROOT" ]; then
echo "Must not run interactively with sudo"
exit 1
fi
# Change directory to the script root directory
# https://stackoverflow.com/questions/59895/getting-the-source-directory-of-a-bash-script-from-within
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
# Do not "cd $DIR". For localRun, the current directory is expected to be the repo location on disk.
# Run
shopt -s nocasematch
if [[ "$1" == "localRun" ]]; then
"$DIR"/bin/Runner.Listener $*
else
"$DIR"/bin/Runner.Listener run $*
# Return code 4 means the run once agent received an update message.
# Sleep 5 seconds to wait for the update process finish and run the agent again.
returnCode=$?
if [[ $returnCode == 4 ]]; then
if [ ! -x "$(command -v sleep)" ]; then
if [ ! -x "$(command -v ping)" ]; then
COUNT="0"
while [[ $COUNT != 5000 ]]; do
echo "SLEEP" >nul
COUNT=$[$COUNT+1]
done
else
ping -n 5 127.0.0.1 >nul
fi
else
sleep 5 >nul
fi
"$DIR"/bin/Runner.Listener run $*
else
exit $returnCode
fi
fi