Compare commits

..

50 Commits

Author SHA1 Message Date
Yashwanth Anantharaju
22df069d5a dump message 2021-06-07 09:53:09 -04:00
Ferenc Hammerl
d4cdb633db Run config remove as user instead of root (#1127)
* Run config remove as user instead of root

* Explain why the runner can't be a service on a container

* Fix typo
2021-06-04 11:30:03 -04:00
Yann Soubeyrand
11939832df Return 1 on service status when service is not installed (#1018) 2021-06-04 10:52:13 -04:00
Rob Bos
ebadce7958 Typo: Changed dockerManger to dockerManager (#1133) 2021-06-04 10:51:30 -04:00
Tingluo Huang
4d5d5b74ee Ignore changes from BuildConstants.cs (#1132) 2021-06-03 16:19:26 -04:00
雪猫
ff12fae2c9 Fix automate script URL (#1089) 2021-06-02 08:47:51 -04:00
Ferenc Hammerl
8e907b19dc Vscode launch scripts (#1117)
* Stop ignoring .vscode (launch scripts)

* Check in launch scripts for config and run

This can cause an issue with existing launch configuration on the machines of contributors.

* Improve error msg when runner is not configured

* Unignore .vscode/launch and tasks only

* Remove stopAtEntry and add eof newline

* Remove Runner.Listener from error message

* Rename tasks and run configs

* Ignore BuildConstants.cs

* Use better error msg

* Explain development steps in depth

* Add launch config to directly debug worker

* Update docs with VS Code tips

* Remove auto-generated comments

* Fix link to quickstart in vscode.md

* Remove ':' from link to quickstart

* Revert "Ignore BuildConstants.cs"

This reverts commit 0f13922a87.

* Replace `.sh` with  `.(sh/cmd)` in docs
2021-06-02 08:34:16 -04:00
Ferenc Hammerl
93ec16e14f Wait for debugger to attach in Runner.Worker process (#1101)
* Wait for debugger to attach in Worker

Only if GITHUB_ACTIONS_RUNNER_ATTACH_DEBUGGER is set

* Only wait if env variable parses to boolean 'true'

* Add 30s timeout to the wait

* Clean up leftover line

* Decrease wait to 20s

* Use ConvertToBoolean isntead of TryParse
2021-05-27 16:05:48 +02:00
Ferenc Hammerl
8863b1fb2c Add configure section to contribute.md (#1119)
Minor formatting improvements
2021-05-26 12:05:38 +02:00
Josh Soref
484ea74ed0 Update 0277-run-action-shell-options.md (#1106) 2021-05-26 12:04:38 +02:00
Josh Soref
f21e280b5c Update 0279-hashFiles-expression-function.md (#1107) 2021-05-26 12:04:24 +02:00
Josh Soref
e0643c694c Update 0361-wrapper-action.md (#1109) 2021-05-26 12:02:54 +02:00
Josh Soref
508d188fb6 Update 0397-runner-registration-labels.md (#1110) 2021-05-26 12:01:52 +02:00
Josh Soref
e7d74da160 Update 0549-composite-run-steps.md (#1111) 2021-05-26 12:01:02 +02:00
Josh Soref
d1f7258356 Update 0263-proxy-support.md (#1104)
Grammar fixes
2021-05-25 16:42:50 +02:00
eric sciple
3a5ab37153 Handle job not found when ensuring previous dispatch finished (#1083) 2021-05-11 09:19:23 -05:00
eric sciple
419ed24c1e rename nestedSteps to embeddedSteps (#1071) 2021-05-01 12:51:01 -05:00
eric sciple
7cc689b0d9 minor cleanup in composite (#1045) 2021-04-30 15:48:53 -05:00
Tingluo Huang
5941cceb7c Unset NODE_ICU_DATA before starting node in NodeHandler. (#1060)
* Unset NODE_ICU_DATA before starting node in NodeHandler.

* Update src/Runner.Worker/Handlers/NodeScriptActionHandler.cs

Co-authored-by: Ross Brodbeck <hross@users.noreply.github.com>

Co-authored-by: Ross Brodbeck <hross@users.noreply.github.com>
2021-04-23 11:10:22 -04:00
Tingluo Huang
088caf5337 Update support platform doc for Apple M1 issue (#1057)
* apple M1 issue

* Update docs/start/envosx.md

Co-authored-by: Ross Brodbeck <hross@users.noreply.github.com>

* Update docs/start/envosx.md

Co-authored-by: Ross Brodbeck <hross@users.noreply.github.com>

Co-authored-by: Ross Brodbeck <hross@users.noreply.github.com>
2021-04-23 09:47:30 -04:00
Ross Brodbeck
08852bd2fc these are not prerelease anymore 2021-04-23 05:26:31 -04:00
Tingluo Huang
57d694197f Not create runner release as pre-release. (#1054) 2021-04-21 17:08:44 -04:00
eric sciple
fc4027b3f1 Bump version (#1048) 2021-04-16 15:45:55 +00:00
eric sciple
d14881b970 do not trucate error message from template evaluation (#1038) 2021-04-06 16:45:40 -04:00
David Wolf
be9632302c Make FileShare ReadWrite (#1033)
* Make FileShare ReadWrite

* Update FileAccess to ReadWrite

* Update dotnet-install.ps1

* Update dotnet-install.ps1

* Update dotnet-install.ps1

* Update dotnet-install.sh
2021-04-01 16:54:23 -04:00
Ross Brodbeck
2b5ddd7c21 Add a default BuildConstants and change encoding on save (#1026) 2021-03-26 12:26:24 -04:00
eric sciple
8109c962f0 mask secrets with double-quotes when passed to docker command line (#1002) 2021-03-05 15:17:55 -06:00
Tim Etchells
af198237ca Delete script files before replacing during update (#984)
* Delete script files before replacing during update

Signed-off-by: Tim Etchells <tetchel@gmail.com>

* Use IOUtil.DelteFile()

Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2021-02-22 23:38:15 -05:00
Alberto Gimeno
1559ff15ec Use GITHUB_TOKEN for ghcr.io containers if credentials are not provided (#990)
* Use GITHUB_TOKEN for ghcr.io containers if credentials are not provided

* Use GITHUB_TOKEN also for containers in containers.pkg.github.com
2021-02-18 21:55:58 -05:00
Thomas Boop
67ff8d3460 Release 2.277.1 runner (#977)
* Revert "Enable tty output from Docker Actions (#916)"

5972bd0060

* Release notes

* add pr
2021-02-09 14:45:33 -05:00
Thomas Boop
6cbfbc3186 Add 2.277.0 release notes (#975)
* add 2.276.2 release notes

* major version these changes
2021-02-09 11:18:55 -05:00
Thomas Boop
195c2db5ef Check Runner Zip Hash on Upgrade (#967)
* Check Hash if it exists on runner update
2021-02-09 10:52:46 -05:00
Tingluo Huang
50994bbb3b add --check to the output of run.sh --help. (#970)
* add --check to the output of run.sh --help.

* feedback.
2021-02-09 10:17:54 -05:00
Lucas Costi
7b03699fbe --check strings grammar improvements (#972) 2021-02-08 00:23:14 -05:00
Hollow Man
8a4cb76508 Fix typos (#969)
accidentially -> accidentally
neglible -> negligible
2021-02-05 13:29:43 -05:00
Yang Cao
bc3099793f Display GITHUB_TOKEN permissions (#966)
* Display GITHUB TOKEN permissions

* Display permission list is best effort

* Remove newtonsoft dependency
2021-02-04 23:10:00 -05:00
Santiago Roman
b76d229da0 Fix usage of /dev/null and ping flag in run.sh (#968)
- Use /dev/null instead of nul
- Use -c instead of -n as a ping flag to specify number of packets to be
  sent
2021-02-04 23:09:27 -05:00
TingluoHuang
fe3994bf1d skip dotnet script testing. 2021-02-04 22:58:10 -05:00
TingluoHuang
0ae09e6713 Revert "update dotnet install script."
This reverts commit 2b4d5542aa.
2021-02-04 22:46:15 -05:00
TingluoHuang
2b4d5542aa update dotnet install script. 2021-02-04 22:32:57 -05:00
Denis Baryshev
6b0f0c00b1 use correct exit code and delay on runner update in run.sh (#963)
Fix runner update script
2021-02-04 22:12:55 -05:00
Tingluo Huang
09760c0d69 Trace process error in RunnerService.js (#955) 2021-02-01 10:03:15 -05:00
Tingluo Huang
8f14466cbb Add http POST to --check. (#949)
* Add http POST to --check.

* feedback.
2021-01-30 22:35:45 -05:00
Thomas Boop
fe8a56f81a Generate SHA's for released packages and include them in package notes (#948)
* Update release.yml

Compute Sha's for release builds

* Update release notes with shas

* Update releaseNote.md

* Update release.yml

* Update release.yml

* Update release.yml

* Add Ability to Get Sha's

* fix typo

* remove debug code
2021-01-28 15:32:41 -05:00
Lokesh Gopu
59b30262ac Update AgentPlatform for job timeline record (#939)
* Update AgentPlatform for job timeline record

* removed unused using
2021-01-25 11:14:28 -05:00
eric sciple
9efcec38cc support authenticated package download (#920) 2021-01-23 14:19:59 -05:00
Joel Dickson
5972bd0060 Enable tty output from Docker Actions (#916)
* Update DockerCommandManager.cs

* Update StepHost.cs

Co-authored-by: Tingluo Huang <tingluohuang@github.com>
2021-01-21 22:35:57 -05:00
Thomas Boop
239cc0d7ca prep 2.276.1 runner release (#929) 2021-01-21 14:02:36 -05:00
Thomas Boop
3fb915450a Runner v2.276.0 fixes (#928)
* Revert "always use Fips Cryptography (#896)"

3b34e203dc

* Revert "Update ldd check with dotnet 5."

4b6ded0a01

* Revert "Update SDK to .NET 5 (#799)"

fc3ca9bb92

* Update dotnet-install scripts
2021-01-21 13:45:16 -05:00
Tingluo Huang
4b6ded0a01 Update ldd check with dotnet 5. 2021-01-15 09:14:55 -05:00
66 changed files with 999 additions and 458 deletions

View File

@@ -45,6 +45,12 @@ jobs:
build:
needs: check
outputs:
linux-x64-sha: ${{ steps.sha.outputs.linux-x64-sha256 }}
linux-arm64-sha: ${{ steps.sha.outputs.linux-arm64-sha256 }}
linux-arm-sha: ${{ steps.sha.outputs.linux-arm-sha256 }}
win-x64-sha: ${{ steps.sha.outputs.win-x64-sha256 }}
osx-x64-sha: ${{ steps.sha.outputs.osx-x64-sha256 }}
strategy:
matrix:
runtime: [ linux-x64, linux-arm64, linux-arm, win-x64, osx-x64 ]
@@ -101,7 +107,19 @@ jobs:
with:
name: runner-packages
path: _package
# compute shas and set as job outputs to use in release notes
- run: brew install coreutils #needed for shasum util
if: ${{ matrix.os == 'macOS-latest' }}
name: Install Dependencies for SHA Calculation (osx)
- run: |
file=$(ls)
sha=$(sha256sum $file | awk '{ print $1 }')
echo "Computed sha256: $sha for $file"
echo "::set-output name=${{matrix.runtime}}-sha256::$sha"
shell: bash
id: sha
name: Compute SHA256
working-directory: _package
release:
needs: build
runs-on: ubuntu-latest
@@ -126,11 +144,15 @@ jobs:
const core = require('@actions/core')
const fs = require('fs');
const runnerVersion = fs.readFileSync('${{ github.workspace }}/src/runnerversion', 'utf8').replace(/\n$/g, '')
const releaseNote = fs.readFileSync('${{ github.workspace }}/releaseNote.md', 'utf8').replace(/<RUNNER_VERSION>/g, runnerVersion)
var releaseNote = fs.readFileSync('${{ github.workspace }}/releaseNote.md', 'utf8').replace(/<RUNNER_VERSION>/g, runnerVersion)
releaseNote = releaseNote.replace(/<WIN_X64_SHA>/g, '${{needs.build.outputs.win-x64-sha}}')
releaseNote = releaseNote.replace(/<OSX_X64_SHA>/g, '${{needs.build.outputs.osx-x64-sha}}')
releaseNote = releaseNote.replace(/<LINUX_X64_SHA>/g, '${{needs.build.outputs.linux-x64-sha}}')
releaseNote = releaseNote.replace(/<LINUX_ARM_SHA>/g, '${{needs.build.outputs.linux-arm-sha}}')
releaseNote = releaseNote.replace(/<LINUX_ARM64_SHA>/g, '${{needs.build.outputs.linux-arm64-sha}}')
console.log(releaseNote)
core.setOutput('version', runnerVersion);
core.setOutput('note', releaseNote);
core.setOutput('note', releaseNote);
# Create GitHub release
- uses: actions/create-release@master
id: createRelease
@@ -142,7 +164,6 @@ jobs:
release_name: "v${{ steps.releaseNote.outputs.version }}"
body: |
${{ steps.releaseNote.outputs.note }}
prerelease: true
# Upload release assets
- name: Upload Release Asset (win-x64)
@@ -193,4 +214,4 @@ jobs:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ${{ github.workspace }}/actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}.tar.gz
asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}.tar.gz
asset_content_type: application/octet-stream
asset_content_type: application/octet-stream

9
.gitignore vendored
View File

@@ -8,10 +8,12 @@
**/*.xproj
**/*.xproj.user
**/.vs
**/.vscode
**/*.error
**/*.json.pretty
.idea/
.vscode
!.vscode/launch.json
!.vscode/tasks.json
# output
node_modules
@@ -22,7 +24,4 @@ _dotnetsdk
TestResults
TestLogs
.DS_Store
**/*.DotSettings.user
#generated
src/Runner.Sdk/BuildConstants.cs
**/*.DotSettings.user

57
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,57 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Run [build]",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build runner layout",
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
"args": [
"run"
],
"cwd": "${workspaceFolder}/src",
"console": "integratedTerminal",
"requireExactSource": false,
},
{
"name": "Run",
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
"args": [
"run"
],
"cwd": "${workspaceFolder}/src",
"console": "integratedTerminal",
"requireExactSource": false,
},
{
"name": "Configure",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "create runner layout",
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
"args": [
"configure"
],
"cwd": "${workspaceFolder}/src",
"console": "integratedTerminal",
"requireExactSource": false,
},
{
"name": "Debug Worker",
"type": "coreclr",
"request": "attach",
"processName": "Runner.Worker",
"requireExactSource": false,
},
{
"name": "Attach Debugger",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}",
"requireExactSource": false,
},
],
}

33
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,33 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "create runner layout",
"detail": "Build and Copy all projects, scripts and external dependencies to _layout from src (run this the first time or after deleting _layout)",
"command": "./dev.sh",
"windows": {
"command": "dev.cmd"
},
"args": [
"layout"
],
"options": {
"cwd": "${workspaceFolder}/src"
},
},
{
"label": "build runner layout",
"detail": "Build and Copy all projects to _layout from src (run this on code change)",
"command": "./dev.sh",
"windows": {
"command": "dev.cmd"
},
"args": [
"build"
],
"options": {
"cwd": "${workspaceFolder}/src"
},
}
],
}

View File

@@ -1,4 +1,4 @@
# ADR 263: Self Hosted Runner Proxies
# ADR 263: Self-Hosted Runner Proxies
**Date**: 2019-11-13
@@ -6,13 +6,13 @@
## Context
- Proxy support is required for some enterprises and organizations to start using their own self hosted runners
- While there is not a standard convention, many applications support setting proxies via the environmental variables `http_proxy`, `https_proxy`, `no_proxy`, such as curl, wget, perl, python, docker, git, R, ect
- Proxy support is required for some enterprises and organizations to start using their own self-hosted runners
- While there is not a standard convention, many applications support setting proxies via the environment variables `http_proxy`, `https_proxy`, `no_proxy`, such as curl, wget, perl, python, docker, git, and R
- Some of these applications use `HTTPS_PROXY` versus `https_proxy`, but most understand or primarily support the lowercase variant
## Decision
We will update the Runner to use the conventional environment variables for proxies: `http_proxy`, `https_proxy` and `no_proxy` if they are set.
We will update the Runner to use the conventional environment variables for proxies: `http_proxy`, `https_proxy`, and `no_proxy` if they are set.
These are described in detail below:
- `https_proxy` a proxy URL for all https traffic. It may contain basic authentication credentials. For example:
- http://proxy.com
@@ -22,20 +22,20 @@ These are described in detail below:
- http://proxy.com
- http://127.0.0.1:8080
- http://user:password@proxy.com
- `no_proxy` a comma separated list of hosts that should not use the proxy. An optional port may be specified
- `no_proxy` a comma-separated list of hosts that should not use the proxy. An optional port may be specified. For example:
- `google.com`
- `yahoo.com:443`
- `google.com,bing.com`
We won't use `http_proxy` for https traffic when `https_proxy` is not set, this behavior lines up with any libcurl based tools (curl, git) and wget.
Otherwise action authors and workflow users need to adjust to differences between the runner proxy convention, and tools used by their actions and scripts.
Otherwise, action authors and workflow users need to adjust to differences between the runner proxy convention, and tools used by their actions and scripts.
Example:
Customer set `http_proxy=http://127.0.0.1:8888` and configure the runner against `https://github.com/owner/repo`, with the `https_proxy` -> `http_proxy` fallback, the runner will connect to the server without any problem. However, if a user runs `git push` to `https://github.com/owner/repo`, `git` won't use the proxy since it requires `https_proxy` to be set for any https traffic.
Customer sets `http_proxy=http://127.0.0.1:8888` and configures the runner against `https://github.com/owner/repo`, with the `https_proxy` -> `http_proxy` fallback, the runner will connect to the server without any problem. However, if a user runs `git push` to `https://github.com/owner/repo`, `git` won't use the proxy since it requires `https_proxy` to be set for any https traffic.
> `golang`, `node.js` and other dev tools from the linux community use `http_proxy` for both http and https traffic based on my research.
> `golang`, `node.js`, and other dev tools from the Linux community use `http_proxy` for both http and https traffic based on my research.
A majority of our users are using Linux where these variables are commonly required to be set by various programs. By reading these values, we simplify the process for self hosted runners to set up proxy, and expose it in a way users are already familiar with.
A majority of our users are using Linux where these variables are commonly required to be set by various programs. By reading these values, we simplify the process for self-hosted runners to set up a proxy and expose it in a way users are already familiar with.
A password provided for a proxy will be masked in the logs.
@@ -43,19 +43,19 @@ We will support the lowercase and uppercase variants, with lowercase taking prio
### No Proxy Format
While exact implementations are different per application on handle `no_proxy` env, most applications accept a comma separated list of hosts. Some accept wildcard characters (*). We are going to do exact case-insensitive matches, and not support wildcards at this time.
While exact implementations are different per application on handling `no_proxy` env, most applications accept a comma-separated list of hosts. Some accept wildcard characters (`*`). We are going to do exact case-insensitive matches, and not support wildcards at this time.
For example:
- example.com will match example.com, foo.example.com, foo.bar.example.com
- foo.example.com will match bar.foo.example.com and foo.example.com
- `example.com` will match `example.com`, `foo.example.com`, and `foo.bar.example.com`
- `foo.example.com` will match `bar.foo.example.com` and `foo.example.com`
We will not support IP addresses for `no_proxy`, only hostnames.
## Consequences
1. Enterprises and organizations needing proxy support will be able to embrace self hosted runners
2. Users will need to set these environmental variables before configuring the runner in order to use a proxy when configuring
3. The runner will read from the environmental variables during config and runtime and use the provided proxy if it exists
4. Users may need to pass these environmental variables into other applications if they do not natively take these variables
5. Action authors may need to update their workflows to react to the these environment variables
6. We will document the way of setting environmental variables for runners using the environment variables and how the runner uses them
7. Like all other secrets, users will be able to relatively easily figure out proxy password if they can modify a workflow file running on a self hosted machine
1. Enterprises and organizations needing proxy support will be able to embrace self-hosted runners
2. Users will need to set these environment variables before configuring the runner in order to use a proxy when configuring
3. The runner will read from the environment variables during config and runtime and use the provided proxy if it exists
4. Users may need to pass these environment variables into other applications if they do not natively take these variables
5. Action authors may need to update their workflows to react to these environment variables
6. We will document the way of setting environment variables for runners using the environment variables and how the runner uses them
7. Like all other secrets, users will be able to relatively easily figure out the proxy password if they can modify a workflow file running on a self-hosted machine

View File

@@ -8,7 +8,7 @@
run-actions run scripts using a platform specific shell:
`bash -eo pipefail` on non-windows, and `cmd.exe /c /d /s` on windows
The `shell` option overwrites this to allow different flags or completely different shells/interpreters
The `shell` option overrides this to allow different flags or completely different shells/interpreters
A small example is:
```yml

View File

@@ -5,7 +5,7 @@
**Status**: Accepted
## Context
First party action `actions/cache` needs a input which is an explicit `key` used for restoring and saving the cache. For packages caching, the most comment `key` might be the hash result of contents from all `package-lock.json` under `node_modules` folder.
First party action `actions/cache` needs a input which is an explicit `key` used for restoring and saving the cache. For packages caching, the most common `key` might be the hash result of contents from all `package-lock.json` under `node_modules` folder.
There are serval different ways to get the hash `key` input for `actions/cache` action.
@@ -38,7 +38,7 @@ There are serval different ways to get the hash `key` input for `actions/cache`
`hashFiles()` will only support hashing files under the `$GITHUB_WORKSPACE` since the expression evaluated on the runner, if customer use job container or container action, the runner won't have access to file system inside the container.
`hashFiles()` will only take 1 parameters:
- `hashFiles('**/package-lock.json')` // Search files under $GITHUB_WORKSPACE and calculate a hash for them
- `hashFiles('**/package-lock.json')` // Search files under `$GITHUB_WORKSPACE` and calculate a hash for them
**Question: Do we need to support more than one match patterns?**
Ex: `hashFiles('**/package-lock.json', '!toolkit/core/package-lock.json', '!toolkit/io/package-lock.json')`
@@ -52,7 +52,7 @@ This will help customer has better experience with the `actions/cache` action's
key: ${{hashFiles('**/package-lock.json')}}-${{github.ref}}-${{runner.os}}
```
For search pattern, we will use basic globbing (`*` `?` and `[]`) and globstar (`**`).
For search pattern, we will use basic globbing (`*`, `?`, and `[]`) and globstar (`**`).
Additional pattern details:
- Root relative paths with `github.workspace` (the main repo)
@@ -68,4 +68,4 @@ Hashing logic:
5. Use SHA256 to hash all stored files' hash results to get the final 64 chars hash result.
**Question: Should we include the folder structure info into the hash?**
Answer: No
Answer: No

View File

@@ -15,7 +15,7 @@ This gives us good coverage across the board for secrets and secrets with a pref
However, we don't have great coverage for cases where the secret has a string appended to it before it is base64 encoded (i.e.: `base64($pass\n))`).
Most notably we've seen this as a result of user error where a user accidentially appends a newline or space character before encoding their secret in base64.
Most notably we've seen this as a result of user error where a user accidentally appends a newline or space character before encoding their secret in base64.
## Decision
@@ -45,4 +45,4 @@ This will result in us only revealing length or bit information when a prefix or
- In the case where a secret has a prefix or suffix added before base64 encoding, we may now reveal up to 20 bits of information and the length of the original string modulo 3, rather then the original 16 bits and no length information
- Secrets with a suffix appended before encoding will now be masked across the board. Previously it was only masked if it was a multiple of 3 characters
- Performance will suffer in a neglible way
- Performance will suffer in a negligible way

View File

@@ -6,10 +6,10 @@
## Context
In addition to action's regular execution, action author may wants their action has a chance to participate in:
- Job initialize
My Action will collect machine resource usage (CPU/RAM/Disk) during a workflow job execution, we need to start perf recorder at the begin of the job.
- Job cleanup
In addition to action's regular execution, action author may wants their action to have a chance to participate in:
- Job initialization
My Action will collect machine resource usage (CPU/RAM/Disk) during a workflow job execution, we need to start perf recorder at the beginning of the job.
- Job cleanup
My Action will dirty local workspace or machine environment during execution, we need to cleanup these changes at the end of the job.
Ex: `actions/checkout@v2` will write `github.token` into local `.git/config` during execution, it has post job cleanup defined to undo the changes.
@@ -46,12 +46,12 @@ Container Action Example:
post-if: 'success()' // Optional
```
Both `pre` and `post` will has default `pre-if/post-if` sets to `always()`.
Both `pre` and `post` will have default `pre-if/post-if` set to `always()`.
Setting `pre` to `always()` will make sure no matter what condition evaluate result the `main` gets at runtime, the `pre` has always run already.
`pre` executes in order of how the steps are defined.
`pre` will always be added to job steps list during job setup.
> Action referenced from local repository (`./my-action`) won't get `pre` setup correctly since the repository haven't checkout during job initialize.
> We can't use GitHub api to download the repository since there is a about 3 mins delay between `git push` and the new commit available to download using GitHub api.
> Action referenced from local repository (`./my-action`) won't get `pre` setup correctly since the repository haven't checked-out during job initialization.
> We can't use GitHub api to download the repository since there is about a 3 minute delay between `git push` and the new commit available to download using GitHub api.
`post` will be pushed into a `poststeps` stack lazily when the action's `pre` or `main` execution passed `if` condition check and about to run, you can't have an action that only contains a `post`, we will pop and run each `post` after all `pre` and `main` finished.
> Currently `post` works for both repository action (`org/repo@v1`) and local action (`./my-action`)
@@ -60,7 +60,7 @@ Valid action:
- only has `main`
- has `pre` and `main`
- has `main` and `post`
- has `pre`, `main` and `post`
- has `pre`, `main`, and `post`
Invalid action:
- only has `pre`

View File

@@ -13,13 +13,13 @@ This is another version of [ADR275](https://github.com/actions/runner/pull/275)
## Decision
This ADR proposes that we add a `--labels` option to `config`, which could be used to add custom additional labels to the configured runner.
This ADR proposes that we add a `--labels` option to the `config`, which could be used to add custom additional labels to the configured runner.
For example, to add a single extra label the operator could run:
For example, to add a single additional label the operator could run:
```bash
./config.sh --labels mylabel
```
> Note: the current runner command line parsing and envvar override algorithm only supports a single argument (key).
> Note: the current runner command line parsing and envvar override algorithm only support a single argument (key).
This would add the label `mylabel` to the runner, and enable users to select the runner in their workflow using this label:
```yaml
@@ -39,17 +39,17 @@ runs-on: [self-hosted, mylabel, anotherlabel]
It would not be possible to remove labels from an existing runner using `config.sh`, instead labels would have to be removed using the GitHub UI.
The labels argument will split on commas, trim and discard empty strings. That effectively means don't use commans in unattended config label names. Alternatively we could choose to escape commans but it's a nice to have.
The labels argument will split on commas, trim and discard empty strings. That effectively means don't use commas in unattended config label names. Alternatively, we could choose to escape commas but it's a nice to have.
## Replace
If an existing runner exists and the option to replace is chosen (interactively of via unattend as in this scenario), then the labels will be replaced / overwritten (not merged).
If an existing runner exists and the option to replace is chosen (interactively or via unattended as in this scenario), then the labels will be replaced/overwritten (not merged).
## Overriding built-in labels
Note that it is possible to register "built-in" hosted labels like `ubuntu-latest` and is not considered an error. This is an effective way for the org / runner admin to dictate by policy through registration that this set of runners will be used without having to edit all the workflow files now and in the future.
Note that it is possible to register "built-in" hosted labels like `ubuntu-latest` and is not considered an error. This is an effective way for the org/runner admin to dictate by policy through registration that this set of runners will be used without having to edit all the workflow files now and in the future.
We will also not make other restrictions such as limiting explicitly adding os / arch labels and validating. We will assume that explicit labels were added for a reason and not restricting offers the most flexibility and future proofing / compat.
We will also not make other restrictions such as limiting explicitly adding os/arch labels and validating. We will assume that explicit labels were added for a reason and not restricting offers the most flexibility and future-proofing / compatibility.
## Consequences

View File

@@ -8,17 +8,17 @@
Customers want to be able to compose actions from actions (ex: https://github.com/actions/runner/issues/438)
An important step towards meeting this goal is to build in functionality for actions where users can simply execute any number of steps.
An important step towards meeting this goal is to build functionality for actions where users can simply execute any number of steps.
### Guiding Principles
We don't want the workflow author to need to know how the internal workings of the action work. Users shouldn't know the internal workings of the composite action (for example, `default.shell` and `default.workingDir` should not be inherited from the workflow file to the action file). When deciding how to design certain parts of composite run steps, we want to think one logical step from the consumer.
We don't want the workflow author to need to know how the internal workings of the action work. Users shouldn't know the internal workings of the composite action (for example, `default.shell` and `default.workingDir` should not be inherited from the workflow file to the action file). When deciding how to design certain parts of composite run steps, we want to treat it as one logical step for the consumer.
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).
**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 their parents' input variables and nested steps can overwrite the input variables).
### Composite Run Steps Features
This feature supports at the top action level:
@@ -92,7 +92,7 @@ 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 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 its `shell` and `working-directory` attributes at the step level for an action.
For example,
@@ -218,9 +218,9 @@ Example Output:
random-number 43243
```
Each of the output variables from the composite action is viewable from the workflow file that uses the composite action. In other words, every child action output(s) is viewable only by its parent using dot notation (ex `steps.foo.outputs.random-number`).
Each of the output variables from the composite action is viewable from the workflow file that uses the composite action. In other words, every child's action output(s) are only viewable by its parent using dot notation (ex `steps.foo.outputs.random-number`).
Moreover, the output ids are only accessible within the scope where it was defined. Note that in the example above, in our `workflow.yml` file, it should not have access to output id (i.e. `random-id`). The reason why we are doing this is because we don't want to require the workflow author to know the internal workings of the composite action.
Moreover, the output ids are only accessible within the scope where it was defined. Note that in the example above, in our `workflow.yml` file, it should not have access to output id (i.e. `random-id`). The reason why we are doing this is that we don't want to require the workflow author to know the internal workings of the composite action.
### Context
@@ -237,9 +237,9 @@ In the Composite Action, you'll only be able to use `::set-env::` to set environ
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-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. **
** `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:
@@ -248,7 +248,7 @@ Example `workflow.yml`:
```yaml
steps:
- run: exit 1
- uses: user/composite@v1 # <--- this will run, as it's marked as always runing
- uses: user/composite@v1 # <--- this will run, as it's marked as always running
if: always()
```
@@ -269,15 +269,15 @@ runs:
shell: bash
```
**We will not support "if Condition" in a composite action for now. This functionality will be focused on in a future ADR.**
**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()`.
**Note that the if condition on the parent does not propagate to the rest of its children though.**
**Note that the "if-condition" on the parent does not propagate to the rest of its children though.**
In the child action (in this example, this is the `action.yml`), it starts with a clean slate (in other words, no imposing if conditions). Similar to the logic in the paragraph above, `echo "I will run, as my current scope is succeeding"` will run since the `if` condition checks if the previous steps **within this composite action** has not failed. `run: echo "I will not run, as my current scope is now failing"` will not run since the previous step resulted in an error and by default, the if expression is set to `success()` if the if condition is not set for a step.
In the child action (in this example, this is the `action.yml`), it starts with a clean slate (in other words, no imposing if-conditions). Similar to the logic in the paragraph above, `echo "I will run, as my current scope is succeeding"` will run since the `if` condition checks if the previous steps **within this composite action** have not failed. `run: echo "I will not run, as my current scope is now failing"` will not run since the previous step resulted in an error and by default, the if expression is set to `success()` if the if-condition is not set for a step.
What if a step has `cancelled()`? We do the opposite of our approach above if `cancelled()` is used for any of our composite run steps. We will cancel any step that has this condition if the workflow is cancelled at all.
@@ -314,13 +314,13 @@ runs:
**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.
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 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).
If the time taken for any of the steps in combination or individually exceeds 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).
For reference, in the example above, if the composite step `foo1` takes 11 minutes to run, that step will fail but the rest of the steps, `foo1` and `foo2`, will proceed as long as their total runtime with the previous failed `foo1` action is less than the composite action's `timeout-minutes` (50 minutes). If the composite step `foo2` takes 51 minutes to run, it will cause the whole composite action job to fail. I
For reference, in the example above, if the composite step `foo1` takes 11 minutes to run, that step will fail but the rest of the steps, `foo1` and `foo2`, will proceed as long as their total runtime with the previous failed `foo1` action is less than the composite action's `timeout-minutes` (50 minutes). If the composite step `foo2` takes 51 minutes to run, it will cause the whole composite action job to fail.
The rationale behind this is that users can configure their steps with the `if` condition to conditionally set how steps rely on each other. Due to the additional capabilities that are offered with combining `timeout-minutes` and/or `if`, we wanted the `timeout-minutes` condition to be as dumb as possible and not effect other steps.
The rationale behind this is that users can configure their steps with the `if` condition to conditionally set how steps rely on each other. Due to the additional capabilities that are offered with combining `timeout-minutes` and/or `if`, we wanted the `timeout-minutes` condition to be as dumb as possible and not affect other steps.
[Usage limits still apply](https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions?query=if%28%29#usage-limits)
@@ -361,7 +361,7 @@ For the composite action steps, it follows the same logic as above. In this exam
### 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.
Here is a visual represenation of the [first example](#Steps)
Here is a visual representation of the [first example](#Steps)
```yaml
| composite_action_node |

View File

@@ -11,7 +11,7 @@ export RUNNER_CFG_PAT=yourPAT
## Create running as a service
**Scenario**: Run on a machine or VM (not container) which automates:
**Scenario**: Run on a machine or VM ([not container](#why-cant-i-use-a-container)) which automates:
- Resolving latest released runner
- Download and extract latest
@@ -23,12 +23,16 @@ export RUNNER_CFG_PAT=yourPAT
Run as a one-liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level)
```bash
curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/create-latest-svc.sh | bash -s yourorg/yourrepo
curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/create-latest-svc.sh | bash -s yourorg/yourrepo
```
### Why can't I use a container?
The runner is installed as a service using `systemd` and `systemctl`. Docker does not support `systemd` for service configuration on a container.
## Uninstall running as service
**Scenario**: Run on a machine or VM (not container) which automates:
**Scenario**: Run on a machine or VM ([not container](#why-cant-i-use-a-container)) which automates:
- Stops and uninstalls the systemd (linux) or Launchd (osx) service
- Acquires a removal token
@@ -38,7 +42,7 @@ curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/create
Repo level one liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level)
```bash
curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/remove-svc.sh | bash -s yourorg/yourrepo
curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/remove-svc.sh | bash -s yourorg/yourrepo
```
### Delete an offline runner
@@ -53,5 +57,5 @@ curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/remove
Repo level one-liner. NOTE: replace with yourorg/yourrepo (repo level) or just yourorg (org level) and replace runnername
```bash
curl -s https://raw.githubusercontent.com/actions/runner/automate/scripts/delete.sh | bash -s yourorg/yourrepo runnername
curl -s https://raw.githubusercontent.com/actions/runner/main/scripts/delete.sh | bash -s yourorg/yourrepo runnername
```

View File

@@ -27,6 +27,7 @@ Make sure the runner has access to actions service for GitHub.com or GitHub Ente
- DNS lookup for pipelines.actions.githubusercontent.com using dotnet
- Ping pipelines.actions.githubusercontent.com using dotnet
- Make HTTP GET to https://pipelines.actions.githubusercontent.com/_apis/health or https://myGHES.com/_services/pipelines/_apis/health using dotnet, check response headers contains `x-vss-e2eid`
- Make HTTP POST to https://pipelines.actions.githubusercontent.com/_apis/health or https://myGHES.com/_services/pipelines/_apis/health using dotnet, check response headers contains `x-vss-e2eid`
## How to fix the issue?

View File

@@ -22,7 +22,7 @@ The test also set environment variable `GIT_TRACE=1` and `GIT_CURL_VERBOSE=1` be
### 1. Check the common network issue
> Please check the [network doc](./network.md)
> Please check the [network doc](./network.md)
### 2. SSL certificate related issue

View File

@@ -10,6 +10,8 @@
- Proxy try to decrypt and exam HTTPS traffic for security purpose but cause the actions-runner to fail to finish SSL handshake due to the lack of trusting proxy's CA.
- Proxy try to modify the HTTPS request (like add or change some http headers) and causes the request become incompatible with the Actions Service (ASP.NetCore), Ex: [Nginx](https://github.com/dotnet/aspnetcore/issues/17081)
- Firewall rules that block action runner from accessing certain hosts, ex: `*.github.com`, `*.actions.githubusercontent.com`, etc.
@@ -21,6 +23,7 @@ Use a 3rd party tool to make the same requests as the runner did would be a good
- Use `nslookup` to check DNS
- Use `ping` to check Ping
- Use `traceroute`, `tracepath`, or `tracert` to check the network route between the runner and the Actions service
- Use `curl -v` to check the network stack, good for verifying default certificate/proxy settings.
- Use `Invoke-WebRequest` from `pwsh` (`PowerShell Core`) to check the dotnet network stack, good for verifying bugs in the dotnet framework.

View File

@@ -19,12 +19,35 @@ We ask that before significant effort is put into code changes, that we have agr
An ADR is an Architectural Decision Record. This allows consensus on the direction forward and also serves as a record of the change and motivation. [Read more here](adrs/README.md)
## Development Life Cycle
### Required Dev Dependencies
## Required Dev Dependencies
![Win](res/win_sm.png) ![*nix](res/linux_sm.png) Git for Windows and Linux [Install Here](https://git-scm.com/downloads) (needed for dev sh script)
## Quickstart: Run a job from a real repository
If you just want to get from building the sourcecode to using it to execute an action, you will need:
- The url of your repository
- A runner registration token. You can find it at `https://github.com/{your-repo}/settings/actions/runners/new`
```bash
git clone https://github.com/actions/runner
cd runner/src
./dev.(sh/cmd) layout # the runner that built from source is in {root}/_layout
cd ../_layout
./config.(sh/cmd) --url https://github.com/{your-repo} --token ABCABCABCABCABCABCABCABCABCAB # accept default name, labels and work folder
./run.(sh/cmd)
```
If you trigger a job now, you can see the runner execute it.
Tip: Make sure your job can run on this runner. The easiest way is to set `runs-on: self-hosted` in the workflow file.
## Development Life Cycle
If you're using VS Code, you can follow [these](contribute/vscode.md) steps instead.
### To Build, Test, Layout
Navigate to the `src` directory and run the following command:
@@ -39,7 +62,7 @@ Navigate to the `src` directory and run the following command:
* `build` (`b`): Build everything and update runner layout folder
* `test` (`t`): Build runner binaries and run unit tests
Sample developer flow:
### Sample developer flow:
```bash
git clone https://github.com/actions/runner
@@ -51,25 +74,81 @@ cd ./src
./dev.(sh/cmd) test # run all unit tests before git commit/push
```
View logs:
Let's break that down.
### Clone repository:
```bash
git clone https://github.com/actions/runner
cd runner
```
If you want to push your changes to a remote, it is recommended you fork the repository and use that fork as your origin instead of `https://github.com/actions/runner`.
### Build Layout:
This command will build all projects, then copies them and other dependencies into a folder called `_layout`. The binaries in this folder are then used for running, debugging the runner.
```bash
cd ./src # execute the script from this folder
./dev.(sh/cmd) layout # the runner that built from source is in {root}/_layout
```
If you make code changes after this point, use the argument `build` to build your code in the `src` folder to keep your `_layout` folder up to date.
```bash
cd ./src
./dev.(sh/cmd) build # {root}/_layout will get updated
```
### Test Layout:
This command runs the suite of unit tests in the project
```bash
cd ./src
./dev.(sh/cmd) test # run all unit tests before git commit/push
```
### Configure Runner:
If you want to manually test your runner and run actions from a real repository, you'll have to configure it before running it.
```bash
cd runner/_layout
./config.(sh/cmd) # configure your custom runner
```
You will need your the name of your repository and a runner registration token.
Check [Quickstart](##Quickstart:-Run-a-job-from-a-real-repository) if you don't know how to get this token.
These can also be passed down as arguments to `config.(sh/cmd)`:
```bash
cd runner/_layout
./config.(sh/cmd) --url https://github.com/{your-repo} --token ABCABCABCABCABCABCABCABCABCAB
```
### Run Runner
All that's left to do is to start the runner:
```bash
cd runner/_layout
./run.(sh/cmd) # run your custom runner
```
### View logs:
```bash
cd runner/_layout/_diag
ls
cat (Runner/Worker)_TIMESTAMP.log # view your log file
```
Run Runner:
```bash
cd runner/_layout
./run.sh # run your custom runner
```
### Editors
## Editors
[Using Visual Studio Code](https://code.visualstudio.com/)
[Using Visual Studio](https://code.visualstudio.com/docs)
### Styling
## Styling
We use the .NET Foundation and CoreCLR style guidelines [located here](
https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md)

52
docs/contribute/vscode.md Normal file
View File

@@ -0,0 +1,52 @@
# Development Life Cycle using VS Code:
These examples use VS Code, but the idea should be similar across all IDEs as long as you attach to the same processes in the right folder.
## Configure
To successfully start the runner, you need to register it using a repository and a runner registration token.
Run `Configure` first to build the source code and set up the runner in `_layout`.
Once it's done creating `_layout`, it asks for the url of your repository and your token in the terminal.
Check [Quickstart](../contribute.md#quickstart-run-a-job-from-a-real-repository) if you don't know how to get this token.
## Debugging
Debugging the full lifecycle of a job can be tricky, because there are multiple processes involved.
All the configs below can be found in `.vscode/launch.json`.
## Debug the Listener
```json
{
"name": "Run [build]",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build runner layout", // use the config called "Run" to launch without rebuild
"program": "${workspaceFolder}/_layout/bin/Runner.Listener",
"args": [
"run" // run without args to print usage
],
"cwd": "${workspaceFolder}/src",
"console": "integratedTerminal",
"requireExactSource": false,
}
```
If you launch `Run` or `Run [build]`, it starts a process called `Runner.Listener`.
This process will receive any job queued on this repository if the job runs on matching labels (e.g `runs-on: self-hosted`).
Once a job is received, a `Runner.Listener` starts a new process of `Runner.Worker`.
Since this is a diferent process, you can't use the same debugger session debug it.
Instead, a parallel debugging session has to be started, using a different launch config.
Luckily, VS Code supports multiple parallel debugging sessions.
## Debug the Worker
Because the worker process is usually started by the listener instead of an IDE, debugging it from start to finish can be tricky.
For this reason, `Runner.Worker` can be configured to wait for a debugger to be attached before it begins any actual work.
Set the environment variable `GITHUB_ACTIONS_RUNNER_ATTACH_DEBUGGER` to `true` or `1` to enable this wait.
All worker processes now will wait 20 seconds before they start working on their task.
This gives enough time to attach a debugger by running `Debug Worker`.
If for some reason you have multiple workers running, run the launch config `Attach` instead.
Select `Runner.Worker` from the running processes when VS Code prompts for it.

View File

@@ -15,16 +15,16 @@ x64
- openSUSE 15+
- SUSE Enterprise Linux (SLES) 12 SP2+
## Install .Net Core 5 Linux Dependencies
## Install .Net Core 3.x Linux Dependencies
The `./config.sh` will check .Net Core 5 dependencies during runner configuration.
The `./config.sh` will check .Net Core 3.x dependencies during runner configuration.
You might see something like this which indicate a dependency's missing.
```bash
./config.sh
libunwind.so.8 => not found
libunwind-x86_64.so.8 => not found
Dependencies is missing for Dotnet 5
Execute ./bin/installdependencies.sh to install any missing Dotnet 5 dependencies.
Dependencies is missing for Dotnet Core 3.0
Execute ./bin/installdependencies.sh to install any missing Dotnet Core 3.0 dependencies.
```
You can easily correct the problem by executing `./bin/installdependencies.sh`.
The `installdependencies.sh` script should install all required dependencies on all supported Linux versions

View File

@@ -5,6 +5,12 @@
## Supported Versions
- macOS High Sierra (10.13) and later versions
## Apple Silicon M1
The runner is currently not supported on devices with an Apple M1 chip.
We are waiting for official .NET support. You can read more here about the [current state of support here](https://github.com/orgs/dotnet/projects/18#card-56812463).
Current .NET project board about M1 support:
https://github.com/orgs/dotnet/projects/18#card-56812463
## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/macos-prerequisites?tabs=netcore30)

View File

@@ -1,11 +1,17 @@
## Features
- Use GITHUB_TOKEN for ghcr.io containers if credentials are not provided (#990)
## Bugs
- Downgrade runner to .NET 3 to address an issue with broken pipes in Ubuntu (#928)
- Fixed an issue where FIPS Cryptography broke back-compat scenarios (#928)
- Do not trucate error message from template evaluation (#1038)
- Make FileShare ReadWrite (#1033)
- Mask secrets with double-quotes when passed to docker command line (#1002)
- Delete script files before replacing during update (#984)
## Misc
- Updated dotnet install scripts (#928)
## 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.
@@ -43,7 +49,7 @@ curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>
tar xzf ./actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz
```
## Linux arm64 (Pre-release)
## Linux arm64
``` bash
# Create a folder
@@ -54,7 +60,7 @@ curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>
tar xzf ./actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz
```
## Linux arm (Pre-release)
## Linux arm
``` bash
# Create a folder
@@ -67,3 +73,13 @@ tar xzf ./actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz
## Using your self hosted runner
For additional details about configuring, running, or shutting down the runner please check out our [product docs.](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/adding-self-hosted-runners)
## SHA-256 Checksums
The SHA-256 checksums for the packages included in this build are shown below:
- actions-runner-win-x64-<RUNNER_VERSION>.zip <!-- BEGIN SHA win-x64 --><WIN_X64_SHA><!-- END SHA win-x64 -->
- actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz <!-- BEGIN SHA osx-x64 --><OSX_X64_SHA><!-- END SHA osx-x64 -->
- actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz <!-- BEGIN SHA linux-x64 --><LINUX_X64_SHA><!-- END SHA linux-x64 -->
- actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz <!-- BEGIN SHA linux-arm64 --><LINUX_ARM64_SHA><!-- END SHA linux-arm64 -->
- actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz <!-- BEGIN SHA linux-arm --><LINUX_ARM_SHA><!-- END SHA linux-arm -->

View File

@@ -1 +1 @@
2.276.1
<Update to ./src/runnerversion when creating release>

View File

@@ -73,4 +73,4 @@ if [ "${runner_plat}" == "linux" ]; then
fi
${prefix}./svc.sh stop
${prefix}./svc.sh uninstall
${prefix}./config.sh remove --token $REMOVE_TOKEN
./config.sh remove --token $REMOVE_TOKEN

View File

@@ -340,9 +340,8 @@ function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel) {
elseif ($Runtime -eq "aspnetcore") {
$VersionFileUrl = "$UncachedFeed/aspnetcore/Runtime/$Channel/latest.version"
}
# Currently, the WindowsDesktop runtime is manufactured with the .Net core runtime
elseif ($Runtime -eq "windowsdesktop") {
$VersionFileUrl = "$UncachedFeed/Runtime/$Channel/latest.version"
$VersionFileUrl = "$UncachedFeed/WindowsDesktop/$Channel/latest.version"
}
elseif (-not $Runtime) {
$VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.version"
@@ -438,7 +437,16 @@ function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string
$PayloadURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/aspnetcore-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip"
}
elseif ($Runtime -eq "windowsdesktop") {
# The windows desktop runtime is part of the core runtime layout prior to 5.0
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip"
if ($SpecificVersion -match '^(\d+)\.(.*)$')
{
$majorVersion = [int]$Matches[1]
if ($majorVersion -ge 5)
{
$PayloadURL = "$AzureFeed/WindowsDesktop/$SpecificVersion/windowsdesktop-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip"
}
}
}
elseif (-not $Runtime) {
$PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificProductVersion-win-$CLIArchitecture.zip"
@@ -480,7 +488,16 @@ function Get-Product-Version([string]$AzureFeed, [string]$SpecificVersion) {
$ProductVersionTxtURL = "$AzureFeed/aspnetcore/Runtime/$SpecificVersion/productVersion.txt"
}
elseif ($Runtime -eq "windowsdesktop") {
# The windows desktop runtime is part of the core runtime layout prior to 5.0
$ProductVersionTxtURL = "$AzureFeed/Runtime/$SpecificVersion/productVersion.txt"
if ($SpecificVersion -match '^(\d+)\.(.*)')
{
$majorVersion = [int]$Matches[1]
if ($majorVersion -ge 5)
{
$ProductVersionTxtURL = "$AzureFeed/WindowsDesktop/$SpecificVersion/productVersion.txt"
}
}
}
elseif (-not $Runtime) {
$ProductVersionTxtURL = "$AzureFeed/Sdk/$SpecificVersion/productVersion.txt"
@@ -885,10 +902,10 @@ Say "Note that the script does not resolve dependencies during installation."
Say "To check the list of dependencies, go to https://docs.microsoft.com/dotnet/core/install/windows#dependencies"
Say "Installation finished"
# SIG # Begin signature block
# MIIjkgYJKoZIhvcNAQcCoIIjgzCCI38CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# MIIjjwYJKoZIhvcNAQcCoIIjgDCCI3wCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD2c707qnCLOLIC
# n6Mu5Gr4+Xp68foyZlGlTycnycc5l6CCDYEwggX/MIID56ADAgECAhMzAAABh3IX
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCNsnhcJvx/hXmM
# w8KjuvvIMDBFonhg9XJFc1QwfTyH4aCCDYEwggX/MIID56ADAgECAhMzAAABh3IX
# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
@@ -960,29 +977,29 @@ Say "Installation finished"
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZzCCFWMCAQEwgZUwfjELMAkG
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZDCCFWACAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgE/MRhWyu
# Zg+EA2WKcxYC31nHVCTE6guHppZppc70RtkwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgpT/bxWwe
# aW0EinKMWCAzDXUjwXkIHldYzR6lw4/1Pc0wQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQBvcYCjRDXUYEIz9j2j0r4GFI2Y3g/CoNxDDBaeQ+gV
# khO0fK0oLh18RbV271Mg6SF7X7+mXB5MnL68voVQDqHnsCYrIAuMF/AEpv9YuDDp
# ZRJuqN7Vwg3HM02l/FyATBIMgf/V79aYzJL3jjtt9bRIyxk6aPU4XcwMeA4usnUQ
# rMhIiQz07DgfSrcQWe4AvGFAIvqTAKE4P944EZWWVnWI/10rvatEAefqJZX3XljW
# sK/6NY/0MyAyiILOuXbvVS0YFbHaR2qd1jUXbrY79fS+H4Ts6qnbufOkHQvmcDxs
# 801wKLHumMdPTtMVzfVMCwPvrHP0wtzsFlmCcKjBbGpvoYIS8TCCEu0GCisGAQQB
# gjcDAwExghLdMIIS2QYJKoZIhvcNAQcCoIISyjCCEsYCAQMxDzANBglghkgBZQME
# BgkqhkiG9w0BAQEFAASCAQCHd7sSQVq0YDg8QDx6/kLWn3s6jtvvIDCCgsO9spHM
# quPd4FPbG67DCsKDClekQs52qrtRO3Zo+JMnCw4j3bS+gZHzeJr2shbftOrpsFoD
# l7OPcUmtrqul9dkQCOp8t0MP3ls0n96/YyNy6lz4BAlTdkdDx957uAxalKaCIBzb
# R9QyppOKIfNFvwD4EI5KI6tpmSy/uH8SrRg7ZExAYZl6J6R18WkL7KHn649lPoAQ
# ujwrIXH10xOJops45ILGzKWQcHmCzLJGYapL4VHUuK+73nT+9ZROGHdk/PyvIcdw
# iERa+C06v305t3DA+CuHFy1tvyw7IFF6RVbLZPwxrJjToYIS7jCCEuoGCisGAQQB
# gjcDAwExghLaMIIS1gYJKoZIhvcNAQcCoIISxzCCEsMCAQMxDzANBglghkgBZQME
# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEINdeoXtuzW+Dihw6n+VdG+91si0f6TvWhJXaPtvW
# oF4cAgZfu+i3IT8YEzIwMjAxMjE3MDYzMDM2LjU0M1owBIACAfSggdSkgdEwgc4x
# MDEwDQYJYIZIAWUDBAIBBQAEIOCaTmvM1AP0WaEVqzKaaCu/R+bTlR4kCrM/ZXsb
# /eNOAgZgGeLsMwsYEzIwMjEwMjAzMjExNzQ5LjU5MVowBIACAfSggdSkgdEwgc4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p
# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjo4OTdBLUUzNTYtMTcwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZaCCDkQwggT1MIID3aADAgECAhMzAAABLCKvRZd1+RvuAAAA
# U3RhbXAgU2VydmljZaCCDkEwggT1MIID3aADAgECAhMzAAABLCKvRZd1+RvuAAAA
# AAEsMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
@@ -1043,7 +1060,7 @@ Say "Installation finished"
# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a
# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ
# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+
# NR4Iuto229Nfj950iEkSoYIC0jCCAjsCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
# NR4Iuto229Nfj950iEkSoYICzzCCAjgCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP
# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo4
@@ -1052,27 +1069,27 @@ Say "Installation finished"
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOOFYaowIhgPMjAyMDEyMTcwODQ4NDJaGA8yMDIwMTIxODA4NDg0MlowdzA9Bgor
# BgEEAYRZCgQBMS8wLTAKAgUA44VhqgIBADAKAgEAAgIoWgIB/zAHAgEAAgISJTAK
# AgUA44azKgIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIB
# AAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAB53NDoDDF4vqFWY
# fwUnSvAy3z0CtqSFeA9RzDKGklPRwVkya5DtmVBDTZUbVQ2ST9hvRAVxhktfyVBZ
# ewapGJsvwMhg7nnEqBOumt6TvueIZpbs+p5z//3+iFYGkT3YFQI0Gd2JkvgBxfs5
# +GptO6JKtiyA+zkKijxqXZvMqMxBMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAEsIq9Fl3X5G+4AAAAAASwwDQYJYIZIAWUD
# BAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0B
# CQQxIgQg3wEUtEvxwCp3aAFB2vGXOOqg/AXHyXZh9P9J+0uArDMwgfoGCyqGSIb3
# DQEJEAIvMYHqMIHnMIHkMIG9BCBbn/0uFFh42hTM5XOoKdXevBaiSxmYK9Ilcn9n
# u5ZH4TCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB
# LCKvRZd1+RvuAAAAAAEsMCIEINBRtGID6jvA2ptfwIuPyG7qPcLRYb9YrJ8aKfVg
# TulFMA0GCSqGSIb3DQEBCwUABIIBACQQpFGWW6JmH5MTKwhaE/8+gyzI2bT8XJnA
# t8k7PHFvEGA7whgp9eNgW+wWJm1gnsmswjx2l7FW4DLg9lghM8FK77JRCg7CJfse
# dSbnTv81/4VhSXOAO0jMP2dALP7DF59vQmlDh50u8/Wu61ActMOt6cArkoUhBRXO
# LnqOQCOEEku5Xy2ES9g9eUfLUvTvlWo6HiAq+cJnNV08QRBOnGWRxdwy8YJ5vwNW
# Pwx0ZG3rTvMtGzOaW6Ve5O36H2ynoEdzCmpakeDaF2sZ86/LNERKyIXiykV/Uig1
# SZh2VLY/Yni9SCVHbYgvTOCh5ZZE5eOi6BwLf0T4xl5alHUx+AA=
# AOPFChkwIhgPMjAyMTAyMDMxNTQwMDlaGA8yMDIxMDIwNDE1NDAwOVowdDA6Bgor
# BgEEAYRZCgQBMSwwKjAKAgUA48UKGQIBADAHAgEAAgIXmDAHAgEAAgIRyTAKAgUA
# 48ZbmQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID
# B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAHeeznL2n6HWCjHH94Fl
# hcdW6TEXzq4XNgp1Gx1W9F8gJ4x+SwoV7elJZkwgGffcpHomLvIY/VSuzsl1NgtJ
# TWM2UxoqSv58BBOrl4eGhH6kkg8Ucy2tdeK5T8cHa8pMkq2j9pFd2mRG/6VMk0dl
# Xz7Uy3Z6bZqkcABMyAfuAaGbMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgUENBIDIwMTACEzMAAAEsIq9Fl3X5G+4AAAAAASwwDQYJYIZIAWUDBAIB
# BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx
# IgQg/QYv7yp+354WTjWUIsXWndTEzXjaYjqwYjcBxCJKjdUwgfoGCyqGSIb3DQEJ
# EAIvMYHqMIHnMIHkMIG9BCBbn/0uFFh42hTM5XOoKdXevBaiSxmYK9Ilcn9nu5ZH
# 4TCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABLCKv
# RZd1+RvuAAAAAAEsMCIEIIfIM3YbzHswb/Kj/qq1l1cHA6QBl+gEXYanUNJomrpT
# MA0GCSqGSIb3DQEBCwUABIIBAAwdcXssUZGO7ho5+NHLjIxLtQk543aKGo+lrRMY
# Q9abE1h/AaaNJl0iGxX4IihNWyfovSfYL3L4eODUBAu68tWSxeceRfWNsb/ZZfUi
# v89hpLssI/Gf1BEgNMA4zCuIGQiC8okusVumEpAhhvCEbSiTTTtBdolTnU/CAKui
# oxaU3R9XkKh1F4oAM26+dJ1J2BLQXPs5afNvvedDsZWNQUPK1sFF3JRfzxiTrwBW
# EJRyflev9gyDoqCHzippgb+6+eti1WTkcA9Q49GIT11S6LOAVqkSC9N7Nqf8ksh8
# ARdwT8jigpsm+mj7lrVU9upDkhVYhKeO8oiZq95Q53Zkteo=
# SIG # End signature block

View File

@@ -303,7 +303,7 @@ get_machine_architecture() {
echo "arm"
return 0
;;
aarch64)
aarch64|arm64)
echo "arm64"
return 0
;;
@@ -489,7 +489,7 @@ get_specific_version_from_version() {
local json_file="$5"
if [ -z "$json_file" ]; then
if [[ "$version" == "latest" ]]; then
if [[ "$version" == "latest" ]]; then
local version_info
version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1
say_verbose "get_specific_version_from_version: version_info=$version_info"
@@ -522,7 +522,7 @@ construct_download_link() {
local specific_version="${4//[$'\t\r\n']}"
local specific_product_version="$(get_specific_product_version "$1" "$4")"
local osname="$5"
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"
@@ -542,7 +542,7 @@ construct_download_link() {
# 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
# 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
@@ -744,13 +744,30 @@ download() {
fi
local failed=false
if machine_has "curl"; then
downloadcurl "$remote_path" "$out_path" || failed=true
elif machine_has "wget"; then
downloadwget "$remote_path" "$out_path" || failed=true
else
failed=true
fi
local attempts=0
while [ $attempts -lt 3 ]; do
attempts=$((attempts+1))
failed=false
if machine_has "curl"; then
downloadcurl "$remote_path" "$out_path" || failed=true
elif machine_has "wget"; then
downloadwget "$remote_path" "$out_path" || failed=true
else
say_err "Missing dependency: neither curl nor wget was found."
exit 1
fi
if [ "$failed" = false ] || [ $attempts -ge 3 ] || { [ ! -z $http_code ] && [ $http_code = "404" ]; }; then
break
fi
say "Download attempt #$attempts has failed: $http_code $download_error_msg"
say "Attempt #$((attempts+1)) will start in $((attempts*10)) seconds."
sleep $((attempts*20))
done
if [ "$failed" = true ]; then
say_verbose "Download failed: $remote_path"
return 1
@@ -761,6 +778,8 @@ download() {
# Updates global variables $http_code and $download_error_msg
downloadcurl() {
eval $invocation
unset http_code
unset download_error_msg
local remote_path="$1"
local out_path="${2:-}"
# Append feed_credential as late as possible before calling curl to avoid logging feed_credential
@@ -789,6 +808,8 @@ downloadcurl() {
# Updates global variables $http_code and $download_error_msg
downloadwget() {
eval $invocation
unset http_code
unset download_error_msg
local remote_path="$1"
local out_path="${2:-}"
# Append feed_credential as late as possible before calling wget to avoid logging feed_credential
@@ -882,12 +903,11 @@ install_dotnet() {
say "Downloading primary link $download_link"
# The download function will set variables $http_code and $download_error_msg in case of failure.
http_code=""; download_error_msg=""
download "$download_link" "$zip_path" 2>&1 || download_failed=true
primary_path_http_code="$http_code"; primary_path_download_error_msg="$download_error_msg"
# if the download fails, download the legacy_download_link
if [ "$download_failed" = true ]; then
primary_path_http_code="$http_code"; primary_path_download_error_msg="$download_error_msg"
case $primary_path_http_code in
404)
say "The resource at $download_link is not available."
@@ -906,11 +926,10 @@ install_dotnet() {
say "Downloading legacy link $download_link"
# The download function will set variables $http_code and $download_error_msg in case of failure.
http_code=""; download_error_msg=""
download "$download_link" "$zip_path" 2>&1 || download_failed=true
legacy_path_http_code="$http_code"; legacy_path_download_error_msg="$download_error_msg"
if [ "$download_failed" = true ]; then
legacy_path_http_code="$http_code"; legacy_path_download_error_msg="$download_error_msg"
case $legacy_path_http_code in
404)
say "The resource at $download_link is not available."
@@ -1112,10 +1131,10 @@ do
echo " --arch,-Architecture,-Arch"
echo " Possible values: x64, arm, and arm64"
echo " --os <system> Specifies operating system to be used when selecting the installer."
echo " Overrides the OS determination approach used by the script. Supported values: osx, linux, linux-musl, freebsd, rhel.6."
echo " In case any other value is provided, the platform will be determined by the script based on machine configuration."
echo " Overrides the OS determination approach used by the script. Supported values: osx, linux, linux-musl, freebsd, rhel.6."
echo " In case any other value is provided, the platform will be determined by the script based on machine configuration."
echo " Not supported for legacy links. Use --runtime-id to specify platform for legacy links."
echo " Refer to: https://aka.ms/dotnet-os-lifecycle for more information."
echo " Refer to: https://aka.ms/dotnet-os-lifecycle for more information."
echo " --runtime <RUNTIME> Installs a shared runtime only, without the SDK."
echo " -Runtime"
echo " Possible values:"
@@ -1140,7 +1159,7 @@ do
echo " Installs just the shared runtime bits, not the entire SDK."
echo " --runtime-id Installs the .NET Tools for the given platform (use linux-x64 for portable linux)."
echo " -RuntimeId" The parameter is obsolete and may be removed in a future version of this script. Should be used only for versions below 2.1.
echo " For primary links to override OS or/and architecture, use --os and --architecture option instead."
echo " For primary links to override OS or/and architecture, use --os and --architecture option instead."
echo ""
echo "Install Location:"
echo " Location is chosen in following order:"
@@ -1177,7 +1196,7 @@ if [ "$dry_run" = true ]; then
if [ "$valid_legacy_download_link" = true ]; then
say "Legacy named payload URL: $legacy_download_link"
fi
repeatable_command="./$script_name --version "\""$specific_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\"" --os "\""$normalized_os"\"""
repeatable_command="./$script_name --version "\""$specific_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\"" --os "\""$normalized_os"\"""
if [[ "$runtime" == "dotnet" ]]; then
repeatable_command+=" --runtime "\""dotnet"\"""
elif [[ "$runtime" == "aspnetcore" ]]; then

View File

@@ -16,11 +16,11 @@ if (supported.indexOf(process.platform) == -1) {
var stopping = false;
var listener = null;
var runService = function() {
var runService = function () {
var listenerExePath = path.join(__dirname, '../bin/Runner.Listener');
var interactive = process.argv[2] === "interactive";
if(!stopping) {
if (!stopping) {
try {
if (interactive) {
console.log('Starting Runner listener interactively');
@@ -30,8 +30,8 @@ var runService = function() {
listener = childProcess.spawn(listenerExePath, ['run', '--startuptype', 'service'], { env: process.env });
}
console.log('Started listener process');
console.log(`Started listener process, pid: ${listener.pid}`);
listener.stdout.on('data', (data) => {
process.stdout.write(data.toString('utf8'));
});
@@ -40,6 +40,10 @@ var runService = function() {
process.stdout.write(data.toString('utf8'));
});
listener.on("error", (err) => {
console.log(`Runner listener fail to start with error ${err.message}`);
});
listener.on('close', (code) => {
console.log(`Runner listener exited with error code ${code}`);
@@ -56,13 +60,13 @@ var runService = function() {
} else {
console.log('Runner listener exit with undefined return code, re-launch runner in 5 seconds.');
}
if(!stopping) {
if (!stopping) {
setTimeout(runService, 5000);
}
});
} catch(ex) {
} catch (ex) {
console.log(ex);
}
}
@@ -71,7 +75,7 @@ var runService = function() {
runService();
console.log('Started running service');
var gracefulShutdown = function(code) {
var gracefulShutdown = function (code) {
console.log('Shutting down runner listener');
stopping = true;
if (listener) {

View File

@@ -14,14 +14,14 @@ fi
function print_errormessage()
{
echo "Can't install dotnet 5 dependencies."
echo "Can't install dotnet core dependencies."
echo "You can manually install all required dependencies based on following documentation"
echo "https://docs.microsoft.com/en-us/dotnet/core/linux-prerequisites?tabs=netcore2x"
}
function print_rhel6message()
{
echo "We did our best effort to install dotnet 5 dependencies"
echo "We did our best effort to install dotnet core dependencies"
echo "However, there are some dependencies which require manual installation"
echo "You can install all remaining required dependencies based on the following documentation"
echo "https://github.com/dotnet/core/blob/master/Documentation/build-and-install-rhel6-prerequisites.md"
@@ -29,7 +29,7 @@ function print_rhel6message()
function print_rhel6errormessage()
{
echo "We couldn't install dotnet 5 dependencies"
echo "We couldn't install dotnet core dependencies"
echo "You can manually install all required dependencies based on following documentation"
echo "https://docs.microsoft.com/en-us/dotnet/core/linux-prerequisites?tabs=netcore2x"
echo "In addition, there are some dependencies which require manual installation. Please follow this documentation"

View File

@@ -124,7 +124,7 @@ function status()
echo
echo "not installed"
echo
return
exit 1
fi
systemctl --no-pager status ${SVC_NAME}

View File

@@ -8,7 +8,7 @@ if [ $user_id -eq 0 -a -z "$RUNNER_ALLOW_RUNASROOT" ]; then
exit 1
fi
# Check dotnet 5 dependencies for Linux
# Check dotnet core 3.0 dependencies for Linux
if [[ (`uname` == "Linux") ]]
then
command -v ldd > /dev/null
@@ -18,25 +18,25 @@ then
exit 1
fi
message="Execute sudo ./bin/installdependencies.sh to install any missing Dotnet 5 dependencies."
message="Execute sudo ./bin/installdependencies.sh to install any missing Dotnet Core 3.0 dependencies."
ldd ./bin/libcoreclr.so | grep 'not found'
if [ $? -eq 0 ]; then
echo "Dependencies is missing for Dotnet 5"
echo "Dependencies is missing for Dotnet Core 3.0"
echo $message
exit 1
fi
ldd ./bin/libSystem.Security.Cryptography.Native.OpenSsl.so | grep 'not found'
ldd ./bin/System.Security.Cryptography.Native.OpenSsl.so | grep 'not found'
if [ $? -eq 0 ]; then
echo "Dependencies is missing for Dotnet 5"
echo "Dependencies is missing for Dotnet Core 3.0"
echo $message
exit 1
fi
ldd ./bin/libSystem.IO.Compression.Native.so | grep 'not found'
ldd ./bin/System.IO.Compression.Native.so | grep 'not found'
if [ $? -eq 0 ]; then
echo "Dependencies is missing for Dotnet 5"
echo "Dependencies is missing for Dotnet Core 3.0"
echo $message
exit 1
fi
@@ -54,7 +54,7 @@ then
libpath=${LD_LIBRARY_PATH:-}
$LDCONFIG_COMMAND -NXv ${libpath//:/ } 2>&1 | grep libicu >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Libicu's dependencies is missing for Dotnet 5"
echo "Libicu's dependencies is missing for Dotnet Core 3.0"
echo $message
exit 1
fi

View File

@@ -26,25 +26,23 @@ if [[ "$1" == "localRun" ]]; then
else
"$DIR"/bin/Runner.Listener run $*
# Return code 4 means the run once runner received an update message.
# Sleep 5 seconds to wait for the update process finish and run the runner again.
# Return code 3 means the run once runner received an update message.
# Sleep 5 seconds to wait for the update process finish
returnCode=$?
if [[ $returnCode == 4 ]]; then
if [[ $returnCode == 3 ]]; then
if [ ! -x "$(command -v sleep)" ]; then
if [ ! -x "$(command -v ping)" ]; then
COUNT="0"
while [[ $COUNT != 5000 ]]; do
echo "SLEEP" >nul
echo "SLEEP" > /dev/null
COUNT=$[$COUNT+1]
done
else
ping -n 5 127.0.0.1 >nul
ping -c 5 127.0.0.1 > /dev/null
fi
else
sleep 5 >nul
sleep 5
fi
"$DIR"/bin/Runner.Listener run $*
else
exit $returnCode
fi

View File

@@ -84,6 +84,7 @@ namespace GitHub.Runner.Common
this.SecretMasker.AddValueEncoder(ValueEncoders.Base64StringEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.Base64StringEscapeShift1);
this.SecretMasker.AddValueEncoder(ValueEncoders.Base64StringEscapeShift2);
this.SecretMasker.AddValueEncoder(ValueEncoders.CommandLineArgumentEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.ExpressionStringEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.JsonStringEscape);
this.SecretMasker.AddValueEncoder(ValueEncoders.UriDataEscape);

View File

@@ -101,7 +101,7 @@ namespace GitHub.Runner.Common
EndPage();
_byteCount = 0;
_dataFileName = Path.Combine(_pagesFolder, $"{_timelineId}_{_timelineRecordId}_{++_pageCount}.log");
_pageData = new FileStream(_dataFileName, FileMode.CreateNew);
_pageData = new FileStream(_dataFileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite);
_pageWriter = new StreamWriter(_pageData, System.Text.Encoding.UTF8);
}

View File

@@ -45,8 +45,8 @@ namespace GitHub.Runner.Common
Task<TaskAgentJobRequest> FinishAgentRequestAsync(int poolId, long requestId, Guid lockToken, DateTime finishTime, TaskResult result, CancellationToken cancellationToken);
// agent package
Task<List<PackageMetadata>> GetPackagesAsync(string packageType, string platform, int top, CancellationToken cancellationToken);
Task<PackageMetadata> GetPackageAsync(string packageType, string platform, string version, CancellationToken cancellationToken);
Task<List<PackageMetadata>> GetPackagesAsync(string packageType, string platform, int top, bool includeToken, CancellationToken cancellationToken);
Task<PackageMetadata> GetPackageAsync(string packageType, string platform, string version, bool includeToken, CancellationToken cancellationToken);
// agent update
Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState);
@@ -317,16 +317,16 @@ namespace GitHub.Runner.Common
//-----------------------------------------------------------------
// Agent Package
//-----------------------------------------------------------------
public Task<List<PackageMetadata>> GetPackagesAsync(string packageType, string platform, int top, CancellationToken cancellationToken)
public Task<List<PackageMetadata>> GetPackagesAsync(string packageType, string platform, int top, bool includeToken, CancellationToken cancellationToken)
{
CheckConnection(RunnerConnectionType.Generic);
return _genericTaskAgentClient.GetPackagesAsync(packageType, platform, top, cancellationToken: cancellationToken);
return _genericTaskAgentClient.GetPackagesAsync(packageType, platform, top, includeToken, cancellationToken: cancellationToken);
}
public Task<PackageMetadata> GetPackageAsync(string packageType, string platform, string version, CancellationToken cancellationToken)
public Task<PackageMetadata> GetPackageAsync(string packageType, string platform, string version, bool includeToken, CancellationToken cancellationToken)
{
CheckConnection(RunnerConnectionType.Generic);
return _genericTaskAgentClient.GetPackageAsync(packageType, platform, version, cancellationToken: cancellationToken);
return _genericTaskAgentClient.GetPackageAsync(packageType, platform, version, includeToken, cancellationToken: cancellationToken);
}
public Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState)

View File

@@ -15,7 +15,7 @@ namespace GitHub.Runner.Listener.Check
public string CheckName => "GitHub Actions Connection";
public string CheckDescription => "Make sure the actions runner have access to the GitHub Actions Service.";
public string CheckDescription => "Check if the Actions runner has access to the GitHub Actions service.";
public string CheckLog => _logFile;
@@ -61,17 +61,20 @@ namespace GitHub.Runner.Listener.Check
// check github api
checkTasks.Add(CheckUtil.CheckDns(githubApiUrl));
checkTasks.Add(CheckUtil.CheckPing(githubApiUrl));
checkTasks.Add(HostContext.CheckHttpsRequests(githubApiUrl, pat, expectedHeader: "X-GitHub-Request-Id"));
checkTasks.Add(HostContext.CheckHttpsGetRequests(githubApiUrl, pat, expectedHeader: "X-GitHub-Request-Id"));
// check actions token service
checkTasks.Add(CheckUtil.CheckDns(actionsTokenServiceUrl));
checkTasks.Add(CheckUtil.CheckPing(actionsTokenServiceUrl));
checkTasks.Add(HostContext.CheckHttpsRequests(actionsTokenServiceUrl, pat, expectedHeader: "x-vss-e2eid"));
checkTasks.Add(HostContext.CheckHttpsGetRequests(actionsTokenServiceUrl, pat, expectedHeader: "x-vss-e2eid"));
// check actions pipelines service
checkTasks.Add(CheckUtil.CheckDns(actionsPipelinesServiceUrl));
checkTasks.Add(CheckUtil.CheckPing(actionsPipelinesServiceUrl));
checkTasks.Add(HostContext.CheckHttpsRequests(actionsPipelinesServiceUrl, pat, expectedHeader: "x-vss-e2eid"));
checkTasks.Add(HostContext.CheckHttpsGetRequests(actionsPipelinesServiceUrl, pat, expectedHeader: "x-vss-e2eid"));
// check HTTP POST to actions pipelines service
checkTasks.Add(HostContext.CheckHttpsPostRequests(actionsPipelinesServiceUrl, pat, expectedHeader: "x-vss-e2eid"));
var result = true;
while (checkTasks.Count > 0)

View File

@@ -117,14 +117,14 @@ namespace GitHub.Runner.Listener.Check
return result;
}
public static async Task<CheckResult> CheckHttpsRequests(this IHostContext hostContext, string url, string pat, string expectedHeader)
public static async Task<CheckResult> CheckHttpsGetRequests(this IHostContext hostContext, string url, string pat, string expectedHeader)
{
var result = new CheckResult();
try
{
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** Send HTTPS Request to {url} ");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** Send HTTPS Request (GET) to {url} ");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
using (var _ = new HttpEventSourceListener(result.Logs))
@@ -159,7 +159,7 @@ namespace GitHub.Runner.Listener.Check
{
result.Pass = false;
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http request 'GET' to {url} succeed but doesn't have expected HTTP Header.");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http request 'GET' to {url} succeed but doesn't have expected HTTP response Header '{expectedHeader}'.");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ");
@@ -189,6 +189,67 @@ namespace GitHub.Runner.Listener.Check
return result;
}
public static async Task<CheckResult> CheckHttpsPostRequests(this IHostContext hostContext, string url, string pat, string expectedHeader)
{
var result = new CheckResult();
try
{
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** Send HTTPS Request (POST) to {url} ");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
using (var _ = new HttpEventSourceListener(result.Logs))
using (var httpClientHandler = hostContext.CreateHttpClientHandler())
using (var httpClient = new HttpClient(httpClientHandler))
{
httpClient.DefaultRequestHeaders.UserAgent.AddRange(hostContext.UserAgents);
if (!string.IsNullOrEmpty(pat))
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("token", pat);
}
// Send empty JSON '{}' to service
var response = await httpClient.PostAsJsonAsync<Dictionary<string, string>>(url, new Dictionary<string, string>());
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http status code: {response.StatusCode}");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http response headers: {response.Headers}");
var responseContent = await response.Content.ReadAsStringAsync();
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http response body: {responseContent}");
if (response.Headers.Contains(expectedHeader))
{
result.Pass = true;
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http request 'POST' to {url} has expected HTTP response header");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ");
}
else
{
result.Pass = false;
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http request 'POST' to {url} doesn't have expected HTTP response Header '{expectedHeader}'.");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ");
}
}
}
catch (Exception ex)
{
result.Pass = false;
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** Https request 'POST' to {url} failed with error: {ex}");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****");
result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************");
}
return result;
}
public static async Task<CheckResult> DownloadExtraCA(this IHostContext hostContext, string url, string pat)
{
var result = new CheckResult();
@@ -289,18 +350,23 @@ namespace GitHub.Runner.Listener.Check
private readonly Dictionary<string, HashSet<string>> _ignoredEvent = new Dictionary<string, HashSet<string>>
{
{
"Private.InternalDiagnostics.System.Net.Http",
"Microsoft-System-Net-Http",
new HashSet<string>
{
"Info",
"Associate"
"Associate",
"Enter",
"Exit"
}
},
{
"Private.InternalDiagnostics.System.Net.Security",
"Microsoft-System-Net-Security",
new HashSet<string>
{
"Enter",
"Exit",
"Info",
"DumpBuffer",
"SslStreamCtor",
"SecureChannelCtor",
"NoDelegateNoClientCert",
@@ -324,8 +390,8 @@ namespace GitHub.Runner.Listener.Check
{
base.OnEventSourceCreated(eventSource);
if (eventSource.Name == "Private.InternalDiagnostics.System.Net.Http" ||
eventSource.Name == "Private.InternalDiagnostics.System.Net.Security")
if (eventSource.Name == "Microsoft-System-Net-Http" ||
eventSource.Name == "Microsoft-System-Net-Security")
{
EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.All);
}

View File

@@ -19,7 +19,7 @@ namespace GitHub.Runner.Listener.Check
public string CheckName => "Git Certificate/Proxy Validation";
public string CheckDescription => "Make sure the git cli can access to GitHub.com or the GitHub Enterprise Server.";
public string CheckDescription => "Check if the Git CLI can access GitHub.com or GitHub Enterprise Server.";
public string CheckLog => _logFile;

View File

@@ -15,7 +15,7 @@ namespace GitHub.Runner.Listener.Check
public string CheckName => "Internet Connection";
public string CheckDescription => "Make sure the actions runner have access to public internet.";
public string CheckDescription => "Check if the Actions runner has internet access.";
public string CheckLog => _logFile;
@@ -40,7 +40,7 @@ namespace GitHub.Runner.Listener.Check
checkTasks.Add(CheckUtil.CheckPing("https://api.github.com"));
// We don't need to pass a PAT since it might be a token for GHES.
checkTasks.Add(HostContext.CheckHttpsRequests("https://api.github.com", pat: null, expectedHeader: "X-GitHub-Request-Id"));
checkTasks.Add(HostContext.CheckHttpsGetRequests("https://api.github.com", pat: null, expectedHeader: "X-GitHub-Request-Id"));
var result = true;
while (checkTasks.Count > 0)

View File

@@ -18,7 +18,7 @@ namespace GitHub.Runner.Listener.Check
public string CheckName => "Node.js Certificate/Proxy Validation";
public string CheckDescription => "Make sure the node.js have access to GitHub.com or the GitHub Enterprise Server.";
public string CheckDescription => "Check if Node.js has access to GitHub.com or GitHub Enterprise Server.";
public string CheckLog => _logFile;

View File

@@ -53,7 +53,7 @@ namespace GitHub.Runner.Listener.Configuration
Trace.Info(nameof(LoadSettings));
if (!IsConfigured())
{
throw new InvalidOperationException("Not configured");
throw new InvalidOperationException("Not configured. Run config.(sh/cmd) to configure the runner.");
}
RunnerSettings settings = _store.GetSettings();

View File

@@ -240,6 +240,15 @@ namespace GitHub.Runner.Listener
{
request = await runnerServer.GetAgentRequestAsync(_poolId, jobDispatch.RequestId, CancellationToken.None);
}
catch (TaskAgentJobNotFoundException ex)
{
Trace.Error($"Catch job-not-found exception while checking jobrequest {jobDispatch.JobId} status. Cancel running worker right away.");
Trace.Error(ex);
jobDispatch.WorkerCancellationTokenSource.Cancel();
// make sure worker process exit before we return, otherwise we might leave orphan worker process behind.
await jobDispatch.WorkerDispatch;
return;
}
catch (Exception ex)
{
// we can't even query for the jobrequest from server, something totally busted, stop runner/worker.

View File

@@ -501,6 +501,7 @@ Options:
--help Prints the help for each command
--version Prints the runner version
--commit Prints the runner commit
--check Check the runner's network connectivity with GitHub server
Config Options:
--unattended Disable interactive prompts for missing arguments. Defaults will be used for missing options
@@ -510,7 +511,8 @@ Config Options:
--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)");
--replace Replace any existing runner with the same name (default false)
--pat GitHub personal access token used for checking network connectivity when executing `.{separator}run.{ext} --check`");
#if OS_WINDOWS
_term.WriteLine($@" --runasservice Run the runner as a service");
_term.WriteLine($@" --windowslogonaccount string Account to run the service as. Requires runasservice");
@@ -518,6 +520,8 @@ Config Options:
#endif
_term.WriteLine($@"
Examples:
Check GitHub server network connectivity:
.{separator}run.{ext} --check --url <url> --pat <pat>
Configure a runner non-interactively:
.{separator}config.{ext} --unattended --url <url> --token <token>
Configure a runner non-interactively, replacing any existing runner with the same name:

View File

@@ -8,7 +8,9 @@ using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Security.Cryptography;
using GitHub.Services.WebApi;
using GitHub.Services.Common;
using GitHub.Runner.Common;
using GitHub.Runner.Sdk;
@@ -110,7 +112,7 @@ namespace GitHub.Runner.Listener
// old server won't send target version as part of update message.
if (string.IsNullOrEmpty(targetVersion))
{
var packages = await _runnerServer.GetPackagesAsync(_packageType, _platform, 1, token);
var packages = await _runnerServer.GetPackagesAsync(_packageType, _platform, 1, true, token);
if (packages == null || packages.Count == 0)
{
Trace.Info($"There is no package for {_packageType} and {_platform}.");
@@ -121,7 +123,7 @@ namespace GitHub.Runner.Listener
}
else
{
_targetPackage = await _runnerServer.GetPackageAsync(_packageType, _platform, targetVersion, token);
_targetPackage = await _runnerServer.GetPackageAsync(_packageType, _platform, targetVersion, true, token);
if (_targetPackage == null)
{
Trace.Info($"There is no package for {_packageType} and {_platform} with version {targetVersion}.");
@@ -211,12 +213,22 @@ namespace GitHub.Runner.Listener
//open zip stream in async mode
using (HttpClient httpClient = new HttpClient(HostContext.CreateHttpClientHandler()))
using (FileStream fs = new FileStream(archiveFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
using (Stream result = await httpClient.GetStreamAsync(_targetPackage.DownloadUrl))
{
//81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k).
await result.CopyToAsync(fs, 81920, downloadCts.Token);
await fs.FlushAsync(downloadCts.Token);
if (!string.IsNullOrEmpty(_targetPackage.Token))
{
Trace.Info($"Adding authorization token ({_targetPackage.Token.Length} chars)");
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _targetPackage.Token);
}
Trace.Info($"Downloading {_targetPackage.DownloadUrl}");
using (FileStream fs = new FileStream(archiveFile, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true))
using (Stream result = await httpClient.GetStreamAsync(_targetPackage.DownloadUrl))
{
//81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k).
await result.CopyToAsync(fs, 81920, downloadCts.Token);
await fs.FlushAsync(downloadCts.Token);
}
}
Trace.Info($"Download runner: finished download");
@@ -246,6 +258,24 @@ namespace GitHub.Runner.Listener
}
// If we got this far, we know that we've successfully downloaded the runner package
// Validate Hash Matches if it is provided
using (FileStream stream = File.OpenRead(archiveFile))
{
if (!String.IsNullOrEmpty(_targetPackage.HashValue))
{
using (SHA256 sha256 = SHA256.Create())
{
byte[] srcHashBytes = await sha256.ComputeHashAsync(stream);
var hash = PrimitiveExtensions.ConvertToHexString(srcHashBytes);
if (hash != _targetPackage.HashValue)
{
// Hash did not match, we can't recover from this, just throw
throw new Exception($"Computed runner hash {hash} did not match expected Runner Hash {_targetPackage.HashValue} for {_targetPackage.Filename}");
}
Trace.Info($"Validated Runner Hash matches {_targetPackage.Filename} : {_targetPackage.HashValue}");
}
}
}
if (archiveFile.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
{
ZipFile.ExtractToDirectory(archiveFile, latestRunnerDirectory);
@@ -327,8 +357,13 @@ namespace GitHub.Runner.Listener
Trace.Info($"Copy any remaining .sh/.cmd files into runner root.");
foreach (FileInfo file in new DirectoryInfo(latestRunnerDirectory).GetFiles() ?? new FileInfo[0])
{
// Copy and replace the file.
file.CopyTo(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), file.Name), true);
string destination = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), file.Name);
// Removing the file instead of just trying to overwrite it works around permissions issues on linux.
// https://github.com/actions/runner/issues/981
Trace.Info($"Copy {file.FullName} to {destination}");
IOUtil.DeleteFile(destination);
file.CopyTo(destination, true);
}
}

View File

@@ -0,0 +1,19 @@
namespace GitHub.Runner.Sdk
{
/***
* WARNING: This file is automatically regenerated on layout so the runner can provide version/commit info (do not manually edit it).
*/
public static class BuildConstants
{
public static class Source
{
public static readonly string CommitHash = "N/A";
}
public static class RunnerPackage
{
public static readonly string PackageName = "N/A";
public static readonly string Version = "0";
}
}
}

View File

@@ -471,12 +471,12 @@ namespace GitHub.Runner.Worker
executionContext.Output($"##[group]Pull down action image '{setupInfo.Container.Image}'");
// Pull down docker image with retry up to 3 times
var dockerManger = HostContext.GetService<IDockerCommandManager>();
var dockerManager = HostContext.GetService<IDockerCommandManager>();
int retryCount = 0;
int pullExitCode = 0;
while (retryCount < 3)
{
pullExitCode = await dockerManger.DockerPull(executionContext, setupInfo.Container.Image);
pullExitCode = await dockerManager.DockerPull(executionContext, setupInfo.Container.Image);
if (pullExitCode == 0)
{
break;
@@ -515,13 +515,13 @@ namespace GitHub.Runner.Worker
executionContext.Output($"##[group]Build container for action use: '{setupInfo.Container.Dockerfile}'.");
// Build docker image with retry up to 3 times
var dockerManger = HostContext.GetService<IDockerCommandManager>();
var dockerManager = HostContext.GetService<IDockerCommandManager>();
int retryCount = 0;
int buildExitCode = 0;
var imageName = $"{dockerManger.DockerInstanceLabel}:{Guid.NewGuid().ToString("N")}";
var imageName = $"{dockerManager.DockerInstanceLabel}:{Guid.NewGuid().ToString("N")}";
while (retryCount < 3)
{
buildExitCode = await dockerManger.DockerBuild(
buildExitCode = await dockerManager.DockerBuild(
executionContext,
setupInfo.Container.WorkingDirectory,
setupInfo.Container.Dockerfile,

View File

@@ -311,7 +311,7 @@ namespace GitHub.Runner.Worker
var result = new TemplateContext
{
CancellationToken = CancellationToken.None,
Errors = new TemplateValidationErrors(10, 500),
Errors = new TemplateValidationErrors(10, int.MaxValue), // Don't truncate error messages otherwise we might not scrub secrets correctly
Memory = new TemplateMemory(
maxDepth: 100,
maxEvents: 1000000,

View File

@@ -24,12 +24,12 @@ namespace GitHub.Runner.Worker
public class ContainerOperationProvider : RunnerService, IContainerOperationProvider
{
private IDockerCommandManager _dockerManger;
private IDockerCommandManager _dockerManager;
public override void Initialize(IHostContext hostContext)
{
base.Initialize(hostContext);
_dockerManger = HostContext.GetService<IDockerCommandManager>();
_dockerManager = HostContext.GetService<IDockerCommandManager>();
}
public async Task StartContainersAsync(IExecutionContext executionContext, object data)
@@ -92,7 +92,7 @@ namespace GitHub.Runner.Worker
// Check docker client/server version
executionContext.Output("##[group]Checking docker version");
DockerVersion dockerVersion = await _dockerManger.DockerVersion(executionContext);
DockerVersion dockerVersion = await _dockerManager.DockerVersion(executionContext);
executionContext.Output("##[endgroup]");
ArgUtil.NotNull(dockerVersion.ServerVersion, nameof(dockerVersion.ServerVersion));
@@ -106,26 +106,26 @@ namespace GitHub.Runner.Worker
if (dockerVersion.ServerVersion < requiredDockerEngineAPIVersion)
{
throw new NotSupportedException($"Min required docker engine API server version is '{requiredDockerEngineAPIVersion}', your docker ('{_dockerManger.DockerPath}') server version is '{dockerVersion.ServerVersion}'");
throw new NotSupportedException($"Min required docker engine API server version is '{requiredDockerEngineAPIVersion}', your docker ('{_dockerManager.DockerPath}') server version is '{dockerVersion.ServerVersion}'");
}
if (dockerVersion.ClientVersion < requiredDockerEngineAPIVersion)
{
throw new NotSupportedException($"Min required docker engine API client version is '{requiredDockerEngineAPIVersion}', your docker ('{_dockerManger.DockerPath}') client version is '{dockerVersion.ClientVersion}'");
throw new NotSupportedException($"Min required docker engine API client version is '{requiredDockerEngineAPIVersion}', your docker ('{_dockerManager.DockerPath}') client version is '{dockerVersion.ClientVersion}'");
}
// Clean up containers left by previous runs
executionContext.Output("##[group]Clean up resources from previous jobs");
var staleContainers = await _dockerManger.DockerPS(executionContext, $"--all --quiet --no-trunc --filter \"label={_dockerManger.DockerInstanceLabel}\"");
var staleContainers = await _dockerManager.DockerPS(executionContext, $"--all --quiet --no-trunc --filter \"label={_dockerManager.DockerInstanceLabel}\"");
foreach (var staleContainer in staleContainers)
{
int containerRemoveExitCode = await _dockerManger.DockerRemove(executionContext, staleContainer);
int containerRemoveExitCode = await _dockerManager.DockerRemove(executionContext, staleContainer);
if (containerRemoveExitCode != 0)
{
executionContext.Warning($"Delete stale containers failed, docker rm fail with exit code {containerRemoveExitCode} for container {staleContainer}");
}
}
int networkPruneExitCode = await _dockerManger.DockerNetworkPrune(executionContext);
int networkPruneExitCode = await _dockerManager.DockerNetworkPrune(executionContext);
if (networkPruneExitCode != 0)
{
executionContext.Warning($"Delete stale container networks failed, docker network prune fail with exit code {networkPruneExitCode}");
@@ -198,8 +198,7 @@ namespace GitHub.Runner.Worker
}
}
// TODO: Add at a later date. This currently no local package registry to test with
// UpdateRegistryAuthForGitHubToken(executionContext, container);
UpdateRegistryAuthForGitHubToken(executionContext, container);
// Before pulling, generate client authentication if required
var configLocation = await ContainerRegistryLogin(executionContext, container);
@@ -209,7 +208,7 @@ namespace GitHub.Runner.Worker
int pullExitCode = 0;
while (retryCount < 3)
{
pullExitCode = await _dockerManger.DockerPull(executionContext, container.ContainerImage, configLocation);
pullExitCode = await _dockerManager.DockerPull(executionContext, container.ContainerImage, configLocation);
if (pullExitCode == 0)
{
break;
@@ -267,11 +266,11 @@ namespace GitHub.Runner.Worker
container.ContainerEntryPointArgs = "\"-f\" \"/dev/null\"";
}
container.ContainerId = await _dockerManger.DockerCreate(executionContext, container);
container.ContainerId = await _dockerManager.DockerCreate(executionContext, container);
ArgUtil.NotNullOrEmpty(container.ContainerId, nameof(container.ContainerId));
// Start container
int startExitCode = await _dockerManger.DockerStart(executionContext, container.ContainerId);
int startExitCode = await _dockerManager.DockerStart(executionContext, container.ContainerId);
if (startExitCode != 0)
{
throw new InvalidOperationException($"Docker start fail with exit code {startExitCode}");
@@ -280,12 +279,12 @@ namespace GitHub.Runner.Worker
try
{
// Make sure container is up and running
var psOutputs = await _dockerManger.DockerPS(executionContext, $"--all --filter id={container.ContainerId} --filter status=running --no-trunc --format \"{{{{.ID}}}} {{{{.Status}}}}\"");
var psOutputs = await _dockerManager.DockerPS(executionContext, $"--all --filter id={container.ContainerId} --filter status=running --no-trunc --format \"{{{{.ID}}}} {{{{.Status}}}}\"");
if (psOutputs.FirstOrDefault(x => !string.IsNullOrEmpty(x))?.StartsWith(container.ContainerId) != true)
{
// container is not up and running, pull docker log for this container.
await _dockerManger.DockerPS(executionContext, $"--all --filter id={container.ContainerId} --no-trunc --format \"{{{{.ID}}}} {{{{.Status}}}}\"");
int logsExitCode = await _dockerManger.DockerLogs(executionContext, container.ContainerId);
await _dockerManager.DockerPS(executionContext, $"--all --filter id={container.ContainerId} --no-trunc --format \"{{{{.ID}}}} {{{{.Status}}}}\"");
int logsExitCode = await _dockerManager.DockerLogs(executionContext, container.ContainerId);
if (logsExitCode != 0)
{
executionContext.Warning($"Docker logs fail with exit code {logsExitCode}");
@@ -310,7 +309,7 @@ namespace GitHub.Runner.Worker
["ports"] = new DictionaryContextData(),
["network"] = new StringContextData(container.ContainerNetwork)
};
container.AddPortMappings(await _dockerManger.DockerPort(executionContext, container.ContainerId));
container.AddPortMappings(await _dockerManager.DockerPort(executionContext, container.ContainerId));
foreach (var port in container.PortMappings)
{
(service["ports"] as DictionaryContextData)[port.ContainerPort] = new StringContextData(port.HostPort);
@@ -320,7 +319,7 @@ namespace GitHub.Runner.Worker
else
{
var configEnvFormat = "--format \"{{range .Config.Env}}{{println .}}{{end}}\"";
var containerEnv = await _dockerManger.DockerInspect(executionContext, container.ContainerId, configEnvFormat);
var containerEnv = await _dockerManager.DockerInspect(executionContext, container.ContainerId, configEnvFormat);
container.ContainerRuntimePath = DockerUtil.ParsePathFromConfigEnv(containerEnv);
executionContext.JobContext.Container["id"] = new StringContextData(container.ContainerId);
}
@@ -337,7 +336,7 @@ namespace GitHub.Runner.Worker
{
executionContext.Output($"Stop and remove container: {container.ContainerDisplayName}");
int rmExitCode = await _dockerManger.DockerRemove(executionContext, container.ContainerId);
int rmExitCode = await _dockerManager.DockerRemove(executionContext, container.ContainerId);
if (rmExitCode != 0)
{
executionContext.Warning($"Docker rm fail with exit code {rmExitCode}");
@@ -397,7 +396,7 @@ namespace GitHub.Runner.Worker
{
Trace.Entering();
ArgUtil.NotNull(executionContext, nameof(executionContext));
int networkExitCode = await _dockerManger.DockerNetworkCreate(executionContext, network);
int networkExitCode = await _dockerManager.DockerNetworkCreate(executionContext, network);
if (networkExitCode != 0)
{
throw new InvalidOperationException($"Docker network create failed with exit code {networkExitCode}");
@@ -412,7 +411,7 @@ namespace GitHub.Runner.Worker
executionContext.Output($"Remove container network: {network}");
int removeExitCode = await _dockerManger.DockerNetworkRemove(executionContext, network);
int removeExitCode = await _dockerManager.DockerNetworkRemove(executionContext, network);
if (removeExitCode != 0)
{
executionContext.Warning($"Docker network rm failed with exit code {removeExitCode}");
@@ -422,7 +421,7 @@ namespace GitHub.Runner.Worker
private async Task ContainerHealthcheck(IExecutionContext executionContext, ContainerInfo container)
{
string healthCheck = "--format=\"{{if .Config.Healthcheck}}{{print .State.Health.Status}}{{end}}\"";
string serviceHealth = (await _dockerManger.DockerInspect(context: executionContext, dockerObject: container.ContainerId, options: healthCheck)).FirstOrDefault();
string serviceHealth = (await _dockerManager.DockerInspect(context: executionContext, dockerObject: container.ContainerId, options: healthCheck)).FirstOrDefault();
if (string.IsNullOrEmpty(serviceHealth))
{
// Container has no HEALTHCHECK
@@ -434,7 +433,7 @@ namespace GitHub.Runner.Worker
TimeSpan backoff = BackoffTimerHelper.GetExponentialBackoff(retryCount, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(32), TimeSpan.FromSeconds(2));
executionContext.Output($"{container.ContainerNetworkAlias} service is starting, waiting {backoff.Seconds} seconds before checking again.");
await Task.Delay(backoff, executionContext.CancellationToken);
serviceHealth = (await _dockerManger.DockerInspect(context: executionContext, dockerObject: container.ContainerId, options: healthCheck)).FirstOrDefault();
serviceHealth = (await _dockerManager.DockerInspect(context: executionContext, dockerObject: container.ContainerId, options: healthCheck)).FirstOrDefault();
retryCount++;
}
if (string.Equals(serviceHealth, "healthy", StringComparison.OrdinalIgnoreCase))
@@ -463,7 +462,7 @@ namespace GitHub.Runner.Worker
{
throw new InvalidOperationException($"Failed to create directory to store registry client credentials: {e.Message}");
}
var loginExitCode = await _dockerManger.DockerLogin(
var loginExitCode = await _dockerManager.DockerLogin(
executionContext,
configLocation,
container.RegistryServer,
@@ -494,31 +493,14 @@ namespace GitHub.Runner.Worker
private void UpdateRegistryAuthForGitHubToken(IExecutionContext executionContext, ContainerInfo container)
{
var registryIsTokenCompatible = container.RegistryServer.Equals("docker.pkg.github.com", StringComparison.OrdinalIgnoreCase);
var registryIsTokenCompatible = container.RegistryServer.Equals("ghcr.io", StringComparison.OrdinalIgnoreCase) || container.RegistryServer.Equals("containers.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)
if (registryCredentialsNotSupplied)
{
container.RegistryAuthUsername = executionContext.GetGitHubContext("actor");
container.RegistryAuthPassword = executionContext.GetGitHubContext("token");

View File

@@ -61,14 +61,15 @@ namespace GitHub.Runner.Worker
bool EchoOnActionCommand { get; set; }
bool InsideComposite { get; }
bool IsEmbedded { get; }
ExecutionContext Root { get; }
// Initialize
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
void CancelToken();
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool insideComposite = false, CancellationTokenSource cancellationTokenSource = null);
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, CancellationTokenSource cancellationTokenSource = null);
IExecutionContext CreateEmbeddedChild(string scopeName, string contextName);
// logging
long Write(string tag, string message);
@@ -99,7 +100,6 @@ namespace GitHub.Runner.Worker
// others
void ForceTaskComplete();
void RegisterPostJobStep(IStep step);
IStep CreateCompositeStep(string scopeName, IActionRunner step, DictionaryContextData inputsData, Dictionary<string, string> envData);
}
public sealed class ExecutionContext : RunnerService, IExecutionContext
@@ -157,7 +157,9 @@ namespace GitHub.Runner.Worker
public bool EchoOnActionCommand { get; set; }
public bool InsideComposite { get; private set; }
// An embedded execution context shares the same record ID, record name, and logger
// as its enclosing execution context.
public bool IsEmbedded { get; private set; }
public TaskResult? Result
{
@@ -253,36 +255,7 @@ namespace GitHub.Runner.Worker
Root.PostJobSteps.Push(step);
}
/// <summary>
/// Helper function used in CompositeActionHandler::RunAsync to
/// add a child node, aka a step, to the current job to the Root.JobSteps based on the location.
/// </summary>
public IStep CreateCompositeStep(
string scopeName,
IActionRunner step,
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.ExpressionValues["inputs"] = inputsData;
step.ExecutionContext.ExpressionValues["steps"] = Global.StepsContext.GetScope(step.ExecutionContext.GetFullyQualifiedContextName());
// Add the composite action environment variables to each step.
#if OS_WINDOWS
var envContext = new DictionaryContextData();
#else
var envContext = new CaseSensitiveDictionaryContextData();
#endif
foreach (var pair in envData)
{
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
}
step.ExecutionContext.ExpressionValues["env"] = envContext;
return step;
}
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool insideComposite = false, CancellationTokenSource cancellationTokenSource = null)
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, CancellationTokenSource cancellationTokenSource = null)
{
Trace.Entering();
@@ -329,11 +302,20 @@ namespace GitHub.Runner.Worker
child._logger.Setup(_mainTimelineId, recordId);
}
child.InsideComposite = insideComposite;
child.IsEmbedded = isEmbedded;
return child;
}
/// <summary>
/// An embedded execution context shares the same record ID, record name, logger,
/// and a linked cancellation token.
/// </summary>
public IExecutionContext CreateEmbeddedChild(string scopeName, string contextName)
{
return Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, contextName, logger: _logger, isEmbedded: true, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token));
}
public void Start(string currentOperation = null)
{
_record.CurrentOperation = currentOperation ?? _record.CurrentOperation;
@@ -858,6 +840,10 @@ namespace GitHub.Runner.Worker
{
_record.ParentId = parentTimelineRecordId;
}
else if (parentTimelineRecordId == null)
{
_record.AgentPlatform = VarUtil.OS;
}
var configuration = HostContext.GetService<IConfigurationStore>();
_record.WorkerName = configuration.GetSettings().AgentName;
@@ -975,7 +961,10 @@ namespace GitHub.Runner.Worker
traceWriter = context.ToTemplateTraceWriter();
}
var schema = PipelineTemplateSchemaFactory.GetSchema();
return new PipelineTemplateEvaluator(traceWriter, schema, context.Global.FileTable);
return new PipelineTemplateEvaluator(traceWriter, schema, context.Global.FileTable)
{
MaxErrorMessageLength = int.MaxValue, // Don't truncate error messages otherwise we might not scrub secrets correctly
};
}
public static ObjectTemplating.ITraceWriter ToTemplateTraceWriter(this IExecutionContext context)

View File

@@ -26,65 +26,60 @@ namespace GitHub.Runner.Worker.Handlers
public async Task RunAsync(ActionRunStage stage)
{
// Validate args.
// Validate args
Trace.Entering();
ArgUtil.NotNull(ExecutionContext, nameof(ExecutionContext));
ArgUtil.NotNull(Inputs, nameof(Inputs));
ArgUtil.NotNull(Data.Steps, nameof(Data.Steps));
// Resolve action steps
var actionSteps = Data.Steps;
// Create Context Data to reuse for each composite action step
var inputsData = new DictionaryContextData();
foreach (var i in Inputs)
{
inputsData[i.Key] = new StringContextData(i.Value);
}
// Initialize Composite Steps List of Steps
var compositeSteps = new List<IStep>();
// Temporary hack until after M271-ish. After M271-ish the server will never send an empty
// context name. Generated context names start with "__"
var childScopeName = ExecutionContext.GetFullyQualifiedContextName();
if (string.IsNullOrEmpty(childScopeName))
{
childScopeName = $"__{Guid.NewGuid()}";
}
foreach (Pipelines.ActionStep actionStep in actionSteps)
{
var actionRunner = HostContext.CreateService<IActionRunner>();
actionRunner.Action = actionStep;
actionRunner.Stage = stage;
actionRunner.Condition = actionStep.Condition;
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.SetGitHubContext("action_path", ActionDirectory);
compositeSteps.Add(step);
}
try
{
// This is where we run each step.
await RunStepsAsync(compositeSteps);
// Inputs of the composite step
var inputsData = new DictionaryContextData();
foreach (var i in Inputs)
{
inputsData[i.Key] = new StringContextData(i.Value);
}
// Get the pointer of the correct "steps" object and pass it to the ExecutionContext so that we can process the outputs correctly
// Temporary hack until after M271-ish. After M271-ish the server will never send an empty
// context name. Generated context names start with "__"
var childScopeName = ExecutionContext.GetFullyQualifiedContextName();
if (string.IsNullOrEmpty(childScopeName))
{
childScopeName = $"__{Guid.NewGuid()}";
}
// Create embedded steps
var embeddedSteps = new List<IStep>();
foreach (Pipelines.ActionStep stepData in Data.Steps)
{
var step = HostContext.CreateService<IActionRunner>();
step.Action = stepData;
step.Stage = stage;
step.Condition = stepData.Condition;
step.ExecutionContext = ExecutionContext.CreateEmbeddedChild(childScopeName, stepData.ContextName);
step.ExecutionContext.ExpressionValues["inputs"] = inputsData;
step.ExecutionContext.ExpressionValues["steps"] = ExecutionContext.Global.StepsContext.GetScope(childScopeName);
// 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.SetGitHubContext("action_path", ActionDirectory);
embeddedSteps.Add(step);
}
// Run embedded steps
await RunStepsAsync(embeddedSteps);
// Set outputs
ExecutionContext.ExpressionValues["inputs"] = inputsData;
ExecutionContext.ExpressionValues["steps"] = ExecutionContext.Global.StepsContext.GetScope(ExecutionContext.GetFullyQualifiedContextName());
ProcessCompositeActionOutputs();
ExecutionContext.ExpressionValues["steps"] = ExecutionContext.Global.StepsContext.GetScope(childScopeName);
ProcessOutputs();
ExecutionContext.Global.StepsContext.ClearScope(childScopeName);
}
catch (Exception ex)
@@ -96,7 +91,7 @@ namespace GitHub.Runner.Worker.Handlers
}
}
private void ProcessCompositeActionOutputs()
private void ProcessOutputs()
{
ArgUtil.NotNull(ExecutionContext, nameof(ExecutionContext));
@@ -113,69 +108,57 @@ namespace GitHub.Runner.Worker.Handlers
evaluateContext[pair.Key] = pair.Value;
}
// Get the evluated composite outputs' values mapped to the outputs named
// Evaluate outputs
DictionaryContextData actionOutputs = actionManifestManager.EvaluateCompositeOutputs(ExecutionContext, Data.Outputs, evaluateContext);
// Set the outputs for the outputs object in the whole composite action
// Each pair is structured like this
// We ignore "description" for now
// {
// "the-output-name": {
// "description": "",
// "value": "the value"
// },
// ...
// }
// Set outputs
//
// Each pair is structured like:
// {
// "the-output-name": {
// "description": "",
// "value": "the value"
// },
// ...
// }
foreach (var pair in actionOutputs)
{
var outputsName = pair.Key;
var outputsAttributes = pair.Value as DictionaryContextData;
outputsAttributes.TryGetValue("value", out var val);
if (val != null)
var outputName = pair.Key;
var outputDefinition = pair.Value as DictionaryContextData;
if (outputDefinition.TryGetValue("value", out var val))
{
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 _);
}
var outputValue = val.AssertString("output value");
ExecutionContext.SetOutput(outputName, outputValue.Value, out _);
}
}
}
}
private async Task RunStepsAsync(List<IStep> compositeSteps)
private async Task RunStepsAsync(List<IStep> embeddedSteps)
{
ArgUtil.NotNull(compositeSteps, nameof(compositeSteps));
ArgUtil.NotNull(embeddedSteps, nameof(embeddedSteps));
// The parent StepsRunner of the whole Composite Action Step handles the cancellation stuff already.
foreach (IStep step in compositeSteps)
foreach (IStep step in embeddedSteps)
{
Trace.Info($"Processing composite step: DisplayName='{step.DisplayName}'");
Trace.Info($"Processing embedded step: DisplayName='{step.DisplayName}'");
step.ExecutionContext.ExpressionValues["steps"] = ExecutionContext.Global.StepsContext.GetScope(step.ExecutionContext.ScopeName);
// Populate env context for each step
Trace.Info("Initialize Env context for step");
// Initialize env context
Trace.Info("Initialize Env context for embedded step");
#if OS_WINDOWS
var envContext = new DictionaryContextData();
#else
var envContext = new CaseSensitiveDictionaryContextData();
#endif
step.ExecutionContext.ExpressionValues["env"] = envContext;
// Global env
// Merge global env
foreach (var pair in ExecutionContext.Global.EnvironmentVariables)
{
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
}
// Stomps over with outside step env
if (step.ExecutionContext.ExpressionValues.TryGetValue("env", out var envContextData))
// Merge composite-step env
if (ExecutionContext.ExpressionValues.TryGetValue("env", out var envContextData))
{
#if OS_WINDOWS
var dict = envContextData as DictionaryContextData;
@@ -188,13 +171,11 @@ namespace GitHub.Runner.Worker.Handlers
}
}
step.ExecutionContext.ExpressionValues["env"] = envContext;
var actionStep = step as IActionRunner;
try
{
// Evaluate and merge action's env block to env context
// Evaluate and merge embedded-step env
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, Common.Util.VarUtil.EnvironmentVariableKeyComparer);
foreach (var env in actionEnvironment)
@@ -204,39 +185,28 @@ namespace GitHub.Runner.Worker.Handlers
}
catch (Exception ex)
{
// fail the step since there is an evaluate error.
Trace.Info("Caught exception in Composite Steps Runner from expression for step.env");
// evaluateStepEnvFailed = true;
// Evaluation error
Trace.Info("Caught exception from expression for embedded step.env");
step.ExecutionContext.Error(ex);
step.ExecutionContext.Complete(TaskResult.Failed);
}
await RunStepAsync(step);
// Directly after the step, check if the step has failed or cancelled
// If so, return that to the output
// Check failed or canceled
if (step.ExecutionContext.Result == TaskResult.Failed || step.ExecutionContext.Result == TaskResult.Canceled)
{
ExecutionContext.Result = step.ExecutionContext.Result;
break;
}
// TODO: Add compat for other types of steps.
}
// Completion Status handled by StepsRunner for the whole Composite Action Step
}
private async Task RunStepAsync(IStep step)
{
// Start the step.
Trace.Info("Starting the step.");
Trace.Info($"Starting: {step.DisplayName}");
step.ExecutionContext.Debug($"Starting: {step.DisplayName}");
// TODO: Fix for Step Level Timeout Attributes for an individual Composite Run Step
// For now, we are not going to support this for an individual composite run step
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
await Common.Util.EncodingUtil.SetEncoding(HostContext, Trace, step.ExecutionContext.CancellationToken);
try
@@ -261,7 +231,7 @@ namespace GitHub.Runner.Worker.Handlers
}
catch (Exception ex)
{
// Log the error and fail the step.
// Log the error and fail the step
Trace.Error($"Caught exception from step: {ex}");
step.ExecutionContext.Error(ex);
step.ExecutionContext.Result = TaskResult.Failed;
@@ -274,9 +244,7 @@ namespace GitHub.Runner.Worker.Handlers
}
Trace.Info($"Step result: {step.ExecutionContext.Result}");
// Complete the step context.
step.ExecutionContext.Debug($"Finishing: {step.DisplayName}");
step.ExecutionContext.Debug($"Finished: {step.DisplayName}");
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System;
@@ -37,7 +37,7 @@ namespace GitHub.Runner.Worker.Handlers
// Update the env dictionary.
AddInputsToEnvironment();
var dockerManger = HostContext.GetService<IDockerCommandManager>();
var dockerManager = HostContext.GetService<IDockerCommandManager>();
// container image haven't built/pull
if (Data.Image.StartsWith("docker://", StringComparison.OrdinalIgnoreCase))
@@ -52,8 +52,8 @@ namespace GitHub.Runner.Worker.Handlers
ExecutionContext.Output($"##[group]Building docker image");
ExecutionContext.Output($"Dockerfile for action: '{dockerFile}'.");
var imageName = $"{dockerManger.DockerInstanceLabel}:{ExecutionContext.Id.ToString("N")}";
var buildExitCode = await dockerManger.DockerBuild(
var imageName = $"{dockerManager.DockerInstanceLabel}:{ExecutionContext.Id.ToString("N")}";
var buildExitCode = await dockerManager.DockerBuild(
ExecutionContext,
ExecutionContext.GetGitHubContext("workspace"),
dockerFile,
@@ -209,7 +209,7 @@ namespace GitHub.Runner.Worker.Handlers
using (var stdoutManager = new OutputManager(ExecutionContext, ActionCommandManager, container))
using (var stderrManager = new OutputManager(ExecutionContext, ActionCommandManager, container))
{
var runExitCode = await dockerManger.DockerRun(ExecutionContext, container, stdoutManager.OnDataReceived, stderrManager.OnDataReceived);
var runExitCode = await dockerManager.DockerRun(ExecutionContext, container, stdoutManager.OnDataReceived, stderrManager.OnDataReceived);
ExecutionContext.Debug($"Docker Action run completed with exit code {runExitCode}");
if (runExitCode != 0)
{

View File

@@ -97,6 +97,9 @@ namespace GitHub.Runner.Worker.Handlers
Encoding outputEncoding = null;
#endif
// Remove environment variable that may cause conflicts with the node within the runner.
Environment.Remove("NODE_ICU_DATA"); // https://github.com/actions/runner/issues/795
using (var stdoutManager = new OutputManager(ExecutionContext, ActionCommandManager))
using (var stderrManager = new OutputManager(ExecutionContext, ActionCommandManager))
{

View File

@@ -26,7 +26,7 @@ namespace GitHub.Runner.Worker.Handlers
// We don't want to display the internal workings if composite (similar/equivalent information can be found in debug)
void writeDetails(string message)
{
if (ExecutionContext.InsideComposite)
if (ExecutionContext.IsEmbedded)
{
ExecutionContext.Debug(message);
}
@@ -52,7 +52,7 @@ namespace GitHub.Runner.Worker.Handlers
firstLine = firstLine.Substring(0, firstNewLine);
}
writeDetails(ExecutionContext.InsideComposite ? $"Run {firstLine}" : $"##[group]Run {firstLine}");
writeDetails(ExecutionContext.IsEmbedded ? $"Run {firstLine}" : $"##[group]Run {firstLine}");
}
else
{
@@ -138,7 +138,7 @@ namespace GitHub.Runner.Worker.Handlers
}
}
writeDetails(ExecutionContext.InsideComposite ? "" : "##[endgroup]");
writeDetails(ExecutionContext.IsEmbedded ? "" : "##[endgroup]");
}
public async Task RunAsync(ActionRunStage stage)

View File

@@ -122,6 +122,26 @@ namespace GitHub.Runner.Worker
}
}
try
{
var tokenPermissions = jobContext.Global.Variables.Get("system.github.token.permissions") ?? "";
if (!string.IsNullOrEmpty(tokenPermissions))
{
context.Output($"##[group]GITHUB_TOKEN Permissions");
var permissions = StringUtil.ConvertFromJson<Dictionary<string, string>>(tokenPermissions);
foreach(KeyValuePair<string, string> entry in permissions)
{
context.Output($"{entry.Key}: {entry.Value}");
}
context.Output("##[endgroup]");
}
}
catch (Exception ex)
{
context.Output($"Fail to parse and display GITHUB_TOKEN permissions list: {ex.Message}");
Trace.Error(ex);
}
var repoFullName = context.GetGitHubContext("repository");
ArgUtil.NotNull(repoFullName, nameof(repoFullName));
context.Debug($"Primary repository: {repoFullName}");

View File

@@ -1,9 +1,9 @@
using GitHub.Runner.Common.Util;
using System;
using System;
using System.Globalization;
using System.Threading.Tasks;
using GitHub.Runner.Common;
using GitHub.Runner.Sdk;
using System.Diagnostics;
namespace GitHub.Runner.Worker
{
@@ -19,11 +19,16 @@ namespace GitHub.Runner.Worker
public static async Task<int> MainAsync(IHostContext context, string[] args)
{
Tracing trace = context.GetTrace(nameof(GitHub.Runner.Worker));
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_ATTACH_DEBUGGER")))
{
await WaitForDebugger(trace);
}
// We may want to consider registering this handler in Worker.cs, similiar to the unloading/SIGTERM handler
//ITerminal registers a CTRL-C handler, which keeps the Runner.Worker process running
//and lets the Runner.Listener handle gracefully the exit.
var term = context.GetService<ITerminal>();
Tracing trace = context.GetTrace(nameof(GitHub.Runner.Worker));
try
{
trace.Info($"Version: {BuildConstants.RunnerPackage.Version}");
@@ -64,5 +69,25 @@ namespace GitHub.Runner.Worker
return 1;
}
/// <summary>
/// Runner.Worker is started by Runner.Listener in a separate process,
/// so the two can't be debugged in the same session.
/// This method halts the Runner.Worker process until a debugger is attached,
/// allowing a developer to debug Runner.Worker from start to finish.
/// </summary>
private static async Task WaitForDebugger(Tracing trace)
{
trace.Info($"Waiting for a debugger to be attached. Edit the 'GITHUB_ACTIONS_RUNNER_ATTACH_DEBUGGER' environment variable to toggle this feature.");
int waitInSeconds = 20;
while (!Debugger.IsAttached && waitInSeconds-- > 0)
{
trace.Info($"Waiting for a debugger to be attached. {waitInSeconds} seconds left.");
await Task.Delay(1000);
}
Debugger.Break();
}
}
}

View File

@@ -10,11 +10,19 @@ using GitHub.Runner.Sdk;
namespace GitHub.Runner.Worker
{
/// <summary>
/// Manages the "steps" context. The "steps" context is used to track individual steps
/// "outcome", "conclusion", and "outputs".
/// </summary>
public sealed class StepsContext
{
private static readonly Regex _propertyRegex = new Regex("^[a-zA-Z_][a-zA-Z0-9_]*$", RegexOptions.Compiled);
private readonly DictionaryContextData _contextData = new DictionaryContextData();
/// <summary>
/// Clears memory for a composite action's isolated "steps" context, after the action
/// is finished executing.
/// </summary>
public void ClearScope(string scopeName)
{
if (_contextData.TryGetValue(scopeName, out _))
@@ -23,6 +31,14 @@ namespace GitHub.Runner.Worker
}
}
/// <summary>
/// Gets the "steps" context for a given scope. The root steps in a workflow use the
/// default "steps" context (i.e. scopeName="").
///
/// An isolated "steps" context is created for each composite action. All child steps
/// within a composite action, share an isolated "steps" context. The scope name matches
/// the composite action's fully qualified context name.
/// </summary>
public DictionaryContextData GetScope(string scopeName)
{
if (scopeName == null)

View File

@@ -82,24 +82,21 @@ namespace GitHub.Runner.Worker
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<SuccessFunction>(PipelineTemplateConstants.Success, 0, 0));
step.ExecutionContext.ExpressionFunctions.Add(new FunctionInfo<HashFilesFunction>(PipelineTemplateConstants.HashFiles, 1, byte.MaxValue));
// Expression values
step.ExecutionContext.ExpressionValues["steps"] = step.ExecutionContext.Global.StepsContext.GetScope(step.ExecutionContext.ScopeName);
// Populate env context for each step
Trace.Info("Initialize Env context for step");
#if OS_WINDOWS
var envContext = new DictionaryContextData();
#else
var envContext = new CaseSensitiveDictionaryContextData();
#endif
step.ExecutionContext.ExpressionValues["env"] = envContext;
// Global env
// Merge global env
foreach (var pair in step.ExecutionContext.Global.EnvironmentVariables)
{
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
}
step.ExecutionContext.ExpressionValues["env"] = envContext;
bool evaluateStepEnvFailed = false;
if (step is IActionRunner actionStep)
{
@@ -108,7 +105,7 @@ namespace GitHub.Runner.Worker
try
{
// Evaluate and merge action's env block to env context
// Evaluate and merge step env
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator();
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, step.ExecutionContext.ExpressionFunctions, VarUtil.EnvironmentVariableKeyComparer);
foreach (var env in actionEnvironment)
@@ -118,7 +115,7 @@ namespace GitHub.Runner.Worker
}
catch (Exception ex)
{
// fail the step since there is an evaluate error.
// Fail the step since there is an evaluate error
Trace.Info("Caught exception from expression for step.env");
evaluateStepEnvFailed = true;
step.ExecutionContext.Error(ex);
@@ -136,7 +133,7 @@ namespace GitHub.Runner.Worker
// Test the condition again. The job was canceled after the condition was originally evaluated.
jobCancelRegister = jobContext.CancellationToken.Register(() =>
{
// mark job as cancelled
// Mark job as cancelled
jobContext.Result = TaskResult.Canceled;
jobContext.JobContext.Status = jobContext.Result?.ToActionResult();
@@ -157,7 +154,7 @@ namespace GitHub.Runner.Worker
}
catch (Exception ex)
{
// Cancel the step since we get exception while re-evaluate step condition.
// Cancel the step since we get exception while re-evaluate step condition
Trace.Info("Caught exception from expression when re-test condition on job cancellation.");
step.ExecutionContext.Error(ex);
}
@@ -165,7 +162,7 @@ namespace GitHub.Runner.Worker
if (!conditionReTestResult)
{
// Cancel the step.
// Cancel the step
Trace.Info("Cancel current running step.");
step.ExecutionContext.CancelToken();
}
@@ -175,13 +172,13 @@ namespace GitHub.Runner.Worker
{
if (jobContext.Result != TaskResult.Canceled)
{
// mark job as cancelled
// Mark job as cancelled
jobContext.Result = TaskResult.Canceled;
jobContext.JobContext.Status = jobContext.Result?.ToActionResult();
}
}
// Evaluate condition.
// Evaluate condition
step.ExecutionContext.Debug($"Evaluating condition for step: '{step.DisplayName}'");
var conditionTraceWriter = new ConditionTraceWriter(Trace, step.ExecutionContext);
var conditionResult = false;
@@ -206,22 +203,21 @@ namespace GitHub.Runner.Worker
}
}
// no evaluate error but condition is false
if (!conditionResult && conditionEvaluateError == null)
{
// Condition == false
// Condition is false
Trace.Info("Skipping step due to condition evaluation.");
CompleteStep(step, TaskResult.Skipped, resultCode: conditionTraceWriter.Trace);
}
else if (conditionEvaluateError != null)
{
// fail the step since there is an evaluate error.
// Condition error
step.ExecutionContext.Error(conditionEvaluateError);
CompleteStep(step, TaskResult.Failed);
}
else
{
// Run the step.
// Run the step
await RunStepAsync(step, jobContext.CancellationToken);
CompleteStep(step);
}
@@ -236,7 +232,7 @@ namespace GitHub.Runner.Worker
}
}
// Update the job result.
// Update the job result
if (step.ExecutionContext.Result == TaskResult.Failed)
{
Trace.Info($"Update job result with current step result '{step.ExecutionContext.Result}'.");
@@ -262,7 +258,7 @@ namespace GitHub.Runner.Worker
step.ExecutionContext.UpdateTimelineRecordDisplayName(actionRunner.DisplayName);
}
// Start the step.
// Start the step
Trace.Info("Starting the step.");
step.ExecutionContext.Debug($"Starting: {step.DisplayName}");
@@ -303,7 +299,7 @@ namespace GitHub.Runner.Worker
}
else
{
// Log the exception and cancel the step.
// Log the exception and cancel the step
Trace.Error($"Caught cancellation exception from step: {ex}");
step.ExecutionContext.Error(ex);
step.ExecutionContext.Result = TaskResult.Canceled;
@@ -311,7 +307,7 @@ namespace GitHub.Runner.Worker
}
catch (Exception ex)
{
// Log the error and fail the step.
// Log the error and fail the step
Trace.Error($"Caught exception from step: {ex}");
step.ExecutionContext.Error(ex);
step.ExecutionContext.Result = TaskResult.Failed;
@@ -323,7 +319,7 @@ namespace GitHub.Runner.Worker
step.ExecutionContext.Result = TaskResultUtil.MergeTaskResults(step.ExecutionContext.Result, step.ExecutionContext.CommandResult.Value);
}
// Fixup the step result if ContinueOnError.
// Fixup the step result if ContinueOnError
if (step.ExecutionContext.Result == TaskResult.Failed)
{
var continueOnError = false;
@@ -348,7 +344,7 @@ namespace GitHub.Runner.Worker
}
Trace.Info($"Step result: {step.ExecutionContext.Result}");
// Complete the step context.
// Complete the step context
step.ExecutionContext.Debug($"Finishing: {step.DisplayName}");
}

View File

@@ -63,6 +63,7 @@ namespace GitHub.Runner.Worker
Trace.Info("Message received.");
ArgUtil.Equal(MessageType.NewJobRequest, channelMessage.MessageType, nameof(channelMessage.MessageType));
ArgUtil.NotNullOrEmpty(channelMessage.Body, nameof(channelMessage.Body));
Trace.Info(channelMessage.Body);
var jobMessage = StringUtil.ConvertFromJson<Pipelines.AgentJobRequestMessage>(channelMessage.Body);
ArgUtil.NotNull(jobMessage, nameof(jobMessage));
HostContext.WritePerfCounter($"WorkerJobMessageReceived_{jobMessage.RequestId.ToString()}");

View File

@@ -32,10 +32,10 @@
"outputs": {
"mapping": {
"loose-key-type": "non-empty-string",
"loose-value-type": "outputs-attributes"
"loose-value-type": "output-definition"
}
},
"outputs-attributes": {
"output-definition": {
"mapping": {
"properties": {
"description": "string",

View File

@@ -0,0 +1,27 @@
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace GitHub.Services.Common
{
public static class HashAlgorithmExtensions
{
public static async Task<byte[]> ComputeHashAsync(this HashAlgorithm hashAlg, Stream inputStream)
{
byte[] buffer = new byte[4096];
while (true)
{
int read = await inputStream.ReadAsync(buffer, 0, buffer.Length);
if (read == 0)
break;
hashAlg.TransformBlock(buffer, 0, read, null, 0);
}
hashAlg.TransformFinalBlock(buffer, 0, 0);
return hashAlg.Hash;
}
}
}

View File

@@ -85,5 +85,19 @@ namespace GitHub.Services.Common
var bytes = FromBase64StringNoPadding(base64String);
return BitConverter.ToString(bytes).Replace("-", String.Empty);
}
/// <summary>
/// Converts byte array into a hex string
/// </summary>
public static String ConvertToHexString(byte[] bytes)
{
// Convert byte array to string
var sBuilder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
sBuilder.Append(bytes[i].ToString("x2"));
}
return sBuilder.ToString();
}
}
}

View File

@@ -587,6 +587,7 @@ namespace GitHub.DistributedTask.WebApi
/// <param name="packageType"></param>
/// <param name="platform"></param>
/// <param name="version"></param>
/// <param name="includeToken"></param>
/// <param name="userState"></param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
@@ -594,6 +595,7 @@ namespace GitHub.DistributedTask.WebApi
string packageType,
string platform,
string version,
bool? includeToken = null,
object userState = null,
CancellationToken cancellationToken = default)
{
@@ -601,11 +603,18 @@ namespace GitHub.DistributedTask.WebApi
Guid locationId = new Guid("8ffcd551-079c-493a-9c02-54346299d144");
object routeValues = new { packageType = packageType, platform = platform, version = version };
List<KeyValuePair<string, string>> queryParams = new List<KeyValuePair<string, string>>();
if (includeToken != null)
{
queryParams.Add("includeToken", includeToken.Value.ToString());
}
return SendAsync<PackageMetadata>(
httpMethod,
locationId,
routeValues: routeValues,
version: new ApiResourceVersion(5.1, 2),
queryParameters: queryParams,
userState: userState,
cancellationToken: cancellationToken);
}
@@ -616,6 +625,7 @@ namespace GitHub.DistributedTask.WebApi
/// <param name="packageType"></param>
/// <param name="platform"></param>
/// <param name="top"></param>
/// <param name="includeToken"></param>
/// <param name="userState"></param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
@@ -623,6 +633,7 @@ namespace GitHub.DistributedTask.WebApi
string packageType,
string platform = null,
int? top = null,
bool? includeToken = null,
object userState = null,
CancellationToken cancellationToken = default)
{
@@ -635,6 +646,10 @@ namespace GitHub.DistributedTask.WebApi
{
queryParams.Add("$top", top.Value.ToString(CultureInfo.InvariantCulture));
}
if (includeToken != null)
{
queryParams.Add("includeToken", includeToken.Value.ToString());
}
return SendAsync<List<PackageMetadata>>(
httpMethod,

View File

@@ -37,6 +37,12 @@ namespace GitHub.DistributedTask.Logging
return Base64StringEscapeShift(value, 2);
}
// Used when we pass environment variables to docker to escape " with \"
public static String CommandLineArgumentEscape(String value)
{
return value.Replace("\"", "\\\"");
}
public static String ExpressionStringEscape(String value)
{
return Expressions2.Sdk.ExpressionUtility.StringEscape(value);

View File

@@ -40,7 +40,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
/// <summary>
/// Gets the maximum error message length before the message will be truncated.
/// </summary>
public Int32 MaxErrorMessageLength => 500;
public Int32 MaxErrorMessageLength { get; set; } = 500;
/// <summary>
/// Gets the maximum number of errors that can be recorded when parsing a pipeline.

View File

@@ -59,6 +59,16 @@ namespace GitHub.DistributedTask.WebApi
set;
}
/// <summary>
/// Auth token to download the package
/// </summary>
[DataMember]
public String Token
{
get;
set;
}
/// <summary>
/// MD5 hash as a base64 string
/// </summary>

View File

@@ -38,6 +38,7 @@ namespace GitHub.DistributedTask.WebApi
this.RefName = recordToBeCloned.RefName;
this.ErrorCount = recordToBeCloned.ErrorCount;
this.WarningCount = recordToBeCloned.WarningCount;
this.AgentPlatform = recordToBeCloned.AgentPlatform;
if (recordToBeCloned.Log != null)
{
@@ -254,6 +255,13 @@ namespace GitHub.DistributedTask.WebApi
set;
}
[DataMember(Order = 132, EmitDefaultValue = false)]
public string AgentPlatform
{
get;
set;
}
public IList<TimelineAttempt> PreviousAttempts
{
get

View File

@@ -2,6 +2,7 @@
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System;
namespace GitHub.Runner.Common.Tests
{
@@ -12,6 +13,12 @@ namespace GitHub.Runner.Common.Tests
[Trait("Category", "Runner")]
public async Task EnsureDotnetsdkBashDownloadScriptUpToDate()
{
if ((DateTime.UtcNow.Month - 1) % 3 != 0)
{
// Only check these script once a quater.
return;
}
string shDownloadUrl = "https://dot.net/v1/dotnet-install.sh";
using (HttpClient downloadClient = new HttpClient())
@@ -36,6 +43,12 @@ namespace GitHub.Runner.Common.Tests
[Trait("Category", "Runner")]
public async Task EnsureDotnetsdkPowershellDownloadScriptUpToDate()
{
if ((DateTime.UtcNow.Month - 1) % 3 != 0)
{
// Only check these script once a quater.
return;
}
string ps1DownloadUrl = "https://dot.net/v1/dotnet-install.ps1";
using (HttpClient downloadClient = new HttpClient())

View File

@@ -25,7 +25,9 @@
<BuildConstants Include="}"/>
</ItemGroup>
<WriteLinesToFile File="Runner.Sdk/BuildConstants.cs" Lines="@(BuildConstants)" Overwrite="true" Encoding="Unicode"/>
<WriteLinesToFile File="Runner.Sdk/BuildConstants.cs" Lines="@(BuildConstants)" Overwrite="true" />
<Exec Command="git update-index --assume-unchanged ./Runner.Sdk/BuildConstants.cs" ConsoleToMSBuild="true" />
</Target>
<ItemGroup>

View File

@@ -1 +1 @@
2.276.1
2.278.0