mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Compare commits
9 Commits
releases/m
...
testingScr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76bf461032 | ||
|
|
48ea146797 | ||
|
|
4dea9dc1c9 | ||
|
|
1ad181673f | ||
|
|
4cf17bc51e | ||
|
|
fd75cdb8ab | ||
|
|
3393f3991c | ||
|
|
faf826d15a | ||
|
|
43610319c4 |
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: Runner CD
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- releaseVersion
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
[](https://github.com/actions/runner/actions)
|
||||
|
||||
The runner is the application that runs a job from a GitHub Actions workflow. It is used by GitHub Actions in the [hosted virtual environments](https://github.com/actions/virtual-environments), or you can [self-host the runner](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners) in your own environment.
|
||||
The runner is the application that runs a job from a GitHub Actions workflow. The runner can run on the [hosted machine pools](https://github.com/actions/virtual-environments) or run on [self-hosted environments](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners).
|
||||
|
||||
## Get Started
|
||||
|
||||
|
||||
@@ -16,32 +16,11 @@ We don't want the workflow author to need to know how the internal workings of t
|
||||
|
||||
A composite action is treated as **one** individual job step (this is known as encapsulation).
|
||||
|
||||
|
||||
## Decision
|
||||
|
||||
**In this ADR, we only support running multiple run steps in an Action.** In doing so, we build in support for mapping and flowing the inputs, outputs, and env variables (ex: All nested steps should have access to its parents' input variables and nested steps can overwrite the input variables).
|
||||
|
||||
### Composite Run Steps Features
|
||||
This feature supports at the top action level:
|
||||
- name
|
||||
- description
|
||||
- inputs
|
||||
- runs
|
||||
- outputs
|
||||
|
||||
This feature supports at the run step level:
|
||||
- name
|
||||
- id
|
||||
- run
|
||||
- env
|
||||
- shell
|
||||
- working-directory
|
||||
|
||||
This feature **does not support** at the run step level:
|
||||
- timeout-minutes
|
||||
- secrets
|
||||
- conditionals (needs, if, etc.)
|
||||
- continue-on-error
|
||||
|
||||
### Steps
|
||||
|
||||
Example `workflow.yml`
|
||||
@@ -70,9 +49,7 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- run: pip install -r requirements.txt
|
||||
shell: bash
|
||||
- run: npm install
|
||||
shell: bash
|
||||
```
|
||||
|
||||
Example Output
|
||||
@@ -86,69 +63,6 @@ echo hello world 4
|
||||
|
||||
We add a token called "composite" which allows our Runner code to process composite actions. By invoking "using: composite", our Runner code then processes the "steps" attribute, converts this template code to a list of steps, and finally runs each run step sequentially. If any step fails and there are no `if` conditions defined, the whole composite action job fails.
|
||||
|
||||
### Defaults
|
||||
|
||||
We will not support "defaults" in a composite action.
|
||||
|
||||
### Shell and Working-directory
|
||||
|
||||
For each run step in a composite action, the action author can set the `shell` and `working-directory` attributes for that step. The shell attribute is **required** for each run step because the action author does not know what the workflow author is using for the operating system so we need to explicitly prevent unknown behavior by making sure that each run step has an explicit shell **set by the action author.** On the other hand, `working-directory` is optional. Moreover, the composite action author can map in values from the `inputs` for it's `shell` and `working-directory` attributes at the step level for an action.
|
||||
|
||||
For example,
|
||||
|
||||
`action.yml`
|
||||
|
||||
|
||||
```yaml
|
||||
inputs:
|
||||
shell_1:
|
||||
description: 'Your name'
|
||||
default: 'pwsh'
|
||||
steps:
|
||||
- run: echo 1
|
||||
shell: ${{ inputs.shell_1 }}
|
||||
```
|
||||
|
||||
Note, the workflow file and action file are treated as separate entities. **So, the workflow `defaults` will never change the `shell` and `working-directory` value in the run steps in a composite action.** Note, `defaults` in a workflow only apply to run steps not "uses" steps (steps that use an action).
|
||||
|
||||
### Running Local Scripts
|
||||
|
||||
Example 'workflow.yml':
|
||||
```yaml
|
||||
jobs:
|
||||
build:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- uses: user/composite@v1
|
||||
```
|
||||
|
||||
Example `user/composite/action.yml`:
|
||||
|
||||
```yaml
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- run: chmod +x ${{ github.action_path }}/test/script2.sh
|
||||
shell: bash
|
||||
- run: chmod +x $GITHUB_ACTION_PATH/script.sh
|
||||
shell: bash
|
||||
- run: ${{ github.action_path }}/test/script2.sh
|
||||
shell: bash
|
||||
- run: $GITHUB_ACTION_PATH/script.sh
|
||||
shell: bash
|
||||
```
|
||||
Where `user/composite` has the file structure:
|
||||
```
|
||||
.
|
||||
+-- action.yml
|
||||
+-- script.sh
|
||||
+-- test
|
||||
| +-- script2.sh
|
||||
```
|
||||
|
||||
|
||||
Users will be able to run scripts located in their action folder by first prepending the relative path and script name with `$GITHUB_ACTION_PATH` or `github.action_path` which contains the path in which the composite action is downloaded to and where those "files" live. Note, you'll have to use `chmod` before running each script if you do not git check in your script files into your github repo with the executable bit turned on.
|
||||
|
||||
### Inputs
|
||||
|
||||
Example `workflow.yml`:
|
||||
@@ -172,7 +86,6 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- run: echo hello ${{ inputs.your_name }}
|
||||
shell: bash
|
||||
```
|
||||
|
||||
Example Output:
|
||||
@@ -193,7 +106,6 @@ steps:
|
||||
- id: foo
|
||||
uses: user/composite@v1
|
||||
- run: echo random-number ${{ steps.foo.outputs.random-number }}
|
||||
shell: bash
|
||||
```
|
||||
|
||||
Example `user/composite/action.yml`:
|
||||
@@ -207,8 +119,7 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- id: random-number-generator
|
||||
run: echo "::set-output name=random-id::$(echo $RANDOM)"
|
||||
shell: bash
|
||||
run: echo "::set-output name=random-number::$(echo $RANDOM)"
|
||||
```
|
||||
|
||||
Example Output:
|
||||
@@ -232,17 +143,13 @@ In the Composite Action, you'll only be able to use `::set-env::` to set environ
|
||||
|
||||
### Secrets
|
||||
|
||||
**We will not support "Secrets" in a composite action for now. This functionality will be focused on in a future ADR.**
|
||||
**Note** : This feature will be focused on in a future ADR.
|
||||
|
||||
We'll pass the secrets from the composite action's parents (ex: the workflow file) to the composite action. Secrets can be created in the composite action with the secrets context. In the actions yaml, we'll automatically mask the secret.
|
||||
|
||||
|
||||
### If Condition
|
||||
|
||||
** If and needs conditions will not be supported in the composite run steps feature. It will be supported later on in a new feature. **
|
||||
|
||||
Old reasoning:
|
||||
|
||||
Example `workflow.yml`:
|
||||
|
||||
```yaml
|
||||
@@ -259,18 +166,12 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- run: echo "just succeeding"
|
||||
shell: bash
|
||||
- run: echo "I will run, as my current scope is succeeding"
|
||||
shell: bash
|
||||
if: success()
|
||||
- run: exit 1
|
||||
shell: bash
|
||||
- run: echo "I will not run, as my current scope is now failing"
|
||||
shell: bash
|
||||
```
|
||||
|
||||
**We will not support "if Condition" in a composite action for now. This functionality will be focused on in a future ADR.**
|
||||
|
||||
See the paragraph below for a rudimentary approach (thank you to @cybojenix for the idea, example, and explanation for this approach):
|
||||
|
||||
The `if` statement in the parent (in the example above, this is the `workflow.yml`) shows whether or not we should run the composite action. So, our composite action will run since the `if` condition for running the composite action is `always()`.
|
||||
@@ -302,18 +203,13 @@ runs:
|
||||
- id: foo1
|
||||
run: echo test 1
|
||||
timeout-minutes: 10
|
||||
shell: bash
|
||||
- id: foo2
|
||||
run: echo test 2
|
||||
shell: bash
|
||||
- id: foo3
|
||||
run: echo test 3
|
||||
timeout-minutes: 10
|
||||
shell: bash
|
||||
```
|
||||
|
||||
**We will not support "timeout-minutes" in a composite action for now. This functionality will be focused on in a future ADR.**
|
||||
|
||||
A composite action in its entirety is a job. You can set both timeout-minutes for the whole composite action or its steps as long as the the sum of the `timeout-minutes` for each composite action step that has the attribute `timeout-minutes` is less than or equals to `timeout-minutes` for the composite action. There is no default timeout-minutes for each composite action step.
|
||||
|
||||
If the time taken for any of the steps in combination or individually exceed the whole composite action `timeout-minutes` attribute, the whole job will fail (1). If an individual step exceeds its own `timeout-minutes` attribute but the total time that has been used including this step is below the overall composite action `timeout-minutes`, the individual step will fail but the rest of the steps will run based on their own `timeout-minutes` attribute (they will still abide by condition (1) though).
|
||||
@@ -347,17 +243,36 @@ runs:
|
||||
steps:
|
||||
- run: exit 1
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
- run: echo "Hello World 2" <----- This step will run
|
||||
shell: bash
|
||||
```
|
||||
|
||||
**We will not support "continue-on-error" in a composite action for now. This functionality will be focused on in a future ADR.**
|
||||
|
||||
If any of the steps fail in the composite action and the `continue-on-error` is set to `false` for the whole composite action step in the workflow file, then the steps below it will run. On the flip side, if `continue-on-error` is set to `true` for the whole composite action step in the workflow file, the next job step will run.
|
||||
|
||||
For the composite action steps, it follows the same logic as above. In this example, `"Hello World 2"` will be outputted because the previous step has `continue-on-error` set to `true` although that previous step errored.
|
||||
|
||||
### Defaults
|
||||
We will not support "defaults" in a composite action.
|
||||
|
||||
### Shell and Working-directory
|
||||
For each run step in a composite action, the action author can set the `shell` and `working-directory` attributes for that step. These attributes are optional for each run step - by default, the `shell` is set to whatever default value is associated with the runner os (ex: bash =\> Mac). Moreover, the composite action author can map in values from the `inputs` for it's `shell` and `working-directory` attributes at the step level for an action.
|
||||
|
||||
For example,
|
||||
|
||||
`action.yml`
|
||||
|
||||
|
||||
```yaml
|
||||
inputs:
|
||||
shell_1:
|
||||
description: 'Your name'
|
||||
default: 'pwsh'
|
||||
steps:
|
||||
- run: echo 1
|
||||
shell: ${{ inputs.shell_1 }}
|
||||
```
|
||||
|
||||
Note, the workflow file and action file are treated as separate entities. **So, the workflow `defaults` will never change the `shell` and `working-directory` value in the run steps in a composite action.** Note, `defaults` in a workflow only apply to run steps not "uses" steps (steps that use an action).
|
||||
|
||||
### Visualizing Composite Action in the GitHub Actions UI
|
||||
We want all the composite action's steps to be condensed into the original composite action node.
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
## Features
|
||||
- N/A
|
||||
|
||||
- Resolve action download info from server (#508, #515, #550)
|
||||
- Print runner and machine name to log. (#539)
|
||||
## Bugs
|
||||
- Raise error for set-env, block set node_options using `::set-env::` (#784)
|
||||
|
||||
- Reduce input validation warnings (#506)
|
||||
- Fix null ref exception in SecretMasker caused by `hashfiles` timeout. (#516)
|
||||
- Add libicu66 to `./installDependencies.sh` for Ubuntu 20.04 (#535)
|
||||
- Fix DataContract with Token service (#532)
|
||||
- Skip search $PATH on command with fully qualified path (#526)
|
||||
- Restore SELinux context on service file when SELinux is enabled (#525)
|
||||
## Misc
|
||||
- N/A
|
||||
- Remove SPS/Token migration code. Remove GHES url manipulate code. (#513)
|
||||
- Add sub-step for developer flow for clarity (#523)
|
||||
- Update Links and Language to Git + VSCode (#522)
|
||||
- Update runner configuration exception message (#540)
|
||||
|
||||
## Windows x64
|
||||
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.273.6
|
||||
<Update to ./src/runnerversion when creating release>
|
||||
|
||||
463
src/Misc/dotnet-install.ps1
vendored
463
src/Misc/dotnet-install.ps1
vendored
@@ -69,8 +69,6 @@
|
||||
.PARAMETER ProxyUseDefaultCredentials
|
||||
Default: false
|
||||
Use default credentials, when using proxy address.
|
||||
.PARAMETER ProxyBypassList
|
||||
If set with ProxyAddress, will provide the list of comma separated urls that will bypass the proxy
|
||||
.PARAMETER SkipNonVersionedFiles
|
||||
Default: false
|
||||
Skips installing non-versioned files if they already exist, such as dotnet.exe.
|
||||
@@ -98,7 +96,6 @@ param(
|
||||
[string]$FeedCredential,
|
||||
[string]$ProxyAddress,
|
||||
[switch]$ProxyUseDefaultCredentials,
|
||||
[string[]]$ProxyBypassList=@(),
|
||||
[switch]$SkipNonVersionedFiles,
|
||||
[switch]$NoCdn
|
||||
)
|
||||
@@ -122,27 +119,11 @@ $VersionRegEx="/\d+\.\d+[^/]+/"
|
||||
$OverrideNonVersionedFiles = !$SkipNonVersionedFiles
|
||||
|
||||
function Say($str) {
|
||||
try
|
||||
{
|
||||
Write-Host "dotnet-install: $str"
|
||||
}
|
||||
catch
|
||||
{
|
||||
# Some platforms cannot utilize Write-Host (Azure Functions, for instance). Fall back to Write-Output
|
||||
Write-Output "dotnet-install: $str"
|
||||
}
|
||||
Write-Host "dotnet-install: $str"
|
||||
}
|
||||
|
||||
function Say-Verbose($str) {
|
||||
try
|
||||
{
|
||||
Write-Verbose "dotnet-install: $str"
|
||||
}
|
||||
catch
|
||||
{
|
||||
# Some platforms cannot utilize Write-Verbose (Azure Functions, for instance). Fall back to Write-Output
|
||||
Write-Output "dotnet-install: $str"
|
||||
}
|
||||
Write-Verbose "dotnet-install: $str"
|
||||
}
|
||||
|
||||
function Say-Invocation($Invocation) {
|
||||
@@ -195,7 +176,7 @@ function Get-CLIArchitecture-From-Architecture([string]$Architecture) {
|
||||
{ $_ -eq "x86" } { return "x86" }
|
||||
{ $_ -eq "arm" } { return "arm" }
|
||||
{ $_ -eq "arm64" } { return "arm64" }
|
||||
default { throw "Architecture '$Architecture' not supported. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues" }
|
||||
default { throw "Architecture not supported. If you think this is a bug, report it at https://github.com/dotnet/sdk/issues" }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,11 +237,7 @@ function GetHTTPResponse([Uri] $Uri)
|
||||
|
||||
if($ProxyAddress) {
|
||||
$HttpClientHandler = New-Object System.Net.Http.HttpClientHandler
|
||||
$HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{
|
||||
Address=$ProxyAddress;
|
||||
UseDefaultCredentials=$ProxyUseDefaultCredentials;
|
||||
BypassList = $ProxyBypassList;
|
||||
}
|
||||
$HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{Address=$ProxyAddress;UseDefaultCredentials=$ProxyUseDefaultCredentials}
|
||||
$HttpClient = New-Object System.Net.Http.HttpClient -ArgumentList $HttpClientHandler
|
||||
}
|
||||
else {
|
||||
@@ -395,20 +372,17 @@ function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$Channel,
|
||||
function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string]$CLIArchitecture) {
|
||||
Say-Invocation $MyInvocation
|
||||
|
||||
# If anything fails in this lookup it will default to $SpecificVersion
|
||||
$SpecificProductVersion = Get-Product-Version -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion
|
||||
|
||||
if ($Runtime -eq "dotnet") {
|
||||
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip"
|
||||
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-runtime-$SpecificVersion-win-$CLIArchitecture.zip"
|
||||
}
|
||||
elseif ($Runtime -eq "aspnetcore") {
|
||||
$PayloadURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/aspnetcore-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip"
|
||||
$PayloadURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/aspnetcore-runtime-$SpecificVersion-win-$CLIArchitecture.zip"
|
||||
}
|
||||
elseif ($Runtime -eq "windowsdesktop") {
|
||||
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip"
|
||||
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificVersion-win-$CLIArchitecture.zip"
|
||||
}
|
||||
elseif (-not $Runtime) {
|
||||
$PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificProductVersion-win-$CLIArchitecture.zip"
|
||||
$PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificVersion-win-$CLIArchitecture.zip"
|
||||
}
|
||||
else {
|
||||
throw "Invalid value for `$Runtime"
|
||||
@@ -416,7 +390,7 @@ function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string
|
||||
|
||||
Say-Verbose "Constructed primary named payload URL: $PayloadURL"
|
||||
|
||||
return $PayloadURL, $SpecificProductVersion
|
||||
return $PayloadURL
|
||||
}
|
||||
|
||||
function Get-LegacyDownload-Link([string]$AzureFeed, [string]$SpecificVersion, [string]$CLIArchitecture) {
|
||||
@@ -437,51 +411,6 @@ function Get-LegacyDownload-Link([string]$AzureFeed, [string]$SpecificVersion, [
|
||||
return $PayloadURL
|
||||
}
|
||||
|
||||
function Get-Product-Version([string]$AzureFeed, [string]$SpecificVersion) {
|
||||
Say-Invocation $MyInvocation
|
||||
|
||||
if ($Runtime -eq "dotnet") {
|
||||
$ProductVersionTxtURL = "$AzureFeed/Runtime/$SpecificVersion/productVersion.txt"
|
||||
}
|
||||
elseif ($Runtime -eq "aspnetcore") {
|
||||
$ProductVersionTxtURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/productVersion.txt"
|
||||
}
|
||||
elseif ($Runtime -eq "windowsdesktop") {
|
||||
$ProductVersionTxtURL = "$AzureFeed/Runtime/$SpecificVersion/productVersion.txt"
|
||||
}
|
||||
elseif (-not $Runtime) {
|
||||
$ProductVersionTxtURL = "$AzureFeed/Sdk/$SpecificVersion/productVersion.txt"
|
||||
}
|
||||
else {
|
||||
throw "Invalid value '$Runtime' specified for `$Runtime"
|
||||
}
|
||||
|
||||
Say-Verbose "Checking for existence of $ProductVersionTxtURL"
|
||||
|
||||
try {
|
||||
$productVersionResponse = GetHTTPResponse($productVersionTxtUrl)
|
||||
|
||||
if ($productVersionResponse.StatusCode -eq 200) {
|
||||
$productVersion = $productVersionResponse.Content.ReadAsStringAsync().Result.Trim()
|
||||
if ($productVersion -ne $SpecificVersion)
|
||||
{
|
||||
Say "Using alternate version $productVersion found in $ProductVersionTxtURL"
|
||||
}
|
||||
|
||||
return $productVersion
|
||||
}
|
||||
else {
|
||||
Say-Verbose "Got StatusCode $($productVersionResponse.StatusCode) trying to get productVersion.txt at $productVersionTxtUrl, so using default value of $SpecificVersion"
|
||||
$productVersion = $SpecificVersion
|
||||
}
|
||||
} catch {
|
||||
Say-Verbose "Could not read productVersion.txt at $productVersionTxtUrl, so using default value of $SpecificVersion (Exception: '$($_.Exception.Message)' )"
|
||||
$productVersion = $SpecificVersion
|
||||
}
|
||||
|
||||
return $productVersion
|
||||
}
|
||||
|
||||
function Get-User-Share-Path() {
|
||||
Say-Invocation $MyInvocation
|
||||
|
||||
@@ -637,7 +566,7 @@ function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot, [string]$BinFolde
|
||||
|
||||
$CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture
|
||||
$SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version -JSonFile $JSonFile
|
||||
$DownloadLink, $EffectiveVersion = Get-Download-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
|
||||
$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
|
||||
@@ -663,11 +592,6 @@ if ($DryRun) {
|
||||
}
|
||||
}
|
||||
Say "Repeatable invocation: $RepeatableCommand"
|
||||
if ($SpecificVersion -ne $EffectiveVersion)
|
||||
{
|
||||
Say "NOTE: Due to finding a version manifest with this runtime, it would actually install with version '$EffectiveVersion'"
|
||||
}
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
@@ -691,12 +615,6 @@ else {
|
||||
throw "Invalid value for `$Runtime"
|
||||
}
|
||||
|
||||
if ($SpecificVersion -ne $EffectiveVersion)
|
||||
{
|
||||
Say "Performing installation checks for effective version: $EffectiveVersion"
|
||||
$SpecificVersion = $EffectiveVersion
|
||||
}
|
||||
|
||||
# Check if the SDK version is already installed.
|
||||
$isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion
|
||||
if ($isAssetInstalled) {
|
||||
@@ -775,195 +693,196 @@ Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath
|
||||
|
||||
Say "Installation finished"
|
||||
exit 0
|
||||
|
||||
# SIG # Begin signature block
|
||||
# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor
|
||||
# MIIjhwYJKoZIhvcNAQcCoIIjeDCCI3QCAQExDzANBglghkgBZQMEAgEFADB5Bgor
|
||||
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
|
||||
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAdMJOqDPFy5F1i
|
||||
# HBXPyOE4hGkUv5EGyQzmS901lRr+baCCDYEwggX/MIID56ADAgECAhMzAAABh3IX
|
||||
# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
|
||||
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAiKYSY4KtkeThH
|
||||
# d5M1aXqv1K0/pff07QwfUbYZ/qX5LqCCDYUwggYDMIID66ADAgECAhMzAAABiK9S
|
||||
# 1rmSbej5AAAAAAGIMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
|
||||
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
|
||||
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
|
||||
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw
|
||||
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ4WhcNMjEwMzAzMTgzOTQ4WjB0MQsw
|
||||
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
|
||||
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
|
||||
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
# AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB
|
||||
# znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH
|
||||
# sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d
|
||||
# weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ
|
||||
# itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV
|
||||
# Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
|
||||
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw
|
||||
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
|
||||
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu
|
||||
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
|
||||
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
|
||||
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
|
||||
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
|
||||
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy
|
||||
# S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K
|
||||
# NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV
|
||||
# BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr
|
||||
# qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx
|
||||
# zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe
|
||||
# yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g
|
||||
# yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf
|
||||
# AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI
|
||||
# 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5
|
||||
# GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea
|
||||
# jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS
|
||||
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
|
||||
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
|
||||
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
|
||||
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
|
||||
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
|
||||
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
|
||||
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
|
||||
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
|
||||
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
|
||||
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
|
||||
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
|
||||
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
|
||||
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
|
||||
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
|
||||
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
|
||||
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
|
||||
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
|
||||
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
|
||||
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
|
||||
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
|
||||
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
|
||||
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
|
||||
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
|
||||
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
|
||||
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
|
||||
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
|
||||
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
|
||||
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
|
||||
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
|
||||
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
|
||||
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
|
||||
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
|
||||
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
|
||||
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
|
||||
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
|
||||
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
|
||||
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
|
||||
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
|
||||
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
|
||||
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG
|
||||
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
|
||||
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
|
||||
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN
|
||||
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
|
||||
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgGfshXxhl
|
||||
# 7+O9cl90lOU62gZCBmJzcomUxEL8+XyoDYQwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
|
||||
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
|
||||
# BgkqhkiG9w0BAQEFAASCAQCPVhcZxxdIzkFdrv/FCW737QgR8fCO1/oRXwhigOyQ
|
||||
# P2MF39fIYsVXuzVnO8pYZZOeW04kMECcWf9420okd4lXP7Xc5m+5UrqPuN1UgNle
|
||||
# hhwLBiXuZaAfllBMWMeQi7DZmg7XW8Yay9TAbc2XSTGQ8foDxPllKFbdPvvQ2DRy
|
||||
# VRLyNNQQEo3IuHHa0nnVNaL2PUYJf0udMCdGkxIMbApAYcitJLSwMLqMzrMkrvS9
|
||||
# ubm7CgigsKRJ3cZtCtFFMUkMsstoVuKLFtu69OvOfgLy1qmKotE6EnF7xudV+qAA
|
||||
# a+UxGVT715tK5kgb5eTr1K2NdWRj517oANQNOjR/m6OPoYIS8TCCEu0GCisGAQQB
|
||||
# gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME
|
||||
# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB
|
||||
# MDEwDQYJYIZIAWUDBAIBBQAEIHYNJoLIl+IWj/Npb6r479Guw3UW/q0/jJhqKgHm
|
||||
# xq1NAgZfdIY1B90YEzIwMjAxMDE0MTcxOTIwLjg5NVowBIACAfSggdSkgdEwgc4x
|
||||
# AQCSCNryE+Cewy2m4t/a74wZ7C9YTwv1PyC4BvM/kSWPNs8n0RTe+FvYfU+E9uf0
|
||||
# t7nYlAzHjK+plif2BhD+NgdhIUQ8sVwWO39tjvQRHjP2//vSvIfmmkRoML1Ihnjs
|
||||
# 9kQiZQzYRDYYRp9xSQYmRwQjk5hl8/U7RgOiQDitVHaU7BT1MI92lfZRuIIDDYBd
|
||||
# vXtbclYJMVOwqZtv0O9zQCret6R+fRSGaDNfEEpcILL+D7RV3M4uaJE4Ta6KAOdv
|
||||
# V+MVaJp1YXFTZPKtpjHO6d9pHQPZiG7NdC6QbnRGmsa48uNQrb6AfmLKDI1Lp31W
|
||||
# MogTaX5tZf+CZT9PSuvjOCLNAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
|
||||
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUj9RJL9zNrPcL10RZdMQIXZN7MG8w
|
||||
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
|
||||
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ1ODM4NjAfBgNVHSMEGDAW
|
||||
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
|
||||
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
|
||||
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
|
||||
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
|
||||
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
|
||||
# ACnXo8hjp7FeT+H6iQlV3CcGnkSbFvIpKYafgzYCFo3UHY1VHYJVb5jHEO8oG26Q
|
||||
# qBELmak6MTI+ra3WKMTGhE1sEIlowTcp4IAs8a5wpCh6Vf4Z/bAtIppP3p3gXk2X
|
||||
# 8UXTc+WxjQYsDkFiSzo/OBa5hkdW1g4EpO43l9mjToBdqEPtIXsZ7Hi1/6y4gK0P
|
||||
# mMiwG8LMpSn0n/oSHGjrUNBgHJPxgs63Slf58QGBznuXiRaXmfTUDdrvhRocdxIM
|
||||
# i8nXQwWACMiQzJSRzBP5S2wUq7nMAqjaTbeXhJqD2SFVHdUYlKruvtPSwbnqSRWT
|
||||
# GI8s4FEXt+TL3w5JnwVZmZkUFoioQDMMjFyaKurdJ6pnzbr1h6QW0R97fWc8xEIz
|
||||
# LIOiU2rjwWAtlQqFO8KNiykjYGyEf5LyAJKAO+rJd9fsYR+VBauIEQoYmjnUbTXM
|
||||
# SY2Lf5KMluWlDOGVh8q6XjmBccpaT+8tCfxpaVYPi1ncnwTwaPQvVq8RjWDRB7Pa
|
||||
# 8ruHgj2HJFi69+hcq7mWx5nTUtzzFa7RSZfE5a1a5AuBmGNRr7f8cNfa01+tiWjV
|
||||
# Kk1a+gJUBSP0sIxecFbVSXTZ7bqeal45XSDIisZBkWb+83TbXdTGMDSUFKTAdtC+
|
||||
# r35GfsN8QVy59Hb5ZYzAXczhgRmk7NyE6jD0Ym5TKiW5MIIHejCCBWKgAwIBAgIK
|
||||
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
|
||||
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
|
||||
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
|
||||
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
|
||||
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
|
||||
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
|
||||
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
|
||||
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
|
||||
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
|
||||
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
|
||||
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
|
||||
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
|
||||
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
|
||||
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
|
||||
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
|
||||
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
|
||||
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
|
||||
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
|
||||
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
|
||||
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
|
||||
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
|
||||
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
|
||||
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
|
||||
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
|
||||
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
|
||||
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
|
||||
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
|
||||
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
|
||||
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
|
||||
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
|
||||
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
|
||||
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
|
||||
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
|
||||
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
|
||||
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
|
||||
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
|
||||
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
|
||||
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
|
||||
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
|
||||
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCFVgwghVUAgEBMIGVMH4x
|
||||
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
|
||||
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p
|
||||
# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg
|
||||
# VFNTIEVTTjo2MEJDLUUzODMtMjYzNTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
|
||||
# U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABJt+6SyK5goIHAAAA
|
||||
# AAEmMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
|
||||
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
|
||||
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAGIr1LWuZJt6PkAAAAA
|
||||
# AYgwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
|
||||
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFxZ
|
||||
# Yezh3liQqiGQuXNa+zYfoSIbLqOpdEn2ZKskBkisMEIGCisGAQQBgjcCAQwxNDAy
|
||||
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
|
||||
# b20wDQYJKoZIhvcNAQEBBQAEggEAjLUrwCXJCPHZulZuKAQSX+MfnIRFAhlN7ru2
|
||||
# 6H8rudvhkWgqMISkLb9gFDPR5FhR4sqdYgKW4P0ERao9ypCGi1FWDLqygC2XBbHj
|
||||
# NEQHBxHJs5SMsMAXNSIcYHqVAvhF3nXoseaNBkhOTrkQ1FS/fW7AfDGRbsiiESzv
|
||||
# lebf92shZylBFKOsKQLAL0mF/B7xrxHJIj5dgQoD1phATRNHOEQj3jgmkidFWowV
|
||||
# 4r8MzbxRhAEORbnJexlUoDQJQH3YwxuUyXkTvrYMTKSbGJLlwRaZQbrcBU0k4gCH
|
||||
# y8Sci+p9Rq+aOTzLCoNrZyh9E7OdwVDm1FJAtY30bV50T2WSFKGCEuIwghLeBgor
|
||||
# BgEEAYI3AwMBMYISzjCCEsoGCSqGSIb3DQEHAqCCErswghK3AgEDMQ8wDQYJYIZI
|
||||
# AWUDBAIBBQAwggFRBgsqhkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGE
|
||||
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCD7JNcBBSfhlKPL1tN3CEKRKJuT/dZ8RO9K
|
||||
# orYLXJeLTwIGXvN89YD7GBMyMDIwMDcwMTE0MTYyMC40MDVaMASAAgH0oIHQpIHN
|
||||
# MIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQx
|
||||
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9z
|
||||
# b2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMg
|
||||
# VFNTIEVTTjoxNzlFLTRCQjAtODI0NjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
|
||||
# U3RhbXAgU2VydmljZaCCDjkwggTxMIID2aADAgECAhMzAAABDKp4btzMQkzBAAAA
|
||||
# AAEMMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
|
||||
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
|
||||
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
|
||||
# MB4XDTE5MTIxOTAxMTQ1OVoXDTIxMDMxNzAxMTQ1OVowgc4xCzAJBgNVBAYTAlVT
|
||||
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
|
||||
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy
|
||||
# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo2MEJD
|
||||
# LUUzODMtMjYzNTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj
|
||||
# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ4wvoacTvMNlXQTtfF/
|
||||
# Cx5Ol3X0fcjUNMvjLgTmO5+WHYJFbp725P3+qvFKDRQHWEI1Sz0gB24urVDIjXjB
|
||||
# h5NVNJVMQJI2tltv7M4/4IbhZJb3xzQW7LolEoZYUZanBTUuyly9osCg4o5joViT
|
||||
# 2GtmyxK+Fv5kC20l2opeaeptd/E7ceDAFRM87hiNCsK/KHyC+8+swnlg4gTOey6z
|
||||
# QqhzgNsG6HrjLBuDtDs9izAMwS2yWT0T52QA9h3Q+B1C9ps2fMKMe+DHpG+0c61D
|
||||
# 94Yh6cV2XHib4SBCnwIFZAeZE2UJ4qPANSYozI8PH+E5rCT3SVqYvHou97HsXvP2
|
||||
# I3MCAwEAAaOCARswggEXMB0GA1UdDgQWBBRJq6wfF7B+mEKN0VimX8ajNA5hQTAf
|
||||
# BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH
|
||||
# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU
|
||||
# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF
|
||||
# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0
|
||||
# YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG
|
||||
# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQBAlvudaOlv9Cfzv56bnX41czF6tLtH
|
||||
# LB46l6XUch+qNN45ZmOTFwLot3JjwSrn4oycQ9qTET1TFDYd1QND0LiXmKz9OqBX
|
||||
# ai6S8XdyCQEZvfL82jIAs9pwsAQ6XvV9jNybPStRgF/sOAM/Deyfmej9Tg9FcRwX
|
||||
# ank2qgzdZZNb8GoEze7f1orcTF0Q89IUXWIlmwEwQFYF1wjn87N4ZxL9Z/xA2m/R
|
||||
# 1zizFylWP/mpamCnVfZZLkafFLNUNVmcvc+9gM7vceJs37d3ydabk4wR6ObR34sW
|
||||
# aLppmyPlsI1Qq5Lu6bJCWoXzYuWpkoK6oEep1gML6SRC3HKVS3UscZhtMIIGcTCC
|
||||
# BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC
|
||||
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
|
||||
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv
|
||||
# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN
|
||||
# MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
|
||||
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
|
||||
# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw
|
||||
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0
|
||||
# VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw
|
||||
# RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe
|
||||
# dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx
|
||||
# Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G
|
||||
# kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA
|
||||
# AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7
|
||||
# fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
|
||||
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
|
||||
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
|
||||
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
|
||||
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
|
||||
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g
|
||||
# AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93
|
||||
# d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB
|
||||
# BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA
|
||||
# bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh
|
||||
# IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS
|
||||
# +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK
|
||||
# kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon
|
||||
# /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi
|
||||
# PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/
|
||||
# fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII
|
||||
# YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0
|
||||
# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a
|
||||
# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ
|
||||
# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+
|
||||
# NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
|
||||
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
|
||||
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP
|
||||
# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo2
|
||||
# MEJDLUUzODMtMjYzNTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
|
||||
# dmljZaIjCgEBMAcGBSsOAwIaAxUACmcyOWmZxErpq06B8dy6oMZ6//yggYMwgYCk
|
||||
# MB4XDTE5MTAyMzIzMTkxNloXDTIxMDEyMTIzMTkxNlowgcoxCzAJBgNVBAYTAlVT
|
||||
# MQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
|
||||
# b2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVy
|
||||
# YXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjE3OUUtNEJC
|
||||
# MC04MjQ2MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIB
|
||||
# IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq5011+XqVJmQKtiw39igeEMv
|
||||
# CLcZ1forbmxsDkpnCN1SrThKI+n2Pr3zqTzJVgdJFCoKm1ks1gtRJ7HaL6tDkrOw
|
||||
# 8XJmfJaxyQAluCQ+e40NI+A4w+u59Gy89AVY5lJNrmCva6gozfg1kxw6abV5WWr+
|
||||
# PjEpNCshO4hxv3UqgMcCKnT2YVSZzF1Gy7APub1fY0P1vNEuOFKrNCEEvWIKRrqs
|
||||
# eyBB73G8KD2yw6jfz0VKxNSRAdhJV/ghOyrDt5a+L6C3m1rpr8sqiof3iohv3ANI
|
||||
# gNqw6ex+4+G+B7JMbIHbGpPdebedL6ePbuBCnbgJoDn340k0aw6ij21GvvUnkQID
|
||||
# AQABo4IBGzCCARcwHQYDVR0OBBYEFAlCOq9DDIa0A0oqgKtM5vjuZeK+MB8GA1Ud
|
||||
# IwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0
|
||||
# dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0
|
||||
# YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKG
|
||||
# Pmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3RhUENB
|
||||
# XzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUH
|
||||
# AwgwDQYJKoZIhvcNAQELBQADggEBAET3xBg/IZ9zdOfwbDGK7cK3qKYt/qUOlbRB
|
||||
# zgeNjb32K86nGeRGkBee10dVOEGWUw6KtBeWh1LQ70b64/tLtiLcsf9JzaAyDYb1
|
||||
# sRmMi5fjRZ753TquaT8V7NJ7RfEuYfvZlubfQD0MVbU4tzsdZdYuxE37V2J9pN89
|
||||
# j7GoFNtAnSnCn1MRxENAILgt9XzeQzTEDhFYW0N2DNphTkRPXGjpDmwi6WtkJ5fv
|
||||
# 0iTyB4dwEC+/ed0lGbFLcytJoMwfTNMdH6gcnHlMzsniornGFZa5PPiV78XoZ9Fe
|
||||
# upKo8ZKNGhLLLB5GTtqfHex5no3ioVSq+NthvhX0I/V+iXJsopowggZxMIIEWaAD
|
||||
# AgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzET
|
||||
# MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
|
||||
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBD
|
||||
# ZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0yNTA3
|
||||
# MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
|
||||
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
|
||||
# JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjANBgkq
|
||||
# hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RUENWl
|
||||
# CgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBED/Fg
|
||||
# iIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50YWeR
|
||||
# X4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd/Xcf
|
||||
# PfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaRtogI
|
||||
# Neh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQABo4IB
|
||||
# 5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8RhvF
|
||||
# M2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAP
|
||||
# BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjE
|
||||
# MFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kv
|
||||
# Y3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEF
|
||||
# BQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
|
||||
# a2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSABAf8E
|
||||
# gZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3dy5t
|
||||
# aWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEFBQcC
|
||||
# AjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBtAGUA
|
||||
# bgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Ehb7Pr
|
||||
# psz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7uVOM
|
||||
# zPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqRUgCv
|
||||
# OA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9Va8v
|
||||
# /rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8+n99
|
||||
# lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+Y1kl
|
||||
# D3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh2rBQ
|
||||
# Hm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRyzR30
|
||||
# uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoouLGp
|
||||
# 25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx16HS
|
||||
# xVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341Hgi6
|
||||
# 2jbb01+P3nSISRKhggLLMIICNAIBATCB+KGB0KSBzTCByjELMAkGA1UEBhMCVVMx
|
||||
# CzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
|
||||
# ZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
|
||||
# dGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046MTc5RS00QkIw
|
||||
# LTgyNDYxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoB
|
||||
# ATAHBgUrDgMCGgMVAMsg9FQ9pgPLXI2Ld5z7xDS0QAZ9oIGDMIGApH4wfDELMAkG
|
||||
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
|
||||
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z
|
||||
# b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDipo0MMCIY
|
||||
# DzIwMjAwNzAxMTIxODIwWhgPMjAyMDA3MDIxMjE4MjBaMHQwOgYKKwYBBAGEWQoE
|
||||
# ATEsMCowCgIFAOKmjQwCAQAwBwIBAAICE70wBwIBAAICEeIwCgIFAOKn3owCAQAw
|
||||
# NgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgC
|
||||
# AQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQCOPjlHOH8nYtgt2XnpKXenxPUR03ED
|
||||
# xPBm8XR5Z1vIq53RU9jG6yYcYNTdK+q38SGZtu0W/SgagTfKCQhjhRakuv7rGSs2
|
||||
# dlhx9LGCoc/q1vqmZpRSjkqWVcc/NzmldUWIWnLlV6rmLGoDmfCH5BcsiU6Eo6wU
|
||||
# iUVwnnXoqsCaBzGCAw0wggMJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
|
||||
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
|
||||
# ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD
|
||||
# QSAyMDEwAhMzAAABDKp4btzMQkzBAAAAAAEMMA0GCWCGSAFlAwQCAQUAoIIBSjAa
|
||||
# BgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIDpwhjyu
|
||||
# zgu3Kmxpnpz86ZlthBqEzG5vaEMOkYRyuFCaMIH6BgsqhkiG9w0BCRACLzGB6jCB
|
||||
# 5zCB5DCBvQQgg5AWKX7M1+m2//+V7qmRvt1K/ww5Muu8XzGJBqygVCkwgZgwgYCk
|
||||
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
|
||||
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
|
||||
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
|
||||
# AOMxeOgwIhgPMjAyMDEwMTQxNzE3MjhaGA8yMDIwMTAxNTE3MTcyOFowdzA9Bgor
|
||||
# BgEEAYRZCgQBMS8wLTAKAgUA4zF46AIBADAKAgEAAgIQPAIB/zAHAgEAAgIRZDAK
|
||||
# AgUA4zLKaAIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
|
||||
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBALEDKhtH6no+VBWb
|
||||
# KHscN3Q0bphy1tgMhLZ0UBYpPSgcrPnF36tX3nswRAci3gLdgc77hjn2Zc6UyVJk
|
||||
# WhFguWv6KoyTunGPejS/fPIGKm1CXQnEV/JUvt1EAf7YRpHImfjZBhNXbVyV61gy
|
||||
# fEGA6fNNgbI+57xQJCZqdKBYX3EFMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC
|
||||
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
|
||||
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
|
||||
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAEm37pLIrmCggcAAAAAASYwDQYJYIZIAWUD
|
||||
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
|
||||
# CQQxIgQgmfmj5y7wRFTyeI0TaXaljaCJoRQMvGBEAXsAQuY3ZOcwgfoGCyqGSIb3
|
||||
# DQEJEAIvMYHqMIHnMIHkMIG9BCA2/c/vnr1ecAzvapOWZ2xGfAkzrkfpGcrvMW07
|
||||
# CQl1DzCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
|
||||
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
|
||||
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
|
||||
# Jt+6SyK5goIHAAAAAAEmMCIEIJ7sOcZ9sNFABAvIMRs2kk0cZhB239DZbXCLYMT8
|
||||
# frPMMA0GCSqGSIb3DQEBCwUABIIBAEiXebYdQ9DIz74YpfQ9FBaLHiSfD3s+jO7x
|
||||
# 1noNe0HIdZaX/Asow0OqsEMzZanOpa3yO8BJskKoDJW9pU//xqCzV1W5FzoOT4Qs
|
||||
# ZJpG0R5f/eHqMMeRBVUPn1FfT4pQVcHfRHOW/I3hWC0G4SeVwU/L9d8JLSQKzl39
|
||||
# 8bMFbtLJWxUJMM4Vp8Tf+cR7ShZdsK9w88QokR9xbuQgn6jsqhOuyw+dUGrwEI7h
|
||||
# GCdUmsT614oSgdnuUBf/g1aew0e3ulmZYYQ2QLKqnDXuqUIFnPtWFB90h++mdlFg
|
||||
# fvIEusNgYkb2kl5xQfxm3wynbxtP249vWF4GACZtqqSj3tcQ+xQ=
|
||||
# SIG # End signature block
|
||||
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAQyqeG7czEJMwQAA
|
||||
# AAABDDAiBCD11urvv5vgo4gFVQ2NMVrzgxT87Yuiq16YdswYbaYeITANBgkqhkiG
|
||||
# 9w0BAQsFAASCAQAi3q8hwcT2ft4b2EleaiyZxOImV/cKusmth1dtCh5/Jb0GbOld
|
||||
# f5cSalrjf42MNPodWAtgmWozkYrQF6HxnsOiYiamfRA8E3E7xyRMy7AFfAhjcwMi
|
||||
# xaW4Iye6E1Ec6LtULANxfDtG/KIdCWdZxKqOezL3nzFNQWmm1mXPV+UnKpnJkA3E
|
||||
# DsQOUWk8J6ojDurhrP536WI+3arg8PcnppHBLd/xNKYdlsTb+6qndgzKXkDDt1CV
|
||||
# 4zCyuZ7bO8eyZAmNoSZz22k7vus9UjBz/CDhXylo20N43nr29rWPItUgH4uvOGQn
|
||||
# t26Y/yjBaQImz32psrfJEMbQ7cl789s8WOx8
|
||||
# SIG # End signature block
|
||||
56
src/Misc/dotnet-install.sh
vendored
56
src/Misc/dotnet-install.sh
vendored
@@ -270,7 +270,7 @@ check_pre_reqs() {
|
||||
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep zlib)" ] && say_warning "Unable to locate zlib. Probable prerequisite missing; install zlib."
|
||||
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep ssl)" ] && say_warning "Unable to locate libssl. Probable prerequisite missing; install libssl."
|
||||
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libicu)" ] && say_warning "Unable to locate libicu. Probable prerequisite missing; install libicu."
|
||||
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep lttng)" ] && say_warning "Unable to locate liblttng. Probable prerequisite missing; install liblttng."
|
||||
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep lttng)" ] && say_warning "Unable to locate liblttng. Probable prerequisite missing; install libcurl."
|
||||
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libcurl)" ] && say_warning "Unable to locate libcurl. Probable prerequisite missing; install libcurl."
|
||||
fi
|
||||
|
||||
@@ -373,7 +373,7 @@ get_normalized_architecture_from_architecture() {
|
||||
;;
|
||||
esac
|
||||
|
||||
say_err "Architecture \`$architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues"
|
||||
say_err "Architecture \`$architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/sdk/issues"
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -545,18 +545,17 @@ construct_download_link() {
|
||||
local channel="$2"
|
||||
local normalized_architecture="$3"
|
||||
local specific_version="${4//[$'\t\r\n']}"
|
||||
local specific_product_version="$(get_specific_product_version "$1" "$4")"
|
||||
|
||||
local osname
|
||||
osname="$(get_current_os_name)" || return 1
|
||||
|
||||
local download_link=null
|
||||
if [[ "$runtime" == "dotnet" ]]; then
|
||||
download_link="$azure_feed/Runtime/$specific_version/dotnet-runtime-$specific_product_version-$osname-$normalized_architecture.tar.gz"
|
||||
download_link="$azure_feed/Runtime/$specific_version/dotnet-runtime-$specific_version-$osname-$normalized_architecture.tar.gz"
|
||||
elif [[ "$runtime" == "aspnetcore" ]]; then
|
||||
download_link="$azure_feed/aspnetcore/Runtime/$specific_version/aspnetcore-runtime-$specific_product_version-$osname-$normalized_architecture.tar.gz"
|
||||
download_link="$azure_feed/aspnetcore/Runtime/$specific_version/aspnetcore-runtime-$specific_version-$osname-$normalized_architecture.tar.gz"
|
||||
elif [ -z "$runtime" ]; then
|
||||
download_link="$azure_feed/Sdk/$specific_version/dotnet-sdk-$specific_product_version-$osname-$normalized_architecture.tar.gz"
|
||||
download_link="$azure_feed/Sdk/$specific_version/dotnet-sdk-$specific_version-$osname-$normalized_architecture.tar.gz"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
@@ -565,44 +564,6 @@ construct_download_link() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# args:
|
||||
# azure_feed - $1
|
||||
# specific_version - $2
|
||||
get_specific_product_version() {
|
||||
# If we find a 'productVersion.txt' at the root of any folder, we'll use its contents
|
||||
# to resolve the version of what's in the folder, superseding the specified version.
|
||||
eval $invocation
|
||||
|
||||
local azure_feed="$1"
|
||||
local specific_version="${2//[$'\t\r\n']}"
|
||||
local specific_product_version=$specific_version
|
||||
|
||||
local download_link=null
|
||||
if [[ "$runtime" == "dotnet" ]]; then
|
||||
download_link="$azure_feed/Runtime/$specific_version/productVersion.txt${feed_credential}"
|
||||
elif [[ "$runtime" == "aspnetcore" ]]; then
|
||||
download_link="$azure_feed/aspnetcore/Runtime/$specific_version/productVersion.txt${feed_credential}"
|
||||
elif [ -z "$runtime" ]; then
|
||||
download_link="$azure_feed/Sdk/$specific_version/productVersion.txt${feed_credential}"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
|
||||
specific_product_version=$(curl -s --fail "$download_link")
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
specific_product_version=$(wget -qO- "$download_link")
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
specific_product_version=$specific_version
|
||||
fi
|
||||
fi
|
||||
specific_product_version="${specific_product_version//[$'\t\r\n']}"
|
||||
|
||||
echo "$specific_product_version"
|
||||
return 0
|
||||
}
|
||||
|
||||
# args:
|
||||
# azure_feed - $1
|
||||
# channel - $2
|
||||
@@ -810,7 +771,6 @@ calculate_vars() {
|
||||
say_verbose "normalized_architecture=$normalized_architecture"
|
||||
|
||||
specific_version="$(get_specific_version_from_version "$azure_feed" "$channel" "$normalized_architecture" "$version" "$json_file")"
|
||||
specific_product_version="$(get_specific_product_version "$azure_feed" "$specific_version")"
|
||||
say_verbose "specific_version=$specific_version"
|
||||
if [ -z "$specific_version" ]; then
|
||||
say_err "Could not resolve version information."
|
||||
@@ -909,12 +869,12 @@ install_dotnet() {
|
||||
fi
|
||||
|
||||
# Check if the standard SDK version is installed.
|
||||
say_verbose "Checking installation: version = $specific_product_version"
|
||||
if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_product_version"; then
|
||||
say_verbose "Checking installation: version = $specific_version"
|
||||
if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_version"; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
say_err "\`$asset_name\` with version = $specific_product_version failed to install with an unknown error."
|
||||
say_err "\`$asset_name\` with version = $specific_version failed to install with an unknown error."
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,5 @@
|
||||
<key>ACTIONS_RUNNER_SVC</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
<key>ProcessType</key>
|
||||
<string>Interactive</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -140,9 +140,6 @@ namespace GitHub.Runner.Common
|
||||
|
||||
public static readonly string InternalTelemetryIssueDataKey = "_internal_telemetry";
|
||||
public static readonly string WorkerCrash = "WORKER_CRASH";
|
||||
public static readonly string UnsupportedCommand = "UNSUPPORTED_COMMAND";
|
||||
public static readonly string UnsupportedCommandMessage = "The `{0}` command is deprecated and will be disabled soon. Please upgrade to using Environment Files. For more information see: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/";
|
||||
public static readonly string UnsupportedCommandMessageDisabled = "The `{0}` command is disabled. Please upgrade to using Environment Files or opt into unsecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_COMMANDS` environment variable to `true`. For more information see: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/";
|
||||
}
|
||||
|
||||
public static class RunnerEvent
|
||||
@@ -201,7 +198,6 @@ namespace GitHub.Runner.Common
|
||||
//
|
||||
// Keep alphabetical
|
||||
//
|
||||
public static readonly string AllowUnsupportedCommands = "ACTIONS_ALLOW_UNSECURE_COMMANDS";
|
||||
public static readonly string RunnerDebug = "ACTIONS_RUNNER_DEBUG";
|
||||
public static readonly string StepDebug = "ACTIONS_STEP_DEBUG";
|
||||
}
|
||||
|
||||
@@ -56,10 +56,6 @@ namespace GitHub.Runner.Common
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.EndGroupCommandExtension, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.EchoCommandExtension, Runner.Worker");
|
||||
break;
|
||||
case "GitHub.Runner.Worker.IFileCommandExtension":
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.AddPathFileCommand, Runner.Worker");
|
||||
Add<T>(extensions, "GitHub.Runner.Worker.SetEnvFileCommand, Runner.Worker");
|
||||
break;
|
||||
default:
|
||||
// This should never happen.
|
||||
throw new NotSupportedException($"Unexpected extension type: '{typeof(T).FullName}'");
|
||||
|
||||
@@ -16,7 +16,6 @@ namespace GitHub.Runner.Common
|
||||
// logging and console
|
||||
Task<TaskLog> AppendLogContentAsync(Guid scopeIdentifier, string hubName, Guid planId, int logId, Stream uploadStream, CancellationToken cancellationToken);
|
||||
Task AppendTimelineRecordFeedAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, Guid stepId, IList<string> lines, CancellationToken cancellationToken);
|
||||
Task AppendTimelineRecordFeedAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, Guid stepId, IList<string> lines, long startLine, CancellationToken cancellationToken);
|
||||
Task<TaskAttachment> CreateAttachmentAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, String type, String name, Stream uploadStream, CancellationToken cancellationToken);
|
||||
Task<TaskLog> CreateLogAsync(Guid scopeIdentifier, string hubName, Guid planId, TaskLog log, CancellationToken cancellationToken);
|
||||
Task<Timeline> CreateTimelineAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, CancellationToken cancellationToken);
|
||||
@@ -80,12 +79,6 @@ namespace GitHub.Runner.Common
|
||||
return _taskClient.AppendTimelineRecordFeedAsync(scopeIdentifier, hubName, planId, timelineId, timelineRecordId, stepId, lines, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public Task AppendTimelineRecordFeedAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, Guid stepId, IList<string> lines, long startLine, CancellationToken cancellationToken)
|
||||
{
|
||||
CheckConnection();
|
||||
return _taskClient.AppendTimelineRecordFeedAsync(scopeIdentifier, hubName, planId, timelineId, timelineRecordId, stepId, lines, startLine, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public Task<TaskAttachment> CreateAttachmentAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, string type, string name, Stream uploadStream, CancellationToken cancellationToken)
|
||||
{
|
||||
CheckConnection();
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace GitHub.Runner.Common
|
||||
event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling;
|
||||
Task ShutdownAsync();
|
||||
void Start(Pipelines.AgentJobRequestMessage jobRequest);
|
||||
void QueueWebConsoleLine(Guid stepRecordId, string line, long? lineNumber = null);
|
||||
void QueueWebConsoleLine(Guid stepRecordId, string line);
|
||||
void QueueFileUpload(Guid timelineId, Guid timelineRecordId, string type, string name, string path, bool deleteSource);
|
||||
void QueueTimelineRecordUpdate(Guid timelineId, TimelineRecord timelineRecord);
|
||||
}
|
||||
@@ -155,10 +155,10 @@ namespace GitHub.Runner.Common
|
||||
Trace.Info("All queue process tasks have been stopped, and all queues are drained.");
|
||||
}
|
||||
|
||||
public void QueueWebConsoleLine(Guid stepRecordId, string line, long? lineNumber)
|
||||
public void QueueWebConsoleLine(Guid stepRecordId, string line)
|
||||
{
|
||||
Trace.Verbose("Enqueue web console line queue: {0}", line);
|
||||
_webConsoleLineQueue.Enqueue(new ConsoleLineInfo(stepRecordId, line, lineNumber));
|
||||
_webConsoleLineQueue.Enqueue(new ConsoleLineInfo(stepRecordId, line));
|
||||
}
|
||||
|
||||
public void QueueFileUpload(Guid timelineId, Guid timelineRecordId, string type, string name, string path, bool deleteSource)
|
||||
@@ -214,7 +214,7 @@ namespace GitHub.Runner.Common
|
||||
}
|
||||
|
||||
// Group consolelines by timeline record of each step
|
||||
Dictionary<Guid, List<TimelineRecordLogLine>> stepsConsoleLines = new Dictionary<Guid, List<TimelineRecordLogLine>>();
|
||||
Dictionary<Guid, List<string>> stepsConsoleLines = new Dictionary<Guid, List<string>>();
|
||||
List<Guid> stepRecordIds = new List<Guid>(); // We need to keep lines in order
|
||||
int linesCounter = 0;
|
||||
ConsoleLineInfo lineInfo;
|
||||
@@ -222,7 +222,7 @@ namespace GitHub.Runner.Common
|
||||
{
|
||||
if (!stepsConsoleLines.ContainsKey(lineInfo.StepRecordId))
|
||||
{
|
||||
stepsConsoleLines[lineInfo.StepRecordId] = new List<TimelineRecordLogLine>();
|
||||
stepsConsoleLines[lineInfo.StepRecordId] = new List<string>();
|
||||
stepRecordIds.Add(lineInfo.StepRecordId);
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ namespace GitHub.Runner.Common
|
||||
lineInfo.Line = $"{lineInfo.Line.Substring(0, 1024)}...";
|
||||
}
|
||||
|
||||
stepsConsoleLines[lineInfo.StepRecordId].Add(new TimelineRecordLogLine(lineInfo.Line, lineInfo.LineNumber));
|
||||
stepsConsoleLines[lineInfo.StepRecordId].Add(lineInfo.Line);
|
||||
linesCounter++;
|
||||
|
||||
// process at most about 500 lines of web console line during regular timer dequeue task.
|
||||
@@ -247,13 +247,13 @@ namespace GitHub.Runner.Common
|
||||
{
|
||||
// Split consolelines into batch, each batch will container at most 100 lines.
|
||||
int batchCounter = 0;
|
||||
List<List<TimelineRecordLogLine>> batchedLines = new List<List<TimelineRecordLogLine>>();
|
||||
List<List<string>> batchedLines = new List<List<string>>();
|
||||
foreach (var line in stepsConsoleLines[stepRecordId])
|
||||
{
|
||||
var currentBatch = batchedLines.ElementAtOrDefault(batchCounter);
|
||||
if (currentBatch == null)
|
||||
{
|
||||
batchedLines.Add(new List<TimelineRecordLogLine>());
|
||||
batchedLines.Add(new List<string>());
|
||||
currentBatch = batchedLines.ElementAt(batchCounter);
|
||||
}
|
||||
|
||||
@@ -275,6 +275,7 @@ namespace GitHub.Runner.Common
|
||||
{
|
||||
Trace.Info($"Skip {batchedLines.Count - 2} batches web console lines for last run");
|
||||
batchedLines = batchedLines.TakeLast(2).ToList();
|
||||
batchedLines[0].Insert(0, "...");
|
||||
}
|
||||
|
||||
int errorCount = 0;
|
||||
@@ -283,15 +284,7 @@ namespace GitHub.Runner.Common
|
||||
try
|
||||
{
|
||||
// we will not requeue failed batch, since the web console lines are time sensitive.
|
||||
if (batch[0].LineNumber.HasValue)
|
||||
{
|
||||
await _jobServer.AppendTimelineRecordFeedAsync(_scopeIdentifier, _hubName, _planId, _jobTimelineId, _jobTimelineRecordId, stepRecordId, batch.Select(logLine => logLine.Line).ToList(), batch[0].LineNumber.Value, default(CancellationToken));
|
||||
}
|
||||
else
|
||||
{
|
||||
await _jobServer.AppendTimelineRecordFeedAsync(_scopeIdentifier, _hubName, _planId, _jobTimelineId, _jobTimelineRecordId, stepRecordId, batch.Select(logLine => logLine.Line).ToList(), default(CancellationToken));
|
||||
}
|
||||
|
||||
await _jobServer.AppendTimelineRecordFeedAsync(_scopeIdentifier, _hubName, _planId, _jobTimelineId, _jobTimelineRecordId, stepRecordId, batch, default(CancellationToken));
|
||||
if (_firstConsoleOutputs)
|
||||
{
|
||||
HostContext.WritePerfCounter($"WorkerJobServerQueueAppendFirstConsoleOutput_{_planId.ToString()}");
|
||||
@@ -660,15 +653,13 @@ namespace GitHub.Runner.Common
|
||||
|
||||
internal class ConsoleLineInfo
|
||||
{
|
||||
public ConsoleLineInfo(Guid recordId, string line, long? lineNumber)
|
||||
public ConsoleLineInfo(Guid recordId, string line)
|
||||
{
|
||||
this.StepRecordId = recordId;
|
||||
this.Line = line;
|
||||
this.LineNumber = lineNumber;
|
||||
}
|
||||
|
||||
public Guid StepRecordId { get; set; }
|
||||
public string Line { get; set; }
|
||||
public long? LineNumber { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,14 +462,13 @@ Options:
|
||||
--commit Prints the runner commit
|
||||
|
||||
Config Options:
|
||||
--unattended Disable interactive prompts for missing arguments. Defaults will be used for missing options
|
||||
--url string Repository to add the runner to. Required if unattended
|
||||
--token string Registration token. Required if unattended
|
||||
--name string Name of the runner to configure (default {Environment.MachineName ?? "myrunner"})
|
||||
--runnergroup string Name of the runner group to add this runner to (defaults to the default runner group)
|
||||
--labels string Extra labels in addition to the default: 'self-hosted,{Constants.Runner.Platform},{Constants.Runner.PlatformArchitecture}'
|
||||
--work string Relative runner work directory (default {Constants.Path.WorkDirectory})
|
||||
--replace Replace any existing runner with the same name (default false)");
|
||||
--unattended Disable interactive prompts for missing arguments. Defaults will be used for missing options
|
||||
--url string Repository to add the runner to. Required if unattended
|
||||
--token string Registration token. Required if unattended
|
||||
--name string Name of the runner to configure (default {Environment.MachineName ?? "myrunner"})
|
||||
--labels string Extra labels in addition to the default: 'self-hosted,{Constants.Runner.Platform},{Constants.Runner.PlatformArchitecture}'
|
||||
--work string Relative runner work directory (default {Constants.Path.WorkDirectory})
|
||||
--replace Replace any existing runner with the same name (default false)");
|
||||
#if OS_WINDOWS
|
||||
_term.WriteLine($@" --runasservice Run the runner as a service");
|
||||
_term.WriteLine($@" --windowslogonaccount string Account to run the service as. Requires runasservice");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
@@ -184,63 +183,11 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
{
|
||||
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
||||
var isHostedServer = configurationStore.GetSettings().IsHostedServer;
|
||||
|
||||
var allowUnsecureCommands = false;
|
||||
bool.TryParse(Environment.GetEnvironmentVariable(Constants.Variables.Actions.AllowUnsupportedCommands), out allowUnsecureCommands);
|
||||
|
||||
// Apply environment from env context, env context contains job level env and action's env block
|
||||
#if OS_WINDOWS
|
||||
var envContext = context.ExpressionValues["env"] as DictionaryContextData;
|
||||
#else
|
||||
var envContext = context.ExpressionValues["env"] as CaseSensitiveDictionaryContextData;
|
||||
#endif
|
||||
if (!allowUnsecureCommands && envContext.ContainsKey(Constants.Variables.Actions.AllowUnsupportedCommands))
|
||||
{
|
||||
bool.TryParse(envContext[Constants.Variables.Actions.AllowUnsupportedCommands].ToString(), out allowUnsecureCommands);
|
||||
}
|
||||
|
||||
// TODO: Eventually remove isHostedServer and apply this to dotcom customers as well
|
||||
if (!isHostedServer && !allowUnsecureCommands)
|
||||
{
|
||||
throw new Exception(String.Format(Constants.Runner.UnsupportedCommandMessageDisabled, this.Command));
|
||||
}
|
||||
else if (!allowUnsecureCommands)
|
||||
{
|
||||
// Log Telemetry and let user know they shouldn't do this
|
||||
var issue = new Issue()
|
||||
{
|
||||
Type = IssueType.Error,
|
||||
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
|
||||
};
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
if (!command.Properties.TryGetValue(SetEnvCommandProperties.Name, out string envName) || string.IsNullOrEmpty(envName))
|
||||
{
|
||||
throw new Exception("Required field 'name' is missing in ##[set-env] command.");
|
||||
}
|
||||
|
||||
|
||||
foreach (var blocked in _setEnvBlockList)
|
||||
{
|
||||
if (string.Equals(blocked, envName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Log Telemetry and let user know they shouldn't do this
|
||||
var issue = new Issue()
|
||||
{
|
||||
Type = IssueType.Error,
|
||||
Message = $"Can't update {blocked} environment variable using ::set-env:: command."
|
||||
};
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = $"{Constants.Runner.UnsupportedCommand}_{envName}";
|
||||
context.AddIssue(issue);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
context.Global.EnvironmentVariables[envName] = command.Data;
|
||||
context.SetEnvContext(envName, command.Data);
|
||||
context.Debug($"{envName}='{command.Data}'");
|
||||
@@ -250,11 +197,6 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
public const String Name = "name";
|
||||
}
|
||||
|
||||
private string[] _setEnvBlockList =
|
||||
{
|
||||
"NODE_OPTIONS"
|
||||
};
|
||||
}
|
||||
|
||||
public sealed class SetOutputCommandExtension : RunnerService, IActionCommandExtension
|
||||
@@ -340,40 +282,6 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
|
||||
{
|
||||
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
||||
var isHostedServer = configurationStore.GetSettings().IsHostedServer;
|
||||
|
||||
var allowUnsecureCommands = false;
|
||||
bool.TryParse(Environment.GetEnvironmentVariable(Constants.Variables.Actions.AllowUnsupportedCommands), out allowUnsecureCommands);
|
||||
|
||||
// Apply environment from env context, env context contains job level env and action's env block
|
||||
#if OS_WINDOWS
|
||||
var envContext = context.ExpressionValues["env"] as DictionaryContextData;
|
||||
#else
|
||||
var envContext = context.ExpressionValues["env"] as CaseSensitiveDictionaryContextData;
|
||||
#endif
|
||||
if (!allowUnsecureCommands && envContext.ContainsKey(Constants.Variables.Actions.AllowUnsupportedCommands))
|
||||
{
|
||||
bool.TryParse(envContext[Constants.Variables.Actions.AllowUnsupportedCommands].ToString(), out allowUnsecureCommands);
|
||||
}
|
||||
|
||||
// TODO: Eventually remove isHostedServer and apply this to dotcom customers as well
|
||||
if (!isHostedServer && !allowUnsecureCommands)
|
||||
{
|
||||
throw new Exception(String.Format(Constants.Runner.UnsupportedCommandMessageDisabled, this.Command));
|
||||
}
|
||||
else if (!allowUnsecureCommands)
|
||||
{
|
||||
// Log Telemetry and let user know they shouldn't do this
|
||||
var issue = new Issue()
|
||||
{
|
||||
Type = IssueType.Error,
|
||||
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
|
||||
};
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
ArgUtil.NotNullOrEmpty(command.Data, "path");
|
||||
context.Global.PrependPath.RemoveAll(x => string.Equals(x, command.Data, StringComparison.CurrentCulture));
|
||||
context.Global.PrependPath.Add(command.Data);
|
||||
|
||||
@@ -492,7 +492,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
}
|
||||
executionContext.Output("##[endgroup]");
|
||||
executionContext.Output("##[endgroup");
|
||||
|
||||
if (retryCount == 3 && pullExitCode != 0)
|
||||
{
|
||||
|
||||
@@ -145,10 +145,6 @@ namespace GitHub.Runner.Worker
|
||||
stepHost = containerStepHost;
|
||||
}
|
||||
|
||||
// Setup File Command Manager
|
||||
var fileCommandManager = HostContext.CreateService<IFileCommandManager>();
|
||||
fileCommandManager.InitializeFiles(ExecutionContext, null);
|
||||
|
||||
// Load the inputs.
|
||||
ExecutionContext.Debug("Loading inputs");
|
||||
var templateEvaluator = ExecutionContext.ToPipelineTemplateEvaluator();
|
||||
@@ -242,15 +238,7 @@ namespace GitHub.Runner.Worker
|
||||
handler.PrintActionDetails(Stage);
|
||||
|
||||
// Run the task.
|
||||
try
|
||||
{
|
||||
await handler.RunAsync(Stage);
|
||||
}
|
||||
finally
|
||||
{
|
||||
fileCommandManager.ProcessFiles(ExecutionContext, ExecutionContext.Global.Container);
|
||||
}
|
||||
|
||||
await handler.RunAsync(Stage);
|
||||
}
|
||||
|
||||
public bool TryEvaluateDisplayName(DictionaryContextData contextData, IExecutionContext context)
|
||||
|
||||
@@ -34,9 +34,6 @@ namespace GitHub.Runner.Worker.Container
|
||||
_environmentVariables = container.Environment;
|
||||
this.IsJobContainer = isJobContainer;
|
||||
this.ContainerNetworkAlias = networkAlias;
|
||||
this.RegistryAuthUsername = container.Credentials?.Username;
|
||||
this.RegistryAuthPassword = container.Credentials?.Password;
|
||||
this.RegistryServer = DockerUtil.ParseRegistryHostnameFromImageName(this.ContainerImage);
|
||||
|
||||
#if OS_WINDOWS
|
||||
_pathMappings.Add(new PathMapping(hostContext.GetDirectory(WellKnownDirectory.Work), "C:\\__w"));
|
||||
@@ -82,9 +79,6 @@ namespace GitHub.Runner.Worker.Container
|
||||
public string ContainerWorkDirectory { get; set; }
|
||||
public string ContainerCreateOptions { get; private set; }
|
||||
public string ContainerRuntimePath { get; set; }
|
||||
public string RegistryServer { get; set; }
|
||||
public string RegistryAuthUsername { get; set; }
|
||||
public string RegistryAuthPassword { get; set; }
|
||||
public bool IsJobContainer { get; set; }
|
||||
|
||||
public IDictionary<string, string> ContainerEnvironmentVariables
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
@@ -18,7 +17,6 @@ namespace GitHub.Runner.Worker.Container
|
||||
string DockerInstanceLabel { get; }
|
||||
Task<DockerVersion> DockerVersion(IExecutionContext context);
|
||||
Task<int> DockerPull(IExecutionContext context, string image);
|
||||
Task<int> DockerPull(IExecutionContext context, string image, string configFileDirectory);
|
||||
Task<int> DockerBuild(IExecutionContext context, string workingDirectory, string dockerFile, string dockerContext, string tag);
|
||||
Task<string> DockerCreate(IExecutionContext context, ContainerInfo container);
|
||||
Task<int> DockerRun(IExecutionContext context, ContainerInfo container, EventHandler<ProcessDataReceivedEventArgs> stdoutDataReceived, EventHandler<ProcessDataReceivedEventArgs> stderrDataReceived);
|
||||
@@ -33,7 +31,6 @@ namespace GitHub.Runner.Worker.Container
|
||||
Task<int> DockerExec(IExecutionContext context, string containerId, string options, string command, List<string> outputs);
|
||||
Task<List<string>> DockerInspect(IExecutionContext context, string dockerObject, string options);
|
||||
Task<List<PortMapping>> DockerPort(IExecutionContext context, string containerId);
|
||||
Task<int> DockerLogin(IExecutionContext context, string configFileDirectory, string registry, string username, string password);
|
||||
}
|
||||
|
||||
public class DockerCommandManager : RunnerService, IDockerCommandManager
|
||||
@@ -85,18 +82,9 @@ namespace GitHub.Runner.Worker.Container
|
||||
return new DockerVersion(serverVersion, clientVersion);
|
||||
}
|
||||
|
||||
public Task<int> DockerPull(IExecutionContext context, string image)
|
||||
public async Task<int> DockerPull(IExecutionContext context, string image)
|
||||
{
|
||||
return DockerPull(context, image, null);
|
||||
}
|
||||
|
||||
public async Task<int> DockerPull(IExecutionContext context, string image, string configFileDirectory)
|
||||
{
|
||||
if (string.IsNullOrEmpty(configFileDirectory))
|
||||
{
|
||||
return await ExecuteDockerCommandAsync(context, $"pull", image, context.CancellationToken);
|
||||
}
|
||||
return await ExecuteDockerCommandAsync(context, $"--config {configFileDirectory} pull", image, context.CancellationToken);
|
||||
return await ExecuteDockerCommandAsync(context, "pull", image, context.CancellationToken);
|
||||
}
|
||||
|
||||
public async Task<int> DockerBuild(IExecutionContext context, string workingDirectory, string dockerFile, string dockerContext, string tag)
|
||||
@@ -358,28 +346,6 @@ namespace GitHub.Runner.Worker.Container
|
||||
return DockerUtil.ParseDockerPort(portMappingLines);
|
||||
}
|
||||
|
||||
public Task<int> DockerLogin(IExecutionContext context, string configFileDirectory, string registry, string username, string password)
|
||||
{
|
||||
string args = $"--config {configFileDirectory} login {registry} -u {username} --password-stdin";
|
||||
context.Command($"{DockerPath} {args}");
|
||||
|
||||
var input = Channel.CreateBounded<string>(new BoundedChannelOptions(1) { SingleReader = true, SingleWriter = true });
|
||||
input.Writer.TryWrite(password);
|
||||
|
||||
var processInvoker = HostContext.CreateService<IProcessInvoker>();
|
||||
|
||||
return processInvoker.ExecuteAsync(
|
||||
workingDirectory: context.GetGitHubContext("workspace"),
|
||||
fileName: DockerPath,
|
||||
arguments: args,
|
||||
environment: null,
|
||||
requireExitCodeZero: false,
|
||||
outputEncoding: null,
|
||||
killProcessOnCancel: false,
|
||||
redirectStandardIn: input,
|
||||
cancellationToken: context.CancellationToken);
|
||||
}
|
||||
|
||||
private Task<int> ExecuteDockerCommandAsync(IExecutionContext context, string command, string options, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return ExecuteDockerCommandAsync(context, command, options, null, cancellationToken);
|
||||
|
||||
@@ -45,21 +45,5 @@ namespace GitHub.Runner.Worker.Container
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static string ParseRegistryHostnameFromImageName(string name)
|
||||
{
|
||||
var nameSplit = name.Split('/');
|
||||
// Single slash is implictly from Dockerhub, unless first part has .tld or :port
|
||||
if (nameSplit.Length == 2 && (nameSplit[0].Contains(":") || nameSplit[0].Contains(".")))
|
||||
{
|
||||
return nameSplit[0];
|
||||
}
|
||||
// All other non Dockerhub registries
|
||||
else if (nameSplit.Length > 2)
|
||||
{
|
||||
return nameSplit[0];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,18 +198,12 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add at a later date. This currently no local package registry to test with
|
||||
// UpdateRegistryAuthForGitHubToken(executionContext, container);
|
||||
|
||||
// Before pulling, generate client authentication if required
|
||||
var configLocation = await ContainerRegistryLogin(executionContext, container);
|
||||
|
||||
// Pull down docker image with retry up to 3 times
|
||||
int retryCount = 0;
|
||||
int pullExitCode = 0;
|
||||
while (retryCount < 3)
|
||||
{
|
||||
pullExitCode = await _dockerManger.DockerPull(executionContext, container.ContainerImage, configLocation);
|
||||
pullExitCode = await _dockerManger.DockerPull(executionContext, container.ContainerImage);
|
||||
if (pullExitCode == 0)
|
||||
{
|
||||
break;
|
||||
@@ -226,9 +220,6 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
// Remove credentials after pulling
|
||||
ContainerRegistryLogout(configLocation);
|
||||
|
||||
if (retryCount == 3 && pullExitCode != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Docker pull failed with exit code {pullExitCode}");
|
||||
@@ -446,83 +437,5 @@ namespace GitHub.Runner.Worker
|
||||
throw new InvalidOperationException($"Failed to initialize, {container.ContainerNetworkAlias} service is {serviceHealth}.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> ContainerRegistryLogin(IExecutionContext executionContext, ContainerInfo container)
|
||||
{
|
||||
if (string.IsNullOrEmpty(container.RegistryAuthUsername) || string.IsNullOrEmpty(container.RegistryAuthPassword))
|
||||
{
|
||||
// No valid client config can be generated
|
||||
return "";
|
||||
}
|
||||
var configLocation = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Temp), $".docker_{Guid.NewGuid()}");
|
||||
try
|
||||
{
|
||||
var dirInfo = Directory.CreateDirectory(configLocation);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to create directory to store registry client credentials: {e.Message}");
|
||||
}
|
||||
var loginExitCode = await _dockerManger.DockerLogin(
|
||||
executionContext,
|
||||
configLocation,
|
||||
container.RegistryServer,
|
||||
container.RegistryAuthUsername,
|
||||
container.RegistryAuthPassword);
|
||||
|
||||
if (loginExitCode != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Docker login for '{container.RegistryServer}' failed with exit code {loginExitCode}");
|
||||
}
|
||||
return configLocation;
|
||||
}
|
||||
|
||||
private void ContainerRegistryLogout(string configLocation)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(configLocation) && Directory.Exists(configLocation))
|
||||
{
|
||||
Directory.Delete(configLocation, recursive: true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to remove directory containing Docker client credentials: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRegistryAuthForGitHubToken(IExecutionContext executionContext, ContainerInfo container)
|
||||
{
|
||||
var registryIsTokenCompatible = container.RegistryServer.Equals("docker.pkg.github.com", StringComparison.OrdinalIgnoreCase);
|
||||
if (!registryIsTokenCompatible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var registryMatchesWorkflow = false;
|
||||
|
||||
// REGISTRY/OWNER/REPO/IMAGE[:TAG]
|
||||
var imageParts = container.ContainerImage.Split('/');
|
||||
if (imageParts.Length != 4)
|
||||
{
|
||||
executionContext.Warning($"Could not identify owner and repo for container image {container.ContainerImage}. Skipping automatic token auth");
|
||||
return;
|
||||
}
|
||||
var owner = imageParts[1];
|
||||
var repo = imageParts[2];
|
||||
var nwo = $"{owner}/{repo}";
|
||||
if (nwo.Equals(executionContext.GetGitHubContext("repository"), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
registryMatchesWorkflow = true;
|
||||
}
|
||||
|
||||
var registryCredentialsNotSupplied = string.IsNullOrEmpty(container.RegistryAuthUsername) && string.IsNullOrEmpty(container.RegistryAuthPassword);
|
||||
if (registryCredentialsNotSupplied && registryMatchesWorkflow)
|
||||
{
|
||||
container.RegistryAuthUsername = executionContext.GetGitHubContext("actor");
|
||||
container.RegistryAuthPassword = executionContext.GetGitHubContext("token");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace GitHub.Runner.Worker
|
||||
JobContext JobContext { get; }
|
||||
|
||||
// Only job level ExecutionContext has JobSteps
|
||||
Queue<IStep> JobSteps { get; }
|
||||
List<IStep> JobSteps { get; }
|
||||
|
||||
// Only job level ExecutionContext has PostJobSteps
|
||||
Stack<IStep> PostJobSteps { get; }
|
||||
@@ -144,7 +144,7 @@ namespace GitHub.Runner.Worker
|
||||
public GlobalContext Global { get; private set; }
|
||||
|
||||
// Only job level ExecutionContext has JobSteps
|
||||
public Queue<IStep> JobSteps { get; private set; }
|
||||
public List<IStep> JobSteps { get; private set; }
|
||||
|
||||
// Only job level ExecutionContext has PostJobSteps
|
||||
public Stack<IStep> PostJobSteps { get; private set; }
|
||||
@@ -260,7 +260,7 @@ namespace GitHub.Runner.Worker
|
||||
DictionaryContextData inputsData,
|
||||
Dictionary<string, string> envData)
|
||||
{
|
||||
step.ExecutionContext = Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, step.Action.ContextName, logger: _logger, insideComposite: true, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token));
|
||||
step.ExecutionContext = Root.CreateChild(_record.Id, step.DisplayName, _record.Id.ToString("N"), scopeName, step.Action.ContextName, logger: _logger, insideComposite: true, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token));
|
||||
step.ExecutionContext.ExpressionValues["inputs"] = inputsData;
|
||||
step.ExecutionContext.ExpressionValues["steps"] = Global.StepsContext.GetScope(step.ExecutionContext.GetFullyQualifiedContextName());
|
||||
|
||||
@@ -384,8 +384,8 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
_logger.End();
|
||||
|
||||
// Skip if generated context name. Generated context names start with "__". After M271-ish the server will never send an empty context name.
|
||||
if (!string.IsNullOrEmpty(ContextName) && !ContextName.StartsWith("__", StringComparison.Ordinal))
|
||||
// todo: Skip if generated context name. After M271-ish the server will never send an empty context name. Generated context names will start with "__"
|
||||
if (!string.IsNullOrEmpty(ContextName))
|
||||
{
|
||||
Global.StepsContext.SetOutcome(ScopeName, ContextName, (Outcome ?? Result ?? TaskResult.Succeeded).ToActionResult());
|
||||
Global.StepsContext.SetConclusion(ScopeName, ContextName, (Result ?? TaskResult.Succeeded).ToActionResult());
|
||||
@@ -447,8 +447,8 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
ArgUtil.NotNullOrEmpty(name, nameof(name));
|
||||
|
||||
// Skip if generated context name. Generated context names start with "__". After M271-ish the server will never send an empty context name.
|
||||
if (string.IsNullOrEmpty(ContextName) || ContextName.StartsWith("__", StringComparison.Ordinal))
|
||||
// todo: Skip if generated context name. After M271-ish the server will never send an empty context name. Generated context names will start with "__"
|
||||
if (string.IsNullOrEmpty(ContextName))
|
||||
{
|
||||
reference = null;
|
||||
return;
|
||||
@@ -663,7 +663,7 @@ namespace GitHub.Runner.Worker
|
||||
Global.PrependPath = new List<string>();
|
||||
|
||||
// JobSteps for job ExecutionContext
|
||||
JobSteps = new Queue<IStep>();
|
||||
JobSteps = new List<IStep>();
|
||||
|
||||
// PostJobSteps for job ExecutionContext
|
||||
PostJobSteps = new Stack<IStep>();
|
||||
@@ -717,8 +717,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
_jobServerQueue.QueueWebConsoleLine(_record.Id, msg, totalLines);
|
||||
|
||||
_jobServerQueue.QueueWebConsoleLine(_record.Id, msg);
|
||||
return totalLines;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
[ServiceLocator(Default = typeof(FileCommandManager))]
|
||||
public interface IFileCommandManager : IRunnerService
|
||||
{
|
||||
void InitializeFiles(IExecutionContext context, ContainerInfo container);
|
||||
void ProcessFiles(IExecutionContext context, ContainerInfo container);
|
||||
|
||||
}
|
||||
|
||||
public sealed class FileCommandManager : RunnerService, IFileCommandManager
|
||||
{
|
||||
private const string _folderName = "_runner_file_commands";
|
||||
private List<IFileCommandExtension> _commandExtensions;
|
||||
private string _fileSuffix = String.Empty;
|
||||
private string _fileCommandDirectory;
|
||||
private Tracing _trace;
|
||||
|
||||
public override void Initialize(IHostContext hostContext)
|
||||
{
|
||||
base.Initialize(hostContext);
|
||||
_trace = HostContext.GetTrace(nameof(FileCommandManager));
|
||||
|
||||
_fileCommandDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Temp), _folderName);
|
||||
if (!Directory.Exists(_fileCommandDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(_fileCommandDirectory);
|
||||
}
|
||||
|
||||
var extensionManager = hostContext.GetService<IExtensionManager>();
|
||||
_commandExtensions = extensionManager.GetExtensions<IFileCommandExtension>() ?? new List<IFileCommandExtension>();
|
||||
}
|
||||
|
||||
public void InitializeFiles(IExecutionContext context, ContainerInfo container)
|
||||
{
|
||||
var oldSuffix = _fileSuffix;
|
||||
_fileSuffix = Guid.NewGuid().ToString();
|
||||
foreach (var fileCommand in _commandExtensions)
|
||||
{
|
||||
var oldPath = Path.Combine(_fileCommandDirectory, fileCommand.FilePrefix + oldSuffix);
|
||||
if (oldSuffix != String.Empty && File.Exists(oldPath))
|
||||
{
|
||||
TryDeleteFile(oldPath);
|
||||
}
|
||||
|
||||
var newPath = Path.Combine(_fileCommandDirectory, fileCommand.FilePrefix + _fileSuffix);
|
||||
TryDeleteFile(newPath);
|
||||
File.Create(newPath).Dispose();
|
||||
|
||||
var pathToSet = container != null ? container.TranslateToContainerPath(newPath) : newPath;
|
||||
context.SetGitHubContext(fileCommand.ContextName, pathToSet);
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessFiles(IExecutionContext context, ContainerInfo container)
|
||||
{
|
||||
foreach (var fileCommand in _commandExtensions)
|
||||
{
|
||||
try
|
||||
{
|
||||
fileCommand.ProcessCommand(context, Path.Combine(_fileCommandDirectory, fileCommand.FilePrefix + _fileSuffix),container);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.Error($"Unable to process file command '{fileCommand.ContextName}' successfully.");
|
||||
context.Error(ex);
|
||||
context.CommandResult = TaskResult.Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryDeleteFile(string path)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
try
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_trace.Warning($"Unable to delete file {path} for reason: {e.ToString()}");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IFileCommandExtension : IExtension
|
||||
{
|
||||
string ContextName { get; }
|
||||
string FilePrefix { get; }
|
||||
|
||||
void ProcessCommand(IExecutionContext context, string filePath, ContainerInfo container);
|
||||
}
|
||||
|
||||
public sealed class AddPathFileCommand : RunnerService, IFileCommandExtension
|
||||
{
|
||||
public string ContextName => "path";
|
||||
public string FilePrefix => "add_path_";
|
||||
|
||||
public Type ExtensionType => typeof(IFileCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string filePath, ContainerInfo container)
|
||||
{
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
var lines = File.ReadAllLines(filePath, Encoding.UTF8);
|
||||
foreach(var line in lines)
|
||||
{
|
||||
if (line == string.Empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
context.Global.PrependPath.RemoveAll(x => string.Equals(x, line, StringComparison.CurrentCulture));
|
||||
context.Global.PrependPath.Add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SetEnvFileCommand : RunnerService, IFileCommandExtension
|
||||
{
|
||||
public string ContextName => "env";
|
||||
public string FilePrefix => "set_env_";
|
||||
|
||||
public Type ExtensionType => typeof(IFileCommandExtension);
|
||||
|
||||
public void ProcessCommand(IExecutionContext context, string filePath, ContainerInfo container)
|
||||
{
|
||||
try
|
||||
{
|
||||
var text = File.ReadAllText(filePath) ?? string.Empty;
|
||||
var index = 0;
|
||||
var line = ReadLine(text, ref index);
|
||||
while (line != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(line))
|
||||
{
|
||||
var equalsIndex = line.IndexOf("=", StringComparison.Ordinal);
|
||||
var heredocIndex = line.IndexOf("<<", StringComparison.Ordinal);
|
||||
|
||||
// Normal style NAME=VALUE
|
||||
if (equalsIndex >= 0 && (heredocIndex < 0 || equalsIndex < heredocIndex))
|
||||
{
|
||||
var split = line.Split(new[] { '=' }, 2, StringSplitOptions.None);
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
throw new Exception($"Invalid environment variable format '{line}'. Environment variable name must not be empty");
|
||||
}
|
||||
SetEnvironmentVariable(context, split[0], split[1]);
|
||||
}
|
||||
// Heredoc style NAME<<EOF
|
||||
else if (heredocIndex >= 0 && (equalsIndex < 0 || heredocIndex < equalsIndex))
|
||||
{
|
||||
var split = line.Split(new[] { "<<" }, 2, StringSplitOptions.None);
|
||||
if (string.IsNullOrEmpty(split[0]) || string.IsNullOrEmpty(split[1]))
|
||||
{
|
||||
throw new Exception($"Invalid environment variable format '{line}'. Environment variable name must not be empty and delimiter must not be empty");
|
||||
}
|
||||
var name = split[0];
|
||||
var delimiter = split[1];
|
||||
var startIndex = index; // Start index of the value (inclusive)
|
||||
var endIndex = index; // End index of the value (exclusive)
|
||||
var tempLine = ReadLine(text, ref index, out var newline);
|
||||
while (!string.Equals(tempLine, delimiter, StringComparison.Ordinal))
|
||||
{
|
||||
if (tempLine == null)
|
||||
{
|
||||
throw new Exception($"Invalid environment variable value. Matching delimiter not found '{delimiter}'");
|
||||
}
|
||||
endIndex = index - newline.Length;
|
||||
tempLine = ReadLine(text, ref index, out newline);
|
||||
}
|
||||
|
||||
var value = endIndex > startIndex ? text.Substring(startIndex, endIndex - startIndex) : string.Empty;
|
||||
SetEnvironmentVariable(context, name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Invalid environment variable format '{line}'");
|
||||
}
|
||||
}
|
||||
|
||||
line = ReadLine(text, ref index);
|
||||
}
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
context.Debug($"Environment variables file does not exist '{filePath}'");
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
context.Debug($"Environment variables file does not exist '{filePath}'");
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetEnvironmentVariable(
|
||||
IExecutionContext context,
|
||||
string name,
|
||||
string value)
|
||||
{
|
||||
context.Global.EnvironmentVariables[name] = value;
|
||||
context.SetEnvContext(name, value);
|
||||
context.Debug($"{name}='{value}'");
|
||||
}
|
||||
|
||||
private static string ReadLine(
|
||||
string text,
|
||||
ref int index)
|
||||
{
|
||||
return ReadLine(text, ref index, out _);
|
||||
}
|
||||
|
||||
private static string ReadLine(
|
||||
string text,
|
||||
ref int index,
|
||||
out string newline)
|
||||
{
|
||||
if (index >= text.Length)
|
||||
{
|
||||
newline = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
var originalIndex = index;
|
||||
var lfIndex = text.IndexOf("\n", index, StringComparison.Ordinal);
|
||||
if (lfIndex < 0)
|
||||
{
|
||||
index = text.Length;
|
||||
newline = null;
|
||||
return text.Substring(originalIndex);
|
||||
}
|
||||
|
||||
#if OS_WINDOWS
|
||||
var crLFIndex = text.IndexOf("\r\n", index, StringComparison.Ordinal);
|
||||
if (crLFIndex >= 0 && crLFIndex < lfIndex)
|
||||
{
|
||||
index = crLFIndex + 2; // Skip over CRLF
|
||||
newline = "\r\n";
|
||||
return text.Substring(originalIndex, crLFIndex - originalIndex);
|
||||
}
|
||||
#endif
|
||||
|
||||
index = lfIndex + 1; // Skip over LF
|
||||
newline = "\n";
|
||||
return text.Substring(originalIndex, lfIndex - originalIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,24 +6,21 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
public sealed class GitHubContext : DictionaryContextData, IEnvironmentContextData
|
||||
{
|
||||
private readonly HashSet<string> _contextEnvAllowlist = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
private readonly HashSet<string> _contextEnvWhitelist = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"action",
|
||||
"action_path",
|
||||
"actor",
|
||||
"api_url",
|
||||
"base_ref",
|
||||
"env",
|
||||
"event_name",
|
||||
"event_path",
|
||||
"graphql_url",
|
||||
"head_ref",
|
||||
"job",
|
||||
"path",
|
||||
"ref",
|
||||
"repository",
|
||||
"repository_owner",
|
||||
"retention_days",
|
||||
"run_id",
|
||||
"run_number",
|
||||
"server_url",
|
||||
@@ -36,23 +33,11 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
foreach (var data in this)
|
||||
{
|
||||
if (_contextEnvAllowlist.Contains(data.Key) && data.Value is StringContextData value)
|
||||
if (_contextEnvWhitelist.Contains(data.Key) && data.Value is StringContextData value)
|
||||
{
|
||||
yield return new KeyValuePair<string, string>($"GITHUB_{data.Key.ToUpperInvariant()}", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GitHubContext ShallowCopy()
|
||||
{
|
||||
var copy = new GitHubContext();
|
||||
|
||||
foreach (var pair in this)
|
||||
{
|
||||
copy[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,9 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
ArgUtil.NotNull(Inputs, nameof(Inputs));
|
||||
ArgUtil.NotNull(Data.Steps, nameof(Data.Steps));
|
||||
|
||||
var githubContext = ExecutionContext.ExpressionValues["github"] as GitHubContext;
|
||||
ArgUtil.NotNull(githubContext, nameof(githubContext));
|
||||
|
||||
// Resolve action steps
|
||||
var actionSteps = Data.Steps;
|
||||
|
||||
@@ -53,6 +56,14 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
childScopeName = $"__{Guid.NewGuid()}";
|
||||
}
|
||||
|
||||
// Copy the github context so that we don't modify the original pointer
|
||||
// We can't use PipelineContextData.Clone() since that creates a null pointer exception for copying a GitHubContext
|
||||
var compositeGitHubContext = new GitHubContext();
|
||||
foreach (var pair in githubContext)
|
||||
{
|
||||
compositeGitHubContext[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
foreach (Pipelines.ActionStep actionStep in actionSteps)
|
||||
{
|
||||
var actionRunner = HostContext.CreateService<IActionRunner>();
|
||||
@@ -62,13 +73,8 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
var step = ExecutionContext.CreateCompositeStep(childScopeName, actionRunner, inputsData, Environment);
|
||||
|
||||
// Shallow copy github context
|
||||
var gitHubContext = step.ExecutionContext.ExpressionValues["github"] as GitHubContext;
|
||||
ArgUtil.NotNull(gitHubContext, nameof(gitHubContext));
|
||||
gitHubContext = gitHubContext.ShallowCopy();
|
||||
step.ExecutionContext.ExpressionValues["github"] = gitHubContext;
|
||||
|
||||
// Set GITHUB_ACTION_PATH
|
||||
step.ExecutionContext.ExpressionValues["github"] = compositeGitHubContext;
|
||||
step.ExecutionContext.SetGitHubContext("action_path", ActionDirectory);
|
||||
|
||||
compositeSteps.Add(step);
|
||||
@@ -84,8 +90,6 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
ExecutionContext.ExpressionValues["steps"] = ExecutionContext.Global.StepsContext.GetScope(ExecutionContext.GetFullyQualifiedContextName());
|
||||
|
||||
ProcessCompositeActionOutputs();
|
||||
|
||||
ExecutionContext.Global.StepsContext.ClearScope(childScopeName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -131,19 +135,12 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
var outputsName = pair.Key;
|
||||
var outputsAttributes = pair.Value as DictionaryContextData;
|
||||
outputsAttributes.TryGetValue("value", out var val);
|
||||
var outputsValue = val as StringContextData;
|
||||
|
||||
if (val != null)
|
||||
// Set output in the whole composite scope.
|
||||
if (!String.IsNullOrEmpty(outputsName) && !String.IsNullOrEmpty(outputsValue))
|
||||
{
|
||||
var outputsValue = val as StringContextData;
|
||||
// Set output in the whole composite scope.
|
||||
if (!String.IsNullOrEmpty(outputsValue))
|
||||
{
|
||||
ExecutionContext.SetOutput(outputsName, outputsValue, out _);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecutionContext.SetOutput(outputsName, "", out _);
|
||||
}
|
||||
ExecutionContext.SetOutput(outputsName, outputsValue, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,21 +161,16 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
Directory.CreateDirectory(tempHomeDirectory);
|
||||
this.Environment["HOME"] = tempHomeDirectory;
|
||||
|
||||
var tempFileCommandDirectory = Path.Combine(tempDirectory, "_runner_file_commands");
|
||||
ArgUtil.Directory(tempFileCommandDirectory, nameof(tempFileCommandDirectory));
|
||||
|
||||
var tempWorkflowDirectory = Path.Combine(tempDirectory, "_github_workflow");
|
||||
ArgUtil.Directory(tempWorkflowDirectory, nameof(tempWorkflowDirectory));
|
||||
|
||||
container.MountVolumes.Add(new MountVolume("/var/run/docker.sock", "/var/run/docker.sock"));
|
||||
container.MountVolumes.Add(new MountVolume(tempHomeDirectory, "/github/home"));
|
||||
container.MountVolumes.Add(new MountVolume(tempWorkflowDirectory, "/github/workflow"));
|
||||
container.MountVolumes.Add(new MountVolume(tempFileCommandDirectory, "/github/file_commands"));
|
||||
container.MountVolumes.Add(new MountVolume(defaultWorkingDirectory, "/github/workspace"));
|
||||
|
||||
container.AddPathTranslateMapping(tempHomeDirectory, "/github/home");
|
||||
container.AddPathTranslateMapping(tempWorkflowDirectory, "/github/workflow");
|
||||
container.AddPathTranslateMapping(tempFileCommandDirectory, "/github/file_commands");
|
||||
container.AddPathTranslateMapping(defaultWorkingDirectory, "/github/workspace");
|
||||
|
||||
container.ContainerWorkDirectory = "/github/workspace";
|
||||
|
||||
@@ -164,6 +164,8 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
string workingDirectory = null;
|
||||
if (!Inputs.TryGetValue("workingDirectory", out workingDirectory))
|
||||
{
|
||||
// TODO: figure out how defaults interact with template later
|
||||
// for now, we won't check job.defaults if we are inside a template.
|
||||
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.Global.JobDefaults.TryGetValue("run", out var runDefaults))
|
||||
{
|
||||
if (runDefaults.TryGetValue("working-directory", out workingDirectory))
|
||||
@@ -178,6 +180,8 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
string shell = null;
|
||||
if (!Inputs.TryGetValue("shell", out shell) || string.IsNullOrEmpty(shell))
|
||||
{
|
||||
// TODO: figure out how defaults interact with template later
|
||||
// for now, we won't check job.defaults if we are inside a template.
|
||||
if (string.IsNullOrEmpty(ExecutionContext.ScopeName) && ExecutionContext.Global.JobDefaults.TryGetValue("run", out var runDefaults))
|
||||
{
|
||||
if (runDefaults.TryGetValue("shell", out shell))
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
foreach (var step in jobSteps)
|
||||
{
|
||||
jobContext.JobSteps.Enqueue(step);
|
||||
jobContext.JobSteps.Add(step);
|
||||
}
|
||||
|
||||
await stepsRunner.RunAsync(jobContext);
|
||||
|
||||
@@ -15,14 +15,6 @@ namespace GitHub.Runner.Worker
|
||||
private static readonly Regex _propertyRegex = new Regex("^[a-zA-Z_][a-zA-Z0-9_]*$", RegexOptions.Compiled);
|
||||
private readonly DictionaryContextData _contextData = new DictionaryContextData();
|
||||
|
||||
public void ClearScope(string scopeName)
|
||||
{
|
||||
if (_contextData.TryGetValue(scopeName, out _))
|
||||
{
|
||||
_contextData[scopeName] = new DictionaryContextData();
|
||||
}
|
||||
}
|
||||
|
||||
public DictionaryContextData GetScope(string scopeName)
|
||||
{
|
||||
if (scopeName == null)
|
||||
|
||||
@@ -59,13 +59,14 @@ namespace GitHub.Runner.Worker
|
||||
checkPostJobActions = true;
|
||||
while (jobContext.PostJobSteps.TryPop(out var postStep))
|
||||
{
|
||||
jobContext.JobSteps.Enqueue(postStep);
|
||||
jobContext.JobSteps.Add(postStep);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var step = jobContext.JobSteps.Dequeue();
|
||||
var step = jobContext.JobSteps[0];
|
||||
jobContext.JobSteps.RemoveAt(0);
|
||||
|
||||
Trace.Info($"Processing step: DisplayName='{step.DisplayName}'");
|
||||
ArgUtil.NotNull(step.ExecutionContext, nameof(step.ExecutionContext));
|
||||
@@ -104,7 +105,16 @@ namespace GitHub.Runner.Worker
|
||||
if (step is IActionRunner actionStep)
|
||||
{
|
||||
// Set GITHUB_ACTION
|
||||
step.ExecutionContext.SetGitHubContext("action", actionStep.Action.Name);
|
||||
// Warning: Do not turn on FF DistributedTask.UseContextNameForGITHUBACTION until after M271-ish. After M271-ish
|
||||
// the server will never send an empty context name. Generated context names start with "__"
|
||||
if (step.ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseContextNameForGITHUBACTION") ?? false)
|
||||
{
|
||||
step.ExecutionContext.SetGitHubContext("action", actionStep.Action.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
step.ExecutionContext.SetGitHubContext("action", step.ExecutionContext.GetFullyQualifiedContextName());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -108,26 +108,19 @@
|
||||
}
|
||||
},
|
||||
"composite-steps": {
|
||||
"context": [
|
||||
"github",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"steps",
|
||||
"inputs",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"sequence": {
|
||||
"item-type": "composite-step"
|
||||
}
|
||||
},
|
||||
"composite-step": {
|
||||
"mapping": {
|
||||
"properties": {
|
||||
"name": "string-steps-context",
|
||||
"id": "non-empty-string",
|
||||
"run": {
|
||||
"type": "string-steps-context",
|
||||
"required": true
|
||||
},
|
||||
"env": "step-env",
|
||||
"working-directory": "string-steps-context",
|
||||
"shell": {
|
||||
"type": "non-empty-string",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
"item-type": "any"
|
||||
}
|
||||
},
|
||||
"container-runs-context": {
|
||||
@@ -164,37 +157,6 @@
|
||||
"string": {
|
||||
"require-non-empty": true
|
||||
}
|
||||
},
|
||||
"string-steps-context": {
|
||||
"context": [
|
||||
"github",
|
||||
"inputs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"string": {}
|
||||
},
|
||||
"step-env": {
|
||||
"context": [
|
||||
"github",
|
||||
"inputs",
|
||||
"strategy",
|
||||
"matrix",
|
||||
"steps",
|
||||
"job",
|
||||
"runner",
|
||||
"env",
|
||||
"hashFiles(1,255)"
|
||||
],
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
"loose-value-type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,36 +56,5 @@ namespace GitHub.DistributedTask.Pipelines
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the credentials used for pulling the container iamge.
|
||||
/// </summary>
|
||||
public ContainerRegistryCredentials Credentials
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public sealed class ContainerRegistryCredentials
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the user to authenticate to a registry with
|
||||
/// </summary>
|
||||
public String Username
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password to authenticate to a registry with
|
||||
/// </summary>
|
||||
public String Password
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
public const String Clean= "clean";
|
||||
public const String Container = "container";
|
||||
public const String ContinueOnError = "continue-on-error";
|
||||
public const String Credentials = "credentials";
|
||||
public const String Defaults = "defaults";
|
||||
public const String Env = "env";
|
||||
public const String Event = "event";
|
||||
@@ -46,7 +45,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
public const String Options = "options";
|
||||
public const String Outputs = "outputs";
|
||||
public const String OutputsPattern = "needs.*.outputs";
|
||||
public const String Password = "password";
|
||||
public const String Path = "path";
|
||||
public const String Pool = "pool";
|
||||
public const String Ports = "ports";
|
||||
@@ -70,7 +68,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
public const String Success = "success";
|
||||
public const String Template = "template";
|
||||
public const String TimeoutMinutes = "timeout-minutes";
|
||||
public const String Username = "username";
|
||||
public const String Uses = "uses";
|
||||
public const String VmImage = "vmImage";
|
||||
public const String Volumes = "volumes";
|
||||
|
||||
@@ -209,30 +209,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
return (Int32)numberToken.Value;
|
||||
}
|
||||
|
||||
internal static ContainerRegistryCredentials ConvertToContainerCredentials(TemplateToken token)
|
||||
{
|
||||
var credentials = token.AssertMapping(PipelineTemplateConstants.Credentials);
|
||||
var result = new ContainerRegistryCredentials();
|
||||
foreach (var credentialProperty in credentials)
|
||||
{
|
||||
var propertyName = credentialProperty.Key.AssertString($"{PipelineTemplateConstants.Credentials} key");
|
||||
switch (propertyName.Value)
|
||||
{
|
||||
case PipelineTemplateConstants.Username:
|
||||
result.Username = credentialProperty.Value.AssertString(PipelineTemplateConstants.Username).Value;
|
||||
break;
|
||||
case PipelineTemplateConstants.Password:
|
||||
result.Password = credentialProperty.Value.AssertString(PipelineTemplateConstants.Password).Value;
|
||||
break;
|
||||
default:
|
||||
propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Credentials} key {propertyName}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static JobContainer ConvertToJobContainer(
|
||||
TemplateContext context,
|
||||
TemplateToken value,
|
||||
@@ -299,9 +275,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
}
|
||||
result.Volumes = volumeList;
|
||||
break;
|
||||
case PipelineTemplateConstants.Credentials:
|
||||
result.Credentials = ConvertToContainerCredentials(containerPropertyPair.Value);
|
||||
break;
|
||||
default:
|
||||
propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Container} key");
|
||||
break;
|
||||
|
||||
@@ -373,8 +373,7 @@
|
||||
"options": "non-empty-string",
|
||||
"env": "container-env",
|
||||
"ports": "sequence-of-non-empty-string",
|
||||
"volumes": "sequence-of-non-empty-string",
|
||||
"credentials": "container-registry-credentials"
|
||||
"volumes": "sequence-of-non-empty-string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -405,20 +404,6 @@
|
||||
]
|
||||
},
|
||||
|
||||
"container-registry-credentials": {
|
||||
"context": [
|
||||
"secrets",
|
||||
"env",
|
||||
"github"
|
||||
],
|
||||
"mapping": {
|
||||
"properties": {
|
||||
"username": "non-empty-string",
|
||||
"password": "non-empty-string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"container-env": {
|
||||
"mapping": {
|
||||
"loose-key-type": "non-empty-string",
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
: base(baseUrl, pipeline, disposeHandler)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public Task AppendTimelineRecordFeedAsync(
|
||||
Guid scopeIdentifier,
|
||||
String planType,
|
||||
@@ -91,28 +91,6 @@ namespace GitHub.DistributedTask.WebApi
|
||||
userState,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public Task AppendTimelineRecordFeedAsync(
|
||||
Guid scopeIdentifier,
|
||||
String planType,
|
||||
Guid planId,
|
||||
Guid timelineId,
|
||||
Guid recordId,
|
||||
Guid stepId,
|
||||
IList<String> lines,
|
||||
long startLine,
|
||||
CancellationToken cancellationToken = default(CancellationToken),
|
||||
Object userState = null)
|
||||
{
|
||||
return AppendTimelineRecordFeedAsync(scopeIdentifier,
|
||||
planType,
|
||||
planId,
|
||||
timelineId,
|
||||
recordId,
|
||||
new TimelineRecordFeedLinesWrapper(stepId, lines, startLine),
|
||||
userState,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
public async Task RaisePlanEventAsync<T>(
|
||||
Guid scopeIdentifier,
|
||||
|
||||
@@ -20,12 +20,6 @@ namespace GitHub.DistributedTask.WebApi
|
||||
this.Count = lines.Count;
|
||||
}
|
||||
|
||||
public TimelineRecordFeedLinesWrapper(Guid stepId, IList<string> lines, Int64 startLine)
|
||||
: this(stepId, lines)
|
||||
{
|
||||
this.StartLine = startLine;
|
||||
}
|
||||
|
||||
[DataMember(Order = 0)]
|
||||
public Int32 Count { get; private set; }
|
||||
|
||||
@@ -37,8 +31,5 @@ namespace GitHub.DistributedTask.WebApi
|
||||
|
||||
[DataMember(EmitDefaultValue = false)]
|
||||
public Guid StepId { get; set; }
|
||||
|
||||
[DataMember (EmitDefaultValue = false)]
|
||||
public Int64? StartLine { get; private set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
[DataContract]
|
||||
public sealed class TimelineRecordLogLine
|
||||
{
|
||||
public TimelineRecordLogLine(String line, long? lineNumber)
|
||||
{
|
||||
this.Line = line;
|
||||
this.LineNumber = lineNumber;
|
||||
}
|
||||
|
||||
[DataMember]
|
||||
public String Line
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[DataMember (EmitDefaultValue = false)]
|
||||
public long? LineNumber
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,23 +126,5 @@ namespace GitHub.Runner.Common.Tests.Worker.Container
|
||||
Assert.NotNull(result5);
|
||||
Assert.Equal("/foo/bar:/baz", result5);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
[InlineData("dockerhub/repo", "")]
|
||||
[InlineData("localhost/doesnt_work", "")]
|
||||
[InlineData("localhost:port/works", "localhost:port")]
|
||||
[InlineData("host.tld/works", "host.tld")]
|
||||
[InlineData("ghcr.io/owner/image", "ghcr.io")]
|
||||
[InlineData("gcr.io/project/image", "gcr.io")]
|
||||
[InlineData("myregistry.azurecr.io/namespace/image", "myregistry.azurecr.io")]
|
||||
[InlineData("account.dkr.ecr.region.amazonaws.com/image", "account.dkr.ecr.region.amazonaws.com")]
|
||||
[InlineData("docker.pkg.github.com/owner/repo/image", "docker.pkg.github.com")]
|
||||
public void ParseRegistryHostnameFromImageName(string input, string expected)
|
||||
{
|
||||
var actual = DockerUtil.ParseRegistryHostnameFromImageName(input);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ namespace GitHub.Runner.Common.Tests
|
||||
{
|
||||
typeof(IActionCommandExtension),
|
||||
typeof(IExecutionContext),
|
||||
typeof(IFileCommandExtension),
|
||||
typeof(IHandler),
|
||||
typeof(IJobExtension),
|
||||
typeof(IStep),
|
||||
|
||||
@@ -32,8 +32,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
private TestHostContext _hc;
|
||||
private ActionRunner _actionRunner;
|
||||
private IActionManifestManager _actionManifestManager;
|
||||
private Mock<IFileCommandManager> _fileCommandManager;
|
||||
|
||||
private DictionaryContextData _context = new DictionaryContextData();
|
||||
|
||||
[Fact]
|
||||
@@ -364,7 +362,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_handlerFactory = new Mock<IHandlerFactory>();
|
||||
_defaultStepHost = new Mock<IDefaultStepHost>();
|
||||
_actionManifestManager = new ActionManifestManager();
|
||||
_fileCommandManager = new Mock<IFileCommandManager>();
|
||||
_actionManifestManager.Initialize(_hc);
|
||||
|
||||
var githubContext = new GitHubContext();
|
||||
@@ -397,8 +394,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_hc.EnqueueInstance<IDefaultStepHost>(_defaultStepHost.Object);
|
||||
|
||||
_hc.EnqueueInstance(_fileCommandManager.Object);
|
||||
|
||||
// Instance to test.
|
||||
_actionRunner = new ActionRunner();
|
||||
_actionRunner.Initialize(_hc);
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var pagingLogger = new Mock<IPagingLogger>();
|
||||
var jobServerQueue = new Mock<IJobServerQueue>();
|
||||
jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()));
|
||||
jobServerQueue.Setup(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>(),It.IsAny<long>())).Callback((Guid id, string msg, long? lineNumber) => { hc.GetTrace().Info(msg); });
|
||||
jobServerQueue.Setup(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>())).Callback((Guid id, string msg) => { hc.GetTrace().Info(msg); });
|
||||
|
||||
hc.EnqueueInstance(pagingLogger.Object);
|
||||
hc.SetSingleton(jobServerQueue.Object);
|
||||
@@ -137,7 +137,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
ec.Complete();
|
||||
|
||||
jobServerQueue.Verify(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<long?>()), Times.Exactly(10));
|
||||
jobServerQueue.Verify(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>()), Times.Exactly(10));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var pagingLogger5 = new Mock<IPagingLogger>();
|
||||
var jobServerQueue = new Mock<IJobServerQueue>();
|
||||
jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()));
|
||||
jobServerQueue.Setup(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<long?>())).Callback((Guid id, string msg, long? lineNumber) => { hc.GetTrace().Info(msg); });
|
||||
jobServerQueue.Setup(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>())).Callback((Guid id, string msg) => { hc.GetTrace().Info(msg); });
|
||||
|
||||
var actionRunner1 = new ActionRunner();
|
||||
actionRunner1.Initialize(hc);
|
||||
@@ -269,7 +269,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var pagingLogger5 = new Mock<IPagingLogger>();
|
||||
var jobServerQueue = new Mock<IJobServerQueue>();
|
||||
jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()));
|
||||
jobServerQueue.Setup(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<long?>())).Callback((Guid id, string msg, long? lineNumber) => { hc.GetTrace().Info(msg); });
|
||||
jobServerQueue.Setup(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>())).Callback((Guid id, string msg) => { hc.GetTrace().Info(msg); });
|
||||
|
||||
var actionRunner1 = new ActionRunner();
|
||||
actionRunner1.Initialize(hc);
|
||||
|
||||
@@ -1,390 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using DTWebApi = GitHub.DistributedTask.WebApi;
|
||||
|
||||
namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
public sealed class SetEnvFileCommandL0
|
||||
{
|
||||
private Mock<IExecutionContext> _executionContext;
|
||||
private List<Tuple<DTWebApi.Issue, string>> _issues;
|
||||
private string _rootDirectory;
|
||||
private SetEnvFileCommand _setEnvFileCommand;
|
||||
private ITraceWriter _trace;
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_DirectoryNotFound()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "directory-not-found", "env");
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(0, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_NotFound()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "file-not-found");
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(0, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_EmptyFile()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "empty-file");
|
||||
var content = new List<string>();
|
||||
WriteContent(envFile, content);
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(0, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_Simple()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "simple");
|
||||
var content = new List<string>
|
||||
{
|
||||
"MY_ENV=MY VALUE",
|
||||
};
|
||||
WriteContent(envFile, content);
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(1, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
Assert.Equal("MY VALUE", _executionContext.Object.Global.EnvironmentVariables["MY_ENV"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_Simple_SkipEmptyLines()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "simple");
|
||||
var content = new List<string>
|
||||
{
|
||||
string.Empty,
|
||||
"MY_ENV=my value",
|
||||
string.Empty,
|
||||
"MY_ENV_2=my second value",
|
||||
string.Empty,
|
||||
};
|
||||
WriteContent(envFile, content);
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(2, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
Assert.Equal("my value", _executionContext.Object.Global.EnvironmentVariables["MY_ENV"]);
|
||||
Assert.Equal("my second value", _executionContext.Object.Global.EnvironmentVariables["MY_ENV_2"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_Simple_EmptyValue()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "simple-empty-value");
|
||||
var content = new List<string>
|
||||
{
|
||||
"MY_ENV=",
|
||||
};
|
||||
WriteContent(envFile, content);
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(1, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
Assert.Equal(string.Empty, _executionContext.Object.Global.EnvironmentVariables["MY_ENV"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_Simple_MultipleValues()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "simple");
|
||||
var content = new List<string>
|
||||
{
|
||||
"MY_ENV=my value",
|
||||
"MY_ENV_2=",
|
||||
"MY_ENV_3=my third value",
|
||||
};
|
||||
WriteContent(envFile, content);
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(3, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
Assert.Equal("my value", _executionContext.Object.Global.EnvironmentVariables["MY_ENV"]);
|
||||
Assert.Equal(string.Empty, _executionContext.Object.Global.EnvironmentVariables["MY_ENV_2"]);
|
||||
Assert.Equal("my third value", _executionContext.Object.Global.EnvironmentVariables["MY_ENV_3"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_Simple_SpecialCharacters()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "simple");
|
||||
var content = new List<string>
|
||||
{
|
||||
"MY_ENV==abc",
|
||||
"MY_ENV_2=def=ghi",
|
||||
"MY_ENV_3=jkl=",
|
||||
};
|
||||
WriteContent(envFile, content);
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(3, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
Assert.Equal("=abc", _executionContext.Object.Global.EnvironmentVariables["MY_ENV"]);
|
||||
Assert.Equal("def=ghi", _executionContext.Object.Global.EnvironmentVariables["MY_ENV_2"]);
|
||||
Assert.Equal("jkl=", _executionContext.Object.Global.EnvironmentVariables["MY_ENV_3"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_Heredoc()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "heredoc");
|
||||
var content = new List<string>
|
||||
{
|
||||
"MY_ENV<<EOF",
|
||||
"line one",
|
||||
"line two",
|
||||
"line three",
|
||||
"EOF",
|
||||
};
|
||||
WriteContent(envFile, content);
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(1, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
Assert.Equal($"line one{Environment.NewLine}line two{Environment.NewLine}line three", _executionContext.Object.Global.EnvironmentVariables["MY_ENV"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_Heredoc_EmptyValue()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "heredoc");
|
||||
var content = new List<string>
|
||||
{
|
||||
"MY_ENV<<EOF",
|
||||
"EOF",
|
||||
};
|
||||
WriteContent(envFile, content);
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(1, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
Assert.Equal(string.Empty, _executionContext.Object.Global.EnvironmentVariables["MY_ENV"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_Heredoc_SkipEmptyLines()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "heredoc");
|
||||
var content = new List<string>
|
||||
{
|
||||
string.Empty,
|
||||
"MY_ENV<<EOF",
|
||||
"hello",
|
||||
"world",
|
||||
"EOF",
|
||||
string.Empty,
|
||||
"MY_ENV_2<<EOF",
|
||||
"HELLO",
|
||||
"AGAIN",
|
||||
"EOF",
|
||||
string.Empty,
|
||||
};
|
||||
WriteContent(envFile, content);
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(2, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
Assert.Equal($"hello{Environment.NewLine}world", _executionContext.Object.Global.EnvironmentVariables["MY_ENV"]);
|
||||
Assert.Equal($"HELLO{Environment.NewLine}AGAIN", _executionContext.Object.Global.EnvironmentVariables["MY_ENV_2"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_Heredoc_SpecialCharacters()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var envFile = Path.Combine(_rootDirectory, "heredoc");
|
||||
var content = new List<string>
|
||||
{
|
||||
"MY_ENV<<=EOF",
|
||||
"hello",
|
||||
"one",
|
||||
"=EOF",
|
||||
"MY_ENV_2<<<EOF",
|
||||
"hello",
|
||||
"two",
|
||||
"<EOF",
|
||||
"MY_ENV_3<<EOF",
|
||||
"hello",
|
||||
string.Empty,
|
||||
"three",
|
||||
string.Empty,
|
||||
"EOF",
|
||||
"MY_ENV_4<<EOF",
|
||||
"hello=four",
|
||||
"EOF",
|
||||
"MY_ENV_5<<EOF",
|
||||
" EOF",
|
||||
"EOF",
|
||||
};
|
||||
WriteContent(envFile, content);
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(5, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
Assert.Equal($"hello{Environment.NewLine}one", _executionContext.Object.Global.EnvironmentVariables["MY_ENV"]);
|
||||
Assert.Equal($"hello{Environment.NewLine}two", _executionContext.Object.Global.EnvironmentVariables["MY_ENV_2"]);
|
||||
Assert.Equal($"hello{Environment.NewLine}{Environment.NewLine}three{Environment.NewLine}", _executionContext.Object.Global.EnvironmentVariables["MY_ENV_3"]);
|
||||
Assert.Equal($"hello=four", _executionContext.Object.Global.EnvironmentVariables["MY_ENV_4"]);
|
||||
Assert.Equal($" EOF", _executionContext.Object.Global.EnvironmentVariables["MY_ENV_5"]);
|
||||
}
|
||||
}
|
||||
|
||||
#if OS_WINDOWS
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void SetEnvFileCommand_Heredoc_PreservesNewline()
|
||||
{
|
||||
using (var hostContext = Setup())
|
||||
{
|
||||
var newline = "\n";
|
||||
var envFile = Path.Combine(_rootDirectory, "heredoc");
|
||||
var content = new List<string>
|
||||
{
|
||||
"MY_ENV<<EOF",
|
||||
"hello",
|
||||
"world",
|
||||
"EOF",
|
||||
};
|
||||
WriteContent(envFile, content, newline: newline);
|
||||
_setEnvFileCommand.ProcessCommand(_executionContext.Object, envFile, null);
|
||||
Assert.Equal(0, _issues.Count);
|
||||
Assert.Equal(1, _executionContext.Object.Global.EnvironmentVariables.Count);
|
||||
Assert.Equal($"hello{newline}world", _executionContext.Object.Global.EnvironmentVariables["MY_ENV"]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void WriteContent(
|
||||
string path,
|
||||
List<string> content,
|
||||
string newline = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(newline))
|
||||
{
|
||||
newline = Environment.NewLine;
|
||||
}
|
||||
|
||||
var encoding = new UTF8Encoding(true); // Emit BOM
|
||||
var contentStr = string.Join(newline, content);
|
||||
File.WriteAllText(path, contentStr, encoding);
|
||||
}
|
||||
|
||||
private TestHostContext Setup([CallerMemberName] string name = "")
|
||||
{
|
||||
_issues = new List<Tuple<DTWebApi.Issue, string>>();
|
||||
|
||||
var hostContext = new TestHostContext(this, name);
|
||||
|
||||
// Trace
|
||||
_trace = hostContext.GetTrace();
|
||||
|
||||
// Directory for test data
|
||||
var workDirectory = hostContext.GetDirectory(WellKnownDirectory.Work);
|
||||
ArgUtil.NotNullOrEmpty(workDirectory, nameof(workDirectory));
|
||||
Directory.CreateDirectory(workDirectory);
|
||||
_rootDirectory = Path.Combine(workDirectory, nameof(SetEnvFileCommandL0));
|
||||
Directory.CreateDirectory(_rootDirectory);
|
||||
|
||||
// Execution context
|
||||
_executionContext = new Mock<IExecutionContext>();
|
||||
_executionContext.Setup(x => x.Global)
|
||||
.Returns(new GlobalContext
|
||||
{
|
||||
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
|
||||
WriteDebug = true,
|
||||
});
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
|
||||
.Callback((DTWebApi.Issue issue, string logMessage) =>
|
||||
{
|
||||
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
|
||||
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
|
||||
_trace.Info($"Issue '{issue.Type}': {message}");
|
||||
});
|
||||
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback((string tag, string message) =>
|
||||
{
|
||||
_trace.Info($"{tag}{message}");
|
||||
});
|
||||
|
||||
// SetEnvFileCommand
|
||||
_setEnvFileCommand = new SetEnvFileCommand();
|
||||
_setEnvFileCommand.Initialize(hostContext);
|
||||
|
||||
return hostContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_contexts = new DictionaryContextData();
|
||||
_jobContext = new JobContext();
|
||||
_contexts["github"] = new GitHubContext();
|
||||
_contexts["github"] = new DictionaryContextData();
|
||||
_contexts["runner"] = new DictionaryContextData();
|
||||
_contexts["job"] = _jobContext;
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(_contexts);
|
||||
@@ -82,7 +82,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -117,7 +117,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -156,7 +156,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -210,7 +210,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -289,7 +289,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -332,7 +332,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Step.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Step.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -363,7 +363,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -393,7 +393,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -419,7 +419,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object }));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -457,7 +457,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object }));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -495,7 +495,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object }));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -526,7 +526,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -562,7 +562,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -602,12 +602,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var stepContext = new Mock<IExecutionContext>();
|
||||
stepContext.SetupAllProperties();
|
||||
stepContext.Setup(x => x.Global).Returns(() => _ec.Object.Global);
|
||||
var expressionValues = new DictionaryContextData();
|
||||
foreach (var pair in _ec.Object.ExpressionValues)
|
||||
{
|
||||
expressionValues[pair.Key] = pair.Value;
|
||||
}
|
||||
stepContext.Setup(x => x.ExpressionValues).Returns(expressionValues);
|
||||
stepContext.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||
stepContext.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
stepContext.Setup(x => x.JobContext).Returns(_jobContext);
|
||||
stepContext.Setup(x => x.ContextName).Returns(step.Object.Action.ContextName);
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.273.6
|
||||
2.272.0
|
||||
|
||||
Reference in New Issue
Block a user