mirror of
https://github.com/actions/runner.git
synced 2025-12-10 20:36:49 +00:00
Compare commits
11 Commits
fhammerl/p
...
users/jww3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3e42889af | ||
|
|
f3961c4895 | ||
|
|
cb89be7aac | ||
|
|
b70f97f183 | ||
|
|
14096a7ee4 | ||
|
|
4eb9adc958 | ||
|
|
efc0a92cc7 | ||
|
|
87ababb858 | ||
|
|
3902257e9d | ||
|
|
7036627d47 | ||
|
|
2eeb90a944 |
@@ -5,10 +5,7 @@
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/docker-in-docker:1": {},
|
||||
"ghcr.io/devcontainers/features/dotnet": {
|
||||
"version": "6.0.405"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "16"
|
||||
"version": "6.0.300"
|
||||
}
|
||||
},
|
||||
"customizations": {
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 🛑 Request a feature in the runner application
|
||||
url: https://github.com/orgs/community/discussions/categories/actions-and-packages
|
||||
about: If you have feature requests for GitHub Actions, please use the Actions and Packages section on the Github Product Feedback page.
|
||||
- name: ✅ Support for GitHub Actions
|
||||
url: https://github.community/c/code-to-cloud/52
|
||||
about: If you have questions about GitHub Actions or need support writing workflows, please ask in the GitHub Community Support forum.
|
||||
|
||||
32
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: 🛑 Request a feature in the runner application
|
||||
about: If you have feature requests for GitHub Actions, please use the "feedback and suggestions for GitHub Actions" link below.
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
👋 You're opening a request for an enhancement in the GitHub Actions **runner application**.
|
||||
|
||||
🛑 Please stop if you're not certain that the feature you want is in the runner application - if you have a suggestion for improving GitHub Actions, please see the [GitHub Actions Feedback](https://github.com/github/feedback/discussions/categories/actions-and-packages-feedback) discussion forum which is actively monitored. Using the forum ensures that we route your problem to the correct team. 😃
|
||||
|
||||
Some additional useful links:
|
||||
* If you have found a security issue [please submit it here](https://hackerone.com/github)
|
||||
* If you have questions or issues with the service, writing workflows or actions, then please [visit the GitHub Community Forum's Actions Board](https://github.community/t5/GitHub-Actions/bd-p/actions)
|
||||
* If you are having an issue or have a question about GitHub Actions then please [contact customer support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-github-actions#contacting-support)
|
||||
|
||||
If you have a feature request that is relevant to this repository, the runner, then please include the information below:
|
||||
-->
|
||||
|
||||
**Describe the enhancement**
|
||||
A clear and concise description of what the features or enhancement you need.
|
||||
|
||||
**Code Snippet**
|
||||
If applicable, add a code snippet.
|
||||
|
||||
**Additional information**
|
||||
Add any other context about the feature here.
|
||||
|
||||
NOTE: if the feature request has been agreed upon then the assignee will create an ADR. See docs/adrs/README.md
|
||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
@@ -38,4 +38,4 @@ jobs:
|
||||
working-directory: src
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
1
.github/workflows/lint.yml
vendored
1
.github/workflows/lint.yml
vendored
@@ -18,6 +18,7 @@ jobs:
|
||||
uses: github/super-linter@v4
|
||||
env:
|
||||
DEFAULT_BRANCH: ${{ github.base_ref }}
|
||||
DISABLE_ERRORS: true
|
||||
EDITORCONFIG_FILE_NAME: .editorconfig
|
||||
LINTER_RULES_PATH: /src/
|
||||
VALIDATE_ALL_CODEBASE: false
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# ADR 000: Update Proxy Behavior of Self-Hosted Runners
|
||||
|
||||
**Date**: 2023-02-21
|
||||
|
||||
**Status**: Pending
|
||||
|
||||
## Context
|
||||
|
||||
Today, the different user-accessible building blocks of GitHub Actions implement proxy behaviour with significant functional differences.
|
||||
Users could realistically run all of the below examples and they would reasonably expect that their proxy settings will have the same networking effects across all of them.
|
||||
|
||||
A user running `actions/actions-runner-controller`, which starts instances of `actions/runner`, that the runs node.js actions made with `actions/toolkit`
|
||||
- ARC and its controller and listener pods in k8s will follow `golang` defaults for proxy behaviour
|
||||
- The `runner` overrides the default proxy behaviour of C# and implements it [explicitly](https://github.com/actions/runner/blob/main/src/Runner.Sdk/RunnerWebProxy.cs), however currently differently from `toolkit`
|
||||
- `toolkit` overrides the default proxy behaviour of node.js and implements it [explicitly](https://github.com/actions/toolkit/blob/main/packages/http-client/src/proxy.ts), however currently differently from `runner`
|
||||
|
||||
|
||||
## Example 1 - ARC
|
||||
|
||||
A user wants to create a scaleset in ARC. They give following settings when creating an ARC Scale Set:
|
||||
- `https_proxy=https://someproxy.company.com`
|
||||
- `no_proxy=8.8.8.8,192.168.1.1/32`
|
||||
The ENV variables are propagated through all actors, but:
|
||||
|
||||
- *ARC operators and listener pods* will: follow the proxy but bypass it for `8.8.8.8` and the CIDR block `192.168.1.1/32`
|
||||
- *The runner* will: use a proxy and ignore these `no_proxy` settings (no IP support in `runner` for `no_proxy`)
|
||||
- *A node.js GitHub Action in a job executed by the runner* will: bypass `8.8.8.8` but use proxy for the CIDR block `192.168.1.1/32`
|
||||
|
||||
## Example 2 - Self-Hosted runner
|
||||
|
||||
Given the following settings when creating an ARC Scale Set:
|
||||
- `https_proxy=someproxy.company.com`
|
||||
|
||||
- *The runner* will: silently ignore `https_proxy` value because it doesn't have a protocol (missing `https://`)
|
||||
- *A node.js GitHub Action in a job executed by the runner* will: throw an exception because proxy could not be parsed (missing `https://`)
|
||||
|
||||
## Decisions
|
||||
## Consequences
|
||||
@@ -158,11 +158,3 @@ cat (Runner/Worker)_TIMESTAMP.log # view your log file
|
||||
|
||||
We use the .NET Foundation and CoreCLR style guidelines [located here](
|
||||
https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md)
|
||||
|
||||
### Format C# Code
|
||||
|
||||
To format both staged and unstaged .cs files
|
||||
```
|
||||
cd ./src
|
||||
./dev.(cmd|sh) format
|
||||
```
|
||||
@@ -35,7 +35,7 @@ All the configs below can be found in `.vscode/launch.json`.
|
||||
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 different process, you can't use the same debugger session debug it.
|
||||
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.
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@ FROM mcr.microsoft.com/dotnet/runtime-deps:6.0 as build
|
||||
|
||||
ARG RUNNER_VERSION
|
||||
ARG RUNNER_ARCH="x64"
|
||||
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.2.0
|
||||
ARG DOCKER_VERSION=20.10.23
|
||||
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.1.3
|
||||
|
||||
RUN apt update -y && apt install curl unzip -y
|
||||
|
||||
@@ -16,19 +15,10 @@ RUN curl -f -L -o runner-container-hooks.zip https://github.com/actions/runner-c
|
||||
&& unzip ./runner-container-hooks.zip -d ./k8s \
|
||||
&& rm runner-container-hooks.zip
|
||||
|
||||
RUN export DOCKER_ARCH=x86_64 \
|
||||
&& if [ "$RUNNER_ARCH" = "arm64" ]; then export DOCKER_ARCH=aarch64 ; fi \
|
||||
&& curl -fLo docker.tgz https://download.docker.com/linux/static/stable/${DOCKER_ARCH}/docker-${DOCKER_VERSION}.tgz \
|
||||
&& tar zxvf docker.tgz \
|
||||
&& rm -rf docker.tgz
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0
|
||||
|
||||
ENV RUNNER_ALLOW_RUNASROOT=1
|
||||
ENV RUNNER_MANUALLY_TRAP_SIG=1
|
||||
ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
|
||||
|
||||
WORKDIR /actions-runner
|
||||
COPY --from=build /actions-runner .
|
||||
|
||||
RUN install -o root -g root -m 755 docker/* /usr/bin/ && rm -rf docker
|
||||
@@ -1,19 +1,18 @@
|
||||
## Features
|
||||
- Add support for ghe.com domain (#2420)
|
||||
- Add docker cli to the runner image. (#2425)
|
||||
- Displays the error logs in dedicated sub-sections of the Initialize containers section (#2182)
|
||||
- Add generateServiceConfig option for configure command (#2226)
|
||||
- Setting debug using GitHub Action variables (#2234)
|
||||
- run.sh installs SIGINT and SIGTERM traps to gracefully stop runner (#2233, #2240)
|
||||
|
||||
|
||||
## Bugs
|
||||
- Fix URL construction bug for RunService (#2396)
|
||||
- Defer evaluation of a step's DisplayName until its condition is evaluated. (#2313)
|
||||
- Replace '(' and ')' with '[' and '] from OS.Description for fixing User-Agent header validation (#2288)
|
||||
- Use Global.Variables instead of JobContext and include action path/ref in the message. (#2214)
|
||||
|
||||
## Misc
|
||||
- Bump dotnet sdk to latest version. (#2392)
|
||||
- Start calling run service for job completion (#2412, #2423)
|
||||
|
||||
_Note: Actions Runner follows a progressive release policy, so the latest release might not be available to your enterprise, organization, or repository yet.
|
||||
To confirm which version of the Actions Runner you should expect, please view the download instructions for your enterprise, organization, or repository.
|
||||
See https://docs.github.com/en/enterprise-cloud@latest/actions/hosting-your-own-runners/adding-self-hosted-runners_
|
||||
- Allow '--disableupdate' in create-latest-svc.sh (#2201)
|
||||
- Fix markup for support link (#2114)
|
||||
- Add runner devcontainer (#2187)
|
||||
- Setup linter for Runner (#2211, #2213, #2216)
|
||||
|
||||
## Windows x64
|
||||
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
|
||||
|
||||
@@ -1 +1 @@
|
||||
39f2a931565d6a10e695ac8ed14bb9dcbb568151410349b32dbf9c27bae29602
|
||||
1d709d93e5d3c6c6c656a61aa6c1781050224788a05b0e6ecc4c3c0408bdf89c
|
||||
@@ -1 +1 @@
|
||||
29ffb303537d8ba674fbebc7729292c21c4ebd17b3198f91ed593ef4cbbb67b5
|
||||
b92a47cfeaad02255b1f7a377060651b73ae5e5db22a188dbbcb4183ab03a03d
|
||||
@@ -1 +1 @@
|
||||
de6868a836fa3cb9e5ddddbc079da1c25e819aa2d2fc193cc9931c353687c57c
|
||||
68a9a8ef0843a8bb74241894f6f63fd76241a82295c5337d3cc7a940a314c78e
|
||||
@@ -1 +1 @@
|
||||
339d3e1a5fd28450c0fe6cb820cc7aae291f0f9e2d153ac34e1f7b080e35d30e
|
||||
02c7126ff4d63ee2a0ae390c81434c125630522aadf35903bbeebb1a99d8af99
|
||||
@@ -1 +1 @@
|
||||
dcb7f606c1d7d290381e5020ee73e7f16dcbd2f20ac9b431362ccbb5120d449c
|
||||
c9d5a542f8d765168855a89e83ae0a8970d00869041c4f9a766651c04c72b212
|
||||
@@ -1 +1 @@
|
||||
1bbcb0e9a2cf4be4b1fce77458de139b70ac58efcbb415a6db028b9373ae1673
|
||||
39d0683f0f115a211cb10c473e9574c16549a19d4e9a6c637ded3d7022bf809f
|
||||
|
||||
@@ -1 +1 @@
|
||||
44cd25f3c104d0abb44d262397a80e0b2c4f206465c5d899a22eec043dac0fb3
|
||||
d94f2fbaf210297162bc9f3add819d73682c3aa6899e321c3872412b924d5504
|
||||
2
src/Misc/contentHash/externals/linux-arm
vendored
2
src/Misc/contentHash/externals/linux-arm
vendored
@@ -1 +1 @@
|
||||
3807dcbf947e840c33535fb466b096d76bf09e5c0254af8fc8cbbb24c6388222
|
||||
6ed30a2c1ee403a610d63e82bb230b9ba846a9c25cec9e4ea8672fb6ed4e1a51
|
||||
2
src/Misc/contentHash/externals/linux-arm64
vendored
2
src/Misc/contentHash/externals/linux-arm64
vendored
@@ -1 +1 @@
|
||||
ee01eee80cd8a460a4b9780ee13fdd20f25c59e754b4ccd99df55fbba2a85634
|
||||
711c30c51ec52c9b7a9a2eb399d6ab2ab5ee1dc72de11879f2f36f919f163d78
|
||||
2
src/Misc/contentHash/externals/linux-x64
vendored
2
src/Misc/contentHash/externals/linux-x64
vendored
@@ -1 +1 @@
|
||||
a9fb9c14e24e79aec97d4da197dd7bfc6364297d6fce573afb2df48cc9a931f8
|
||||
a49479ca4b4988a06c097e8d22c51fd08a11c13f40807366236213d0e008cf6a
|
||||
2
src/Misc/contentHash/externals/osx-arm64
vendored
2
src/Misc/contentHash/externals/osx-arm64
vendored
@@ -1 +1 @@
|
||||
a4e0e8fc62eba0967a39c7d693dcd0aeb8b2bed0765f9c38df80d42884f65341
|
||||
cc4708962a80325de0baa5ae8484e0cb9ae976ac6a4178c1c0d448b8c52bd7f7
|
||||
2
src/Misc/contentHash/externals/osx-x64
vendored
2
src/Misc/contentHash/externals/osx-x64
vendored
@@ -1 +1 @@
|
||||
17ac17fbe785b3d6fa2868d8d17185ebfe0c90b4b0ddf6b67eac70e42bcd989b
|
||||
8e97df75230b843462a9b4c578ccec604ee4b4a1066120c85b04374317fa372b
|
||||
2
src/Misc/contentHash/externals/win-arm64
vendored
2
src/Misc/contentHash/externals/win-arm64
vendored
@@ -1 +1 @@
|
||||
89f24657a550f1e818b0e9975e5b80edcf4dd22b7d4bccbb9e48e37f45d30fb1
|
||||
e5dace2d41cc0682d096dcce4970079ad48ec7107e46195970eecfdb3df2acef
|
||||
|
||||
2
src/Misc/contentHash/externals/win-x64
vendored
2
src/Misc/contentHash/externals/win-x64
vendored
@@ -1 +1 @@
|
||||
24fd131b5dce33ef16038b771407bc0507da8682a72fb3b7780607235f76db0b
|
||||
f75a671e5a188c76680739689aa75331a2c09d483dce9c80023518c48fd67a18
|
||||
43
src/Misc/expressionFunc/hashFiles/package-lock.json
generated
43
src/Misc/expressionFunc/hashFiles/package-lock.json
generated
@@ -14,7 +14,7 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.7.12",
|
||||
"@typescript-eslint/parser": "^5.15.0",
|
||||
"@vercel/ncc": "^0.36.0",
|
||||
"@zeit/ncc": "^0.20.5",
|
||||
"eslint": "^8.11.0",
|
||||
"eslint-plugin-github": "^4.3.5",
|
||||
"prettier": "^1.19.1",
|
||||
@@ -346,10 +346,11 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/ncc": {
|
||||
"version": "0.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.36.0.tgz",
|
||||
"integrity": "sha512-/ZTUJ/ZkRt694k7KJNimgmHjtQcRuVwsST2Z6XfYveQIuBbHR+EqkTc1jfgPkQmMyk/vtpxo3nVxe8CNuau86A==",
|
||||
"node_modules/@zeit/ncc": {
|
||||
"version": "0.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.20.5.tgz",
|
||||
"integrity": "sha512-XU6uzwvv95DqxciQx+aOLhbyBx/13ky+RK1y88Age9Du3BlA4mMPCy13BGjayOrrumOzlq1XV3SD/BWiZENXlw==",
|
||||
"deprecated": "@zeit/ncc is no longer maintained. Please use @vercel/ncc instead.",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"ncc": "dist/ncc/cli.js"
|
||||
@@ -1721,9 +1722,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.0"
|
||||
@@ -1823,9 +1824,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
@@ -2746,10 +2747,10 @@
|
||||
"eslint-visitor-keys": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@vercel/ncc": {
|
||||
"version": "0.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.36.0.tgz",
|
||||
"integrity": "sha512-/ZTUJ/ZkRt694k7KJNimgmHjtQcRuVwsST2Z6XfYveQIuBbHR+EqkTc1jfgPkQmMyk/vtpxo3nVxe8CNuau86A==",
|
||||
"@zeit/ncc": {
|
||||
"version": "0.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@zeit/ncc/-/ncc-0.20.5.tgz",
|
||||
"integrity": "sha512-XU6uzwvv95DqxciQx+aOLhbyBx/13ky+RK1y88Age9Du3BlA4mMPCy13BGjayOrrumOzlq1XV3SD/BWiZENXlw==",
|
||||
"dev": true
|
||||
},
|
||||
"acorn": {
|
||||
@@ -3755,9 +3756,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
@@ -3839,9 +3840,9 @@
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.7.12",
|
||||
"@typescript-eslint/parser": "^5.15.0",
|
||||
"@vercel/ncc": "^0.36.0",
|
||||
"@zeit/ncc": "^0.20.5",
|
||||
"eslint": "^8.11.0",
|
||||
"eslint-plugin-github": "^4.3.5",
|
||||
"prettier": "^1.19.1",
|
||||
|
||||
@@ -5,7 +5,7 @@ PRECACHE=$2
|
||||
NODE_URL=https://nodejs.org/dist
|
||||
UNOFFICIAL_NODE_URL=https://unofficial-builds.nodejs.org/download/release
|
||||
NODE12_VERSION="12.22.7"
|
||||
NODE16_VERSION="16.16.0"
|
||||
NODE16_VERSION="16.13.0"
|
||||
|
||||
get_abs_path() {
|
||||
# exploits the fact that pwd will print abs path when no args
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,20 +18,6 @@ while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symli
|
||||
done
|
||||
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||
|
||||
# Wait for docker to start
|
||||
if [ ! -z "$RUNNER_WAIT_FOR_DOCKER_IN_SECONDS" ]; then
|
||||
if [ "$RUNNER_WAIT_FOR_DOCKER_IN_SECONDS" -gt 0 ]; then
|
||||
echo "Waiting for docker to be ready."
|
||||
for i in $(seq "$RUNNER_WAIT_FOR_DOCKER_IN_SECONDS"); do
|
||||
if docker ps > /dev/null 2>&1; then
|
||||
echo "Docker is ready."
|
||||
break
|
||||
fi
|
||||
"$DIR"/safe_sleep.sh 1
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
updateFile="update.finished"
|
||||
"$DIR"/bin/Runner.Listener run $*
|
||||
|
||||
|
||||
@@ -74,7 +74,6 @@ Microsoft.Win32.Registry.dll
|
||||
mscordaccore.dll
|
||||
mscordaccore_amd64_amd64_6.0.522.21309.dll
|
||||
mscordaccore_arm64_arm64_6.0.522.21309.dll
|
||||
mscordaccore_amd64_amd64_6.0.1322.58009.dll
|
||||
mscordbi.dll
|
||||
mscorlib.dll
|
||||
mscorrc.debug.dll
|
||||
|
||||
@@ -90,6 +90,7 @@ namespace GitHub.Runner.Common
|
||||
public static class Args
|
||||
{
|
||||
public static readonly string Auth = "auth";
|
||||
public static readonly string JitConfig = "jitconfig";
|
||||
public static readonly string Labels = "labels";
|
||||
public static readonly string MonitorSocketAddress = "monitorsocketaddress";
|
||||
public static readonly string Name = "name";
|
||||
@@ -104,13 +105,11 @@ namespace GitHub.Runner.Common
|
||||
public static readonly string Token = "token";
|
||||
public static readonly string PAT = "pat";
|
||||
public static readonly string WindowsLogonPassword = "windowslogonpassword";
|
||||
public static readonly string JitConfig = "jitconfig";
|
||||
public static string[] Secrets => new[]
|
||||
{
|
||||
PAT,
|
||||
Token,
|
||||
WindowsLogonPassword,
|
||||
JitConfig,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -131,7 +130,6 @@ namespace GitHub.Runner.Common
|
||||
public static readonly string Ephemeral = "ephemeral";
|
||||
public static readonly string GenerateServiceConfig = "generateServiceConfig";
|
||||
public static readonly string Help = "help";
|
||||
public static readonly string Local = "local";
|
||||
public static readonly string Replace = "replace";
|
||||
public static readonly string DisableUpdate = "disableupdate";
|
||||
public static readonly string Once = "once"; // Keep this around since customers still relies on it
|
||||
@@ -159,16 +157,13 @@ namespace GitHub.Runner.Common
|
||||
}
|
||||
|
||||
public static readonly string InternalTelemetryIssueDataKey = "_internal_telemetry";
|
||||
public static readonly Guid TelemetryRecordId = new Guid("11111111-1111-1111-1111-111111111111");
|
||||
public static readonly string WorkerCrash = "WORKER_CRASH";
|
||||
public static readonly string LowDiskSpace = "LOW_DISK_SPACE";
|
||||
public static readonly string UnsupportedCommand = "UNSUPPORTED_COMMAND";
|
||||
public static readonly string ResultsUploadFailure = "RESULTS_UPLOAD_FAILURE";
|
||||
public static readonly string UnsupportedCommandMessage = "The `{0}` command is deprecated and will be disabled soon. Please upgrade to using Environment Files. For more information see: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/";
|
||||
public static readonly string UnsupportedCommandMessageDisabled = "The `{0}` command is disabled. Please upgrade to using Environment Files or opt into unsecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_COMMANDS` environment variable to `true`. For more information see: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/";
|
||||
public static readonly string UnsupportedStopCommandTokenDisabled = "You cannot use a endToken that is an empty string, the string 'pause-logging', or another workflow command. For more information see: https://docs.github.com/actions/learn-github-actions/workflow-commands-for-github-actions#example-stopping-and-starting-workflow-commands or opt into insecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_STOPCOMMAND_TOKENS` environment variable to `true`.";
|
||||
public static readonly string UnsupportedSummarySize = "$GITHUB_STEP_SUMMARY upload aborted, supports content up to a size of {0}k, got {1}k. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";
|
||||
public static readonly string SummaryUploadError = "$GITHUB_STEP_SUMMARY upload aborted, an error occurred when uploading the summary. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";
|
||||
public static readonly string Node12DetectedAfterEndOfLife = "Node.js 12 actions are deprecated. Please update the following actions to use Node.js 16: {0}. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/.";
|
||||
}
|
||||
|
||||
|
||||
@@ -226,20 +226,6 @@ namespace GitHub.Runner.Common
|
||||
}
|
||||
|
||||
_userAgents.Add(new ProductInfoHeaderValue("CommitSHA", BuildConstants.Source.CommitHash));
|
||||
|
||||
var extraUserAgent = Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_EXTRA_USER_AGENT");
|
||||
if (!string.IsNullOrEmpty(extraUserAgent))
|
||||
{
|
||||
var extraUserAgentSplit = extraUserAgent.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (extraUserAgentSplit.Length != 2)
|
||||
{
|
||||
_trace.Error($"GITHUB_ACTIONS_RUNNER_EXTRA_USER_AGENT is not in the format of 'name/version'.");
|
||||
}
|
||||
|
||||
var extraUserAgentHeader = new ProductInfoHeaderValue(extraUserAgentSplit[0], extraUserAgentSplit[1]);
|
||||
_trace.Info($"Adding extra user agent '{extraUserAgentHeader}' to all HTTP requests.");
|
||||
_userAgents.Add(extraUserAgentHeader);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetDirectory(WellKnownDirectory directory)
|
||||
|
||||
@@ -13,8 +13,6 @@ using GitHub.Runner.Sdk;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Services.WebApi;
|
||||
using GitHub.Services.WebApi.Utilities.Internal;
|
||||
using GitHub.Services.Results.Client;
|
||||
using GitHub.Services.OAuth;
|
||||
|
||||
namespace GitHub.Runner.Common
|
||||
{
|
||||
@@ -24,15 +22,11 @@ namespace GitHub.Runner.Common
|
||||
Task ConnectAsync(VssConnection jobConnection);
|
||||
|
||||
void InitializeWebsocketClient(ServiceEndpoint serviceEndpoint);
|
||||
void InitializeResultsClient(Uri uri, string token);
|
||||
|
||||
// logging and console
|
||||
Task<TaskLog> AppendLogContentAsync(Guid scopeIdentifier, string hubName, Guid planId, int logId, Stream uploadStream, CancellationToken cancellationToken);
|
||||
Task AppendTimelineRecordFeedAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, Guid stepId, IList<string> lines, long? startLine, CancellationToken cancellationToken);
|
||||
Task<TaskAttachment> CreateAttachmentAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, String type, String name, Stream uploadStream, CancellationToken cancellationToken);
|
||||
Task CreateStepSummaryAsync(string planId, string jobId, Guid stepId, string file, CancellationToken cancellationToken);
|
||||
Task CreateResultsStepLogAsync(string planId, string jobId, Guid stepId, string file, bool finalize, bool firstBlock, long lineCount, CancellationToken cancellationToken);
|
||||
Task CreateResultsJobLogAsync(string planId, string jobId, string file, bool finalize, bool firstBlock, long lineCount, CancellationToken cancellationToken);
|
||||
Task<TaskLog> CreateLogAsync(Guid scopeIdentifier, string hubName, Guid planId, TaskLog log, CancellationToken cancellationToken);
|
||||
Task<Timeline> CreateTimelineAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, CancellationToken cancellationToken);
|
||||
Task<List<TimelineRecord>> UpdateTimelineRecordsAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, IEnumerable<TimelineRecord> records, CancellationToken cancellationToken);
|
||||
@@ -46,7 +40,6 @@ namespace GitHub.Runner.Common
|
||||
private bool _hasConnection;
|
||||
private VssConnection _connection;
|
||||
private TaskHttpClient _taskClient;
|
||||
private ResultsHttpClient _resultsClient;
|
||||
private ClientWebSocket _websocketClient;
|
||||
|
||||
private ServiceEndpoint _serviceEndpoint;
|
||||
@@ -150,12 +143,6 @@ namespace GitHub.Runner.Common
|
||||
InitializeWebsocketClient(TimeSpan.Zero);
|
||||
}
|
||||
|
||||
public void InitializeResultsClient(Uri uri, string token)
|
||||
{
|
||||
var httpMessageHandler = HostContext.CreateHttpClientHandler();
|
||||
this._resultsClient = new ResultsHttpClient(uri, httpMessageHandler, token, disposeHandler: true);
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
CloseWebSocket(WebSocketCloseStatus.NormalClosure, CancellationToken.None);
|
||||
@@ -318,33 +305,6 @@ namespace GitHub.Runner.Common
|
||||
return _taskClient.CreateAttachmentAsync(scopeIdentifier, hubName, planId, timelineId, timelineRecordId, type, name, uploadStream, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public Task CreateStepSummaryAsync(string planId, string jobId, Guid stepId, string file, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_resultsClient != null)
|
||||
{
|
||||
return _resultsClient.UploadStepSummaryAsync(planId, jobId, stepId, file, cancellationToken: cancellationToken);
|
||||
}
|
||||
throw new InvalidOperationException("Results client is not initialized.");
|
||||
}
|
||||
|
||||
public Task CreateResultsStepLogAsync(string planId, string jobId, Guid stepId, string file, bool finalize, bool firstBlock, long lineCount, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_resultsClient != null)
|
||||
{
|
||||
return _resultsClient.UploadResultsStepLogAsync(planId, jobId, stepId, file, finalize, firstBlock, lineCount, cancellationToken: cancellationToken);
|
||||
}
|
||||
throw new InvalidOperationException("Results client is not initialized.");
|
||||
}
|
||||
|
||||
public Task CreateResultsJobLogAsync(string planId, string jobId, string file, bool finalize, bool firstBlock, long lineCount, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_resultsClient != null)
|
||||
{
|
||||
return _resultsClient.UploadResultsJobLogAsync(planId, jobId, file, finalize, firstBlock, lineCount, cancellationToken: cancellationToken);
|
||||
}
|
||||
throw new InvalidOperationException("Results client is not initialized.");
|
||||
}
|
||||
|
||||
public Task<TaskLog> CreateLogAsync(Guid scopeIdentifier, string hubName, Guid planId, TaskLog log, CancellationToken cancellationToken)
|
||||
{
|
||||
CheckConnection();
|
||||
|
||||
@@ -20,7 +20,6 @@ namespace GitHub.Runner.Common
|
||||
void Start(Pipelines.AgentJobRequestMessage jobRequest);
|
||||
void QueueWebConsoleLine(Guid stepRecordId, string line, long? lineNumber = null);
|
||||
void QueueFileUpload(Guid timelineId, Guid timelineRecordId, string type, string name, string path, bool deleteSource);
|
||||
void QueueResultsUpload(Guid timelineRecordId, string name, string path, string type, bool deleteSource, bool finalize, bool firstBlock, long totalLines);
|
||||
void QueueTimelineRecordUpdate(Guid timelineId, TimelineRecord timelineRecord);
|
||||
}
|
||||
|
||||
@@ -31,7 +30,6 @@ namespace GitHub.Runner.Common
|
||||
private static readonly TimeSpan _delayForWebConsoleLineDequeue = TimeSpan.FromMilliseconds(500);
|
||||
private static readonly TimeSpan _delayForTimelineUpdateDequeue = TimeSpan.FromMilliseconds(500);
|
||||
private static readonly TimeSpan _delayForFileUploadDequeue = TimeSpan.FromMilliseconds(1000);
|
||||
private static readonly TimeSpan _delayForResultsUploadDequeue = TimeSpan.FromMilliseconds(1000);
|
||||
|
||||
// Job message information
|
||||
private Guid _scopeIdentifier;
|
||||
@@ -46,8 +44,6 @@ namespace GitHub.Runner.Common
|
||||
// queue for file upload (log file or attachment)
|
||||
private readonly ConcurrentQueue<UploadFileInfo> _fileUploadQueue = new();
|
||||
|
||||
private readonly ConcurrentQueue<ResultsUploadFileInfo> _resultsFileUploadQueue = new();
|
||||
|
||||
// queue for timeline or timeline record update (one queue per timeline)
|
||||
private readonly ConcurrentDictionary<Guid, ConcurrentQueue<TimelineRecord>> _timelineUpdateQueue = new();
|
||||
|
||||
@@ -60,7 +56,6 @@ namespace GitHub.Runner.Common
|
||||
// Task for each queue's dequeue process
|
||||
private Task _webConsoleLineDequeueTask;
|
||||
private Task _fileUploadDequeueTask;
|
||||
private Task _resultsUploadDequeueTask;
|
||||
private Task _timelineUpdateDequeueTask;
|
||||
|
||||
// common
|
||||
@@ -84,9 +79,6 @@ namespace GitHub.Runner.Common
|
||||
private bool _webConsoleLineAggressiveDequeue = true;
|
||||
private bool _firstConsoleOutputs = true;
|
||||
|
||||
private bool _resultsClientInitiated = false;
|
||||
private delegate Task ResultsFileUploadHandler(ResultsUploadFileInfo file);
|
||||
|
||||
public override void Initialize(IHostContext hostContext)
|
||||
{
|
||||
base.Initialize(hostContext);
|
||||
@@ -101,20 +93,6 @@ namespace GitHub.Runner.Common
|
||||
|
||||
_jobServer.InitializeWebsocketClient(serviceEndPoint);
|
||||
|
||||
// This code is usually wrapped by an instance of IExecutionContext which isn't available here.
|
||||
jobRequest.Variables.TryGetValue("system.github.results_endpoint", out VariableValue resultsEndpointVariable);
|
||||
var resultsReceiverEndpoint = resultsEndpointVariable?.Value;
|
||||
|
||||
if (serviceEndPoint?.Authorization != null &&
|
||||
serviceEndPoint.Authorization.Parameters.TryGetValue("AccessToken", out var accessToken) &&
|
||||
!string.IsNullOrEmpty(accessToken) &&
|
||||
!string.IsNullOrEmpty(resultsReceiverEndpoint))
|
||||
{
|
||||
Trace.Info("Initializing results client");
|
||||
_jobServer.InitializeResultsClient(new Uri(resultsReceiverEndpoint), accessToken);
|
||||
_resultsClientInitiated = true;
|
||||
}
|
||||
|
||||
if (_queueInProcess)
|
||||
{
|
||||
Trace.Info("No-opt, all queue process tasks are running.");
|
||||
@@ -142,13 +120,10 @@ namespace GitHub.Runner.Common
|
||||
Trace.Info("Start process file upload queue.");
|
||||
_fileUploadDequeueTask = ProcessFilesUploadQueueAsync();
|
||||
|
||||
Trace.Info("Start results file upload queue.");
|
||||
_resultsUploadDequeueTask = ProcessResultsUploadQueueAsync();
|
||||
|
||||
Trace.Info("Start process timeline update queue.");
|
||||
_timelineUpdateDequeueTask = ProcessTimelinesUpdateQueueAsync();
|
||||
|
||||
_allDequeueTasks = new Task[] { _webConsoleLineDequeueTask, _fileUploadDequeueTask, _timelineUpdateDequeueTask, _resultsUploadDequeueTask };
|
||||
_allDequeueTasks = new Task[] { _webConsoleLineDequeueTask, _fileUploadDequeueTask, _timelineUpdateDequeueTask };
|
||||
_queueInProcess = true;
|
||||
}
|
||||
|
||||
@@ -179,10 +154,6 @@ namespace GitHub.Runner.Common
|
||||
await ProcessFilesUploadQueueAsync(runOnce: true);
|
||||
Trace.Info("File upload queue drained.");
|
||||
|
||||
Trace.Verbose("Draining results upload queue.");
|
||||
await ProcessResultsUploadQueueAsync(runOnce: true);
|
||||
Trace.Info("Results upload queue drained.");
|
||||
|
||||
// ProcessTimelinesUpdateQueueAsync() will throw exception during shutdown
|
||||
// if there is any timeline records that failed to update contains output variabls.
|
||||
Trace.Verbose("Draining timeline update queue.");
|
||||
@@ -233,45 +204,6 @@ namespace GitHub.Runner.Common
|
||||
_fileUploadQueue.Enqueue(newFile);
|
||||
}
|
||||
|
||||
public void QueueResultsUpload(Guid timelineRecordId, string name, string path, string type, bool deleteSource, bool finalize, bool firstBlock, long totalLines)
|
||||
{
|
||||
if (!_resultsClientInitiated)
|
||||
{
|
||||
Trace.Verbose("Skipping results upload");
|
||||
try
|
||||
{
|
||||
if (deleteSource)
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Info("Catch exception during delete skipped results upload file.");
|
||||
Trace.Error(ex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// all parameter not null, file path exist.
|
||||
var newFile = new ResultsUploadFileInfo()
|
||||
{
|
||||
Name = name,
|
||||
Path = path,
|
||||
Type = type,
|
||||
PlanId = _planId.ToString(),
|
||||
JobId = _jobTimelineRecordId.ToString(),
|
||||
RecordId = timelineRecordId,
|
||||
DeleteSource = deleteSource,
|
||||
Finalize = finalize,
|
||||
FirstBlock = firstBlock,
|
||||
TotalLines = totalLines,
|
||||
};
|
||||
|
||||
Trace.Verbose("Enqueue results file upload queue: file '{0}' attach to job {1} step {2}", newFile.Path, _jobTimelineRecordId, timelineRecordId);
|
||||
_resultsFileUploadQueue.Enqueue(newFile);
|
||||
}
|
||||
|
||||
public void QueueTimelineRecordUpdate(Guid timelineId, TimelineRecord timelineRecord)
|
||||
{
|
||||
ArgUtil.NotEmpty(timelineId, nameof(timelineId));
|
||||
@@ -462,86 +394,6 @@ namespace GitHub.Runner.Common
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessResultsUploadQueueAsync(bool runOnce = false)
|
||||
{
|
||||
Trace.Info("Starting results-based upload queue...");
|
||||
|
||||
while (!_jobCompletionSource.Task.IsCompleted || runOnce)
|
||||
{
|
||||
List<ResultsUploadFileInfo> filesToUpload = new();
|
||||
ResultsUploadFileInfo dequeueFile;
|
||||
while (_resultsFileUploadQueue.TryDequeue(out dequeueFile))
|
||||
{
|
||||
filesToUpload.Add(dequeueFile);
|
||||
// process at most 10 file uploads.
|
||||
if (!runOnce && filesToUpload.Count > 10)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (filesToUpload.Count > 0)
|
||||
{
|
||||
if (runOnce)
|
||||
{
|
||||
Trace.Info($"Uploading {filesToUpload.Count} file(s) in one shot through results service.");
|
||||
}
|
||||
|
||||
int errorCount = 0;
|
||||
foreach (var file in filesToUpload)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (String.Equals(file.Type, ChecksAttachmentType.StepSummary, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await UploadSummaryFile(file);
|
||||
}
|
||||
else if (String.Equals(file.Type, CoreAttachmentType.ResultsLog, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (file.RecordId != _jobTimelineRecordId)
|
||||
{
|
||||
Trace.Info($"Got a step log file to send to results service.");
|
||||
await UploadResultsStepLogFile(file);
|
||||
}
|
||||
else if (file.RecordId == _jobTimelineRecordId)
|
||||
{
|
||||
Trace.Info($"Got a job log file to send to results service.");
|
||||
await UploadResultsJobLogFile(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var issue = new Issue() { Type = IssueType.Warning, Message = $"Caught exception during file upload to results. {ex.Message}" };
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.ResultsUploadFailure;
|
||||
|
||||
var telemetryRecord = new TimelineRecord()
|
||||
{
|
||||
Id = Constants.Runner.TelemetryRecordId,
|
||||
};
|
||||
telemetryRecord.Issues.Add(issue);
|
||||
QueueTimelineRecordUpdate(_jobTimelineId, telemetryRecord);
|
||||
|
||||
Trace.Info("Catch exception during file upload to results, keep going since the process is best effort.");
|
||||
Trace.Error(ex);
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Trace.Info("Tried to upload {0} file(s) to results, success rate: {1}/{0}.", filesToUpload.Count, filesToUpload.Count - errorCount);
|
||||
}
|
||||
|
||||
if (runOnce)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(_delayForResultsUploadDequeue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessTimelinesUpdateQueueAsync(bool runOnce = false)
|
||||
{
|
||||
while (!_jobCompletionSource.Task.IsCompleted || runOnce)
|
||||
@@ -748,8 +600,7 @@ namespace GitHub.Runner.Common
|
||||
{
|
||||
foreach (var issue in record.Issues)
|
||||
{
|
||||
String source;
|
||||
issue.Data.TryGetValue("sourcepath", out source);
|
||||
string source = issue["sourcepath"];
|
||||
Trace.Verbose($" Issue: c={issue.Category}, t={issue.Type}, s={source ?? string.Empty}, m={issue.Message}");
|
||||
}
|
||||
}
|
||||
@@ -813,64 +664,6 @@ namespace GitHub.Runner.Common
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UploadSummaryFile(ResultsUploadFileInfo file)
|
||||
{
|
||||
Trace.Info($"Starting to upload summary file to results service {file.Name}, {file.Path}");
|
||||
ResultsFileUploadHandler summaryHandler = async (file) =>
|
||||
{
|
||||
await _jobServer.CreateStepSummaryAsync(file.PlanId, file.JobId, file.RecordId, file.Path, CancellationToken.None);
|
||||
};
|
||||
|
||||
await UploadResultsFile(file, summaryHandler);
|
||||
}
|
||||
|
||||
private async Task UploadResultsStepLogFile(ResultsUploadFileInfo file)
|
||||
{
|
||||
Trace.Info($"Starting upload of step log file to results service {file.Name}, {file.Path}");
|
||||
ResultsFileUploadHandler stepLogHandler = async (file) =>
|
||||
{
|
||||
await _jobServer.CreateResultsStepLogAsync(file.PlanId, file.JobId, file.RecordId, file.Path, file.Finalize, file.FirstBlock, file.TotalLines, CancellationToken.None);
|
||||
};
|
||||
|
||||
await UploadResultsFile(file, stepLogHandler);
|
||||
}
|
||||
|
||||
private async Task UploadResultsJobLogFile(ResultsUploadFileInfo file)
|
||||
{
|
||||
Trace.Info($"Starting upload of job log file to results service {file.Name}, {file.Path}");
|
||||
ResultsFileUploadHandler jobLogHandler = async (file) =>
|
||||
{
|
||||
await _jobServer.CreateResultsJobLogAsync(file.PlanId, file.JobId, file.Path, file.Finalize, file.FirstBlock, file.TotalLines, CancellationToken.None);
|
||||
};
|
||||
|
||||
await UploadResultsFile(file, jobLogHandler);
|
||||
}
|
||||
|
||||
private async Task UploadResultsFile(ResultsUploadFileInfo file, ResultsFileUploadHandler uploadHandler)
|
||||
{
|
||||
bool uploadSucceed = false;
|
||||
try
|
||||
{
|
||||
await uploadHandler(file);
|
||||
uploadSucceed = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (uploadSucceed && file.DeleteSource)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(file.Path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Info("Exception encountered during deletion of a temporary file that was already successfully uploaded to results.");
|
||||
Trace.Error(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class PendingTimelineRecord
|
||||
@@ -889,21 +682,6 @@ namespace GitHub.Runner.Common
|
||||
public bool DeleteSource { get; set; }
|
||||
}
|
||||
|
||||
internal class ResultsUploadFileInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string PlanId { get; set; }
|
||||
public string JobId { get; set; }
|
||||
public Guid RecordId { get; set; }
|
||||
public bool DeleteSource { get; set; }
|
||||
public bool Finalize { get; set; }
|
||||
public bool FirstBlock { get; set; }
|
||||
public long TotalLines { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal class ConsoleLineInfo
|
||||
{
|
||||
|
||||
@@ -21,12 +21,6 @@ namespace GitHub.Runner.Common
|
||||
// 8 MB
|
||||
public const int PageSize = 8 * 1024 * 1024;
|
||||
|
||||
// For Results
|
||||
public static string BlocksFolder = "blocks";
|
||||
|
||||
// 2 MB
|
||||
public const int BlockSize = 2 * 1024 * 1024;
|
||||
|
||||
private Guid _timelineId;
|
||||
private Guid _timelineRecordId;
|
||||
private FileStream _pageData;
|
||||
@@ -38,13 +32,6 @@ namespace GitHub.Runner.Common
|
||||
private string _pagesFolder;
|
||||
private IJobServerQueue _jobServerQueue;
|
||||
|
||||
private string _resultsDataFileName;
|
||||
private FileStream _resultsBlockData;
|
||||
private StreamWriter _resultsBlockWriter;
|
||||
private string _resultsBlockFolder;
|
||||
private int _blockByteCount;
|
||||
private int _blockCount;
|
||||
|
||||
public long TotalLines => _totalLines;
|
||||
|
||||
public override void Initialize(IHostContext hostContext)
|
||||
@@ -52,10 +39,8 @@ namespace GitHub.Runner.Common
|
||||
base.Initialize(hostContext);
|
||||
_totalLines = 0;
|
||||
_pagesFolder = Path.Combine(hostContext.GetDirectory(WellKnownDirectory.Diag), PagingFolder);
|
||||
Directory.CreateDirectory(_pagesFolder);
|
||||
_resultsBlockFolder = Path.Combine(hostContext.GetDirectory(WellKnownDirectory.Diag), BlocksFolder);
|
||||
Directory.CreateDirectory(_resultsBlockFolder);
|
||||
_jobServerQueue = HostContext.GetService<IJobServerQueue>();
|
||||
Directory.CreateDirectory(_pagesFolder);
|
||||
}
|
||||
|
||||
public void Setup(Guid timelineId, Guid timelineRecordId)
|
||||
@@ -75,17 +60,11 @@ namespace GitHub.Runner.Common
|
||||
// lazy creation on write
|
||||
if (_pageWriter == null)
|
||||
{
|
||||
NewPage();
|
||||
}
|
||||
|
||||
if (_resultsBlockWriter == null)
|
||||
{
|
||||
NewBlock();
|
||||
Create();
|
||||
}
|
||||
|
||||
string line = $"{DateTime.UtcNow.ToString("O")} {message}";
|
||||
_pageWriter.WriteLine(line);
|
||||
_resultsBlockWriter.WriteLine(line);
|
||||
|
||||
_totalLines++;
|
||||
if (line.IndexOf('\n') != -1)
|
||||
@@ -99,25 +78,21 @@ namespace GitHub.Runner.Common
|
||||
}
|
||||
}
|
||||
|
||||
var bytes = System.Text.Encoding.UTF8.GetByteCount(line);
|
||||
_byteCount += bytes;
|
||||
_blockByteCount += bytes;
|
||||
_byteCount += System.Text.Encoding.UTF8.GetByteCount(line);
|
||||
if (_byteCount >= PageSize)
|
||||
{
|
||||
NewPage();
|
||||
}
|
||||
|
||||
if (_blockByteCount >= BlockSize)
|
||||
{
|
||||
NewBlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void End()
|
||||
{
|
||||
EndPage();
|
||||
EndBlock(true);
|
||||
}
|
||||
|
||||
private void Create()
|
||||
{
|
||||
NewPage();
|
||||
}
|
||||
|
||||
private void NewPage()
|
||||
@@ -142,27 +117,5 @@ namespace GitHub.Runner.Common
|
||||
_jobServerQueue.QueueFileUpload(_timelineId, _timelineRecordId, "DistributedTask.Core.Log", "CustomToolLog", _dataFileName, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void NewBlock()
|
||||
{
|
||||
EndBlock(false);
|
||||
_blockByteCount = 0;
|
||||
_resultsDataFileName = Path.Combine(_resultsBlockFolder, $"{_timelineId}_{_timelineRecordId}.{++_blockCount}");
|
||||
_resultsBlockData = new FileStream(_resultsDataFileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
_resultsBlockWriter = new StreamWriter(_resultsBlockData, System.Text.Encoding.UTF8);
|
||||
}
|
||||
|
||||
private void EndBlock(bool finalize)
|
||||
{
|
||||
if (_resultsBlockWriter != null)
|
||||
{
|
||||
_resultsBlockWriter.Flush();
|
||||
_resultsBlockData.Flush();
|
||||
_resultsBlockWriter.Dispose();
|
||||
_resultsBlockWriter = null;
|
||||
_resultsBlockData = null;
|
||||
_jobServerQueue.QueueResultsUpload(_timelineRecordId, "ResultsLog", _resultsDataFileName, "Results.Core.Log", deleteSource: true, finalize, firstBlock: _resultsDataFileName.EndsWith(".1"), totalLines: _totalLines);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Actions.RunService.WebApi;
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Services.WebApi;
|
||||
using Sdk.WebApi.WebApi.RawClient;
|
||||
|
||||
namespace GitHub.Runner.Common
|
||||
@@ -17,8 +16,6 @@ namespace GitHub.Runner.Common
|
||||
Task ConnectAsync(Uri serverUrl, VssCredentials credentials);
|
||||
|
||||
Task<AgentJobRequestMessage> GetJobMessageAsync(string id, CancellationToken token);
|
||||
|
||||
Task CompleteJobAsync(Guid planId, Guid jobId, TaskResult result, Dictionary<String, VariableValue> outputs, IList<StepResult> stepResults, CancellationToken token);
|
||||
}
|
||||
|
||||
public sealed class RunServer : RunnerService, IRunServer
|
||||
@@ -32,7 +29,7 @@ namespace GitHub.Runner.Common
|
||||
{
|
||||
requestUri = serverUri;
|
||||
|
||||
_connection = VssUtil.CreateRawConnection(serverUri, credentials);
|
||||
_connection = VssUtil.CreateRawConnection(new Uri(serverUri.Authority), credentials);
|
||||
_runServiceHttpClient = await _connection.GetClientAsync<RunServiceHttpClient>();
|
||||
_hasConnection = true;
|
||||
}
|
||||
@@ -58,11 +55,5 @@ namespace GitHub.Runner.Common
|
||||
return jobMessage;
|
||||
}
|
||||
|
||||
public Task CompleteJobAsync(Guid planId, Guid jobId, TaskResult result, Dictionary<String, VariableValue> outputs, IList<StepResult> stepResults, CancellationToken cancellationToken)
|
||||
{
|
||||
CheckConnection();
|
||||
return RetryRequest(
|
||||
async () => await _runServiceHttpClient.CompleteJobAsync(requestUri, planId, jobId, result, outputs, stepResults, cancellationToken), cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,19 +68,6 @@ namespace GitHub.Runner.Common
|
||||
throw new InvalidOperationException(nameof(EstablishVssConnection));
|
||||
}
|
||||
|
||||
protected async Task RetryRequest(Func<Task> func,
|
||||
CancellationToken cancellationToken,
|
||||
int maxRetryAttemptsCount = 5
|
||||
)
|
||||
{
|
||||
async Task<Unit> wrappedFunc()
|
||||
{
|
||||
await func();
|
||||
return Unit.Value;
|
||||
}
|
||||
await RetryRequest<Unit>(wrappedFunc, cancellationToken, maxRetryAttemptsCount);
|
||||
}
|
||||
|
||||
protected async Task<T> RetryRequest<T>(Func<Task<T>> func,
|
||||
CancellationToken cancellationToken,
|
||||
int maxRetryAttemptsCount = 5
|
||||
@@ -98,7 +85,7 @@ namespace GitHub.Runner.Common
|
||||
// TODO: Add handling of non-retriable exceptions: https://github.com/github/actions-broker/issues/122
|
||||
catch (Exception ex) when (retryCount < maxRetryAttemptsCount)
|
||||
{
|
||||
Trace.Error("Catch exception during request");
|
||||
Trace.Error("Catch exception during get full job message");
|
||||
Trace.Error(ex);
|
||||
var backOff = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(15));
|
||||
Trace.Warning($"Back off {backOff.TotalSeconds} seconds before next retry. {maxRetryAttemptsCount - retryCount} attempt left.");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using GitHub.Runner.Sdk;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using GitHub.Runner.Sdk;
|
||||
|
||||
namespace GitHub.Runner.Common
|
||||
{
|
||||
@@ -24,16 +24,9 @@ namespace GitHub.Runner.Common
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
{
|
||||
var messageLines = message.Split(Environment.NewLine);
|
||||
foreach (var messageLine in messageLines)
|
||||
{
|
||||
WriteHeader(source, eventType, id);
|
||||
WriteLine(messageLine);
|
||||
WriteFooter(eventCache);
|
||||
}
|
||||
}
|
||||
WriteHeader(source, eventType, id);
|
||||
WriteLine(message);
|
||||
WriteFooter(eventCache);
|
||||
}
|
||||
|
||||
internal bool IsEnabled(TraceOptions opts)
|
||||
@@ -93,4 +86,5 @@ namespace GitHub.Runner.Common
|
||||
IndentLevel--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// Represents absence of value.
|
||||
namespace GitHub.Runner.Common
|
||||
{
|
||||
public readonly struct Unit
|
||||
{
|
||||
public static readonly Unit Value = default;
|
||||
}
|
||||
}
|
||||
@@ -56,8 +56,7 @@ namespace GitHub.Runner.Listener
|
||||
new string[]
|
||||
{
|
||||
Constants.Runner.CommandLine.Args.Token,
|
||||
Constants.Runner.CommandLine.Args.PAT,
|
||||
Constants.Runner.CommandLine.Flags.Local
|
||||
Constants.Runner.CommandLine.Args.PAT
|
||||
},
|
||||
// Valid run flags and args
|
||||
[Constants.Runner.CommandLine.Commands.Run] =
|
||||
@@ -87,7 +86,6 @@ namespace GitHub.Runner.Listener
|
||||
public bool Help => TestFlag(Constants.Runner.CommandLine.Flags.Help);
|
||||
public bool Unattended => TestFlag(Constants.Runner.CommandLine.Flags.Unattended);
|
||||
public bool Version => TestFlag(Constants.Runner.CommandLine.Flags.Version);
|
||||
public bool RemoveLocalConfig => TestFlag(Constants.Runner.CommandLine.Flags.Local);
|
||||
|
||||
// Keep this around since customers still relies on it
|
||||
public bool RunOnce => TestFlag(Constants.Runner.CommandLine.Flags.Once);
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Services.Common.Internal;
|
||||
using GitHub.Services.OAuth;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -7,13 +14,6 @@ using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Services.Common.Internal;
|
||||
using GitHub.Services.OAuth;
|
||||
|
||||
namespace GitHub.Runner.Listener.Configuration
|
||||
{
|
||||
@@ -636,7 +636,7 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
}
|
||||
|
||||
int retryCount = 0;
|
||||
while (retryCount < 3)
|
||||
while(retryCount < 3)
|
||||
{
|
||||
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
||||
using (var httpClient = new HttpClient(httpClientHandler))
|
||||
@@ -646,29 +646,28 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("basic", base64EncodingToken);
|
||||
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
||||
httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github.v3+json");
|
||||
|
||||
|
||||
var responseStatus = System.Net.HttpStatusCode.OK;
|
||||
try
|
||||
{
|
||||
var response = await httpClient.PostAsync(githubApiUrl, new StringContent(string.Empty));
|
||||
responseStatus = response.StatusCode;
|
||||
var githubRequestId = GetGitHubRequestId(response.Headers);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
Trace.Info($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}' ({githubRequestId})");
|
||||
Trace.Info($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}'");
|
||||
var jsonResponse = await response.Content.ReadAsStringAsync();
|
||||
return StringUtil.ConvertFromJson<GitHubRunnerRegisterToken>(jsonResponse);
|
||||
}
|
||||
else
|
||||
{
|
||||
_term.WriteError($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}' (Request Id: {githubRequestId})");
|
||||
_term.WriteError($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}'");
|
||||
var errorResponse = await response.Content.ReadAsStringAsync();
|
||||
_term.WriteError(errorResponse);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (retryCount < 2 && responseStatus != System.Net.HttpStatusCode.NotFound)
|
||||
catch(Exception ex) when (retryCount < 2 && responseStatus != System.Net.HttpStatusCode.NotFound)
|
||||
{
|
||||
retryCount++;
|
||||
Trace.Error($"Failed to get JIT runner token -- Atempt: {retryCount}");
|
||||
@@ -715,23 +714,22 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
{
|
||||
var response = await httpClient.PostAsync(githubApiUrl, new StringContent(StringUtil.ConvertToJson(bodyObject), null, "application/json"));
|
||||
responseStatus = response.StatusCode;
|
||||
var githubRequestId = GetGitHubRequestId(response.Headers);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
if(response.IsSuccessStatusCode)
|
||||
{
|
||||
Trace.Info($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}' ({githubRequestId})");
|
||||
Trace.Info($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}'");
|
||||
var jsonResponse = await response.Content.ReadAsStringAsync();
|
||||
return StringUtil.ConvertFromJson<GitHubAuthResult>(jsonResponse);
|
||||
}
|
||||
else
|
||||
{
|
||||
_term.WriteError($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}' (Request Id: {githubRequestId})");
|
||||
_term.WriteError($"Http response code: {response.StatusCode} from 'POST {githubApiUrl}'");
|
||||
var errorResponse = await response.Content.ReadAsStringAsync();
|
||||
_term.WriteError(errorResponse);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (retryCount < 2 && responseStatus != System.Net.HttpStatusCode.NotFound)
|
||||
catch(Exception ex) when (retryCount < 2 && responseStatus != System.Net.HttpStatusCode.NotFound)
|
||||
{
|
||||
retryCount++;
|
||||
Trace.Error($"Failed to get tenant credentials -- Atempt: {retryCount}");
|
||||
@@ -744,14 +742,5 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private string GetGitHubRequestId(HttpResponseHeaders headers)
|
||||
{
|
||||
if (headers.TryGetValues("x-github-request-id", out var headerValues))
|
||||
{
|
||||
return headerValues.FirstOrDefault();
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace GitHub.Runner.Listener
|
||||
// This implementation of IJobDispatcher is not thread safe.
|
||||
// It is based on the fact that the current design of the runner is a dequeue
|
||||
// and processes one message from the message queue at a time.
|
||||
// In addition, it only executes one job every time,
|
||||
// In addition, it only executes one job every time,
|
||||
// and the server will not send another job while this one is still running.
|
||||
public sealed class JobDispatcher : RunnerService, IJobDispatcher
|
||||
{
|
||||
@@ -426,7 +426,7 @@ namespace GitHub.Runner.Listener
|
||||
{
|
||||
workerOutput.Add(stdout.Data);
|
||||
}
|
||||
|
||||
|
||||
if (printToStdout)
|
||||
{
|
||||
term.WriteLine(stdout.Data, skipTracing: true);
|
||||
@@ -512,7 +512,7 @@ namespace GitHub.Runner.Listener
|
||||
var accessToken = systemConnection?.Authorization?.Parameters["AccessToken"];
|
||||
notification.JobStarted(message.JobId, accessToken, systemConnection.Url);
|
||||
|
||||
HostContext.WritePerfCounter($"SentJobToWorker_{requestId.ToString()}");
|
||||
HostContext.WritePerfCounter($"SentJobToWorker_{requestId}");
|
||||
|
||||
try
|
||||
{
|
||||
@@ -620,7 +620,7 @@ namespace GitHub.Runner.Listener
|
||||
}
|
||||
}
|
||||
|
||||
// wait worker to exit
|
||||
// wait worker to exit
|
||||
// if worker doesn't exit within timeout, then kill worker.
|
||||
completedTask = await Task.WhenAny(workerProcessTask, Task.Delay(-1, workerCancelTimeoutKillToken));
|
||||
|
||||
@@ -1014,7 +1014,7 @@ namespace GitHub.Runner.Listener
|
||||
}
|
||||
|
||||
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = errorMessage };
|
||||
unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
|
||||
unhandledExceptionIssue[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
|
||||
jobRecord.ErrorCount++;
|
||||
jobRecord.Issues.Add(unhandledExceptionIssue);
|
||||
await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, new TimelineRecord[] { jobRecord }, CancellationToken.None);
|
||||
|
||||
@@ -135,12 +135,6 @@ namespace GitHub.Runner.Listener
|
||||
// remove config files, remove service, and exit
|
||||
if (command.Remove)
|
||||
{
|
||||
// only remove local config files and exit
|
||||
if(command.RemoveLocalConfig)
|
||||
{
|
||||
configManager.DeleteLocalRunnerConfig();
|
||||
return Constants.Runner.ReturnCode.Success;
|
||||
}
|
||||
try
|
||||
{
|
||||
await configManager.UnconfigureAsync(command);
|
||||
@@ -653,7 +647,6 @@ Config Options:
|
||||
--name string Name of the runner to configure (default {Environment.MachineName ?? "myrunner"})
|
||||
--runnergroup string Name of the runner group to add this runner to (defaults to the default runner group)
|
||||
--labels string Extra labels in addition to the default: 'self-hosted,{Constants.Runner.Platform},{Constants.Runner.PlatformArchitecture}'
|
||||
--local Removes the runner config files from your local machine. Used as an option to the remove command
|
||||
--work string Relative runner work directory (default {Constants.Path.WorkDirectory})
|
||||
--replace Replace any existing runner with the same name (default false)
|
||||
--pat GitHub personal access token with repo scope. Used for checking network connectivity when executing `.{separator}run.{ext} --check`
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace GitHub.Runner.Sdk
|
||||
{
|
||||
var headerValues = new List<ProductInfoHeaderValue>();
|
||||
headerValues.Add(new ProductInfoHeaderValue($"GitHubActionsRunner-Plugin", BuildConstants.RunnerPackage.Version));
|
||||
headerValues.Add(new ProductInfoHeaderValue($"({StringUtil.SanitizeUserAgentHeader(RuntimeInformation.OSDescription)})"));
|
||||
headerValues.Add(new ProductInfoHeaderValue($"({RuntimeInformation.OSDescription.Trim()})"));
|
||||
|
||||
if (VssClientHttpRequestSettings.Default.UserAgent != null && VssClientHttpRequestSettings.Default.UserAgent.Count > 0)
|
||||
{
|
||||
|
||||
@@ -264,17 +264,7 @@ namespace GitHub.Runner.Sdk
|
||||
{
|
||||
foreach (KeyValuePair<string, string> kvp in environment)
|
||||
{
|
||||
#if OS_WINDOWS
|
||||
string tempKey = String.IsNullOrWhiteSpace(kvp.Key) ? kvp.Key : kvp.Key.Split('\0')[0];
|
||||
string tempValue = String.IsNullOrWhiteSpace(kvp.Value) ? kvp.Value : kvp.Value.Split('\0')[0];
|
||||
if(!String.IsNullOrWhiteSpace(tempKey))
|
||||
{
|
||||
_proc.StartInfo.Environment[tempKey] = tempValue;
|
||||
}
|
||||
#else
|
||||
_proc.StartInfo.Environment[kvp.Key] = kvp.Value;
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -123,12 +123,5 @@ namespace GitHub.Runner.Sdk
|
||||
{
|
||||
return value?.Substring(0, Math.Min(value.Length, count));
|
||||
}
|
||||
|
||||
// Fixes format violations e.g. https://github.com/actions/runner/issues/2165
|
||||
public static string SanitizeUserAgentHeader(string header)
|
||||
{
|
||||
return header.Replace("(", "[").Replace(")", "]").Trim();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,9 @@ namespace GitHub.Runner.Sdk
|
||||
{
|
||||
public static bool IsHostedServer(UriBuilder gitHubUrl)
|
||||
{
|
||||
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_FORCE_GHES")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return
|
||||
string.Equals(gitHubUrl.Host, "github.com", StringComparison.OrdinalIgnoreCase) ||
|
||||
return string.Equals(gitHubUrl.Host, "github.com", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(gitHubUrl.Host, "www.github.com", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(gitHubUrl.Host, "github.localhost", StringComparison.OrdinalIgnoreCase) ||
|
||||
gitHubUrl.Host.EndsWith(".ghe.com", StringComparison.OrdinalIgnoreCase);
|
||||
string.Equals(gitHubUrl.Host, "github.localhost", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static Uri GetCredentialEmbeddedUrl(Uri baseUrl, string username, string password)
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace GitHub.Runner.Sdk
|
||||
{
|
||||
var headerValues = new List<ProductInfoHeaderValue>();
|
||||
headerValues.AddRange(additionalUserAgents);
|
||||
headerValues.Add(new ProductInfoHeaderValue($"({StringUtil.SanitizeUserAgentHeader(RuntimeInformation.OSDescription)})"));
|
||||
headerValues.Add(new ProductInfoHeaderValue($"({RuntimeInformation.OSDescription.Trim()})"));
|
||||
|
||||
if (VssClientHttpRequestSettings.Default.UserAgent != null && VssClientHttpRequestSettings.Default.UserAgent.Count > 0)
|
||||
{
|
||||
@@ -116,7 +116,7 @@ namespace GitHub.Runner.Sdk
|
||||
// settings are applied to an HttpRequestMessage.
|
||||
settings.AcceptLanguages.Remove(CultureInfo.InvariantCulture);
|
||||
|
||||
RawConnection connection = new(serverUri, new RawHttpMessageHandler(credentials.Federated, settings), additionalDelegatingHandler);
|
||||
RawConnection connection = new(serverUri, new RawHttpMessageHandler(credentials.ToOAuthCredentials(), settings), additionalDelegatingHandler);
|
||||
return connection;
|
||||
}
|
||||
|
||||
|
||||
@@ -270,12 +270,9 @@ namespace GitHub.Runner.Worker
|
||||
if (string.Equals(blocked, envName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Log Telemetry and let user know they shouldn't do this
|
||||
var issue = new Issue()
|
||||
{
|
||||
Type = IssueType.Error,
|
||||
Message = $"Can't update {blocked} environment variable using ::set-env:: command."
|
||||
};
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = $"{Constants.Runner.UnsupportedCommand}_{envName}";
|
||||
var message = $"Can't update {blocked} environment variable using ::set-env:: command.";
|
||||
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, $"{Constants.Runner.UnsupportedCommand}_{envName}");
|
||||
var issue = context.CreateIssue(IssueType.Error, message, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
|
||||
return;
|
||||
@@ -309,12 +306,9 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
if (context.Global.Variables.GetBoolean("DistributedTask.DeprecateStepOutputCommands") ?? false)
|
||||
{
|
||||
var issue = new Issue()
|
||||
{
|
||||
Type = IssueType.Warning,
|
||||
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
|
||||
};
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
|
||||
var message = string.Format(Constants.Runner.UnsupportedCommandMessage, this.Command);
|
||||
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, Constants.Runner.UnsupportedCommand);
|
||||
var issue = context.CreateIssue(IssueType.Warning, message, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
@@ -344,12 +338,9 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
if (context.Global.Variables.GetBoolean("DistributedTask.DeprecateStepOutputCommands") ?? false)
|
||||
{
|
||||
var issue = new Issue()
|
||||
{
|
||||
Type = IssueType.Warning,
|
||||
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
|
||||
};
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
|
||||
var message = string.Format(Constants.Runner.UnsupportedCommandMessage, this.Command);
|
||||
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, Constants.Runner.UnsupportedCommand);
|
||||
var issue = context.CreateIssue(IssueType.Warning, message, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
@@ -618,16 +609,11 @@ namespace GitHub.Runner.Worker
|
||||
context.Debug("Enhanced Annotations not enabled on the server. The 'title', 'end_line', and 'end_column' fields are unsupported.");
|
||||
}
|
||||
|
||||
Issue issue = new()
|
||||
{
|
||||
Category = "General",
|
||||
Type = this.Type,
|
||||
Message = command.Data
|
||||
};
|
||||
var issueCategory = "General";
|
||||
|
||||
if (!string.IsNullOrEmpty(file))
|
||||
{
|
||||
issue.Category = "Code";
|
||||
issueCategory = "Code";
|
||||
|
||||
if (container != null)
|
||||
{
|
||||
@@ -658,14 +644,13 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var property in command.Properties)
|
||||
{
|
||||
if (!string.Equals(property.Key, Constants.Runner.InternalTelemetryIssueDataKey, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
issue.Data[property.Key] = property.Value;
|
||||
}
|
||||
}
|
||||
string keyToExclude = Constants.Runner.InternalTelemetryIssueDataKey;
|
||||
var filteredDictionaryEntries = command.Properties
|
||||
.Where(kvp => !string.Equals(kvp.Key, keyToExclude, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
var metadata = new IssueMetadata(issueCategory, false, null, filteredDictionaryEntries);
|
||||
var issue = context.CreateIssue(this.Type, command.Data, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.IO.Compression; // required for OS_WINDOWS
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace GitHub.Runner.Worker
|
||||
public interface IActionRunner : IStep, IRunnerService
|
||||
{
|
||||
ActionRunStage Stage { get; set; }
|
||||
bool TryEvaluateDisplayName(DictionaryContextData contextData, IExecutionContext context);
|
||||
Pipelines.ActionStep Action { get; set; }
|
||||
}
|
||||
|
||||
@@ -284,67 +285,25 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to update the DisplayName.
|
||||
/// As the "Try..." name implies, this method should never throw an exception.
|
||||
/// Returns true if the DisplayName is already present or it was successfully updated.
|
||||
/// </summary>
|
||||
public bool TryUpdateDisplayName(out bool updated)
|
||||
{
|
||||
updated = false;
|
||||
|
||||
// REVIEW: This try/catch can be removed if some future implementation of EvaluateDisplayName and UpdateTimelineRecordDisplayName
|
||||
// can make reasonable guarantees that they won't throw an exception.
|
||||
try
|
||||
{
|
||||
// This attempt is only worthwhile at the "Main" stage.
|
||||
// When the job starts, there's an initial attempt to evaluate the DisplayName. (see JobExtension::InitializeJob)
|
||||
// During the "Pre" stage, we expect that no contexts will have changed since the initial evaluation.
|
||||
// "Main" stage is handled here.
|
||||
// During the "Post" stage, it no longer matters.
|
||||
if (this.Stage == ActionRunStage.Main && EvaluateDisplayName(this.ExecutionContext.ExpressionValues, this.ExecutionContext, out updated))
|
||||
{
|
||||
if (updated)
|
||||
{
|
||||
this.ExecutionContext.UpdateTimelineRecordDisplayName(this.DisplayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Warning("Caught exception while attempting to evaulate/update the step's DisplayName. Exception Details: {0}", ex);
|
||||
}
|
||||
|
||||
// For consistency with other implementations of TryUpdateDisplayName we use !string.IsNullOrEmpty below,
|
||||
// but note that (at the time of this writing) ActionRunner::DisplayName::get always returns a non-empty string due to its fallback logic.
|
||||
// In other words, the net effect is that this particular implementation of TryUpdateDisplayName will always return true.
|
||||
return !string.IsNullOrEmpty(this.DisplayName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to evaluate the DisplayName of this IActionRunner.
|
||||
/// Returns true if the DisplayName is already present or it was successfully evaluated.
|
||||
/// </summary>
|
||||
public bool EvaluateDisplayName(DictionaryContextData contextData, IExecutionContext context, out bool updated)
|
||||
public bool TryEvaluateDisplayName(DictionaryContextData contextData, IExecutionContext context)
|
||||
{
|
||||
ArgUtil.NotNull(context, nameof(context));
|
||||
ArgUtil.NotNull(Action, nameof(Action));
|
||||
|
||||
updated = false;
|
||||
// If we have already expanded the display name, don't bother attempting [re-]expansion.
|
||||
// If we have already expanded the display name, there is no need to expand it again
|
||||
// TODO: Remove the ShouldEvaluateDisplayName check and field post m158 deploy, we should do it by default once the server is updated
|
||||
if (_didFullyEvaluateDisplayName || !string.IsNullOrEmpty(Action.DisplayName))
|
||||
{
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
_displayName = GenerateDisplayName(Action, contextData, context, out bool didFullyEvaluate);
|
||||
bool didFullyEvaluate;
|
||||
_displayName = GenerateDisplayName(Action, contextData, context, out didFullyEvaluate);
|
||||
|
||||
// If we evaluated, fully mask any secrets
|
||||
// If we evaluated fully mask any secrets
|
||||
if (didFullyEvaluate)
|
||||
{
|
||||
_displayName = HostContext.SecretMasker.MaskSecrets(_displayName);
|
||||
updated = true;
|
||||
}
|
||||
context.Debug($"Set step '{Action.Name}' display name to: '{_displayName}'");
|
||||
_didFullyEvaluateDisplayName = didFullyEvaluate;
|
||||
|
||||
@@ -33,14 +33,8 @@ namespace GitHub.Runner.Worker
|
||||
public override void Initialize(IHostContext hostContext)
|
||||
{
|
||||
base.Initialize(hostContext);
|
||||
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(Constants.Hooks.ContainerHooksPath)))
|
||||
{
|
||||
_dockerManager = HostContext.GetService<IDockerCommandManager>();
|
||||
}
|
||||
else
|
||||
{
|
||||
_containerHookManager = HostContext.GetService<IContainerHookManager>();
|
||||
}
|
||||
_dockerManager = HostContext.GetService<IDockerCommandManager>();
|
||||
_containerHookManager = HostContext.GetService<IContainerHookManager>();
|
||||
}
|
||||
|
||||
public async Task StartContainersAsync(IExecutionContext executionContext, object data)
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.IO.Compression;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.Threading.Tasks;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
@@ -32,7 +31,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
private static string DateTimeFormat = "yyyyMMdd-HHmmss";
|
||||
public void UploadDiagnosticLogs(IExecutionContext executionContext,
|
||||
IExecutionContext parentContext,
|
||||
IExecutionContext parentContext,
|
||||
Pipelines.AgentJobRequestMessage message,
|
||||
DateTime jobStartTimeUtc)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
@@ -6,7 +6,6 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Actions.RunService.WebApi;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
@@ -17,6 +16,7 @@ using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using GitHub.Services.Common;
|
||||
using Newtonsoft.Json;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
@@ -81,7 +81,6 @@ namespace GitHub.Runner.Worker
|
||||
// logging
|
||||
long Write(string tag, string message);
|
||||
void QueueAttachFile(string type, string name, string filePath);
|
||||
void QueueSummaryFile(string name, string filePath, Guid stepRecordId);
|
||||
|
||||
// timeline record update methods
|
||||
void Start(string currentOperation = null);
|
||||
@@ -92,7 +91,8 @@ namespace GitHub.Runner.Worker
|
||||
void SetGitHubContext(string name, string value);
|
||||
void SetOutput(string name, string value, out string reference);
|
||||
void SetTimeout(TimeSpan? timeout);
|
||||
void AddIssue(Issue issue, string message = null);
|
||||
IReadOnlyIssue CreateIssue(IssueType issueType, string rawMessage, IssueMetadata metadata, bool writeToLog);
|
||||
void AddIssue(IReadOnlyIssue issue);
|
||||
void Progress(int percentage, string currentOperation = null);
|
||||
void UpdateDetailTimelineRecord(TimelineRecord record);
|
||||
|
||||
@@ -126,8 +126,10 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
private readonly TimelineRecord _record = new();
|
||||
private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new();
|
||||
private readonly List<IReadOnlyIssue> _embeddedIssueCollector;
|
||||
private readonly object _loggerLock = new();
|
||||
private readonly object _matchersLock = new();
|
||||
private readonly ExecutionContext _parentExecutionContext;
|
||||
|
||||
private event OnMatcherChanged _onMatcherChanged;
|
||||
|
||||
@@ -135,7 +137,6 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
private IPagingLogger _logger;
|
||||
private IJobServerQueue _jobServerQueue;
|
||||
private ExecutionContext _parentExecutionContext;
|
||||
|
||||
private Guid _mainTimelineId;
|
||||
private Guid _detailTimelineId;
|
||||
@@ -149,6 +150,29 @@ namespace GitHub.Runner.Worker
|
||||
private long _totalThrottlingDelayInMilliseconds = 0;
|
||||
private bool _stepTelemetryPublished = false;
|
||||
|
||||
public ExecutionContext()
|
||||
: this(null, false)
|
||||
{
|
||||
}
|
||||
|
||||
private ExecutionContext(ExecutionContext parent, bool embedded)
|
||||
{
|
||||
if (embedded)
|
||||
{
|
||||
ArgUtil.NotNull(parent, nameof(parent));
|
||||
}
|
||||
|
||||
_parentExecutionContext = parent;
|
||||
this.IsEmbedded = embedded;
|
||||
this.StepTelemetry = new ActionsStepTelemetry
|
||||
{
|
||||
IsEmbedded = embedded
|
||||
};
|
||||
|
||||
//Embedded Execution Contexts pseudo-inherit their parent's embeddedIssueCollector.
|
||||
_embeddedIssueCollector = embedded ? parent._embeddedIssueCollector : new();
|
||||
}
|
||||
|
||||
public Guid Id => _record.Id;
|
||||
public Guid EmbeddedId { get; private set; }
|
||||
public string ScopeName { get; private set; }
|
||||
@@ -161,7 +185,7 @@ namespace GitHub.Runner.Worker
|
||||
public Dictionary<string, VariableValue> JobOutputs { get; private set; }
|
||||
|
||||
public ActionsEnvironmentReference ActionsEnvironment { get; private set; }
|
||||
public ActionsStepTelemetry StepTelemetry { get; } = new ActionsStepTelemetry();
|
||||
public ActionsStepTelemetry StepTelemetry { get; private init; }
|
||||
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
|
||||
public IList<IFunctionInfo> ExpressionFunctions { get; } = new List<IFunctionInfo>();
|
||||
|
||||
@@ -186,7 +210,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// 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 bool IsEmbedded { get; private init; }
|
||||
|
||||
public TaskResult? Result
|
||||
{
|
||||
@@ -321,7 +345,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
Trace.Entering();
|
||||
|
||||
var child = new ExecutionContext();
|
||||
var child = new ExecutionContext(this, isEmbedded);
|
||||
child.Initialize(HostContext);
|
||||
child.Global = Global;
|
||||
child.ScopeName = scopeName;
|
||||
@@ -346,7 +370,6 @@ namespace GitHub.Runner.Worker
|
||||
child.ExpressionFunctions.Add(item);
|
||||
}
|
||||
child._cancellationTokenSource = cancellationTokenSource ?? new CancellationTokenSource();
|
||||
child._parentExecutionContext = this;
|
||||
child.EchoOnActionCommand = EchoOnActionCommand;
|
||||
|
||||
if (recordOrder != null)
|
||||
@@ -367,11 +390,9 @@ namespace GitHub.Runner.Worker
|
||||
child._logger.Setup(_mainTimelineId, recordId);
|
||||
}
|
||||
|
||||
child.IsEmbedded = isEmbedded;
|
||||
child.StepTelemetry.StepId = recordId;
|
||||
child.StepTelemetry.Stage = stage.ToString();
|
||||
child.StepTelemetry.IsEmbedded = isEmbedded;
|
||||
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName(); ;
|
||||
child.StepTelemetry.StepContextName = child.GetFullyQualifiedContextName();
|
||||
|
||||
return child;
|
||||
}
|
||||
@@ -413,13 +434,24 @@ namespace GitHub.Runner.Worker
|
||||
this.Warning($"The job has experienced {TimeSpan.FromMilliseconds(_totalThrottlingDelayInMilliseconds).TotalSeconds} seconds total delay caused by server throttling.");
|
||||
}
|
||||
|
||||
DateTime now = DateTime.UtcNow;
|
||||
_record.CurrentOperation = currentOperation ?? _record.CurrentOperation;
|
||||
_record.ResultCode = resultCode ?? _record.ResultCode;
|
||||
_record.FinishTime = DateTime.UtcNow;
|
||||
_record.FinishTime = now;
|
||||
_record.PercentComplete = 100;
|
||||
_record.Result = _record.Result ?? TaskResult.Succeeded;
|
||||
_record.State = TimelineRecordState.Completed;
|
||||
|
||||
// Before our main timeline's final QueueTimelineRecordUpdate,
|
||||
// inject any issues collected by embedded ExecutionContexts.
|
||||
if (!this.IsEmbedded)
|
||||
{
|
||||
foreach (var issue in _embeddedIssueCollector)
|
||||
{
|
||||
AddIssue(issue);
|
||||
}
|
||||
}
|
||||
|
||||
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
|
||||
|
||||
// complete all detail timeline records.
|
||||
@@ -427,7 +459,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
foreach (var record in _detailRecords)
|
||||
{
|
||||
record.Value.FinishTime = record.Value.FinishTime ?? DateTime.UtcNow;
|
||||
record.Value.FinishTime = record.Value.FinishTime ?? now;
|
||||
record.Value.PercentComplete = record.Value.PercentComplete ?? 100;
|
||||
record.Value.Result = record.Value.Result ?? TaskResult.Succeeded;
|
||||
record.Value.State = TimelineRecordState.Completed;
|
||||
@@ -438,17 +470,6 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
PublishStepTelemetry();
|
||||
|
||||
var stepResult = new StepResult();
|
||||
stepResult.ExternalID = _record.Id;
|
||||
stepResult.Conclusion = _record.Result ?? TaskResult.Succeeded;
|
||||
stepResult.Status = _record.State;
|
||||
stepResult.Number = _record.Order;
|
||||
stepResult.Name = _record.Name;
|
||||
stepResult.StartedAt = _record.StartTime;
|
||||
stepResult.CompletedAt = _record.FinishTime;
|
||||
|
||||
Global.StepsResult.Add(stepResult);
|
||||
|
||||
if (Root != this)
|
||||
{
|
||||
// only dispose TokenSource for step level ExecutionContext
|
||||
@@ -558,76 +579,89 @@ namespace GitHub.Runner.Worker
|
||||
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
|
||||
}
|
||||
|
||||
// This is not thread safe, the caller need to take lock before calling issue()
|
||||
public void AddIssue(Issue issue, string logMessage = null)
|
||||
// This is not thread safe, the caller needs to take lock before calling issue()
|
||||
public IReadOnlyIssue CreateIssue(IssueType issueType, string rawMessage, IssueMetadata metadata, bool writeToLog)
|
||||
{
|
||||
string refinedMessage = PrimitiveExtensions.TrimExcess(HostContext.SecretMasker.MaskSecrets(rawMessage), _maxIssueMessageLength);
|
||||
|
||||
var result = new Issue() {
|
||||
Type = issueType,
|
||||
Message = refinedMessage,
|
||||
};
|
||||
|
||||
if (metadata != null)
|
||||
{
|
||||
result.Category = metadata.Category;
|
||||
result.IsInfrastructureIssue = metadata.IsInfrastructureIssue;
|
||||
foreach (var kvp in metadata.Data)
|
||||
{
|
||||
result[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// It's important to keep track of the step number (key:stepNumber) and the line number (key:logFileLineNumber) of every issue that gets logged.
|
||||
// Actions UI from the run summary page use both values to easily link to an exact locations in logs where annotations originate from.
|
||||
if (_record.Order != null)
|
||||
{
|
||||
result["stepNumber"] = _record.Order.ToString();
|
||||
}
|
||||
|
||||
string wellKnownTag = null;
|
||||
Int32? previousCountForIssueType = null;
|
||||
switch (issueType)
|
||||
{
|
||||
case IssueType.Error:
|
||||
wellKnownTag = WellKnownTags.Error;
|
||||
previousCountForIssueType = _record.ErrorCount++;
|
||||
break;
|
||||
case IssueType.Warning:
|
||||
wellKnownTag = WellKnownTags.Warning;
|
||||
previousCountForIssueType = _record.WarningCount++;
|
||||
break;
|
||||
case IssueType.Notice:
|
||||
wellKnownTag = WellKnownTags.Notice;
|
||||
previousCountForIssueType = _record.NoticeCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(wellKnownTag))
|
||||
{
|
||||
if (writeToLog)
|
||||
{
|
||||
//Note that ::Write() has it's own secret masking logic
|
||||
string logText = metadata?.LogMessageOverride ?? result.Message;
|
||||
if (!string.IsNullOrEmpty(logText))
|
||||
{
|
||||
long logLineNumber = Write(wellKnownTag, logText);
|
||||
result["logFileLineNumber"] = logLineNumber.ToString();
|
||||
}
|
||||
}
|
||||
if (previousCountForIssueType.GetValueOrDefault(0) < _maxIssueCount)
|
||||
{
|
||||
_record.Issues.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// This is not thread safe, the caller needs to take lock before calling issue()
|
||||
public void AddIssue(IReadOnlyIssue issue)
|
||||
{
|
||||
ArgUtil.NotNull(issue, nameof(issue));
|
||||
|
||||
if (string.IsNullOrEmpty(logMessage))
|
||||
// Embedded ExecutionContexts (a.k.a. Composite actions) should never upload a timeline record to the server.
|
||||
// Instead, we store processed issues on a shared (psuedo-inherited) list (belonging to the closest
|
||||
// non-embedded ancestor ExecutionContext) so that they can be processed when that ancestor completes.
|
||||
if (this.IsEmbedded)
|
||||
{
|
||||
logMessage = issue.Message;
|
||||
_embeddedIssueCollector.Add(issue);
|
||||
}
|
||||
|
||||
issue.Message = HostContext.SecretMasker.MaskSecrets(issue.Message);
|
||||
if (issue.Message.Length > _maxIssueMessageLength)
|
||||
else
|
||||
{
|
||||
issue.Message = issue.Message[.._maxIssueMessageLength];
|
||||
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
|
||||
}
|
||||
|
||||
// Tracking the line number (logFileLineNumber) and step number (stepNumber) for each issue that gets created
|
||||
// Actions UI from the run summary page use both values to easily link to an exact locations in logs where annotations originate from
|
||||
if (_record.Order != null)
|
||||
{
|
||||
issue.Data["stepNumber"] = _record.Order.ToString();
|
||||
}
|
||||
|
||||
if (issue.Type == IssueType.Error)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(logMessage))
|
||||
{
|
||||
long logLineNumber = Write(WellKnownTags.Error, logMessage);
|
||||
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
|
||||
}
|
||||
|
||||
if (_record.ErrorCount < _maxIssueCount)
|
||||
{
|
||||
_record.Issues.Add(issue);
|
||||
}
|
||||
|
||||
_record.ErrorCount++;
|
||||
}
|
||||
else if (issue.Type == IssueType.Warning)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(logMessage))
|
||||
{
|
||||
long logLineNumber = Write(WellKnownTags.Warning, logMessage);
|
||||
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
|
||||
}
|
||||
|
||||
if (_record.WarningCount < _maxIssueCount)
|
||||
{
|
||||
_record.Issues.Add(issue);
|
||||
}
|
||||
|
||||
_record.WarningCount++;
|
||||
}
|
||||
else if (issue.Type == IssueType.Notice)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(logMessage))
|
||||
{
|
||||
long logLineNumber = Write(WellKnownTags.Notice, logMessage);
|
||||
issue.Data["logFileLineNumber"] = logLineNumber.ToString();
|
||||
}
|
||||
|
||||
if (_record.NoticeCount < _maxIssueCount)
|
||||
{
|
||||
_record.Issues.Add(issue);
|
||||
}
|
||||
|
||||
_record.NoticeCount++;
|
||||
}
|
||||
|
||||
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
|
||||
}
|
||||
|
||||
public void UpdateDetailTimelineRecord(TimelineRecord record)
|
||||
@@ -722,9 +756,6 @@ namespace GitHub.Runner.Worker
|
||||
// ActionsStepTelemetry for entire job
|
||||
Global.StepsTelemetry = new List<ActionsStepTelemetry>();
|
||||
|
||||
// Steps results for entire job
|
||||
Global.StepsResult = new List<StepResult>();
|
||||
|
||||
// Job Outputs
|
||||
JobOutputs = new Dictionary<string, VariableValue>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
@@ -862,18 +893,6 @@ namespace GitHub.Runner.Worker
|
||||
_jobServerQueue.QueueFileUpload(_mainTimelineId, _record.Id, type, name, filePath, deleteSource: false);
|
||||
}
|
||||
|
||||
public void QueueSummaryFile(string name, string filePath, Guid stepRecordId)
|
||||
{
|
||||
ArgUtil.NotNullOrEmpty(name, nameof(name));
|
||||
ArgUtil.NotNullOrEmpty(filePath, nameof(filePath));
|
||||
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
throw new FileNotFoundException($"Can't upload (name:{name}) file: {filePath}. File does not exist.");
|
||||
}
|
||||
_jobServerQueue.QueueResultsUpload(stepRecordId, name, filePath, ChecksAttachmentType.StepSummary, deleteSource: false, finalize: true, firstBlock: true, totalLines: 0);
|
||||
}
|
||||
|
||||
// Add OnMatcherChanged
|
||||
public void Add(OnMatcherChanged handler)
|
||||
{
|
||||
@@ -1003,16 +1022,7 @@ namespace GitHub.Runner.Worker
|
||||
if ((issue.Type == IssueType.Error || issue.Type == IssueType.Warning) &&
|
||||
!string.IsNullOrEmpty(issue.Message))
|
||||
{
|
||||
string issueTelemetry;
|
||||
if (issue.Message.Length > _maxIssueMessageLengthInTelemetry)
|
||||
{
|
||||
issueTelemetry = $"{issue.Message[.._maxIssueMessageLengthInTelemetry]}";
|
||||
}
|
||||
else
|
||||
{
|
||||
issueTelemetry = issue.Message;
|
||||
}
|
||||
|
||||
string issueTelemetry = PrimitiveExtensions.TrimExcess(issue.Message, _maxIssueMessageLengthInTelemetry);
|
||||
StepTelemetry.ErrorMessages.Add(issueTelemetry);
|
||||
|
||||
// Only send over the first 3 issues to avoid sending too much data.
|
||||
@@ -1186,19 +1196,23 @@ namespace GitHub.Runner.Worker
|
||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||
public static void Error(this IExecutionContext context, string message)
|
||||
{
|
||||
context.AddIssue(new Issue() { Type = IssueType.Error, Message = message });
|
||||
var issue = context.CreateIssue(IssueType.Error, message, null, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||
public static void InfrastructureError(this IExecutionContext context, string message)
|
||||
{
|
||||
context.AddIssue(new Issue() { Type = IssueType.Error, Message = message, IsInfrastructureIssue = true });
|
||||
var metadata = new IssueMetadata(null, true, null, Enumerable.Empty<KeyValuePair<string, string>>());
|
||||
var issue = context.CreateIssue(IssueType.Error, message, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||
public static void Warning(this IExecutionContext context, string message)
|
||||
{
|
||||
context.AddIssue(new Issue() { Type = IssueType.Warning, Message = message });
|
||||
var issue = context.CreateIssue(IssueType.Warning, message, null, true);
|
||||
context.AddIssue(issue);
|
||||
}
|
||||
|
||||
// Do not add a format string overload. See comment on ExecutionContext.Write().
|
||||
|
||||
@@ -204,23 +204,13 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
var attachmentName = !context.IsEmbedded
|
||||
? context.Id.ToString()
|
||||
var attachmentName = !context.IsEmbedded
|
||||
? context.Id.ToString()
|
||||
: context.EmbeddedId.ToString();
|
||||
|
||||
Trace.Info($"Queueing file ({filePath}) for attachment upload ({attachmentName})");
|
||||
// Attachments must be added to the parent context (job), not the current context (step)
|
||||
context.Root.QueueAttachFile(ChecksAttachmentType.StepSummary, attachmentName, scrubbedFilePath);
|
||||
|
||||
// Dual upload the same files to Results Service
|
||||
context.Global.Variables.TryGetValue("system.github.results_endpoint", out string resultsReceiverEndpoint);
|
||||
if (resultsReceiverEndpoint != null)
|
||||
{
|
||||
Trace.Info($"Queueing results file ({filePath}) for attachment upload ({attachmentName})");
|
||||
var stepId = context.Id;
|
||||
// Attachments must be added to the parent context (job), not the current context (step)
|
||||
context.Root.QueueSummaryFile(attachmentName, scrubbedFilePath, stepId);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GitHub.Actions.RunService.WebApi;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
@@ -17,7 +16,6 @@ namespace GitHub.Runner.Worker
|
||||
public IList<String> FileTable { get; set; }
|
||||
public IDictionary<String, IDictionary<String, String>> JobDefaults { get; set; }
|
||||
public List<ActionsStepTelemetry> StepsTelemetry { get; set; }
|
||||
public List<StepResult> StepsResult { get; set; }
|
||||
public List<JobTelemetry> JobTelemetry { get; set; }
|
||||
public TaskOrchestrationPlanReference Plan { get; set; }
|
||||
public List<string> PrependPath { get; set; }
|
||||
|
||||
@@ -294,7 +294,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
// Evaluation error
|
||||
Trace.Info("Caught exception from expression for embedded step.env");
|
||||
step.ExecutionContext.Error(ex);
|
||||
step.ExecutionContext.Complete(TaskResult.Failed);
|
||||
SetStepConclusion(step, TaskResult.Failed);
|
||||
}
|
||||
|
||||
// Register Callback
|
||||
|
||||
@@ -38,17 +38,8 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
// Update the env dictionary.
|
||||
AddInputsToEnvironment();
|
||||
|
||||
IDockerCommandManager dockerManager = null;
|
||||
IContainerHookManager containerHookManager = null;
|
||||
if (FeatureManager.IsContainerHooksEnabled(ExecutionContext.Global.Variables))
|
||||
{
|
||||
containerHookManager = HostContext.GetService<IContainerHookManager>();
|
||||
}
|
||||
else
|
||||
{
|
||||
dockerManager = HostContext.GetService<IDockerCommandManager>();
|
||||
}
|
||||
|
||||
var dockerManager = HostContext.GetService<IDockerCommandManager>();
|
||||
var containerHookManager = HostContext.GetService<IContainerHookManager>();
|
||||
string dockerFile = null;
|
||||
|
||||
// container image haven't built/pull
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
@@ -97,7 +98,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
var matchers = _matchers;
|
||||
|
||||
// Strip color codes
|
||||
var stripped = line.Contains(_colorCodePrefix) ? _colorCodeRegex.Replace(line, string.Empty) : line;
|
||||
var refinedLine = line.Contains(_colorCodePrefix) ? _colorCodeRegex.Replace(line, string.Empty) : line;
|
||||
|
||||
foreach (var matcher in matchers)
|
||||
{
|
||||
@@ -107,8 +108,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
// Match
|
||||
try
|
||||
{
|
||||
match = matcher.Match(stripped);
|
||||
|
||||
match = matcher.Match(refinedLine);
|
||||
break;
|
||||
}
|
||||
catch (RegexMatchTimeoutException ex)
|
||||
@@ -116,7 +116,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
if (attempt < _maxAttempts)
|
||||
{
|
||||
// Debug
|
||||
_executionContext.Debug($"Timeout processing issue matcher '{matcher.Owner}' against line '{stripped}'. Exception: {ex.ToString()}");
|
||||
_executionContext.Debug($"Timeout processing issue matcher '{matcher.Owner}' against line '{refinedLine}'. Exception: {ex}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -139,12 +139,10 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
// Convert to issue
|
||||
var issue = ConvertToIssue(match);
|
||||
|
||||
if (issue != null)
|
||||
{
|
||||
// Log issue
|
||||
_executionContext.AddIssue(issue, stripped);
|
||||
|
||||
_executionContext.AddIssue(issue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -196,7 +194,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
}
|
||||
|
||||
private DTWebApi.Issue ConvertToIssue(IssueMatch match)
|
||||
private DTWebApi.IReadOnlyIssue ConvertToIssue(IssueMatch match)
|
||||
{
|
||||
// Validate the message
|
||||
if (string.IsNullOrWhiteSpace(match.Message))
|
||||
@@ -225,18 +223,14 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
return null;
|
||||
}
|
||||
|
||||
var issue = new DTWebApi.Issue
|
||||
{
|
||||
Message = match.Message,
|
||||
Type = issueType,
|
||||
};
|
||||
var issueData = new Dictionary<string, string>();
|
||||
|
||||
// Line
|
||||
if (!string.IsNullOrEmpty(match.Line))
|
||||
{
|
||||
if (int.TryParse(match.Line, NumberStyles.None, CultureInfo.InvariantCulture, out var line))
|
||||
{
|
||||
issue.Data["line"] = line.ToString(CultureInfo.InvariantCulture);
|
||||
issueData["line"] = line.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -249,7 +243,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
{
|
||||
if (int.TryParse(match.Column, NumberStyles.None, CultureInfo.InvariantCulture, out var column))
|
||||
{
|
||||
issue.Data["col"] = column.ToString(CultureInfo.InvariantCulture);
|
||||
issueData["col"] = column.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -260,7 +254,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
// Code
|
||||
if (!string.IsNullOrWhiteSpace(match.Code))
|
||||
{
|
||||
issue.Data["code"] = match.Code.Trim();
|
||||
issueData["code"] = match.Code.Trim();
|
||||
}
|
||||
|
||||
// File
|
||||
@@ -312,7 +306,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
var relativePath = file.Substring(repositoryPath.Length).TrimStart(Path.DirectorySeparatorChar);
|
||||
|
||||
// Prefer `/` on all platforms
|
||||
issue.Data["file"] = relativePath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
issueData["file"] = relativePath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -327,9 +321,11 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_executionContext.Debug($"Dropping file value '{match.File}' and fromPath value '{match.FromPath}'. Exception during validation: {ex.ToString()}");
|
||||
_executionContext.Debug($"Dropping file value '{match.File}' and fromPath value '{match.FromPath}'. Exception during validation: {ex}");
|
||||
}
|
||||
|
||||
var metadata = new IssueMetadata(null, false, match.SourceText, issueData);
|
||||
var issue = _executionContext.CreateIssue(issueType, match.Message, metadata, true);
|
||||
return issue;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,31 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
public delegate void OnMatcherChanged(object sender, MatcherChangedEventArgs e);
|
||||
|
||||
|
||||
public sealed class IssueMetadata
|
||||
{
|
||||
public IssueMetadata(string key, string value)
|
||||
: this(null, false, null, new []{ KeyValuePair.Create(key, value) })
|
||||
{
|
||||
}
|
||||
|
||||
public IssueMetadata(string category, bool infrastructureIssue, string logMessageOverride, IEnumerable<KeyValuePair<string, string>> data)
|
||||
{
|
||||
this.Category = category;
|
||||
this.IsInfrastructureIssue = infrastructureIssue;
|
||||
this.LogMessageOverride = logMessageOverride;
|
||||
|
||||
// Close-over the incoming IEnumerable to force immediate evaluation.
|
||||
var empty = Enumerable.Empty<KeyValuePair<string, string>>();
|
||||
this.Data = new Dictionary<string, string>(data ?? empty, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public readonly string Category;
|
||||
public readonly bool IsInfrastructureIssue;
|
||||
public readonly string LogMessageOverride;
|
||||
public readonly IEnumerable<KeyValuePair<string, string>> Data;
|
||||
}
|
||||
|
||||
public sealed class MatcherChangedEventArgs : EventArgs
|
||||
{
|
||||
public MatcherChangedEventArgs(IssueMatcherConfig config)
|
||||
@@ -69,7 +94,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
if (regexMatch.Success)
|
||||
{
|
||||
return new IssueMatch(null, pattern, regexMatch.Groups, DefaultSeverity);
|
||||
return new IssueMatch(line, null, pattern, regexMatch.Groups, DefaultSeverity);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -110,13 +135,13 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
|
||||
// Return
|
||||
return new IssueMatch(runningMatch, pattern, regexMatch.Groups, DefaultSeverity);
|
||||
return new IssueMatch(line, runningMatch, pattern, regexMatch.Groups, DefaultSeverity);
|
||||
}
|
||||
// Not the last pattern
|
||||
else
|
||||
{
|
||||
// Store the match
|
||||
_state[i] = new IssueMatch(runningMatch, pattern, regexMatch.Groups);
|
||||
_state[i] = new IssueMatch(line, runningMatch, pattern, regexMatch.Groups);
|
||||
}
|
||||
}
|
||||
// Not matched
|
||||
@@ -184,8 +209,9 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public sealed class IssueMatch
|
||||
{
|
||||
public IssueMatch(IssueMatch runningMatch, IssuePattern pattern, GroupCollection groups, string defaultSeverity = null)
|
||||
public IssueMatch(string sourceText, IssueMatch runningMatch, IssuePattern pattern, GroupCollection groups, string defaultSeverity = null)
|
||||
{
|
||||
SourceText = sourceText;
|
||||
File = runningMatch?.File ?? GetValue(groups, pattern.File);
|
||||
Line = runningMatch?.Line ?? GetValue(groups, pattern.Line);
|
||||
Column = runningMatch?.Column ?? GetValue(groups, pattern.Column);
|
||||
@@ -200,6 +226,8 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
public string SourceText { get; }
|
||||
|
||||
public string File { get; }
|
||||
|
||||
public string Line { get; }
|
||||
@@ -455,7 +483,7 @@ namespace GitHub.Runner.Worker
|
||||
if (Loop && Message == null)
|
||||
{
|
||||
throw new ArgumentException($"The {_loopPropertyName} pattern must set '{_messagePropertyName}'");
|
||||
}
|
||||
}
|
||||
|
||||
var regex = new Regex(Pattern ?? string.Empty, RegexOptions);
|
||||
var groupCount = regex.GetGroupNumbers().Length;
|
||||
|
||||
@@ -306,13 +306,13 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
actionRunner.EvaluateDisplayName(contextData, context, out _);
|
||||
actionRunner.TryEvaluateDisplayName(contextData, context);
|
||||
jobSteps.Add(actionRunner);
|
||||
|
||||
if (prepareResult.PreStepTracker.TryGetValue(step.Id, out var preStep))
|
||||
{
|
||||
Trace.Info($"Adding pre-{action.DisplayName}.");
|
||||
preStep.EvaluateDisplayName(contextData, context, out _);
|
||||
preStep.TryEvaluateDisplayName(contextData, context);
|
||||
preStep.DisplayName = $"Pre {preStep.DisplayName}";
|
||||
preJobSteps.Add(preStep);
|
||||
}
|
||||
@@ -321,10 +321,7 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
if (message.Variables.TryGetValue("system.workflowFileFullPath", out VariableValue workflowFileFullPath))
|
||||
{
|
||||
var usesLogText = $"Uses: {workflowFileFullPath.Value}";
|
||||
var reference = GetWorkflowReference(message.Variables);
|
||||
context.Output(usesLogText + reference);
|
||||
|
||||
context.Output($"Uses: {workflowFileFullPath.Value}");
|
||||
if (message.ContextData.TryGetValue("inputs", out var pipelineContextData))
|
||||
{
|
||||
var inputs = pipelineContextData.AssertDictionary("inputs");
|
||||
@@ -338,11 +335,11 @@ namespace GitHub.Runner.Worker
|
||||
context.Output("##[endgroup]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(message.JobDisplayName))
|
||||
{
|
||||
context.Output($"Complete job name: {message.JobDisplayName}");
|
||||
if (!string.IsNullOrWhiteSpace(message.JobDisplayName))
|
||||
{
|
||||
context.Output($"Complete job name: {message.JobDisplayName}");
|
||||
}
|
||||
}
|
||||
|
||||
var intraActionStates = new Dictionary<Guid, Dictionary<string, string>>();
|
||||
@@ -455,24 +452,6 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
private string GetWorkflowReference(IDictionary<string, VariableValue> variables)
|
||||
{
|
||||
var reference = "";
|
||||
if (variables.TryGetValue("system.workflowFileSha", out VariableValue workflowFileSha))
|
||||
{
|
||||
if (variables.TryGetValue("system.workflowFileRef", out VariableValue workflowFileRef)
|
||||
&& !string.IsNullOrEmpty(workflowFileRef.Value))
|
||||
{
|
||||
reference += $"@{workflowFileRef.Value} ({workflowFileSha.Value})";
|
||||
}
|
||||
else
|
||||
{
|
||||
reference += $"@{workflowFileSha.Value}";
|
||||
}
|
||||
}
|
||||
return reference;
|
||||
}
|
||||
|
||||
public void FinalizeJob(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message, DateTime jobStartTimeUtc)
|
||||
{
|
||||
Trace.Entering();
|
||||
@@ -681,8 +660,9 @@ namespace GitHub.Runner.Worker
|
||||
var freeSpaceInMB = driveInfo.AvailableFreeSpace / 1024 / 1024;
|
||||
if (freeSpaceInMB < lowDiskSpaceThreshold)
|
||||
{
|
||||
var issue = new Issue() { Type = IssueType.Warning, Message = $"You are running out of disk space. The runner will stop working when the machine runs out of disk space. Free space left: {freeSpaceInMB} MB" };
|
||||
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.LowDiskSpace;
|
||||
var message = $"You are running out of disk space. The runner will stop working when the machine runs out of disk space. Free space left: {freeSpaceInMB} MB";
|
||||
var metadata = new IssueMetadata(Constants.Runner.InternalTelemetryIssueDataKey, Constants.Runner.LowDiskSpace);
|
||||
var issue = context.CreateIssue(IssueType.Warning, message, metadata, true);
|
||||
context.AddIssue(issue);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
@@ -33,18 +32,5 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
await _runAsync(ExecutionContext, _data);
|
||||
}
|
||||
|
||||
public bool TryUpdateDisplayName(out bool updated)
|
||||
{
|
||||
updated = false;
|
||||
return !string.IsNullOrEmpty(this.DisplayName);
|
||||
}
|
||||
|
||||
public bool EvaluateDisplayName(DictionaryContextData contextData, IExecutionContext context, out bool updated)
|
||||
{
|
||||
updated = false;
|
||||
return !string.IsNullOrEmpty(this.DisplayName);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Common.Util;
|
||||
@@ -39,34 +40,21 @@ namespace GitHub.Runner.Worker
|
||||
Trace.Info("Job ID {0}", message.JobId);
|
||||
|
||||
DateTime jobStartTimeUtc = DateTime.UtcNow;
|
||||
IRunnerService server = null;
|
||||
|
||||
ServiceEndpoint systemConnection = message.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||
if (string.Equals(message.MessageType, JobRequestMessageTypes.RunnerJobRequest, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var runServer = HostContext.GetService<IRunServer>();
|
||||
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
|
||||
await runServer.ConnectAsync(systemConnection.Url, jobServerCredential);
|
||||
server = runServer;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Setup the job server and job server queue.
|
||||
var jobServer = HostContext.GetService<IJobServer>();
|
||||
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
|
||||
Uri jobServerUrl = systemConnection.Url;
|
||||
|
||||
Trace.Info($"Creating job server with URL: {jobServerUrl}");
|
||||
// jobServerQueue is the throttling reporter.
|
||||
_jobServerQueue = HostContext.GetService<IJobServerQueue>();
|
||||
VssConnection jobConnection = VssUtil.CreateConnection(jobServerUrl, jobServerCredential, new DelegatingHandler[] { new ThrottlingReportHandler(_jobServerQueue) });
|
||||
await jobServer.ConnectAsync(jobConnection);
|
||||
// Setup the job server and job server queue.
|
||||
var jobServer = HostContext.GetService<IJobServer>();
|
||||
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
|
||||
Uri jobServerUrl = systemConnection.Url;
|
||||
|
||||
_jobServerQueue.Start(message);
|
||||
server = jobServer;
|
||||
}
|
||||
|
||||
Trace.Info($"Creating job server with URL: {jobServerUrl}");
|
||||
// jobServerQueue is the throttling reporter.
|
||||
_jobServerQueue = HostContext.GetService<IJobServerQueue>();
|
||||
VssConnection jobConnection = VssUtil.CreateConnection(jobServerUrl, jobServerCredential, new DelegatingHandler[] { new ThrottlingReportHandler(_jobServerQueue) });
|
||||
await jobServer.ConnectAsync(jobConnection);
|
||||
|
||||
_jobServerQueue.Start(message);
|
||||
HostContext.WritePerfCounter($"WorkerJobServerQueueStarted_{message.RequestId.ToString()}");
|
||||
|
||||
IExecutionContext jobContext = null;
|
||||
@@ -96,7 +84,8 @@ namespace GitHub.Runner.Worker
|
||||
default:
|
||||
throw new ArgumentException(HostContext.RunnerShutdownReason.ToString(), nameof(HostContext.RunnerShutdownReason));
|
||||
}
|
||||
jobContext.AddIssue(new Issue() { Type = IssueType.Error, Message = errorMessage });
|
||||
var issue = jobContext.CreateIssue(IssueType.Error, errorMessage, null, true);
|
||||
jobContext.AddIssue(issue);
|
||||
});
|
||||
|
||||
// Validate directory permissions.
|
||||
@@ -111,7 +100,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
Trace.Error(ex);
|
||||
jobContext.Error(ex);
|
||||
return await CompleteJobAsync(server, jobContext, message, TaskResult.Failed);
|
||||
return await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed);
|
||||
}
|
||||
|
||||
if (jobContext.Global.WriteDebug)
|
||||
@@ -148,7 +137,7 @@ namespace GitHub.Runner.Worker
|
||||
// don't log error issue to job ExecutionContext, since server owns the job level issue
|
||||
Trace.Error($"Job is cancelled during initialize.");
|
||||
Trace.Error($"Caught exception: {ex}");
|
||||
return await CompleteJobAsync(server, jobContext, message, TaskResult.Canceled);
|
||||
return await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Canceled);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -156,7 +145,7 @@ namespace GitHub.Runner.Worker
|
||||
// don't log error issue to job ExecutionContext, since server owns the job level issue
|
||||
Trace.Error($"Job initialize failed.");
|
||||
Trace.Error($"Caught exception from {nameof(jobExtension.InitializeJob)}: {ex}");
|
||||
return await CompleteJobAsync(server, jobContext, message, TaskResult.Failed);
|
||||
return await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed);
|
||||
}
|
||||
|
||||
// trace out all steps
|
||||
@@ -193,7 +182,7 @@ namespace GitHub.Runner.Worker
|
||||
// Log the error and fail the job.
|
||||
Trace.Error($"Caught exception from job steps {nameof(StepsRunner)}: {ex}");
|
||||
jobContext.Error(ex);
|
||||
return await CompleteJobAsync(server, jobContext, message, TaskResult.Failed);
|
||||
return await CompleteJobAsync(jobServer, jobContext, message, TaskResult.Failed);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -204,7 +193,7 @@ namespace GitHub.Runner.Worker
|
||||
Trace.Info($"Job result after all job steps finish: {jobContext.Result ?? TaskResult.Succeeded}");
|
||||
|
||||
Trace.Info("Completing the job execution context.");
|
||||
return await CompleteJobAsync(server, jobContext, message);
|
||||
return await CompleteJobAsync(jobServer, jobContext, message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -218,66 +207,6 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<TaskResult> CompleteJobAsync(IRunnerService server, IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message, TaskResult? taskResult = null)
|
||||
{
|
||||
if (server is IRunServer runServer)
|
||||
{
|
||||
return await CompleteJobAsync(runServer, jobContext, message, taskResult);
|
||||
}
|
||||
else if (server is IJobServer jobServer)
|
||||
{
|
||||
return await CompleteJobAsync(jobServer, jobContext, message, taskResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<TaskResult> CompleteJobAsync(IRunServer runServer, IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message, TaskResult? taskResult = null)
|
||||
{
|
||||
jobContext.Debug($"Finishing: {message.JobDisplayName}");
|
||||
TaskResult result = jobContext.Complete(taskResult);
|
||||
if (jobContext.Global.Variables.TryGetValue("Node12ActionsWarnings", out var node12Warnings))
|
||||
{
|
||||
var actions = string.Join(", ", StringUtil.ConvertFromJson<HashSet<string>>(node12Warnings));
|
||||
jobContext.Warning(string.Format(Constants.Runner.Node12DetectedAfterEndOfLife, actions));
|
||||
}
|
||||
|
||||
// Make sure to clean temp after file upload since they may be pending fileupload still use the TEMP dir.
|
||||
_tempDirectoryManager?.CleanupTempDirectory();
|
||||
|
||||
// Load any upgrade telemetry
|
||||
LoadFromTelemetryFile(jobContext.Global.JobTelemetry);
|
||||
|
||||
// Make sure we don't submit secrets as telemetry
|
||||
MaskTelemetrySecrets(jobContext.Global.JobTelemetry);
|
||||
|
||||
Trace.Info($"Raising job completed against run service");
|
||||
var completeJobRetryLimit = 5;
|
||||
var exceptions = new List<Exception>();
|
||||
while (completeJobRetryLimit-- > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
await runServer.CompleteJobAsync(message.Plan.PlanId, message.JobId, result, jobContext.JobOutputs, jobContext.Global.StepsResult, default);
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Error($"Catch exception while attempting to complete job {message.JobId}, job request {message.RequestId}.");
|
||||
Trace.Error(ex);
|
||||
exceptions.Add(ex);
|
||||
}
|
||||
|
||||
// delay 5 seconds before next retry.
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
|
||||
// rethrow exceptions from all attempts.
|
||||
throw new AggregateException(exceptions);
|
||||
}
|
||||
|
||||
private async Task<TaskResult> CompleteJobAsync(IJobServer jobServer, IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message, TaskResult? taskResult = null)
|
||||
{
|
||||
jobContext.Debug($"Finishing: {message.JobDisplayName}");
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.DistributedTask.Expressions2;
|
||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
@@ -11,6 +14,8 @@ using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker.Expressions;
|
||||
using ObjectTemplating = GitHub.DistributedTask.ObjectTemplating;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
@@ -21,8 +26,6 @@ namespace GitHub.Runner.Worker
|
||||
string DisplayName { get; set; }
|
||||
IExecutionContext ExecutionContext { get; set; }
|
||||
TemplateToken Timeout { get; }
|
||||
bool TryUpdateDisplayName(out bool updated);
|
||||
bool EvaluateDisplayName(DictionaryContextData contextData, IExecutionContext context, out bool updated);
|
||||
Task RunAsync();
|
||||
}
|
||||
|
||||
@@ -192,12 +195,6 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is our last, best chance to expand the display name. (At this point, all the requirements for successful expansion should be met.)
|
||||
// That being said, evaluating the display name should still be considered as a "best effort" exercise. (It's not critical or paramount.)
|
||||
// For that reason, we call a safe "Try..." wrapper method to ensure that any potential problems we encounter in evaluating the display name
|
||||
// don't interfere with our ultimate goal within this code block: evaluation of the condition.
|
||||
step.TryUpdateDisplayName(out _);
|
||||
|
||||
try
|
||||
{
|
||||
var templateEvaluator = step.ExecutionContext.ToPipelineTemplateEvaluator(conditionTraceWriter);
|
||||
@@ -259,6 +256,14 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
private async Task RunStepAsync(IStep step, CancellationToken jobCancellationToken)
|
||||
{
|
||||
// Check to see if we can expand the display name
|
||||
if (step is IActionRunner actionRunner &&
|
||||
actionRunner.Stage == ActionRunStage.Main &&
|
||||
actionRunner.TryEvaluateDisplayName(step.ExecutionContext.ExpressionValues, step.ExecutionContext))
|
||||
{
|
||||
step.ExecutionContext.UpdateTimelineRecordDisplayName(actionRunner.DisplayName);
|
||||
}
|
||||
|
||||
// Start the step
|
||||
Trace.Info("Starting the step.");
|
||||
step.ExecutionContext.Debug($"Starting: {step.DisplayName}");
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using GitHub.Services.OAuth;
|
||||
|
||||
namespace GitHub.Services.Common
|
||||
{
|
||||
public static class VssCredentialsExtension
|
||||
{
|
||||
public static VssOAuthCredential ToOAuthCredentials(
|
||||
this VssCredentials credentials)
|
||||
{
|
||||
if (credentials.Federated.CredentialType == VssCredentialsType.OAuth)
|
||||
{
|
||||
return credentials.Federated as VssOAuthCredential;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,20 +12,20 @@ namespace GitHub.Services.Common
|
||||
public class RawHttpMessageHandler: HttpMessageHandler
|
||||
{
|
||||
public RawHttpMessageHandler(
|
||||
FederatedCredential credentials)
|
||||
VssOAuthCredential credentials)
|
||||
: this(credentials, new RawClientHttpRequestSettings())
|
||||
{
|
||||
}
|
||||
|
||||
public RawHttpMessageHandler(
|
||||
FederatedCredential credentials,
|
||||
VssOAuthCredential credentials,
|
||||
RawClientHttpRequestSettings settings)
|
||||
: this(credentials, settings, new HttpClientHandler())
|
||||
{
|
||||
}
|
||||
|
||||
public RawHttpMessageHandler(
|
||||
FederatedCredential credentials,
|
||||
VssOAuthCredential credentials,
|
||||
RawClientHttpRequestSettings settings,
|
||||
HttpMessageHandler innerHandler)
|
||||
{
|
||||
@@ -56,7 +56,7 @@ namespace GitHub.Services.Common
|
||||
/// <summary>
|
||||
/// Gets the credentials associated with this handler.
|
||||
/// </summary>
|
||||
public FederatedCredential Credentials
|
||||
public VssOAuthCredential Credentials
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
@@ -111,7 +111,7 @@ namespace GitHub.Services.Common
|
||||
// Ensure that we attempt to use the most appropriate authentication mechanism by default.
|
||||
if (m_tokenProvider == null)
|
||||
{
|
||||
m_tokenProvider = this.Credentials.CreateTokenProvider(request.RequestUri, null, null);
|
||||
m_tokenProvider = this.Credentials.GetTokenProvider(request.RequestUri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace GitHub.Services.Common
|
||||
private CredentialWrapper m_credentialWrapper;
|
||||
private object m_thisLock;
|
||||
private const Int32 m_maxAuthRetries = 3;
|
||||
private IssuedTokenProvider m_tokenProvider;
|
||||
private VssOAuthTokenProvider m_tokenProvider;
|
||||
|
||||
//.Net Core does not attempt NTLM schema on Linux, unless ICredentials is a CredentialCache instance
|
||||
//This workaround may not be needed after this corefx fix is consumed: https://github.com/dotnet/corefx/pull/7923
|
||||
|
||||
@@ -27,6 +27,18 @@ namespace GitHub.Services.Common
|
||||
}
|
||||
}
|
||||
|
||||
public static string TrimExcess(string text, int maxLength)
|
||||
{
|
||||
string result = text;
|
||||
|
||||
if (!string.IsNullOrEmpty(result) && result.Length > maxLength)
|
||||
{
|
||||
result = result.Substring(0, maxLength);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string ToBase64StringNoPaddingFromString(string utf8String)
|
||||
{
|
||||
return ToBase64StringNoPadding(Encoding.UTF8.GetBytes(utf8String));
|
||||
@@ -46,7 +58,7 @@ namespace GitHub.Services.Common
|
||||
|
||||
//These methods convert To and From base64 strings without padding
|
||||
//for JWT scenarios
|
||||
//code taken from the JWS spec here:
|
||||
//code taken from the JWS spec here:
|
||||
//http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-08#appendix-C
|
||||
public static String ToBase64StringNoPadding(this byte[] bytes)
|
||||
{
|
||||
|
||||
@@ -42,10 +42,9 @@ namespace GitHub.DistributedTask.Pipelines
|
||||
IList<String> fileTable,
|
||||
TemplateToken jobOutputs,
|
||||
IList<TemplateToken> defaults,
|
||||
ActionsEnvironmentReference actionsEnvironment,
|
||||
String messageType = JobRequestMessageTypes.PipelineAgentJobRequest)
|
||||
ActionsEnvironmentReference actionsEnvironment)
|
||||
{
|
||||
this.MessageType = messageType;
|
||||
this.MessageType = JobRequestMessageTypes.PipelineAgentJobRequest;
|
||||
this.Plan = plan;
|
||||
this.JobId = jobId;
|
||||
this.JobDisplayName = jobDisplayName;
|
||||
|
||||
@@ -455,6 +455,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
private readonly String[] s_expressionValueNames = new[]
|
||||
{
|
||||
PipelineTemplateConstants.GitHub,
|
||||
PipelineTemplateConstants.Needs,
|
||||
PipelineTemplateConstants.Strategy,
|
||||
PipelineTemplateConstants.Matrix,
|
||||
PipelineTemplateConstants.Needs,
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using GitHub.Services.Common;
|
||||
|
||||
namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
public interface IReadOnlyIssue
|
||||
{
|
||||
IssueType Type { get; }
|
||||
string Category { get; }
|
||||
string Message { get; }
|
||||
bool? IsInfrastructureIssue { get; }
|
||||
string this[string key] { get; }
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public class Issue
|
||||
public class Issue : IReadOnlyIssue
|
||||
{
|
||||
|
||||
public Issue()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
private Issue(Issue issueToBeCloned)
|
||||
private Issue(Issue original)
|
||||
{
|
||||
this.Type = issueToBeCloned.Type;
|
||||
this.Category = issueToBeCloned.Category;
|
||||
this.Message = issueToBeCloned.Message;
|
||||
this.IsInfrastructureIssue = issueToBeCloned.IsInfrastructureIssue;
|
||||
|
||||
if (issueToBeCloned.m_data != null)
|
||||
this.EnsureInitialized();
|
||||
if (original != null)
|
||||
{
|
||||
foreach (var item in issueToBeCloned.m_data)
|
||||
{
|
||||
this.Data.Add(item);
|
||||
}
|
||||
this.Type = original.Type;
|
||||
this.Category = original.Category;
|
||||
this.Message = original.Message;
|
||||
this.IsInfrastructureIssue = original.IsInfrastructureIssue;
|
||||
m_data.AddRange(original.m_data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,14 +44,14 @@ namespace GitHub.DistributedTask.WebApi
|
||||
}
|
||||
|
||||
[DataMember(Order = 2)]
|
||||
public String Category
|
||||
public string Category
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[DataMember(Order = 3)]
|
||||
public String Message
|
||||
public string Message
|
||||
{
|
||||
get;
|
||||
set;
|
||||
@@ -56,15 +64,16 @@ namespace GitHub.DistributedTask.WebApi
|
||||
set;
|
||||
}
|
||||
|
||||
public IDictionary<String, String> Data
|
||||
public string this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_data == null)
|
||||
{
|
||||
m_data = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
return m_data;
|
||||
m_data.TryGetValue(key, out string result);
|
||||
return result;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_data[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +86,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
private void OnDeserialized(StreamingContext context)
|
||||
{
|
||||
SerializationHelper.Copy(ref m_serializedData, ref m_data, StringComparer.OrdinalIgnoreCase, true);
|
||||
this.EnsureInitialized();
|
||||
}
|
||||
|
||||
[OnSerializing]
|
||||
@@ -91,9 +101,21 @@ namespace GitHub.DistributedTask.WebApi
|
||||
m_serializedData = null;
|
||||
}
|
||||
|
||||
[DataMember(Name = "Data", EmitDefaultValue = false, Order = 4)]
|
||||
private IDictionary<String, String> m_serializedData;
|
||||
/// <summary>
|
||||
/// DataContractSerializer bypasses all constructor logic and inline initialization!
|
||||
/// This method takes the place of a workhorse constructor for baseline initialization.
|
||||
/// The expectation is for this logic to be accessible to constructors and also to the OnDeserialized helper.
|
||||
/// </summary>
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
//Note that ?? is a short-circuiting operator.
|
||||
m_data = m_data ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[DataMember(Name = "Data", EmitDefaultValue = false, Order = 4)]
|
||||
private IDictionary<string, string> m_serializedData;
|
||||
|
||||
private IDictionary<string, string> m_data;
|
||||
|
||||
private IDictionary<String, String> m_data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Services.OAuth;
|
||||
using GitHub.Services.WebApi;
|
||||
using Sdk.WebApi.WebApi;
|
||||
|
||||
namespace GitHub.Actions.RunService.WebApi
|
||||
namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
[ResourceArea(TaskResourceIds.AreaId)]
|
||||
public class RunServiceHttpClient : RawHttpClientBase
|
||||
{
|
||||
public RunServiceHttpClient(
|
||||
@@ -54,54 +52,24 @@ namespace GitHub.Actions.RunService.WebApi
|
||||
{
|
||||
}
|
||||
|
||||
public Task<AgentJobRequestMessage> GetJobMessageAsync(
|
||||
public Task<Pipelines.AgentJobRequestMessage> GetJobMessageAsync(
|
||||
Uri requestUri,
|
||||
string messageId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
HttpMethod httpMethod = new HttpMethod("POST");
|
||||
var payload = new AcquireJobRequest
|
||||
{
|
||||
var payload = new {
|
||||
StreamID = messageId
|
||||
};
|
||||
|
||||
requestUri = new Uri(requestUri, "acquirejob");
|
||||
|
||||
var requestContent = new ObjectContent<AcquireJobRequest>(payload, new VssJsonMediaTypeFormatter(true));
|
||||
return SendAsync<AgentJobRequestMessage>(
|
||||
var payloadJson = JsonUtility.ToString(payload);
|
||||
var requestContent = new StringContent(payloadJson, System.Text.Encoding.UTF8, "application/json");
|
||||
return SendAsync<Pipelines.AgentJobRequestMessage>(
|
||||
httpMethod,
|
||||
additionalHeaders: null,
|
||||
requestUri: requestUri,
|
||||
content: requestContent,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public Task CompleteJobAsync(
|
||||
Uri requestUri,
|
||||
Guid planId,
|
||||
Guid jobId,
|
||||
TaskResult result,
|
||||
Dictionary<String, VariableValue> outputs,
|
||||
IList<StepResult> stepResults,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
HttpMethod httpMethod = new HttpMethod("POST");
|
||||
var payload = new CompleteJobRequest()
|
||||
{
|
||||
PlanID = planId,
|
||||
JobID = jobId,
|
||||
Conclusion = result,
|
||||
Outputs = outputs,
|
||||
StepResults = stepResults
|
||||
};
|
||||
|
||||
requestUri = new Uri(requestUri, "completejob");
|
||||
|
||||
var requestContent = new ObjectContent<CompleteJobRequest>(payload, new VssJsonMediaTypeFormatter(true));
|
||||
return SendAsync(
|
||||
httpMethod,
|
||||
requestUri,
|
||||
content: requestContent,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Services.WebApi;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
@@ -27,6 +27,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
this.Type = type;
|
||||
this.Name = name;
|
||||
}
|
||||
|
||||
|
||||
[DataMember]
|
||||
public String Type
|
||||
@@ -100,7 +101,6 @@ namespace GitHub.DistributedTask.WebApi
|
||||
public static readonly String Summary = "DistributedTask.Core.Summary";
|
||||
public static readonly String FileAttachment = "DistributedTask.Core.FileAttachment";
|
||||
public static readonly String DiagnosticLog = "DistributedTask.Core.DiagnosticLog";
|
||||
public static readonly String ResultsLog = "Results.Core.Log";
|
||||
}
|
||||
|
||||
[GenerateAllConstants]
|
||||
|
||||
@@ -10,69 +10,76 @@ namespace GitHub.DistributedTask.WebApi
|
||||
public sealed class TimelineRecord
|
||||
{
|
||||
public TimelineRecord()
|
||||
: this(null)
|
||||
{
|
||||
this.Attempt = 1;
|
||||
}
|
||||
|
||||
private TimelineRecord(TimelineRecord recordToBeCloned)
|
||||
{
|
||||
this.Attempt = recordToBeCloned.Attempt;
|
||||
this.ChangeId = recordToBeCloned.ChangeId;
|
||||
this.CurrentOperation = recordToBeCloned.CurrentOperation;
|
||||
this.FinishTime = recordToBeCloned.FinishTime;
|
||||
this.Id = recordToBeCloned.Id;
|
||||
this.Identifier = recordToBeCloned.Identifier;
|
||||
this.LastModified = recordToBeCloned.LastModified;
|
||||
this.Location = recordToBeCloned.Location;
|
||||
this.Name = recordToBeCloned.Name;
|
||||
this.Order = recordToBeCloned.Order;
|
||||
this.ParentId = recordToBeCloned.ParentId;
|
||||
this.PercentComplete = recordToBeCloned.PercentComplete;
|
||||
this.RecordType = recordToBeCloned.RecordType;
|
||||
this.Result = recordToBeCloned.Result;
|
||||
this.ResultCode = recordToBeCloned.ResultCode;
|
||||
this.StartTime = recordToBeCloned.StartTime;
|
||||
this.State = recordToBeCloned.State;
|
||||
this.TimelineId = recordToBeCloned.TimelineId;
|
||||
this.WorkerName = recordToBeCloned.WorkerName;
|
||||
this.RefName = recordToBeCloned.RefName;
|
||||
this.ErrorCount = recordToBeCloned.ErrorCount;
|
||||
this.WarningCount = recordToBeCloned.WarningCount;
|
||||
this.NoticeCount = recordToBeCloned.NoticeCount;
|
||||
this.AgentPlatform = recordToBeCloned.AgentPlatform;
|
||||
|
||||
if (recordToBeCloned.Log != null)
|
||||
this.EnsureInitialized();
|
||||
if (recordToBeCloned != null)
|
||||
{
|
||||
this.Log = new TaskLogReference
|
||||
this.Attempt = recordToBeCloned.Attempt;
|
||||
this.ChangeId = recordToBeCloned.ChangeId;
|
||||
this.CurrentOperation = recordToBeCloned.CurrentOperation;
|
||||
this.FinishTime = recordToBeCloned.FinishTime;
|
||||
this.Id = recordToBeCloned.Id;
|
||||
this.Identifier = recordToBeCloned.Identifier;
|
||||
this.LastModified = recordToBeCloned.LastModified;
|
||||
this.Location = recordToBeCloned.Location;
|
||||
this.Name = recordToBeCloned.Name;
|
||||
this.Order = recordToBeCloned.Order;
|
||||
this.ParentId = recordToBeCloned.ParentId;
|
||||
this.PercentComplete = recordToBeCloned.PercentComplete;
|
||||
this.RecordType = recordToBeCloned.RecordType;
|
||||
this.Result = recordToBeCloned.Result;
|
||||
this.ResultCode = recordToBeCloned.ResultCode;
|
||||
this.StartTime = recordToBeCloned.StartTime;
|
||||
this.State = recordToBeCloned.State;
|
||||
this.TimelineId = recordToBeCloned.TimelineId;
|
||||
this.WorkerName = recordToBeCloned.WorkerName;
|
||||
this.RefName = recordToBeCloned.RefName;
|
||||
this.ErrorCount = recordToBeCloned.ErrorCount;
|
||||
this.WarningCount = recordToBeCloned.WarningCount;
|
||||
this.NoticeCount = recordToBeCloned.NoticeCount;
|
||||
this.AgentPlatform = recordToBeCloned.AgentPlatform;
|
||||
|
||||
if (recordToBeCloned.Log != null)
|
||||
{
|
||||
Id = recordToBeCloned.Log.Id,
|
||||
Location = recordToBeCloned.Log.Location,
|
||||
};
|
||||
}
|
||||
this.Log = new TaskLogReference
|
||||
{
|
||||
Id = recordToBeCloned.Log.Id,
|
||||
Location = recordToBeCloned.Log.Location,
|
||||
};
|
||||
}
|
||||
|
||||
if (recordToBeCloned.Details != null)
|
||||
{
|
||||
this.Details = new TimelineReference
|
||||
if (recordToBeCloned.Details != null)
|
||||
{
|
||||
ChangeId = recordToBeCloned.Details.ChangeId,
|
||||
Id = recordToBeCloned.Details.Id,
|
||||
Location = recordToBeCloned.Details.Location,
|
||||
};
|
||||
}
|
||||
this.Details = new TimelineReference
|
||||
{
|
||||
ChangeId = recordToBeCloned.Details.ChangeId,
|
||||
Id = recordToBeCloned.Details.Id,
|
||||
Location = recordToBeCloned.Details.Location,
|
||||
};
|
||||
}
|
||||
|
||||
if (recordToBeCloned.m_issues?.Count> 0)
|
||||
{
|
||||
this.Issues.AddRange(recordToBeCloned.Issues.Select(i => i.Clone()));
|
||||
}
|
||||
if (recordToBeCloned.m_issues?.Count > 0)
|
||||
{
|
||||
m_issues.AddRange(recordToBeCloned.m_issues.Select(i => i.Clone()));
|
||||
}
|
||||
|
||||
if (recordToBeCloned.m_previousAttempts?.Count > 0)
|
||||
{
|
||||
this.PreviousAttempts.AddRange(recordToBeCloned.PreviousAttempts);
|
||||
}
|
||||
if (recordToBeCloned.m_previousAttempts?.Count > 0)
|
||||
{
|
||||
m_previousAttempts.AddRange(recordToBeCloned.m_previousAttempts);
|
||||
}
|
||||
|
||||
if (recordToBeCloned.m_variables?.Count > 0)
|
||||
{
|
||||
this.m_variables = recordToBeCloned.Variables.ToDictionary(k => k.Key, v => v.Value.Clone());
|
||||
if (recordToBeCloned.m_variables?.Count > 0)
|
||||
{
|
||||
// Don't pave over the case-insensitive Dictionary we initialized above.
|
||||
foreach (var kvp in recordToBeCloned.m_variables) {
|
||||
m_variables[kvp.Key] = kvp.Value.Clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,10 +241,6 @@ namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_issues == null)
|
||||
{
|
||||
m_issues = new List<Issue>();
|
||||
}
|
||||
return m_issues;
|
||||
}
|
||||
}
|
||||
@@ -274,22 +277,14 @@ namespace GitHub.DistributedTask.WebApi
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_previousAttempts == null)
|
||||
{
|
||||
m_previousAttempts = new List<TimelineAttempt>();
|
||||
}
|
||||
return m_previousAttempts;
|
||||
}
|
||||
}
|
||||
|
||||
public IDictionary<String, VariableValue> Variables
|
||||
public IDictionary<string, VariableValue> Variables
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_variables == null)
|
||||
{
|
||||
m_variables = new Dictionary<String, VariableValue>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
return m_variables;
|
||||
}
|
||||
}
|
||||
@@ -299,11 +294,32 @@ namespace GitHub.DistributedTask.WebApi
|
||||
return new TimelineRecord(this);
|
||||
}
|
||||
|
||||
[OnDeserialized]
|
||||
private void OnDeserialized(StreamingContext context)
|
||||
{
|
||||
this.EnsureInitialized();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DataContractSerializer bypasses all constructor logic and inline initialization!
|
||||
/// This method takes the place of a workhorse constructor for baseline initialization.
|
||||
/// The expectation is for this logic to be accessible to constructors and also to the OnDeserialized helper.
|
||||
/// </summary>
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
//Note that ?? is a short-circuiting operator.
|
||||
m_issues = m_issues ?? new List<Issue>();
|
||||
m_variables = m_variables ?? new Dictionary<string, VariableValue>(StringComparer.OrdinalIgnoreCase);
|
||||
m_previousAttempts = m_previousAttempts ?? new List<TimelineAttempt>();
|
||||
this.Attempt = Math.Max(this.Attempt, 1);
|
||||
}
|
||||
|
||||
|
||||
[DataMember(Name = "Issues", EmitDefaultValue = false, Order = 60)]
|
||||
private List<Issue> m_issues;
|
||||
|
||||
[DataMember(Name = "Variables", EmitDefaultValue = false, Order = 80)]
|
||||
private Dictionary<String, VariableValue> m_variables;
|
||||
private Dictionary<string, VariableValue> m_variables;
|
||||
|
||||
[DataMember(Name = "PreviousAttempts", EmitDefaultValue = false, Order = 120)]
|
||||
private List<TimelineAttempt> m_previousAttempts;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
|
||||
namespace GitHub.Actions.RunService.WebApi
|
||||
{
|
||||
[DataContract]
|
||||
public class AcquireJobRequest
|
||||
{
|
||||
[DataMember(Name = "streamId", EmitDefaultValue = false)]
|
||||
public string StreamID { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
|
||||
namespace GitHub.Actions.RunService.WebApi
|
||||
{
|
||||
[DataContract]
|
||||
public class CompleteJobRequest
|
||||
{
|
||||
[DataMember(Name = "planId", EmitDefaultValue = false)]
|
||||
public Guid PlanID { get; set; }
|
||||
|
||||
[DataMember(Name = "jobId", EmitDefaultValue = false)]
|
||||
public Guid JobID { get; set; }
|
||||
|
||||
[DataMember(Name = "conclusion")]
|
||||
public TaskResult Conclusion { get; set; }
|
||||
|
||||
[DataMember(Name = "outputs", EmitDefaultValue = false)]
|
||||
public Dictionary<string, VariableValue> Outputs { get; set; }
|
||||
|
||||
[DataMember(Name = "stepResults", EmitDefaultValue = false)]
|
||||
public IList<StepResult> StepResults { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
|
||||
namespace GitHub.Actions.RunService.WebApi
|
||||
{
|
||||
[DataContract]
|
||||
public class StepResult
|
||||
{
|
||||
[DataMember(Name = "external_id", EmitDefaultValue = false)]
|
||||
public Guid ExternalID { get; set; }
|
||||
|
||||
[DataMember(Name = "number", EmitDefaultValue = false)]
|
||||
public int? Number { get; set; }
|
||||
|
||||
[DataMember(Name = "name", EmitDefaultValue = false)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "status")]
|
||||
public TimelineRecordState? Status { get; set; }
|
||||
|
||||
[DataMember(Name = "conclusion")]
|
||||
public TaskResult? Conclusion { get; set; }
|
||||
|
||||
[DataMember(Name = "started_at", EmitDefaultValue = false)]
|
||||
public DateTime? StartedAt { get; set; }
|
||||
|
||||
[DataMember(Name = "completed_at", EmitDefaultValue = false)]
|
||||
public DateTime? CompletedAt { get; set; }
|
||||
|
||||
[DataMember(Name = "completed_log_url", EmitDefaultValue = false)]
|
||||
public string CompletedLogURL { get; set; }
|
||||
|
||||
[DataMember(Name = "completed_log_lines", EmitDefaultValue = false)]
|
||||
public long? CompletedLogLines { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace GitHub.Services.Results.Contracts
|
||||
{
|
||||
[DataContract]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
public class GetSignedStepSummaryURLRequest
|
||||
{
|
||||
[DataMember]
|
||||
public string WorkflowJobRunBackendId;
|
||||
[DataMember]
|
||||
public string WorkflowRunBackendId;
|
||||
[DataMember]
|
||||
public string StepBackendId;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
public class GetSignedStepSummaryURLResponse
|
||||
{
|
||||
[DataMember]
|
||||
public string SummaryUrl;
|
||||
[DataMember]
|
||||
public long SoftSizeLimit;
|
||||
[DataMember]
|
||||
public string BlobStorageType;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
public class StepSummaryMetadataCreate
|
||||
{
|
||||
[DataMember]
|
||||
public string StepBackendId;
|
||||
[DataMember]
|
||||
public string WorkflowRunBackendId;
|
||||
[DataMember]
|
||||
public string WorkflowJobRunBackendId;
|
||||
[DataMember]
|
||||
public long Size;
|
||||
[DataMember]
|
||||
public string UploadedAt;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
public class GetSignedJobLogsURLRequest
|
||||
{
|
||||
[DataMember]
|
||||
public string WorkflowJobRunBackendId;
|
||||
[DataMember]
|
||||
public string WorkflowRunBackendId;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
public class GetSignedJobLogsURLResponse
|
||||
{
|
||||
[DataMember]
|
||||
public string LogsUrl;
|
||||
[DataMember]
|
||||
public string BlobStorageType;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
public class GetSignedStepLogsURLRequest
|
||||
{
|
||||
[DataMember]
|
||||
public string WorkflowJobRunBackendId;
|
||||
[DataMember]
|
||||
public string WorkflowRunBackendId;
|
||||
[DataMember]
|
||||
public string StepBackendId;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
public class GetSignedStepLogsURLResponse
|
||||
{
|
||||
[DataMember]
|
||||
public string LogsUrl;
|
||||
[DataMember]
|
||||
public string BlobStorageType;
|
||||
[DataMember]
|
||||
public long SoftSizeLimit;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
public class JobLogsMetadataCreate
|
||||
{
|
||||
[DataMember]
|
||||
public string WorkflowRunBackendId;
|
||||
[DataMember]
|
||||
public string WorkflowJobRunBackendId;
|
||||
[DataMember]
|
||||
public string UploadedAt;
|
||||
[DataMember]
|
||||
public long LineCount;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
public class StepLogsMetadataCreate
|
||||
{
|
||||
[DataMember]
|
||||
public string WorkflowRunBackendId;
|
||||
[DataMember]
|
||||
public string WorkflowJobRunBackendId;
|
||||
[DataMember]
|
||||
public string StepBackendId;
|
||||
[DataMember]
|
||||
public string UploadedAt;
|
||||
[DataMember]
|
||||
public long LineCount;
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
|
||||
public class CreateMetadataResponse
|
||||
{
|
||||
[DataMember]
|
||||
public bool Ok;
|
||||
}
|
||||
|
||||
public static class BlobStorageTypes
|
||||
{
|
||||
public static readonly string AzureBlobStorage = "BLOB_STORAGE_TYPE_AZURE";
|
||||
public static readonly string Unspecified = "BLOB_STORAGE_TYPE_UNSPECIFIED";
|
||||
}
|
||||
}
|
||||
@@ -101,17 +101,6 @@ namespace Sdk.WebApi.WebApi
|
||||
}
|
||||
}
|
||||
|
||||
protected Task<T> SendAsync<T>(
|
||||
HttpMethod method,
|
||||
Uri requestUri,
|
||||
HttpContent content = null,
|
||||
IEnumerable<KeyValuePair<String, String>> queryParameters = null,
|
||||
Object userState = null,
|
||||
CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
return SendAsync<T>(method, null, requestUri, content, queryParameters, userState, cancellationToken);
|
||||
}
|
||||
|
||||
protected async Task<T> SendAsync<T>(
|
||||
HttpMethod method,
|
||||
IEnumerable<KeyValuePair<String, String>> additionalHeaders,
|
||||
|
||||
@@ -1,341 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Services.Results.Contracts;
|
||||
using System.Net.Http.Formatting;
|
||||
using Sdk.WebApi.WebApi;
|
||||
|
||||
namespace GitHub.Services.Results.Client
|
||||
{
|
||||
public class ResultsHttpClient : RawHttpClientBase
|
||||
{
|
||||
public ResultsHttpClient(
|
||||
Uri baseUrl,
|
||||
HttpMessageHandler pipeline,
|
||||
string token,
|
||||
bool disposeHandler)
|
||||
: base(baseUrl, pipeline, disposeHandler)
|
||||
{
|
||||
m_token = token;
|
||||
m_resultsServiceUrl = baseUrl;
|
||||
m_formatter = new JsonMediaTypeFormatter();
|
||||
}
|
||||
|
||||
// Get Sas URL calls
|
||||
private async Task<T> GetResultsSignedURLResponse<R, T>(Uri uri, CancellationToken cancellationToken, R request)
|
||||
{
|
||||
using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, uri))
|
||||
{
|
||||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", m_token);
|
||||
requestMessage.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
|
||||
|
||||
using (HttpContent content = new ObjectContent<R>(request, m_formatter))
|
||||
{
|
||||
requestMessage.Content = content;
|
||||
using (var response = await SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, cancellationToken: cancellationToken))
|
||||
{
|
||||
return await ReadJsonContentAsync<T>(response, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<GetSignedStepSummaryURLResponse> GetStepSummaryUploadUrlAsync(string planId, string jobId, Guid stepId, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = new GetSignedStepSummaryURLRequest()
|
||||
{
|
||||
WorkflowJobRunBackendId = jobId,
|
||||
WorkflowRunBackendId = planId,
|
||||
StepBackendId = stepId.ToString()
|
||||
};
|
||||
|
||||
var getStepSummarySignedBlobURLEndpoint = new Uri(m_resultsServiceUrl, Constants.GetStepSummarySignedBlobURL);
|
||||
|
||||
return await GetResultsSignedURLResponse<GetSignedStepSummaryURLRequest, GetSignedStepSummaryURLResponse>(getStepSummarySignedBlobURLEndpoint, cancellationToken, request);
|
||||
}
|
||||
|
||||
private async Task<GetSignedStepLogsURLResponse> GetStepLogUploadUrlAsync(string planId, string jobId, Guid stepId, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = new GetSignedStepLogsURLRequest()
|
||||
{
|
||||
WorkflowJobRunBackendId = jobId,
|
||||
WorkflowRunBackendId = planId,
|
||||
StepBackendId = stepId.ToString(),
|
||||
};
|
||||
|
||||
var getStepLogsSignedBlobURLEndpoint = new Uri(m_resultsServiceUrl, Constants.GetStepLogsSignedBlobURL);
|
||||
|
||||
return await GetResultsSignedURLResponse<GetSignedStepLogsURLRequest, GetSignedStepLogsURLResponse>(getStepLogsSignedBlobURLEndpoint, cancellationToken, request);
|
||||
}
|
||||
|
||||
private async Task<GetSignedJobLogsURLResponse> GetJobLogUploadUrlAsync(string planId, string jobId, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = new GetSignedJobLogsURLRequest()
|
||||
{
|
||||
WorkflowJobRunBackendId = jobId,
|
||||
WorkflowRunBackendId = planId,
|
||||
};
|
||||
|
||||
var getJobLogsSignedBlobURLEndpoint = new Uri(m_resultsServiceUrl, Constants.GetJobLogsSignedBlobURL);
|
||||
|
||||
return await GetResultsSignedURLResponse<GetSignedJobLogsURLRequest, GetSignedJobLogsURLResponse>(getJobLogsSignedBlobURLEndpoint, cancellationToken, request);
|
||||
}
|
||||
|
||||
// Create metadata calls
|
||||
|
||||
private async Task CreateMetadata<R>(Uri uri, CancellationToken cancellationToken, R request, string timestamp)
|
||||
{
|
||||
using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, uri))
|
||||
{
|
||||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", m_token);
|
||||
requestMessage.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json"));
|
||||
|
||||
using (HttpContent content = new ObjectContent<R>(request, m_formatter))
|
||||
{
|
||||
requestMessage.Content = content;
|
||||
using (var response = await SendAsync(requestMessage, HttpCompletionOption.ResponseContentRead, cancellationToken: cancellationToken))
|
||||
{
|
||||
var jsonResponse = await ReadJsonContentAsync<CreateMetadataResponse>(response, cancellationToken);
|
||||
if (!jsonResponse.Ok)
|
||||
{
|
||||
throw new Exception($"Failed to mark {typeof(R).Name} upload as complete, status code: {response.StatusCode}, ok: {jsonResponse.Ok}, timestamp: {timestamp}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StepSummaryUploadCompleteAsync(string planId, string jobId, Guid stepId, long size, CancellationToken cancellationToken)
|
||||
{
|
||||
var timestamp = DateTime.UtcNow.ToString(Constants.TimestampFormat);
|
||||
var request = new StepSummaryMetadataCreate()
|
||||
{
|
||||
WorkflowJobRunBackendId = jobId,
|
||||
WorkflowRunBackendId = planId,
|
||||
StepBackendId = stepId.ToString(),
|
||||
Size = size,
|
||||
UploadedAt = timestamp
|
||||
};
|
||||
|
||||
var createStepSummaryMetadataEndpoint = new Uri(m_resultsServiceUrl, Constants.CreateStepSummaryMetadata);
|
||||
await CreateMetadata<StepSummaryMetadataCreate>(createStepSummaryMetadataEndpoint, cancellationToken, request, timestamp);
|
||||
}
|
||||
|
||||
private async Task StepLogUploadCompleteAsync(string planId, string jobId, Guid stepId, long lineCount, CancellationToken cancellationToken)
|
||||
{
|
||||
var timestamp = DateTime.UtcNow.ToString(Constants.TimestampFormat);
|
||||
var request = new StepLogsMetadataCreate()
|
||||
{
|
||||
WorkflowJobRunBackendId = jobId,
|
||||
WorkflowRunBackendId = planId,
|
||||
StepBackendId = stepId.ToString(),
|
||||
UploadedAt = timestamp,
|
||||
LineCount = lineCount,
|
||||
};
|
||||
|
||||
var createStepLogsMetadataEndpoint = new Uri(m_resultsServiceUrl, Constants.CreateStepLogsMetadata);
|
||||
await CreateMetadata<StepLogsMetadataCreate>(createStepLogsMetadataEndpoint, cancellationToken, request, timestamp);
|
||||
}
|
||||
|
||||
private async Task JobLogUploadCompleteAsync(string planId, string jobId, long lineCount, CancellationToken cancellationToken)
|
||||
{
|
||||
var timestamp = DateTime.UtcNow.ToString(Constants.TimestampFormat);
|
||||
var request = new JobLogsMetadataCreate()
|
||||
{
|
||||
WorkflowJobRunBackendId = jobId,
|
||||
WorkflowRunBackendId = planId,
|
||||
UploadedAt = timestamp,
|
||||
LineCount = lineCount,
|
||||
};
|
||||
|
||||
var createJobLogsMetadataEndpoint = new Uri(m_resultsServiceUrl, Constants.CreateJobLogsMetadata);
|
||||
await CreateMetadata<JobLogsMetadataCreate>(createJobLogsMetadataEndpoint, cancellationToken, request, timestamp);
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> UploadBlockFileAsync(string url, string blobStorageType, FileStream file, CancellationToken cancellationToken)
|
||||
{
|
||||
// Upload the file to the url
|
||||
var request = new HttpRequestMessage(HttpMethod.Put, url)
|
||||
{
|
||||
Content = new StreamContent(file)
|
||||
};
|
||||
|
||||
if (blobStorageType == BlobStorageTypes.AzureBlobStorage)
|
||||
{
|
||||
request.Content.Headers.Add(Constants.AzureBlobTypeHeader, Constants.AzureBlockBlob);
|
||||
}
|
||||
|
||||
using (var response = await SendAsync(request, HttpCompletionOption.ResponseHeadersRead, userState: null, cancellationToken))
|
||||
{
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new Exception($"Failed to upload file, status code: {response.StatusCode}, reason: {response.ReasonPhrase}");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> CreateAppendFileAsync(string url, string blobStorageType, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Put, url)
|
||||
{
|
||||
Content = new StringContent("")
|
||||
};
|
||||
if (blobStorageType == BlobStorageTypes.AzureBlobStorage)
|
||||
{
|
||||
request.Content.Headers.Add(Constants.AzureBlobTypeHeader, Constants.AzureAppendBlob);
|
||||
request.Content.Headers.Add("Content-Length", "0");
|
||||
}
|
||||
|
||||
using (var response = await SendAsync(request, HttpCompletionOption.ResponseHeadersRead, userState: null, cancellationToken))
|
||||
{
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new Exception($"Failed to create append file, status code: {response.StatusCode}, reason: {response.ReasonPhrase}");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> UploadAppendFileAsync(string url, string blobStorageType, FileStream file, bool finalize, long fileSize, CancellationToken cancellationToken)
|
||||
{
|
||||
var comp = finalize ? "&comp=appendblock&seal=true" : "&comp=appendblock";
|
||||
// Upload the file to the url
|
||||
var request = new HttpRequestMessage(HttpMethod.Put, url + comp)
|
||||
{
|
||||
Content = new StreamContent(file)
|
||||
};
|
||||
|
||||
if (blobStorageType == BlobStorageTypes.AzureBlobStorage)
|
||||
{
|
||||
request.Content.Headers.Add("Content-Length", fileSize.ToString());
|
||||
request.Content.Headers.Add(Constants.AzureBlobSealedHeader, finalize.ToString());
|
||||
}
|
||||
|
||||
using (var response = await SendAsync(request, HttpCompletionOption.ResponseHeadersRead, userState: null, cancellationToken))
|
||||
{
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new Exception($"Failed to upload append file, status code: {response.StatusCode}, reason: {response.ReasonPhrase}, object: {response}, fileSize: {fileSize}");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle file upload for step summary
|
||||
public async Task UploadStepSummaryAsync(string planId, string jobId, Guid stepId, string file, CancellationToken cancellationToken)
|
||||
{
|
||||
// Get the upload url
|
||||
var uploadUrlResponse = await GetStepSummaryUploadUrlAsync(planId, jobId, stepId, cancellationToken);
|
||||
if (uploadUrlResponse == null)
|
||||
{
|
||||
throw new Exception("Failed to get step summary upload url");
|
||||
}
|
||||
|
||||
// Do we want to throw an exception here or should we just be uploading/truncating the data
|
||||
var fileSize = new FileInfo(file).Length;
|
||||
if (fileSize > uploadUrlResponse.SoftSizeLimit)
|
||||
{
|
||||
throw new Exception($"File size is larger than the upload url allows, file size: {fileSize}, upload url size: {uploadUrlResponse.SoftSizeLimit}");
|
||||
}
|
||||
|
||||
// Upload the file
|
||||
using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
|
||||
{
|
||||
var response = await UploadBlockFileAsync(uploadUrlResponse.SummaryUrl, uploadUrlResponse.BlobStorageType, fileStream, cancellationToken);
|
||||
}
|
||||
|
||||
// Send step summary upload complete message
|
||||
await StepSummaryUploadCompleteAsync(planId, jobId, stepId, fileSize, cancellationToken);
|
||||
}
|
||||
|
||||
// Handle file upload for step log
|
||||
public async Task UploadResultsStepLogAsync(string planId, string jobId, Guid stepId, string file, bool finalize, bool firstBlock, long lineCount, CancellationToken cancellationToken)
|
||||
{
|
||||
// Get the upload url
|
||||
var uploadUrlResponse = await GetStepLogUploadUrlAsync(planId, jobId, stepId, cancellationToken);
|
||||
if (uploadUrlResponse == null || uploadUrlResponse.LogsUrl == null)
|
||||
{
|
||||
throw new Exception("Failed to get step log upload url");
|
||||
}
|
||||
|
||||
// Create the Append blob
|
||||
if (firstBlock)
|
||||
{
|
||||
await CreateAppendFileAsync(uploadUrlResponse.LogsUrl, uploadUrlResponse.BlobStorageType, cancellationToken);
|
||||
}
|
||||
|
||||
// Upload content
|
||||
var fileSize = new FileInfo(file).Length;
|
||||
using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
|
||||
{
|
||||
var response = await UploadAppendFileAsync(uploadUrlResponse.LogsUrl, uploadUrlResponse.BlobStorageType, fileStream, finalize, fileSize, cancellationToken);
|
||||
}
|
||||
|
||||
// Update metadata
|
||||
if (finalize)
|
||||
{
|
||||
// Send step log upload complete message
|
||||
await StepLogUploadCompleteAsync(planId, jobId, stepId, lineCount, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle file upload for job log
|
||||
public async Task UploadResultsJobLogAsync(string planId, string jobId, string file, bool finalize, bool firstBlock, long lineCount, CancellationToken cancellationToken)
|
||||
{
|
||||
// Get the upload url
|
||||
var uploadUrlResponse = await GetJobLogUploadUrlAsync(planId, jobId, cancellationToken);
|
||||
if (uploadUrlResponse == null || uploadUrlResponse.LogsUrl == null)
|
||||
{
|
||||
throw new Exception("Failed to get job log upload url");
|
||||
}
|
||||
|
||||
// Create the Append blob
|
||||
if (firstBlock)
|
||||
{
|
||||
await CreateAppendFileAsync(uploadUrlResponse.LogsUrl, uploadUrlResponse.BlobStorageType, cancellationToken);
|
||||
}
|
||||
|
||||
// Upload content
|
||||
var fileSize = new FileInfo(file).Length;
|
||||
using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
|
||||
{
|
||||
var response = await UploadAppendFileAsync(uploadUrlResponse.LogsUrl, uploadUrlResponse.BlobStorageType, fileStream, finalize, fileSize, cancellationToken);
|
||||
}
|
||||
|
||||
// Update metadata
|
||||
if (finalize)
|
||||
{
|
||||
// Send step log upload complete message
|
||||
await JobLogUploadCompleteAsync(planId, jobId, lineCount, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private MediaTypeFormatter m_formatter;
|
||||
private Uri m_resultsServiceUrl;
|
||||
private string m_token;
|
||||
}
|
||||
|
||||
// Constants specific to results
|
||||
public static class Constants
|
||||
{
|
||||
public static readonly string TimestampFormat = "yyyy-MM-dd'T'HH:mm:ss.fffK";
|
||||
|
||||
public static readonly string ResultsReceiverTwirpEndpoint = "twirp/results.services.receiver.Receiver/";
|
||||
public static readonly string GetStepSummarySignedBlobURL = ResultsReceiverTwirpEndpoint + "GetStepSummarySignedBlobURL";
|
||||
public static readonly string CreateStepSummaryMetadata = ResultsReceiverTwirpEndpoint + "CreateStepSummaryMetadata";
|
||||
public static readonly string GetStepLogsSignedBlobURL = ResultsReceiverTwirpEndpoint + "GetStepLogsSignedBlobURL";
|
||||
public static readonly string CreateStepLogsMetadata = ResultsReceiverTwirpEndpoint + "CreateStepLogsMetadata";
|
||||
public static readonly string GetJobLogsSignedBlobURL = ResultsReceiverTwirpEndpoint + "GetJobLogsSignedBlobURL";
|
||||
public static readonly string CreateJobLogsMetadata = ResultsReceiverTwirpEndpoint + "CreateJobLogsMetadata";
|
||||
|
||||
public static readonly string AzureBlobSealedHeader = "x-ms-blob-sealed";
|
||||
public static readonly string AzureBlobTypeHeader = "x-ms-blob-type";
|
||||
public static readonly string AzureBlockBlob = "BlockBlob";
|
||||
public static readonly string AzureAppendBlob = "AppendBlob";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -824,7 +824,6 @@ namespace GitHub.Runner.Common.Tests
|
||||
[InlineData("remove", "version")]
|
||||
[InlineData("remove", "commit")]
|
||||
[InlineData("remove", "check")]
|
||||
[InlineData("remove", "local")]
|
||||
[InlineData("run", "help")]
|
||||
[InlineData("run", "version")]
|
||||
[InlineData("run", "commit")]
|
||||
|
||||
@@ -502,34 +502,5 @@ namespace GitHub.Runner.Common.Tests.Listener
|
||||
_messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()), Times.Once());
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Runner")]
|
||||
public async void TestRemoveLocalRunnerConfig()
|
||||
{
|
||||
using (var hc = new TestHostContext(this))
|
||||
{
|
||||
hc.SetSingleton<IConfigurationManager>(_configurationManager.Object);
|
||||
hc.SetSingleton<IConfigurationStore>(_configStore.Object);
|
||||
hc.SetSingleton<IPromptManager>(_promptManager.Object);
|
||||
|
||||
var command = new CommandSettings(hc, new[] { "remove", "--local" });
|
||||
|
||||
_configStore.Setup(x => x.IsConfigured())
|
||||
.Returns(true);
|
||||
|
||||
_configStore.Setup(x => x.HasCredentials())
|
||||
.Returns(true);
|
||||
|
||||
|
||||
var runner = new Runner.Listener.Runner();
|
||||
runner.Initialize(hc);
|
||||
await runner.ExecuteCommand(command);
|
||||
|
||||
// verify that we delete the local runner config with the correct remove parameter
|
||||
_configurationManager.Verify(x => x.DeleteLocalRunnerConfig(), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,76 +128,7 @@ namespace GitHub.Runner.Common.Tests
|
||||
}
|
||||
}
|
||||
}
|
||||
#if OS_WINDOWS
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public async Task SetTestEnvWithNullInKey()
|
||||
{
|
||||
using (TestHostContext hc = new(this))
|
||||
{
|
||||
Tracing trace = hc.GetTrace();
|
||||
|
||||
Int32 exitCode = -1;
|
||||
var processInvoker = new ProcessInvokerWrapper();
|
||||
processInvoker.Initialize(hc);
|
||||
var stdout = new List<string>();
|
||||
var stderr = new List<string>();
|
||||
processInvoker.OutputDataReceived += (object sender, ProcessDataReceivedEventArgs e) =>
|
||||
{
|
||||
trace.Info(e.Data);
|
||||
stdout.Add(e.Data);
|
||||
};
|
||||
processInvoker.ErrorDataReceived += (object sender, ProcessDataReceivedEventArgs e) =>
|
||||
{
|
||||
trace.Info(e.Data);
|
||||
stderr.Add(e.Data);
|
||||
};
|
||||
|
||||
exitCode = await processInvoker.ExecuteAsync("", "cmd.exe", "/c \"echo %TEST%\"", new Dictionary<string, string>() { { "TEST\0second", "first" } }, CancellationToken.None);
|
||||
|
||||
|
||||
trace.Info("Exit Code: {0}", exitCode);
|
||||
Assert.Equal(0, exitCode);
|
||||
Assert.Equal("first", stdout.First(x => !string.IsNullOrWhiteSpace(x)));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public async Task SetTestEnvWithNullInValue()
|
||||
{
|
||||
using (TestHostContext hc = new(this))
|
||||
{
|
||||
Tracing trace = hc.GetTrace();
|
||||
|
||||
Int32 exitCode = -1;
|
||||
var processInvoker = new ProcessInvokerWrapper();
|
||||
processInvoker.Initialize(hc);
|
||||
var stdout = new List<string>();
|
||||
var stderr = new List<string>();
|
||||
processInvoker.OutputDataReceived += (object sender, ProcessDataReceivedEventArgs e) =>
|
||||
{
|
||||
trace.Info(e.Data);
|
||||
stdout.Add(e.Data);
|
||||
};
|
||||
processInvoker.ErrorDataReceived += (object sender, ProcessDataReceivedEventArgs e) =>
|
||||
{
|
||||
trace.Info(e.Data);
|
||||
stderr.Add(e.Data);
|
||||
};
|
||||
|
||||
exitCode = await processInvoker.ExecuteAsync("", "cmd.exe", "/c \"echo %TEST%\"", new Dictionary<string, string>() { { "TEST", "first\0second" } }, CancellationToken.None);
|
||||
|
||||
trace.Info("Exit Code: {0}", exitCode);
|
||||
Assert.Equal(0, exitCode);
|
||||
Assert.Equal("first", stdout.First(x => !string.IsNullOrWhiteSpace(x)));
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using Xunit;
|
||||
using GitHub.Runner.Sdk;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Worker;
|
||||
|
||||
namespace GitHub.Runner.Common.Tests
|
||||
{
|
||||
@@ -41,5 +43,26 @@ namespace GitHub.Runner.Common.Tests
|
||||
Assert.True(Directory.Exists(testDataDir));
|
||||
return testDataDir;
|
||||
}
|
||||
|
||||
public static IReadOnlyIssue CreateTestIssue(IssueType type, string message, IssueMetadata metadata, bool writeToLog)
|
||||
{
|
||||
var result = new Issue()
|
||||
{
|
||||
Type = type,
|
||||
Message = message,
|
||||
};
|
||||
|
||||
if (metadata != null)
|
||||
{
|
||||
result.Category = metadata.Category;
|
||||
result.IsInfrastructureIssue = metadata.IsInfrastructureIssue;
|
||||
foreach (var kvp in metadata.Data)
|
||||
{
|
||||
result[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using System.Globalization;
|
||||
using Xunit;
|
||||
|
||||
@@ -185,19 +186,5 @@ namespace GitHub.Runner.Common.Tests.Util
|
||||
Assert.False(result9, $"'{undefineString3}' should convert to false.");
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "")]
|
||||
[InlineData("(())", "[[]]")]
|
||||
[InlineData("()()", "[][]")]
|
||||
[InlineData(" Liquorix kernel OS Description is poorly formatted (linux version ", "Liquorix kernel OS Description is poorly formatted [linux version")]
|
||||
[InlineData("Liquorix kernel OS Description is poorly formatted (linux version", "Liquorix kernel OS Description is poorly formatted [linux version")]
|
||||
[InlineData("()((.", "[][[.")]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void SanitizeUserAgentHeader(string input, string expected)
|
||||
{
|
||||
Assert.Equal(expected, StringUtil.SanitizeUserAgentHeader(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,10 +32,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
hc.GetTrace().Info($"{tag} {line}");
|
||||
return 1;
|
||||
});
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
|
||||
.Callback((Issue issue, string message) =>
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>()))
|
||||
.Callback((IReadOnlyIssue issue) =>
|
||||
{
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message}");
|
||||
});
|
||||
|
||||
_commandManager.EnablePluginInternalCommand();
|
||||
@@ -59,10 +59,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
hc.GetTrace().Info($"{tag} {line}");
|
||||
return 1;
|
||||
});
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
|
||||
.Callback((Issue issue, string message) =>
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>()))
|
||||
.Callback((IReadOnlyIssue issue) =>
|
||||
{
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message}");
|
||||
});
|
||||
|
||||
_commandManager.EnablePluginInternalCommand();
|
||||
@@ -92,10 +92,12 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
return 1;
|
||||
});
|
||||
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>()))
|
||||
.Callback((Issue issue, string message) =>
|
||||
_ec.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>()))
|
||||
.Callback((IReadOnlyIssue issue) =>
|
||||
{
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message} {message ?? string.Empty}");
|
||||
hc.GetTrace().Info($"{issue.Type} {issue.Message}");
|
||||
});
|
||||
|
||||
_ec.Object.Global.EnvironmentVariables = new Dictionary<string, string>();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -14,6 +13,11 @@ using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
|
||||
#if OS_WINDOWS
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
|
||||
using Moq;
|
||||
using Moq.Protected;
|
||||
using Xunit;
|
||||
@@ -2147,7 +2151,9 @@ runs:
|
||||
_ec.Object.Global.FileTable = new List<String>();
|
||||
_ec.Object.Global.Plan = new TaskOrchestrationPlanReference();
|
||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||
_ec.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>())).Callback((IReadOnlyIssue issue) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message}"); });
|
||||
_ec.Setup(x => x.GetGitHubContext("workspace")).Returns(Path.Combine(_workFolder, "actions", "actions"));
|
||||
|
||||
_dockerManager = new Mock<IDockerCommandManager>();
|
||||
|
||||
@@ -4,10 +4,10 @@ using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Expressions;
|
||||
using GitHub.Services.Common;
|
||||
using Moq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
@@ -670,7 +670,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
@@ -715,7 +715,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
//Assert
|
||||
var err = Assert.Throws<ArgumentException>(() => actionManifest.Load(_ec.Object, action_path));
|
||||
Assert.Contains($"Fail to load {action_path}", err.Message);
|
||||
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12' or 'node16'.")), It.IsAny<string>()), Times.Once);
|
||||
_ec.Verify(x => x.AddIssue(It.Is<IReadOnlyIssue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12' or 'node16'."))), Times.Once);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -860,7 +860,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"{tag}{message}"); });
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||
|
||||
_ec.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>())).Callback((IReadOnlyIssue issue) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message}"); });
|
||||
}
|
||||
|
||||
private void Teardown()
|
||||
|
||||
@@ -5,6 +5,7 @@ using GitHub.DistributedTask.Pipelines.ContextData;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using GitHub.Services.Common;
|
||||
using Moq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
@@ -143,12 +144,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_context.Add("matrix", matrixData);
|
||||
|
||||
// Act
|
||||
// Should report success with no updated required if there's already a valid display name.
|
||||
var validDisplayName = _actionRunner.EvaluateDisplayName(_context, _actionRunner.ExecutionContext, out bool updated);
|
||||
// Should not do anything if we don't have a displayNameToken to expand
|
||||
var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(validDisplayName);
|
||||
Assert.False(updated);
|
||||
Assert.False(didUpdateDisplayName);
|
||||
Assert.Equal(actionDisplayName, _actionRunner.DisplayName);
|
||||
}
|
||||
|
||||
@@ -178,51 +178,13 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
// Act
|
||||
// Should expand the displaynameToken and set the display name to that
|
||||
var validDisplayName = _actionRunner.EvaluateDisplayName(_context, _actionRunner.ExecutionContext, out bool updated);
|
||||
var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(validDisplayName);
|
||||
Assert.True(updated);
|
||||
Assert.True(didUpdateDisplayName);
|
||||
Assert.Equal(expectedString, _actionRunner.DisplayName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void IgnoreDisplayNameTokenWhenDisplayNameIsExplicitlySet()
|
||||
{
|
||||
var explicitDisplayName = "Explcitly Set Name";
|
||||
|
||||
// Arrange
|
||||
Setup();
|
||||
var actionId = Guid.NewGuid();
|
||||
var action = new Pipelines.ActionStep()
|
||||
{
|
||||
Name = "action",
|
||||
Id = actionId,
|
||||
DisplayName = explicitDisplayName,
|
||||
DisplayNameToken = new BasicExpressionToken(null, null, null, "matrix.node"),
|
||||
};
|
||||
|
||||
_actionRunner.Action = action;
|
||||
|
||||
var matrixData = new DictionaryContextData
|
||||
{
|
||||
["node"] = new StringContextData("8")
|
||||
};
|
||||
_context.Add("matrix", matrixData);
|
||||
|
||||
// Act
|
||||
// Should ignore the displayNameToken since there's already an explicit value for DisplayName
|
||||
var validDisplayName = _actionRunner.EvaluateDisplayName(_context, _actionRunner.ExecutionContext, out bool updated);
|
||||
|
||||
// Assert
|
||||
Assert.True(validDisplayName);
|
||||
Assert.False(updated);
|
||||
Assert.Equal(explicitDisplayName, _actionRunner.DisplayName);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
@@ -251,11 +213,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
// Act
|
||||
// Should expand the displaynameToken and set the display name to that
|
||||
var validDisplayName = _actionRunner.EvaluateDisplayName(_context, _actionRunner.ExecutionContext, out bool updated);
|
||||
var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(validDisplayName);
|
||||
Assert.True(updated);
|
||||
Assert.True(didUpdateDisplayName);
|
||||
Assert.Equal("Run 8", _actionRunner.DisplayName);
|
||||
}
|
||||
|
||||
@@ -280,11 +241,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
// Act
|
||||
// Should expand the displaynameToken and set the display name to that
|
||||
var validDisplayName = _actionRunner.EvaluateDisplayName(_context, _actionRunner.ExecutionContext, out bool updated);
|
||||
var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(validDisplayName);
|
||||
Assert.True(updated);
|
||||
Assert.True(didUpdateDisplayName);
|
||||
Assert.Equal("Run TestImageName:latest", _actionRunner.DisplayName);
|
||||
}
|
||||
|
||||
@@ -307,11 +267,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
// Act
|
||||
// Should not do anything if we don't have context on the display name
|
||||
var validDisplayName = _actionRunner.EvaluateDisplayName(_context, _actionRunner.ExecutionContext, out bool updated);
|
||||
var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(validDisplayName);
|
||||
Assert.False(updated);
|
||||
Assert.False(didUpdateDisplayName);
|
||||
// Should use the pretty display name until we can eval
|
||||
Assert.Equal("${{ matrix.node }}", _actionRunner.DisplayName);
|
||||
}
|
||||
@@ -366,7 +325,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Assert.Equal("invalid1", finialInputs["invalid1"]);
|
||||
Assert.Equal("invalid2", finialInputs["invalid2"]);
|
||||
|
||||
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Unexpected input(s) 'invalid1', 'invalid2'")), It.IsAny<string>()), Times.Once);
|
||||
_ec.Verify(x => x.AddIssue(It.Is<IReadOnlyIssue>(s => s.Message.Contains("Unexpected input(s) 'invalid1', 'invalid2'"))), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -485,7 +444,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
||||
_ec.Object.Global.Variables = new Variables(_hc, new Dictionary<string, VariableValue>());
|
||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||
_ec.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<IReadOnlyIssue>())).Callback((IReadOnlyIssue issue) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message}"); });
|
||||
|
||||
_hc.SetSingleton<IActionManager>(_actionManager.Object);
|
||||
_hc.SetSingleton<IHandlerFactory>(_handlerFactory.Object);
|
||||
|
||||
@@ -99,46 +99,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void InitializeWithCorrectManager()
|
||||
{
|
||||
containers.Add(new ContainerInfo() { ContainerImage = "ubuntu:16.04" });
|
||||
_hc = new TestHostContext(this, "Test");
|
||||
_ec = new Mock<IExecutionContext>();
|
||||
serverQueue = new Mock<IJobServerQueue>();
|
||||
pagingLogger = new Mock<IPagingLogger>();
|
||||
|
||||
containerOperationProvider = new ContainerOperationProvider();
|
||||
|
||||
_hc.SetSingleton<IJobServerQueue>(serverQueue.Object);
|
||||
_hc.SetSingleton<IPagingLogger>(pagingLogger.Object);
|
||||
|
||||
|
||||
_ec.Setup(x => x.Global).Returns(new GlobalContext());
|
||||
|
||||
Environment.SetEnvironmentVariable(Constants.Hooks.ContainerHooksPath, "/tmp/k8s/index.js");
|
||||
_dockerManager = new Mock<IDockerCommandManager>();
|
||||
_dockerManager.Setup(x => x.Initialize(_hc)).Throws(new Exception("Docker manager's Initialize should not be called"));
|
||||
|
||||
_containerHookManager = new Mock<IContainerHookManager>();
|
||||
_hc.SetSingleton<IDockerCommandManager>(_dockerManager.Object);
|
||||
_hc.SetSingleton<IContainerHookManager>(_containerHookManager.Object);
|
||||
|
||||
containerOperationProvider.Initialize(_hc);
|
||||
|
||||
Environment.SetEnvironmentVariable(Constants.Hooks.ContainerHooksPath, null);
|
||||
_containerHookManager = new Mock<IContainerHookManager>();
|
||||
_containerHookManager.Setup(x => x.Initialize(_hc)).Throws(new Exception("Container hook manager's Initialize should not be called"));
|
||||
|
||||
_dockerManager = new Mock<IDockerCommandManager>();
|
||||
_hc.SetSingleton<IDockerCommandManager>(_dockerManager.Object);
|
||||
_hc.SetSingleton<IContainerHookManager>(_containerHookManager.Object);
|
||||
|
||||
containerOperationProvider.Initialize(_hc);
|
||||
}
|
||||
|
||||
private void Setup([CallerMemberName] string testName = "")
|
||||
{
|
||||
containers.Add(new ContainerInfo() { ContainerImage = "ubuntu:16.04" });
|
||||
@@ -151,6 +111,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_containerHookManager = new Mock<IContainerHookManager>();
|
||||
containerOperationProvider = new ContainerOperationProvider();
|
||||
|
||||
_hc.SetSingleton<IDockerCommandManager>(_dockerManager.Object);
|
||||
_hc.SetSingleton<IJobServerQueue>(serverQueue.Object);
|
||||
_hc.SetSingleton<IPagingLogger>(pagingLogger.Object);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using Xunit;
|
||||
using DTWebApi = GitHub.DistributedTask.WebApi;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.Services.Common;
|
||||
|
||||
namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
@@ -19,7 +20,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
private Mock<IExecutionContext> _executionContext;
|
||||
private Mock<IJobServerQueue> _jobServerQueue;
|
||||
private ExecutionContext _jobExecutionContext;
|
||||
private List<Tuple<DTWebApi.Issue, string>> _issues;
|
||||
private List<DTWebApi.IReadOnlyIssue> _issues;
|
||||
private Variables _variables;
|
||||
private string _rootDirectory;
|
||||
private CreateStepSummaryCommand _createStepCommand;
|
||||
@@ -186,7 +187,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
var hostContext = new TestHostContext(this, name);
|
||||
|
||||
_issues = new List<Tuple<DTWebApi.Issue, string>>();
|
||||
_issues = new List<DTWebApi.IReadOnlyIssue>();
|
||||
|
||||
// Setup a job request
|
||||
TaskOrchestrationPlanReference plan = new();
|
||||
@@ -247,13 +248,14 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
WriteDebug = true,
|
||||
Variables = _variables,
|
||||
});
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
|
||||
.Callback((DTWebApi.Issue issue, string logMessage) =>
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>()))
|
||||
.Callback((DTWebApi.IReadOnlyIssue issue) =>
|
||||
{
|
||||
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
|
||||
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
|
||||
_trace.Info($"Issue '{issue.Type}': {message}");
|
||||
_issues.Add(issue);
|
||||
_trace.Info($"Issue '{issue.Type}': {issue.Message}");
|
||||
});
|
||||
_executionContext.Setup(x => x.CreateIssue(It.IsAny<IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback((string tag, string message) =>
|
||||
{
|
||||
|
||||
@@ -52,36 +52,36 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
// Act.
|
||||
ec.InitializeJob(jobRequest, CancellationToken.None);
|
||||
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
|
||||
ec.Complete();
|
||||
|
||||
@@ -190,9 +190,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
bigMessage += "a";
|
||||
}
|
||||
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = bigMessage });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = bigMessage });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = bigMessage });
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, bigMessage, null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, bigMessage, null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Notice, bigMessage, null, true));
|
||||
|
||||
ec.Complete();
|
||||
|
||||
@@ -242,13 +242,13 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var embeddedStep = ec.CreateChild(Guid.NewGuid(), "action_1_pre", "action_1_pre", null, null, ActionRunStage.Main, isEmbedded: true);
|
||||
embeddedStep.Start();
|
||||
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error annotation that should have step and line number information" });
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning annotation that should have step and line number information" });
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice annotation that should have step and line number information" });
|
||||
embeddedStep.AddIssue(embeddedStep.CreateIssue(IssueType.Error, "error annotation that should have step and line number information", null, true));
|
||||
embeddedStep.AddIssue(embeddedStep.CreateIssue(IssueType.Warning, "warning annotation that should have step and line number information", null, true));
|
||||
embeddedStep.AddIssue(embeddedStep.CreateIssue(IssueType.Notice, "notice annotation that should have step and line number information", null, true));
|
||||
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Error).Count() == 1)), Times.AtLeastOnce);
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Warning).Count() == 1)), Times.AtLeastOnce);
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i.Data.ContainsKey("stepNumber") && i.Data.ContainsKey("logFileLineNumber") && i.Type == IssueType.Notice).Count() == 1)), Times.AtLeastOnce);
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i["stepNumber"] != null && i["logFileLineNumber"] != null && i.Type == IssueType.Error).Count() == 1)), Times.AtLeastOnce);
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i["stepNumber"] != null && i["logFileLineNumber"] != null && i.Type == IssueType.Warning).Count() == 1)), Times.AtLeastOnce);
|
||||
jobServerQueue.Verify(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.Is<TimelineRecord>(t => t.Issues.Where(i => i["stepNumber"] != null && i["logFileLineNumber"] != null && i.Type == IssueType.Notice).Count() == 1)), Times.AtLeastOnce);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,12 +626,12 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
ec.StepTelemetry.StepId = Guid.NewGuid();
|
||||
ec.StepTelemetry.Stage = "main";
|
||||
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
ec.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Notice, "notice", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
ec.AddIssue(ec.CreateIssue(IssueType.Notice, "notice", null, true));
|
||||
|
||||
ec.Complete();
|
||||
|
||||
@@ -692,9 +692,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
embeddedStep.StepTelemetry.Action = "actions/checkout";
|
||||
embeddedStep.StepTelemetry.Ref = "v2";
|
||||
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
|
||||
embeddedStep.AddIssue(ec.CreateIssue(IssueType.Error, "error", null, true));
|
||||
embeddedStep.AddIssue(ec.CreateIssue(IssueType.Warning, "warning", null, true));
|
||||
embeddedStep.AddIssue(ec.CreateIssue(IssueType.Notice, "notice", null, true));
|
||||
|
||||
embeddedStep.PublishStepTelemetry();
|
||||
|
||||
@@ -711,63 +711,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void PublishStepResult_EmbeddedStep()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
// Arrange: Create a job request message.
|
||||
TaskOrchestrationPlanReference plan = new();
|
||||
TimelineReference timeline = new();
|
||||
Guid jobId = Guid.NewGuid();
|
||||
string jobName = "some job name";
|
||||
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
||||
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
||||
{
|
||||
Alias = Pipelines.PipelineConstants.SelfAlias,
|
||||
Id = "github",
|
||||
Version = "sha1"
|
||||
});
|
||||
jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
|
||||
|
||||
// Arrange: Setup the paging logger.
|
||||
var pagingLogger = new Mock<IPagingLogger>();
|
||||
var pagingLogger2 = new Mock<IPagingLogger>();
|
||||
var jobServerQueue = new Mock<IJobServerQueue>();
|
||||
jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()));
|
||||
|
||||
hc.EnqueueInstance(pagingLogger.Object);
|
||||
hc.EnqueueInstance(pagingLogger2.Object);
|
||||
hc.SetSingleton(jobServerQueue.Object);
|
||||
|
||||
var ec = new Runner.Worker.ExecutionContext();
|
||||
ec.Initialize(hc);
|
||||
|
||||
// Act.
|
||||
ec.InitializeJob(jobRequest, CancellationToken.None);
|
||||
ec.Start();
|
||||
|
||||
var embeddedStep = ec.CreateChild(Guid.NewGuid(), "action_1_pre", "action_1_pre", null, null, ActionRunStage.Main, isEmbedded: true);
|
||||
embeddedStep.Start();
|
||||
|
||||
embeddedStep.StepTelemetry.Type = "node16";
|
||||
embeddedStep.StepTelemetry.Action = "actions/checkout";
|
||||
embeddedStep.StepTelemetry.Ref = "v2";
|
||||
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Error, Message = "error" });
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Warning, Message = "warning" });
|
||||
embeddedStep.AddIssue(new Issue() { Type = IssueType.Notice, Message = "notice" });
|
||||
|
||||
ec.Complete();
|
||||
|
||||
// Assert.
|
||||
Assert.Equal(1, ec.Global.StepsResult.Count);
|
||||
Assert.Equal(TaskResult.Succeeded, ec.Global.StepsResult.Single().Conclusion);
|
||||
}
|
||||
}
|
||||
|
||||
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
|
||||
{
|
||||
var hc = new TestHostContext(this, testName);
|
||||
@@ -927,7 +870,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
inputVarsContext["VARIABLE_2"] = new StringContextData("value2");
|
||||
jobRequest.ContextData["vars"] = inputVarsContext;
|
||||
|
||||
// Arrange: Setup the paging logger.
|
||||
// Arrange: Setup the paging logger.
|
||||
var pagingLogger1 = new Mock<IPagingLogger>();
|
||||
var jobServerQueue = new Mock<IJobServerQueue>();
|
||||
hc.EnqueueInstance(pagingLogger1.Object);
|
||||
@@ -941,7 +884,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var expected = new DictionaryContextData();
|
||||
expected["VARIABLE_1"] = new StringContextData("value1");
|
||||
expected["VARIABLE_2"] = new StringContextData("value1");
|
||||
|
||||
|
||||
Assert.True(ExpressionValuesAssertEqual(expected, jobContext.ExpressionValues["vars"] as DictionaryContextData));
|
||||
}
|
||||
}
|
||||
@@ -972,7 +915,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
inputVarsContext[Constants.Variables.Actions.RunnerDebug] = new StringContextData("true");
|
||||
jobRequest.ContextData["vars"] = inputVarsContext;
|
||||
|
||||
// Arrange: Setup the paging logger.
|
||||
// Arrange: Setup the paging logger.
|
||||
var pagingLogger1 = new Mock<IPagingLogger>();
|
||||
var jobServerQueue = new Mock<IJobServerQueue>();
|
||||
hc.EnqueueInstance(pagingLogger1.Object);
|
||||
@@ -983,7 +926,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
jobContext.InitializeJob(jobRequest, CancellationToken.None);
|
||||
|
||||
|
||||
|
||||
Assert.Equal("true", jobContext.Global.Variables.Get(Constants.Variables.Actions.StepDebug));
|
||||
Assert.Equal("true", jobContext.Global.Variables.Get(Constants.Variables.Actions.RunnerDebug));
|
||||
}
|
||||
@@ -1018,7 +961,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
jobRequest.Variables[Constants.Variables.Actions.StepDebug] = "false";
|
||||
jobRequest.Variables[Constants.Variables.Actions.RunnerDebug] = "false";
|
||||
|
||||
// Arrange: Setup the paging logger.
|
||||
// Arrange: Setup the paging logger.
|
||||
var pagingLogger1 = new Mock<IPagingLogger>();
|
||||
var jobServerQueue = new Mock<IJobServerQueue>();
|
||||
hc.EnqueueInstance(pagingLogger1.Object);
|
||||
@@ -1029,7 +972,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
jobContext.InitializeJob(jobRequest, CancellationToken.None);
|
||||
|
||||
|
||||
|
||||
Assert.Equal("false", jobContext.Global.Variables.Get(Constants.Variables.Actions.StepDebug));
|
||||
Assert.Equal("false", jobContext.Global.Variables.Get(Constants.Variables.Actions.RunnerDebug));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GitHub.Actions.RunService.WebApi;
|
||||
using GitHub.DistributedTask.Pipelines;
|
||||
using GitHub.DistributedTask.WebApi;
|
||||
using GitHub.Runner.Sdk;
|
||||
|
||||
@@ -16,10 +16,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
private IExecutionContext _jobEc;
|
||||
private JobRunner _jobRunner;
|
||||
private List<IStep> _initResult = new();
|
||||
private Pipelines.AgentJobRequestMessage _message;
|
||||
private CancellationTokenSource _tokenSource;
|
||||
private Mock<IJobServer> _jobServer;
|
||||
|
||||
private Mock<IRunServer> _runServer;
|
||||
private Mock<IJobServerQueue> _jobServerQueue;
|
||||
private Mock<IConfigurationStore> _config;
|
||||
private Mock<IExtensionManager> _extensions;
|
||||
@@ -39,7 +38,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_extensions = new Mock<IExtensionManager>();
|
||||
_jobExtension = new Mock<IJobExtension>();
|
||||
_jobServer = new Mock<IJobServer>();
|
||||
_runServer = new Mock<IRunServer>();
|
||||
_jobServerQueue = new Mock<IJobServerQueue>();
|
||||
_stepRunner = new Mock<IStepsRunner>();
|
||||
_logger = new Mock<IPagingLogger>();
|
||||
@@ -57,6 +55,33 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_jobRunner = new JobRunner();
|
||||
_jobRunner.Initialize(hc);
|
||||
|
||||
TaskOrchestrationPlanReference plan = new();
|
||||
TimelineReference timeline = new Timeline(Guid.NewGuid());
|
||||
Guid jobId = Guid.NewGuid();
|
||||
_message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, testName, testName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
|
||||
_message.Variables[Constants.Variables.System.Culture] = "en-US";
|
||||
_message.Resources.Endpoints.Add(new ServiceEndpoint()
|
||||
{
|
||||
Name = WellKnownServiceEndpointNames.SystemVssConnection,
|
||||
Url = new Uri("https://pipelines.actions.githubusercontent.com"),
|
||||
Authorization = new EndpointAuthorization()
|
||||
{
|
||||
Scheme = "Test",
|
||||
Parameters = {
|
||||
{"AccessToken", "token"}
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
_message.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
||||
{
|
||||
Alias = Pipelines.PipelineConstants.SelfAlias,
|
||||
Id = "github",
|
||||
Version = "sha1"
|
||||
});
|
||||
_message.ContextData.Add("github", new Pipelines.ContextData.DictionaryContextData());
|
||||
|
||||
_initResult.Clear();
|
||||
|
||||
_jobExtension.Setup(x => x.InitializeJob(It.IsAny<IExecutionContext>(), It.IsAny<Pipelines.AgentJobRequestMessage>())).
|
||||
@@ -77,7 +102,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
hc.SetSingleton(_config.Object);
|
||||
hc.SetSingleton(_jobServer.Object);
|
||||
hc.SetSingleton(_runServer.Object);
|
||||
hc.SetSingleton(_jobServerQueue.Object);
|
||||
hc.SetSingleton(_stepRunner.Object);
|
||||
hc.SetSingleton(_extensions.Object);
|
||||
@@ -89,43 +113,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
return hc;
|
||||
}
|
||||
|
||||
private Pipelines.AgentJobRequestMessage GetMessage(String messageType = JobRequestMessageTypes.PipelineAgentJobRequest, [CallerMemberName] String testName = "")
|
||||
{
|
||||
TaskOrchestrationPlanReference plan = new();
|
||||
TimelineReference timeline = new Timeline(Guid.NewGuid());
|
||||
Guid jobId = Guid.NewGuid();
|
||||
var message = new Pipelines.AgentJobRequestMessage(
|
||||
plan,
|
||||
timeline,
|
||||
jobId,
|
||||
testName,
|
||||
testName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null,
|
||||
messageType: messageType);
|
||||
message.Variables[Constants.Variables.System.Culture] = "en-US";
|
||||
message.Resources.Endpoints.Add(new ServiceEndpoint()
|
||||
{
|
||||
Name = WellKnownServiceEndpointNames.SystemVssConnection,
|
||||
Url = new Uri("https://pipelines.actions.githubusercontent.com"),
|
||||
Authorization = new EndpointAuthorization()
|
||||
{
|
||||
Scheme = "Test",
|
||||
Parameters = {
|
||||
{"AccessToken", "token"}
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
message.Resources.Repositories.Add(new Pipelines.RepositoryResource()
|
||||
{
|
||||
Alias = Pipelines.PipelineConstants.SelfAlias,
|
||||
Id = "github",
|
||||
Version = "sha1"
|
||||
});
|
||||
message.ContextData.Add("github", new Pipelines.ContextData.DictionaryContextData());
|
||||
return message;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
@@ -136,7 +123,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_jobExtension.Setup(x => x.InitializeJob(It.IsAny<IExecutionContext>(), It.IsAny<Pipelines.AgentJobRequestMessage>()))
|
||||
.Throws(new Exception());
|
||||
|
||||
await _jobRunner.RunAsync(GetMessage(), _tokenSource.Token);
|
||||
await _jobRunner.RunAsync(_message, _tokenSource.Token);
|
||||
|
||||
Assert.Equal(TaskResult.Failed, _jobEc.Result);
|
||||
_stepRunner.Verify(x => x.RunAsync(It.IsAny<IExecutionContext>()), Times.Never);
|
||||
@@ -154,24 +141,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
.Throws(new OperationCanceledException());
|
||||
_tokenSource.Cancel();
|
||||
|
||||
await _jobRunner.RunAsync(GetMessage(), _tokenSource.Token);
|
||||
await _jobRunner.RunAsync(_message, _tokenSource.Token);
|
||||
|
||||
Assert.Equal(TaskResult.Canceled, _jobEc.Result);
|
||||
_stepRunner.Verify(x => x.RunAsync(It.IsAny<IExecutionContext>()), Times.Never);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task WorksWithRunnerJobRequestMessageType()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
var message = GetMessage(JobRequestMessageTypes.RunnerJobRequest);
|
||||
await _jobRunner.RunAsync(message, _tokenSource.Token);
|
||||
Assert.Equal(TaskResult.Succeeded, _jobEc.Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Actions.RunService.WebApi;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using GitHub.Services.Common;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using DTWebApi = GitHub.DistributedTask.WebApi;
|
||||
@@ -22,7 +22,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
private Mock<IActionCommandManager> _commandManager;
|
||||
private Variables _variables;
|
||||
private OnMatcherChanged _onMatcherChanged;
|
||||
private List<Tuple<DTWebApi.Issue, string>> _issues;
|
||||
private List<DTWebApi.IReadOnlyIssue> _issues;
|
||||
private List<string> _messages;
|
||||
private List<string> _commands;
|
||||
private OutputManager _outputManager;
|
||||
@@ -83,10 +83,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("ERROR: message 4");
|
||||
Process("NOT GOOD: message 5");
|
||||
Assert.Equal(4, _issues.Count);
|
||||
Assert.Equal("message 1", _issues[0].Item1.Message);
|
||||
Assert.Equal("message 2", _issues[1].Item1.Message);
|
||||
Assert.Equal("message 3", _issues[2].Item1.Message);
|
||||
Assert.Equal("message 5", _issues[3].Item1.Message);
|
||||
Assert.Equal("message 1", _issues[0].Message);
|
||||
Assert.Equal("message 2", _issues[1].Message);
|
||||
Assert.Equal("message 3", _issues[2].Message);
|
||||
Assert.Equal("message 5", _issues[3].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(1, _messages.Count);
|
||||
Assert.Equal("ERROR: message 4", _messages[0]);
|
||||
@@ -149,11 +149,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("ERROR: message 4");
|
||||
Process("NOT GOOD: message 5");
|
||||
Assert.Equal(5, _issues.Count);
|
||||
Assert.Equal("message 1", _issues[0].Item1.Message);
|
||||
Assert.Equal("message 2", _issues[1].Item1.Message);
|
||||
Assert.Equal("message 3", _issues[2].Item1.Message);
|
||||
Assert.Equal("message 4", _issues[3].Item1.Message);
|
||||
Assert.Equal("message 5", _issues[4].Item1.Message);
|
||||
Assert.Equal("message 1", _issues[0].Message);
|
||||
Assert.Equal("message 2", _issues[1].Message);
|
||||
Assert.Equal("message 3", _issues[2].Message);
|
||||
Assert.Equal("message 4", _issues[3].Message);
|
||||
Assert.Equal("message 5", _issues[4].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(0, _messages.Count);
|
||||
}
|
||||
@@ -189,10 +189,10 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("BAD: real bad");
|
||||
Process(": not working");
|
||||
Assert.Equal(2, _issues.Count);
|
||||
Assert.Equal("real bad", _issues[0].Item1.Message);
|
||||
Assert.Equal("BAD", _issues[0].Item1.Data["code"]);
|
||||
Assert.Equal("not working", _issues[1].Item1.Message);
|
||||
Assert.False(_issues[1].Item1.Data.ContainsKey("code"));
|
||||
Assert.Equal("real bad", _issues[0].Message);
|
||||
Assert.Equal("BAD", _issues[0]["code"]);
|
||||
Assert.Equal("not working", _issues[1].Message);
|
||||
Assert.Null(_issues[1]["code"]);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(0, _messages.Count);
|
||||
}
|
||||
@@ -239,11 +239,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("Error: real bad");
|
||||
Process("regular message 2");
|
||||
Assert.Equal(5, _issues.Count);
|
||||
Assert.Equal("it broke", _issues[0].Item1.Message);
|
||||
Assert.Equal("oh no", _issues[1].Item1.Message);
|
||||
Assert.Equal("not good", _issues[2].Item1.Message);
|
||||
Assert.Equal("it broke again", _issues[3].Item1.Message);
|
||||
Assert.Equal("real bad", _issues[4].Item1.Message);
|
||||
Assert.Equal("it broke", _issues[0].Message);
|
||||
Assert.Equal("oh no", _issues[1].Message);
|
||||
Assert.Equal("not good", _issues[2].Message);
|
||||
Assert.Equal("it broke again", _issues[3].Message);
|
||||
Assert.Equal("real bad", _issues[4].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(4, _messages.Count);
|
||||
Assert.Equal("Start: hello", _messages[0]);
|
||||
@@ -294,8 +294,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("ERROR: it is broken");
|
||||
Process("NOT GOOD: that did not work");
|
||||
Assert.Equal(2, _issues.Count);
|
||||
Assert.Equal("it is broken", _issues[0].Item1.Message);
|
||||
Assert.Equal("that did not work", _issues[1].Item1.Message);
|
||||
Assert.Equal("it is broken", _issues[0].Message);
|
||||
Assert.Equal("that did not work", _issues[1].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(0, _messages.Count);
|
||||
}
|
||||
@@ -333,15 +333,15 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("(12,thirty-four): it is broken");
|
||||
Process("(twelve,34): not working");
|
||||
Assert.Equal(3, _issues.Count);
|
||||
Assert.Equal("real bad", _issues[0].Item1.Message);
|
||||
Assert.Equal("12", _issues[0].Item1.Data["line"]);
|
||||
Assert.Equal("34", _issues[0].Item1.Data["col"]);
|
||||
Assert.Equal("it is broken", _issues[1].Item1.Message);
|
||||
Assert.Equal("12", _issues[1].Item1.Data["line"]);
|
||||
Assert.False(_issues[1].Item1.Data.ContainsKey("col"));
|
||||
Assert.Equal("not working", _issues[2].Item1.Message);
|
||||
Assert.False(_issues[2].Item1.Data.ContainsKey("line"));
|
||||
Assert.Equal("34", _issues[2].Item1.Data["col"]);
|
||||
Assert.Equal("real bad", _issues[0].Message);
|
||||
Assert.Equal("12", _issues[0]["line"]);
|
||||
Assert.Equal("34", _issues[0]["col"]);
|
||||
Assert.Equal("it is broken", _issues[1].Message);
|
||||
Assert.Equal("12", _issues[1]["line"]);
|
||||
Assert.Null(_issues[1]["col"]);
|
||||
Assert.Equal("not working", _issues[2].Message);
|
||||
Assert.Null(_issues[2]["line"]);
|
||||
Assert.Equal("34", _issues[2]["col"]);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(2, _messages.Count);
|
||||
Assert.Equal("##[debug]Unable to parse column number 'thirty-four'", _messages[0]);
|
||||
@@ -374,8 +374,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("this line is a command too ##[some-command]even though it contains ERROR: not working again");
|
||||
Process("##[not-command]this line is an ERROR: it is broken again");
|
||||
Assert.Equal(2, _issues.Count);
|
||||
Assert.Equal("it is broken", _issues[0].Item1.Message);
|
||||
Assert.Equal("it is broken again", _issues[1].Item1.Message);
|
||||
Assert.Equal("it is broken", _issues[0].Message);
|
||||
Assert.Equal("it is broken again", _issues[1].Message);
|
||||
Assert.Equal(2, _commands.Count);
|
||||
Assert.Equal("##[some-command]this line is a command even though it contains ERROR: not working", _commands[0]);
|
||||
Assert.Equal("this line is a command too ##[some-command]even though it contains ERROR: not working again", _commands[1]);
|
||||
@@ -405,8 +405,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
});
|
||||
Process("the error: \033[31mred, \033[1;31mbright red, \033[mreset");
|
||||
Assert.Equal(1, _issues.Count);
|
||||
Assert.Equal("red, bright red, reset", _issues[0].Item1.Message);
|
||||
Assert.Equal("the error: red, bright red, reset", _issues[0].Item2);
|
||||
Assert.Equal("red, bright red, reset", _issues[0].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(0, _messages.Count);
|
||||
}
|
||||
@@ -456,9 +455,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("ERROR: message 3");
|
||||
Process("NOT GOOD: message 4");
|
||||
Assert.Equal(3, _issues.Count);
|
||||
Assert.Equal("message 1", _issues[0].Item1.Message);
|
||||
Assert.Equal("message 2", _issues[1].Item1.Message);
|
||||
Assert.Equal("message 4", _issues[2].Item1.Message);
|
||||
Assert.Equal("message 1", _issues[0].Message);
|
||||
Assert.Equal("message 2", _issues[1].Message);
|
||||
Assert.Equal("message 4", _issues[2].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(1, _messages.Count);
|
||||
Assert.Equal("ERROR: message 3", _messages[0]);
|
||||
@@ -518,8 +517,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("Matches both line 1: hello again");
|
||||
Process("oh no, another error");
|
||||
Assert.Equal(2, _issues.Count);
|
||||
Assert.Equal("it broke", _issues[0].Item1.Message);
|
||||
Assert.Equal("oh no, another error", _issues[1].Item1.Message);
|
||||
Assert.Equal("it broke", _issues[0].Message);
|
||||
Assert.Equal("oh no, another error", _issues[1].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(4, _messages.Count);
|
||||
Assert.Equal("Matches both line 1: hello", _messages[0]);
|
||||
@@ -574,14 +573,14 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process(": not working");
|
||||
Process("ERROR! uh oh");
|
||||
Assert.Equal(4, _issues.Count);
|
||||
Assert.Equal("real bad", _issues[0].Item1.Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[0].Item1.Type);
|
||||
Assert.Equal("not great", _issues[1].Item1.Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Warning, _issues[1].Item1.Type);
|
||||
Assert.Equal("not working", _issues[2].Item1.Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[2].Item1.Type);
|
||||
Assert.Equal("uh oh", _issues[3].Item1.Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[3].Item1.Type);
|
||||
Assert.Equal("real bad", _issues[0].Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[0].Type);
|
||||
Assert.Equal("not great", _issues[1].Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Warning, _issues[1].Type);
|
||||
Assert.Equal("not working", _issues[2].Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[2].Type);
|
||||
Assert.Equal("uh oh", _issues[3].Message);
|
||||
Assert.Equal(DTWebApi.IssueType.Error, _issues[3].Type);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(2, _messages.Count);
|
||||
Assert.StartsWith("##[debug]Skipped", _messages[0]);
|
||||
@@ -634,9 +633,9 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Process("jane.doe@contoso.com");
|
||||
Process("ERR: this error");
|
||||
Assert.Equal(3, _issues.Count);
|
||||
Assert.Equal("john.doe@contoso.com", _issues[0].Item1.Message);
|
||||
Assert.Contains("Removing issue matcher 'email'", _issues[1].Item1.Message);
|
||||
Assert.Equal("this error", _issues[2].Item1.Message);
|
||||
Assert.Equal("john.doe@contoso.com", _issues[0].Message);
|
||||
Assert.Contains("Removing issue matcher 'email'", _issues[1].Message);
|
||||
Assert.Equal("this error", _issues[2].Message);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(2, _messages.Where(x => x.StartsWith("##[debug]Timeout processing issue matcher")).Count());
|
||||
Assert.Equal(1, _messages.Where(x => x.Equals("t@t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.t.c%20")).Count());
|
||||
@@ -726,32 +725,32 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
Assert.Equal(9, _issues.Count);
|
||||
|
||||
Assert.Equal("some error 1", _issues[0].Item1.Message);
|
||||
Assert.False(_issues[0].Item1.Data.ContainsKey("file"));
|
||||
Assert.Equal("some error 1", _issues[0].Message);
|
||||
Assert.Null(_issues[0]["file"]);
|
||||
|
||||
Assert.Equal("some error 2", _issues[1].Item1.Message);
|
||||
Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[1].Item1.Data["file"]);
|
||||
Assert.Equal("some error 2", _issues[1].Message);
|
||||
Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[1]["file"]);
|
||||
|
||||
Assert.Equal("some error 3", _issues[2].Item1.Message);
|
||||
Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[2].Item1.Data["file"]);
|
||||
Assert.Equal("some error 3", _issues[2].Message);
|
||||
Assert.Equal(file_workflowRepository.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[2]["file"]);
|
||||
|
||||
Assert.Equal("some error 4", _issues[3].Item1.Message);
|
||||
Assert.Equal(file_workflowRepository_nestedDirectory.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[3].Item1.Data["file"]);
|
||||
Assert.Equal("some error 4", _issues[3].Message);
|
||||
Assert.Equal(file_workflowRepository_nestedDirectory.Substring(workflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[3]["file"]);
|
||||
|
||||
Assert.Equal("some error 5", _issues[4].Item1.Message);
|
||||
Assert.False(_issues[4].Item1.Data.ContainsKey("file"));
|
||||
Assert.Equal("some error 5", _issues[4].Message);
|
||||
Assert.Null(_issues[4]["file"]);
|
||||
|
||||
Assert.Equal("some error 6", _issues[5].Item1.Message);
|
||||
Assert.False(_issues[5].Item1.Data.ContainsKey("file"));
|
||||
Assert.Equal("some error 6", _issues[5].Message);
|
||||
Assert.Null(_issues[5]["file"]);
|
||||
|
||||
Assert.Equal("some error 7", _issues[6].Item1.Message);
|
||||
Assert.False(_issues[6].Item1.Data.ContainsKey("file"));
|
||||
Assert.Equal("some error 7", _issues[6].Message);
|
||||
Assert.Null(_issues[6]["file"]);
|
||||
|
||||
Assert.Equal("some error 8", _issues[7].Item1.Message);
|
||||
Assert.Equal(file_nestedWorkflowRepository.Substring(nestedWorkflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[7].Item1.Data["file"]);
|
||||
Assert.Equal("some error 8", _issues[7].Message);
|
||||
Assert.Equal(file_nestedWorkflowRepository.Substring(nestedWorkflowRepository.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[7]["file"]);
|
||||
|
||||
Assert.Equal("some error 9", _issues[8].Item1.Message);
|
||||
Assert.Equal(file_workflowRepositoryUsingSsh.Substring(workflowRepositoryUsingSsh.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[8].Item1.Data["file"]);
|
||||
Assert.Equal("some error 9", _issues[8].Message);
|
||||
Assert.Equal(file_workflowRepositoryUsingSsh.Substring(workflowRepositoryUsingSsh.Length + 1).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), _issues[8]["file"]);
|
||||
}
|
||||
|
||||
Environment.SetEnvironmentVariable("RUNNER_TEST_GET_REPOSITORY_PATH_FAILSAFE", "");
|
||||
@@ -811,11 +810,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
Assert.Equal(2, _issues.Count);
|
||||
|
||||
Assert.Equal("some error 1", _issues[0].Item1.Message);
|
||||
Assert.Equal("some-file.txt", _issues[0].Item1.Data["file"]);
|
||||
Assert.Equal("some error 1", _issues[0].Message);
|
||||
Assert.Equal("some-file.txt", _issues[0]["file"]);
|
||||
|
||||
Assert.Equal("some error 2", _issues[1].Item1.Message);
|
||||
Assert.Equal("some-file.txt", _issues[1].Item1.Data["file"]);
|
||||
Assert.Equal("some error 2", _issues[1].Message);
|
||||
Assert.Equal("some-file.txt", _issues[1]["file"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -872,11 +871,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
Assert.Equal(2, _issues.Count);
|
||||
|
||||
Assert.Equal("some error 1", _issues[0].Item1.Message);
|
||||
Assert.Equal("some-file.txt", _issues[0].Item1.Data["file"]);
|
||||
Assert.Equal("some error 1", _issues[0].Message);
|
||||
Assert.Equal("some-file.txt", _issues[0]["file"]);
|
||||
|
||||
Assert.Equal("some error 2", _issues[1].Item1.Message);
|
||||
Assert.Equal("some-file.txt", _issues[1].Item1.Data["file"]);
|
||||
Assert.Equal("some error 2", _issues[1].Message);
|
||||
Assert.Equal("some-file.txt", _issues[1]["file"]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -930,8 +929,8 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
// Process
|
||||
Process("some-directory/some-file.txt: some error [workflow-repo/some-project/some-project.proj]");
|
||||
Assert.Equal(1, _issues.Count);
|
||||
Assert.Equal("some error", _issues[0].Item1.Message);
|
||||
Assert.Equal("some-project/some-directory/some-file.txt", _issues[0].Item1.Data["file"]);
|
||||
Assert.Equal("some error", _issues[0].Message);
|
||||
Assert.Equal("some-project/some-directory/some-file.txt", _issues[0]["file"]);
|
||||
Assert.Equal(0, _commands.Count);
|
||||
Assert.Equal(0, _messages.Count);
|
||||
}
|
||||
@@ -959,7 +958,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
matchers?.Validate();
|
||||
|
||||
_onMatcherChanged = null;
|
||||
_issues = new List<Tuple<DTWebApi.Issue, string>>();
|
||||
_issues = new List<DTWebApi.IReadOnlyIssue>();
|
||||
_messages = new List<string>();
|
||||
_commands = new List<string>();
|
||||
|
||||
@@ -984,10 +983,12 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_onMatcherChanged = handler;
|
||||
});
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
|
||||
.Callback((DTWebApi.Issue issue, string logMessage) =>
|
||||
_executionContext.Setup(x => x.CreateIssue(It.IsAny<DTWebApi.IssueType>(), It.IsAny<string>(), It.IsAny<IssueMetadata>(), It.IsAny<bool>()))
|
||||
.Returns(TestUtil.CreateTestIssue);
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>()))
|
||||
.Callback((DTWebApi.IReadOnlyIssue issue) =>
|
||||
{
|
||||
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
|
||||
_issues.Add(issue);
|
||||
});
|
||||
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback((string tag, string message) =>
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
public sealed class SaveStateFileCommandL0
|
||||
{
|
||||
private Mock<IExecutionContext> _executionContext;
|
||||
private List<Tuple<DTWebApi.Issue, string>> _issues;
|
||||
private List<DTWebApi.IReadOnlyIssue> _issues;
|
||||
private string _rootDirectory;
|
||||
private SaveStateFileCommand _saveStateFileCommand;
|
||||
private Dictionary<string, string> _intraActionState;
|
||||
@@ -390,7 +390,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
private TestHostContext Setup([CallerMemberName] string name = "")
|
||||
{
|
||||
_issues = new List<Tuple<DTWebApi.Issue, string>>();
|
||||
_issues = new List<DTWebApi.IReadOnlyIssue>();
|
||||
_intraActionState = new Dictionary<string, string>();
|
||||
|
||||
var hostContext = new TestHostContext(this, name);
|
||||
@@ -413,12 +413,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
|
||||
WriteDebug = true,
|
||||
});
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
|
||||
.Callback((DTWebApi.Issue issue, string logMessage) =>
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>()))
|
||||
.Callback((DTWebApi.IReadOnlyIssue issue) =>
|
||||
{
|
||||
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
|
||||
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
|
||||
_trace.Info($"Issue '{issue.Type}': {message}");
|
||||
_issues.Add(issue);
|
||||
_trace.Info($"Issue '{issue.Type}': {issue.Message}");
|
||||
});
|
||||
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback((string tag, string message) =>
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using DTWebApi = GitHub.DistributedTask.WebApi;
|
||||
@@ -21,7 +15,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
public sealed class SetEnvFileCommandL0
|
||||
{
|
||||
private Mock<IExecutionContext> _executionContext;
|
||||
private List<Tuple<DTWebApi.Issue, string>> _issues;
|
||||
private List<DTWebApi.IReadOnlyIssue> _issues;
|
||||
private string _rootDirectory;
|
||||
private SetEnvFileCommand _setEnvFileCommand;
|
||||
private ITraceWriter _trace;
|
||||
@@ -389,7 +383,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
private TestHostContext Setup([CallerMemberName] string name = "")
|
||||
{
|
||||
_issues = new List<Tuple<DTWebApi.Issue, string>>();
|
||||
_issues = new List<DTWebApi.IReadOnlyIssue>();
|
||||
|
||||
var hostContext = new TestHostContext(this, name);
|
||||
|
||||
@@ -411,12 +405,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
|
||||
WriteDebug = true,
|
||||
});
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
|
||||
.Callback((DTWebApi.Issue issue, string logMessage) =>
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>()))
|
||||
.Callback((DTWebApi.IReadOnlyIssue issue) =>
|
||||
{
|
||||
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
|
||||
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
|
||||
_trace.Info($"Issue '{issue.Type}': {message}");
|
||||
_issues.Add(issue);
|
||||
_trace.Info($"Issue '{issue.Type}': {issue.Message}");
|
||||
});
|
||||
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback((string tag, string message) =>
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.CompilerServices;
|
||||
using GitHub.Runner.Common.Util;
|
||||
using GitHub.Runner.Sdk;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using DTWebApi = GitHub.DistributedTask.WebApi;
|
||||
@@ -21,7 +15,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
public sealed class SetOutputFileCommandL0
|
||||
{
|
||||
private Mock<IExecutionContext> _executionContext;
|
||||
private List<Tuple<DTWebApi.Issue, string>> _issues;
|
||||
private List<DTWebApi.IReadOnlyIssue> _issues;
|
||||
private Dictionary<string, string> _outputs;
|
||||
private string _rootDirectory;
|
||||
private SetOutputFileCommand _setOutputFileCommand;
|
||||
@@ -390,7 +384,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
private TestHostContext Setup([CallerMemberName] string name = "")
|
||||
{
|
||||
_issues = new List<Tuple<DTWebApi.Issue, string>>();
|
||||
_issues = new List<DTWebApi.IReadOnlyIssue>();
|
||||
_outputs = new Dictionary<string, string>();
|
||||
|
||||
var hostContext = new TestHostContext(this, name);
|
||||
@@ -413,12 +407,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer),
|
||||
WriteDebug = true,
|
||||
});
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.Issue>(), It.IsAny<string>()))
|
||||
.Callback((DTWebApi.Issue issue, string logMessage) =>
|
||||
_executionContext.Setup(x => x.AddIssue(It.IsAny<DTWebApi.IReadOnlyIssue>()))
|
||||
.Callback((DTWebApi.IReadOnlyIssue issue) =>
|
||||
{
|
||||
_issues.Add(new Tuple<DTWebApi.Issue, string>(issue, logMessage));
|
||||
var message = !string.IsNullOrEmpty(logMessage) ? logMessage : issue.Message;
|
||||
_trace.Info($"Issue '{issue.Type}': {message}");
|
||||
_issues.Add(issue);
|
||||
_trace.Info($"Issue '{issue.Type}': {issue.Message}");
|
||||
});
|
||||
_executionContext.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>()))
|
||||
.Callback((string tag, string message) =>
|
||||
|
||||
13
src/dev.sh
13
src/dev.sh
@@ -22,7 +22,7 @@ DOWNLOAD_DIR="$SCRIPT_DIR/../_downloads/netcore2x"
|
||||
PACKAGE_DIR="$SCRIPT_DIR/../_package"
|
||||
PACKAGE_TRIMS_DIR="$SCRIPT_DIR/../_package_trims"
|
||||
DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk"
|
||||
DOTNETSDK_VERSION="6.0.405"
|
||||
DOTNETSDK_VERSION="6.0.300"
|
||||
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
|
||||
RUNNER_VERSION=$(cat runnerversion)
|
||||
|
||||
@@ -203,13 +203,6 @@ function runtest ()
|
||||
dotnet msbuild -t:test -p:PackageRuntime="${RUNTIME_ID}" -p:BUILDCONFIG="${BUILD_CONFIG}" -p:RunnerVersion="${RUNNER_VERSION}" ./dir.proj || failed "failed tests"
|
||||
}
|
||||
|
||||
function format()
|
||||
{
|
||||
heading "Formatting..."
|
||||
files="$(git status -s "*.cs" | awk '{print $2}' | tr '\n' ' ')"
|
||||
dotnet format ${SCRIPT_DIR}/ActionsRunner.sln --exclude / --include $files || failed "failed formatting"
|
||||
}
|
||||
|
||||
function package ()
|
||||
{
|
||||
if [ ! -d "${LAYOUT_DIR}/bin" ]; then
|
||||
@@ -367,9 +360,7 @@ case $DEV_CMD in
|
||||
"l") layout;;
|
||||
"package") package;;
|
||||
"p") package;;
|
||||
"format") format;;
|
||||
"f") format;;
|
||||
*) echo "Invalid cmd. Use build(b), test(t), layout(l), package(p), or format(f)";;
|
||||
*) echo "Invalid cmd. Use build(b), test(t), layout(l) or package(p)";;
|
||||
esac
|
||||
|
||||
popd
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "6.0.405"
|
||||
"version": "6.0.300"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.302.1
|
||||
2.299.1
|
||||
|
||||
Reference in New Issue
Block a user