mirror of
https://github.com/actions/runner.git
synced 2025-12-10 20:36:49 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dc005ee36 | ||
|
|
84b1bea43e | ||
|
|
ce4d7be00f | ||
|
|
bd7235ef62 | ||
|
|
0f15173045 | ||
|
|
76dc3a28c0 | ||
|
|
c67e7f2813 | ||
|
|
54052b94fb | ||
|
|
f2c05de91c | ||
|
|
18803bdff6 | ||
|
|
04b07b6675 | ||
|
|
dd9fcfc5b2 | ||
|
|
5107c5efb2 | ||
|
|
1b61d78c07 | ||
|
|
2e0eb2c11f | ||
|
|
2d83e1d88f | ||
|
|
4a1e38095b | ||
|
|
f467e9e125 | ||
|
|
77e0bfbb8a | ||
|
|
a52c53955c | ||
|
|
8ebf298bcd | ||
|
|
4b85145661 | ||
|
|
bc8b6e0152 | ||
|
|
82e01c6173 | ||
|
|
93bc1cd918 | ||
|
|
692d910868 | ||
|
|
2c8c941622 | ||
|
|
86d6211c75 | ||
|
|
aa90563cae | ||
|
|
4cb3cb2962 | ||
|
|
d7777fd632 |
@@ -4,7 +4,7 @@
|
|||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/docker-in-docker:1": {},
|
"ghcr.io/devcontainers/features/docker-in-docker:1": {},
|
||||||
"ghcr.io/devcontainers/features/dotnet": {
|
"ghcr.io/devcontainers/features/dotnet": {
|
||||||
"version": "6.0.419"
|
"version": "6.0.421"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": "16"
|
"version": "16"
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ FROM mcr.microsoft.com/dotnet/runtime-deps:6.0-jammy as build
|
|||||||
ARG TARGETOS
|
ARG TARGETOS
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG RUNNER_VERSION
|
ARG RUNNER_VERSION
|
||||||
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.5.1
|
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.6.0
|
||||||
ARG DOCKER_VERSION=25.0.2
|
ARG DOCKER_VERSION=25.0.4
|
||||||
ARG BUILDX_VERSION=0.12.1
|
ARG BUILDX_VERSION=0.13.1
|
||||||
|
|
||||||
RUN apt update -y && apt install curl unzip -y
|
RUN apt update -y && apt install curl unzip -y
|
||||||
|
|
||||||
@@ -39,12 +39,15 @@ ENV RUNNER_MANUALLY_TRAP_SIG=1
|
|||||||
ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
|
ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
|
||||||
ENV ImageOS=ubuntu22
|
ENV ImageOS=ubuntu22
|
||||||
|
|
||||||
RUN apt-get update -y \
|
# 'gpg-agent' and 'software-properties-common' are needed for the 'add-apt-repository' command that follows
|
||||||
&& apt-get install -y --no-install-recommends \
|
RUN apt update -y \
|
||||||
sudo \
|
&& apt install -y --no-install-recommends sudo lsb-release gpg-agent software-properties-common \
|
||||||
lsb-release \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Configure git-core/ppa based on guidance here: https://git-scm.com/download/linux
|
||||||
|
RUN add-apt-repository ppa:git-core/ppa \
|
||||||
|
&& apt update -y
|
||||||
|
|
||||||
RUN adduser --disabled-password --gecos "" --uid 1001 runner \
|
RUN adduser --disabled-password --gecos "" --uid 1001 runner \
|
||||||
&& groupadd docker --gid 123 \
|
&& groupadd docker --gid 123 \
|
||||||
&& usermod -aG sudo runner \
|
&& usermod -aG sudo runner \
|
||||||
|
|||||||
@@ -1,32 +1,24 @@
|
|||||||
## What's Changed
|
## What's Changed
|
||||||
* Prepare v2.313.0 Release by @luketomlinson in https://github.com/actions/runner/pull/3137
|
|
||||||
* Pass RunnerOS during job acquire. by @TingluoHuang in https://github.com/actions/runner/pull/3140
|
|
||||||
* Process `snapshot` tokens by @davidomid in https://github.com/actions/runner/pull/3135
|
|
||||||
* Update dotnet sdk to latest version @6.0.419 by @github-actions in https://github.com/actions/runner/pull/3158
|
|
||||||
* handle broker run service exception handling by @yaananth in https://github.com/actions/runner/pull/3163
|
|
||||||
* Add a retry logic to docker login operation by @enescakir in https://github.com/actions/runner/pull/3089
|
|
||||||
* Broker fixes for token refreshes and AccessDeniedException by @luketomlinson in https://github.com/actions/runner/pull/3161
|
|
||||||
* Remove USE_BROKER_FLOW by @luketomlinson in https://github.com/actions/runner/pull/3162
|
|
||||||
* Refresh Token for BrokerServer by @luketomlinson in https://github.com/actions/runner/pull/3167
|
|
||||||
* Better step timeout message. by @TingluoHuang in https://github.com/actions/runner/pull/3166
|
|
||||||
* Fix summaries for actions results by @SrRyan in https://github.com/actions/runner/pull/3174
|
|
||||||
|
|
||||||
## New Contributors
|
- Do not give up when uploading steps metadata by @yacaovsnc in https://github.com/actions/runner/pull/3280
|
||||||
* @davidomid made their first contribution in https://github.com/actions/runner/pull/3135
|
- Upgrade node20 to 20.13.1 by @pje in https://github.com/actions/runner/pull/3284
|
||||||
* @enescakir made their first contribution in https://github.com/actions/runner/pull/3089
|
- Delete all the contentHash files by @pje in https://github.com/actions/runner/pull/3285
|
||||||
* @SrRyan made their first contribution in https://github.com/actions/runner/pull/3174
|
- Make it easy to install `git` on an Action Runner Image by @jww3 in https://github.com/actions/runner/pull/3273
|
||||||
|
- Install `gpg-agent` during actions/runner container image build by @jww3 in https://github.com/actions/runner/pull/3294
|
||||||
|
|
||||||
**Full Changelog**: https://github.com/actions/runner/compare/v2.313.0...v2.314.0
|
**Full Changelog**: https://github.com/actions/runner/compare/v2.316.1...v2.317.0
|
||||||
|
|
||||||
_Note: Actions Runner follows a progressive release policy, so the latest release might not be available to your enterprise, organization, or repository yet.
|
_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.
|
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_
|
See https://docs.github.com/en/enterprise-cloud@latest/actions/hosting-your-own-runners/adding-self-hosted-runners_
|
||||||
|
|
||||||
## Windows x64
|
## 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.
|
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.
|
||||||
|
|
||||||
The following snipped needs to be run on `powershell`:
|
The following snipped needs to be run on `powershell`:
|
||||||
``` powershell
|
|
||||||
|
```powershell
|
||||||
# Create a folder under the drive root
|
# Create a folder under the drive root
|
||||||
mkdir \actions-runner ; cd \actions-runner
|
mkdir \actions-runner ; cd \actions-runner
|
||||||
# Download the latest runner package
|
# Download the latest runner package
|
||||||
@@ -37,12 +29,14 @@ Add-Type -AssemblyName System.IO.Compression.FileSystem ;
|
|||||||
```
|
```
|
||||||
|
|
||||||
## [Pre-release] Windows arm64
|
## [Pre-release] Windows arm64
|
||||||
|
|
||||||
**Warning:** Windows arm64 runners are currently in preview status and use [unofficial versions of nodejs](https://unofficial-builds.nodejs.org/). They are not intended for production workflows.
|
**Warning:** Windows arm64 runners are currently in preview status and use [unofficial versions of nodejs](https://unofficial-builds.nodejs.org/). They are not intended for production workflows.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
The following snipped needs to be run on `powershell`:
|
The following snipped needs to be run on `powershell`:
|
||||||
``` powershell
|
|
||||||
|
```powershell
|
||||||
# Create a folder under the drive root
|
# Create a folder under the drive root
|
||||||
mkdir \actions-runner ; cd \actions-runner
|
mkdir \actions-runner ; cd \actions-runner
|
||||||
# Download the latest runner package
|
# Download the latest runner package
|
||||||
@@ -54,7 +48,7 @@ Add-Type -AssemblyName System.IO.Compression.FileSystem ;
|
|||||||
|
|
||||||
## OSX x64
|
## OSX x64
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
# Create a folder
|
# Create a folder
|
||||||
mkdir actions-runner && cd actions-runner
|
mkdir actions-runner && cd actions-runner
|
||||||
# Download the latest runner package
|
# Download the latest runner package
|
||||||
@@ -65,7 +59,7 @@ tar xzf ./actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz
|
|||||||
|
|
||||||
## OSX arm64 (Apple silicon)
|
## OSX arm64 (Apple silicon)
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
# Create a folder
|
# Create a folder
|
||||||
mkdir actions-runner && cd actions-runner
|
mkdir actions-runner && cd actions-runner
|
||||||
# Download the latest runner package
|
# Download the latest runner package
|
||||||
@@ -76,7 +70,7 @@ tar xzf ./actions-runner-osx-arm64-<RUNNER_VERSION>.tar.gz
|
|||||||
|
|
||||||
## Linux x64
|
## Linux x64
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
# Create a folder
|
# Create a folder
|
||||||
mkdir actions-runner && cd actions-runner
|
mkdir actions-runner && cd actions-runner
|
||||||
# Download the latest runner package
|
# Download the latest runner package
|
||||||
@@ -87,7 +81,7 @@ tar xzf ./actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz
|
|||||||
|
|
||||||
## Linux arm64
|
## Linux arm64
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
# Create a folder
|
# Create a folder
|
||||||
mkdir actions-runner && cd actions-runner
|
mkdir actions-runner && cd actions-runner
|
||||||
# Download the latest runner package
|
# Download the latest runner package
|
||||||
@@ -98,7 +92,7 @@ tar xzf ./actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz
|
|||||||
|
|
||||||
## Linux arm
|
## Linux arm
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
# Create a folder
|
# Create a folder
|
||||||
mkdir actions-runner && cd actions-runner
|
mkdir actions-runner && cd actions-runner
|
||||||
# Download the latest runner package
|
# Download the latest runner package
|
||||||
@@ -108,6 +102,7 @@ tar xzf ./actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Using your self hosted runner
|
## Using your self hosted runner
|
||||||
|
|
||||||
For additional details about configuring, running, or shutting down the runner please check out our [product docs.](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/adding-self-hosted-runners)
|
For additional details about configuring, running, or shutting down the runner please check out our [product docs.](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/adding-self-hosted-runners)
|
||||||
|
|
||||||
## SHA-256 Checksums
|
## SHA-256 Checksums
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.314.1
|
2.317.0
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
54d95a44d118dba852395991224a6b9c1abe916858c87138656f80c619e85331
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
68015af17f06a824fa478e62ae7393766ce627fd5599ab916432a14656a19a52
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
a2628119ca419cb54e279103ffae7986cdbd0814d57c73ff0dc74c38be08b9ae
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
de71ca09ead807e1a2ce9df0a5b23eb7690cb71fff51169a77e4c3992be53dda
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
d009e05e6b26d614d65be736a15d1bd151932121c16a9ff1b986deadecc982b9
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
f730db39c2305800b4653795360ba9c10c68f384a46b85d808f1f9f0ed3c42e4
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
a35b5722375490e9473cdcccb5e18b41eba3dbf4344fe31abc9821e21f18ea5a
|
|
||||||
1
src/Misc/contentHash/externals/linux-arm
vendored
1
src/Misc/contentHash/externals/linux-arm
vendored
@@ -1 +0,0 @@
|
|||||||
4bf3e1af0d482af1b2eaf9f08250248a8c1aea8ec20a3c5be116d58cdd930009
|
|
||||||
1
src/Misc/contentHash/externals/linux-arm64
vendored
1
src/Misc/contentHash/externals/linux-arm64
vendored
@@ -1 +0,0 @@
|
|||||||
ec1719a8cb4d8687328aa64f4aa7c4e3498a715d8939117874782e3e6e63a14b
|
|
||||||
1
src/Misc/contentHash/externals/linux-x64
vendored
1
src/Misc/contentHash/externals/linux-x64
vendored
@@ -1 +0,0 @@
|
|||||||
50538de29f173bb73f708c4ed2c8328a62b8795829b97b2a6cb57197e2305287
|
|
||||||
1
src/Misc/contentHash/externals/osx-arm64
vendored
1
src/Misc/contentHash/externals/osx-arm64
vendored
@@ -1 +0,0 @@
|
|||||||
a0a96cbb7593643b69e669bf14d7b29b7f27800b3a00bb3305aebe041456c701
|
|
||||||
1
src/Misc/contentHash/externals/osx-x64
vendored
1
src/Misc/contentHash/externals/osx-x64
vendored
@@ -1 +0,0 @@
|
|||||||
6255b22692779467047ecebd60ad46984866d75cdfe10421d593a7b51d620b09
|
|
||||||
1
src/Misc/contentHash/externals/win-arm64
vendored
1
src/Misc/contentHash/externals/win-arm64
vendored
@@ -1 +0,0 @@
|
|||||||
6ff1abd055dc35bfbf06f75c2f08908f660346f66ad1d8f81c910068e9ba029d
|
|
||||||
1
src/Misc/contentHash/externals/win-x64
vendored
1
src/Misc/contentHash/externals/win-x64
vendored
@@ -1 +0,0 @@
|
|||||||
433a6d748742d12abd20dc2a79b62ac3d9718ae47ef26f8e84dc8c180eea3659
|
|
||||||
@@ -5,10 +5,11 @@ PRECACHE=$2
|
|||||||
NODE_URL=https://nodejs.org/dist
|
NODE_URL=https://nodejs.org/dist
|
||||||
UNOFFICIAL_NODE_URL=https://unofficial-builds.nodejs.org/download/release
|
UNOFFICIAL_NODE_URL=https://unofficial-builds.nodejs.org/download/release
|
||||||
NODE_ALPINE_URL=https://github.com/actions/alpine_nodejs/releases/download
|
NODE_ALPINE_URL=https://github.com/actions/alpine_nodejs/releases/download
|
||||||
|
# When you update Node versions you must also create a new release of alpine_nodejs at that updated version.
|
||||||
|
# Follow the instructions here: https://github.com/actions/alpine_nodejs?tab=readme-ov-file#getting-started
|
||||||
NODE16_VERSION="16.20.2"
|
NODE16_VERSION="16.20.2"
|
||||||
NODE20_VERSION="20.8.1"
|
NODE20_VERSION="20.13.1"
|
||||||
# used only for win-arm64, remove node16 unofficial version when official version is available
|
NODE16_UNOFFICIAL_VERSION="16.20.0" # used only for win-arm64, remove node16 unofficial version when official version is available
|
||||||
NODE16_UNOFFICIAL_VERSION="16.20.0"
|
|
||||||
|
|
||||||
get_abs_path() {
|
get_abs_path() {
|
||||||
# exploits the fact that pwd will print abs path when no args
|
# exploits the fact that pwd will print abs path when no args
|
||||||
|
|||||||
@@ -114,6 +114,11 @@ var runService = function () {
|
|||||||
);
|
);
|
||||||
stopping = true;
|
stopping = true;
|
||||||
}
|
}
|
||||||
|
} else if (code === 5) {
|
||||||
|
console.log(
|
||||||
|
"Runner listener exit with Session Conflict error, stop the service, no retry needed."
|
||||||
|
);
|
||||||
|
stopping = true;
|
||||||
} else {
|
} else {
|
||||||
var messagePrefix = "Runner listener exit with undefined return code";
|
var messagePrefix = "Runner listener exit with undefined return code";
|
||||||
unknownFailureRetryCount++;
|
unknownFailureRetryCount++;
|
||||||
|
|||||||
@@ -49,5 +49,10 @@ if %ERRORLEVEL% EQU 4 (
|
|||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if %ERRORLEVEL% EQU 5 (
|
||||||
|
echo "Runner listener exit with Session Conflict error, stop the service, no retry needed."
|
||||||
|
exit /b 0
|
||||||
|
)
|
||||||
|
|
||||||
echo "Exiting after unknown error code: %ERRORLEVEL%"
|
echo "Exiting after unknown error code: %ERRORLEVEL%"
|
||||||
exit /b 0
|
exit /b 0
|
||||||
@@ -70,6 +70,9 @@ elif [[ $returnCode == 4 ]]; then
|
|||||||
"$DIR"/safe_sleep.sh 1
|
"$DIR"/safe_sleep.sh 1
|
||||||
done
|
done
|
||||||
exit 2
|
exit 2
|
||||||
|
elif [[ $returnCode == 5 ]]; then
|
||||||
|
echo "Runner listener exit with Session Conflict error, stop the service, no retry needed."
|
||||||
|
exit 0
|
||||||
else
|
else
|
||||||
echo "Exiting with unknown error code: ${returnCode}"
|
echo "Exiting with unknown error code: ${returnCode}"
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ runWithManualTrap() {
|
|||||||
cp -f "$DIR"/run-helper.sh.template "$DIR"/run-helper.sh
|
cp -f "$DIR"/run-helper.sh.template "$DIR"/run-helper.sh
|
||||||
"$DIR"/run-helper.sh $* &
|
"$DIR"/run-helper.sh $* &
|
||||||
PID=$!
|
PID=$!
|
||||||
wait -f $PID
|
wait $PID
|
||||||
returnCode=$?
|
returnCode=$?
|
||||||
if [[ $returnCode -eq 2 ]]; then
|
if [[ $returnCode -eq 2 ]]; then
|
||||||
echo "Restarting runner..."
|
echo "Restarting runner..."
|
||||||
@@ -84,4 +84,4 @@ if [[ -z "$RUNNER_MANUALLY_TRAP_SIG" ]]; then
|
|||||||
run $*
|
run $*
|
||||||
else
|
else
|
||||||
runWithManualTrap $*
|
runWithManualTrap $*
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
private bool _hasConnection;
|
private bool _hasConnection;
|
||||||
private VssConnection _connection;
|
private VssConnection _connection;
|
||||||
private TaskAgentHttpClient _taskAgentClient;
|
private ActionsRunServerHttpClient _actionsRunServerClient;
|
||||||
|
|
||||||
public async Task ConnectAsync(Uri serverUrl, VssCredentials credentials)
|
public async Task ConnectAsync(Uri serverUrl, VssCredentials credentials)
|
||||||
{
|
{
|
||||||
_connection = await EstablishVssConnection(serverUrl, credentials, TimeSpan.FromSeconds(100));
|
_connection = await EstablishVssConnection(serverUrl, credentials, TimeSpan.FromSeconds(100));
|
||||||
_taskAgentClient = _connection.GetClient<TaskAgentHttpClient>();
|
_actionsRunServerClient = _connection.GetClient<ActionsRunServerHttpClient>();
|
||||||
_hasConnection = true;
|
_hasConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ namespace GitHub.Runner.Common
|
|||||||
CheckConnection();
|
CheckConnection();
|
||||||
var jobMessage = RetryRequest<AgentJobRequestMessage>(async () =>
|
var jobMessage = RetryRequest<AgentJobRequestMessage>(async () =>
|
||||||
{
|
{
|
||||||
return await _taskAgentClient.GetJobMessageAsync(id, cancellationToken);
|
return await _actionsRunServerClient.GetJobMessageAsync(id, cancellationToken);
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
|
|
||||||
return jobMessage;
|
return jobMessage;
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ namespace GitHub.Runner.Common
|
|||||||
public const int RetryableError = 2;
|
public const int RetryableError = 2;
|
||||||
public const int RunnerUpdating = 3;
|
public const int RunnerUpdating = 3;
|
||||||
public const int RunOnceRunnerUpdating = 4;
|
public const int RunOnceRunnerUpdating = 4;
|
||||||
|
public const int SessionConflict = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Features
|
public static class Features
|
||||||
@@ -180,6 +181,9 @@ namespace GitHub.Runner.Common
|
|||||||
public static readonly string DeprecatedNodeVersion = "node16";
|
public static readonly string DeprecatedNodeVersion = "node16";
|
||||||
public static readonly string EnforcedNode12DetectedAfterEndOfLife = "The following actions uses node12 which is deprecated and will be forced to run on node16: {0}. For more info: https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/";
|
public static readonly string EnforcedNode12DetectedAfterEndOfLife = "The following actions uses node12 which is deprecated and will be forced to run on node16: {0}. For more info: https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/";
|
||||||
public static readonly string EnforcedNode12DetectedAfterEndOfLifeEnvVariable = "Node16ForceActionsWarnings";
|
public static readonly string EnforcedNode12DetectedAfterEndOfLifeEnvVariable = "Node16ForceActionsWarnings";
|
||||||
|
public static readonly string EnforcedNode16DetectedAfterEndOfLife = "The following actions uses Node.js version which is deprecated and will be forced to run on node20: {0}. For more info: https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/";
|
||||||
|
public static readonly string EnforcedNode16DetectedAfterEndOfLifeEnvVariable = "Node20ForceActionsWarnings";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RunnerEvent
|
public static class RunnerEvent
|
||||||
@@ -251,6 +255,7 @@ namespace GitHub.Runner.Common
|
|||||||
public static readonly string RunnerDebug = "ACTIONS_RUNNER_DEBUG";
|
public static readonly string RunnerDebug = "ACTIONS_RUNNER_DEBUG";
|
||||||
public static readonly string StepDebug = "ACTIONS_STEP_DEBUG";
|
public static readonly string StepDebug = "ACTIONS_STEP_DEBUG";
|
||||||
public static readonly string AllowActionsUseUnsecureNodeVersion = "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION";
|
public static readonly string AllowActionsUseUnsecureNodeVersion = "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION";
|
||||||
|
public static readonly string ManualForceActionsToNode20 = "FORCE_JAVASCRIPT_ACTIONS_TO_NODE20";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Agent
|
public static class Agent
|
||||||
@@ -262,6 +267,7 @@ namespace GitHub.Runner.Common
|
|||||||
public static readonly string ForcedActionsNodeVersion = "ACTIONS_RUNNER_FORCE_ACTIONS_NODE_VERSION";
|
public static readonly string ForcedActionsNodeVersion = "ACTIONS_RUNNER_FORCE_ACTIONS_NODE_VERSION";
|
||||||
public static readonly string PrintLogToStdout = "ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT";
|
public static readonly string PrintLogToStdout = "ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT";
|
||||||
public static readonly string ActionArchiveCacheDirectory = "ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE";
|
public static readonly string ActionArchiveCacheDirectory = "ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE";
|
||||||
|
public static readonly string ManualForceActionsToNode20 = "FORCE_JAVASCRIPT_ACTIONS_TO_NODE20";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class System
|
public static class System
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace GitHub.Runner.Common
|
|||||||
TaskCompletionSource<int> JobRecordUpdated { get; }
|
TaskCompletionSource<int> JobRecordUpdated { get; }
|
||||||
event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling;
|
event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling;
|
||||||
Task ShutdownAsync();
|
Task ShutdownAsync();
|
||||||
void Start(Pipelines.AgentJobRequestMessage jobRequest, bool resultsServiceOnly = false, bool enableTelemetry = false);
|
void Start(Pipelines.AgentJobRequestMessage jobRequest, bool resultsServiceOnly = false);
|
||||||
void QueueWebConsoleLine(Guid stepRecordId, string line, long? lineNumber = null);
|
void QueueWebConsoleLine(Guid stepRecordId, string line, long? lineNumber = null);
|
||||||
void QueueFileUpload(Guid timelineId, Guid timelineRecordId, string type, string name, string path, bool deleteSource);
|
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 QueueResultsUpload(Guid timelineRecordId, string name, string path, string type, bool deleteSource, bool finalize, bool firstBlock, long totalLines);
|
||||||
@@ -74,6 +74,7 @@ namespace GitHub.Runner.Common
|
|||||||
private readonly List<JobTelemetry> _jobTelemetries = new();
|
private readonly List<JobTelemetry> _jobTelemetries = new();
|
||||||
private bool _queueInProcess = false;
|
private bool _queueInProcess = false;
|
||||||
private bool _resultsServiceOnly = false;
|
private bool _resultsServiceOnly = false;
|
||||||
|
private int _resultsServiceExceptionsCount = 0;
|
||||||
private Stopwatch _resultsUploadTimer = new();
|
private Stopwatch _resultsUploadTimer = new();
|
||||||
private Stopwatch _actionsUploadTimer = new();
|
private Stopwatch _actionsUploadTimer = new();
|
||||||
|
|
||||||
@@ -104,11 +105,10 @@ namespace GitHub.Runner.Common
|
|||||||
_resultsServer = hostContext.GetService<IResultsServer>();
|
_resultsServer = hostContext.GetService<IResultsServer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(Pipelines.AgentJobRequestMessage jobRequest, bool resultsServiceOnly = false, bool enableTelemetry = false)
|
public void Start(Pipelines.AgentJobRequestMessage jobRequest, bool resultsServiceOnly = false)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
_resultsServiceOnly = resultsServiceOnly;
|
_resultsServiceOnly = resultsServiceOnly;
|
||||||
_enableTelemetry = enableTelemetry;
|
|
||||||
|
|
||||||
var serviceEndPoint = jobRequest.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
var serviceEndPoint = jobRequest.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
@@ -139,6 +139,12 @@ namespace GitHub.Runner.Common
|
|||||||
_resultsClientInitiated = true;
|
_resultsClientInitiated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable telemetry if we have both results service and actions service
|
||||||
|
if (_resultsClientInitiated && !_resultsServiceOnly)
|
||||||
|
{
|
||||||
|
_enableTelemetry = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (_queueInProcess)
|
if (_queueInProcess)
|
||||||
{
|
{
|
||||||
Trace.Info("No-opt, all queue process tasks are running.");
|
Trace.Info("No-opt, all queue process tasks are running.");
|
||||||
@@ -574,9 +580,9 @@ namespace GitHub.Runner.Common
|
|||||||
Trace.Info("Catch exception during file upload to results, keep going since the process is best effort.");
|
Trace.Info("Catch exception during file upload to results, keep going since the process is best effort.");
|
||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
errorCount++;
|
errorCount++;
|
||||||
|
_resultsServiceExceptionsCount++;
|
||||||
// If we hit any exceptions uploading to Results, let's skip any additional uploads to Results unless Results is serving logs
|
// If we hit any exceptions uploading to Results, let's skip any additional uploads to Results unless Results is serving logs
|
||||||
if (!_resultsServiceOnly)
|
if (!_resultsServiceOnly && _resultsServiceExceptionsCount > 3)
|
||||||
{
|
{
|
||||||
_resultsClientInitiated = false;
|
_resultsClientInitiated = false;
|
||||||
SendResultsTelemetry(ex);
|
SendResultsTelemetry(ex);
|
||||||
@@ -703,7 +709,9 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
Trace.Info("Catch exception during update steps, skip update Results.");
|
Trace.Info("Catch exception during update steps, skip update Results.");
|
||||||
Trace.Error(e);
|
Trace.Error(e);
|
||||||
if (!_resultsServiceOnly)
|
_resultsServiceExceptionsCount++;
|
||||||
|
// If we hit any exceptions uploading to Results, let's skip any additional uploads to Results unless Results is serving logs
|
||||||
|
if (!_resultsServiceOnly && _resultsServiceExceptionsCount > 3)
|
||||||
{
|
{
|
||||||
_resultsClientInitiated = false;
|
_resultsClientInitiated = false;
|
||||||
SendResultsTelemetry(e);
|
SendResultsTelemetry(e);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace GitHub.Runner.Listener
|
|||||||
_brokerServer = HostContext.GetService<IBrokerServer>();
|
_brokerServer = HostContext.GetService<IBrokerServer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Boolean> CreateSessionAsync(CancellationToken token)
|
public async Task<CreateSessionResult> CreateSessionAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ namespace GitHub.Runner.Listener
|
|||||||
encounteringError = false;
|
encounteringError = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return CreateSessionResult.Success;
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@@ -123,7 +123,7 @@ namespace GitHub.Runner.Listener
|
|||||||
if (string.Equals(vssOAuthEx.Error, "invalid_client", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(vssOAuthEx.Error, "invalid_client", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_term.WriteError("Failed to create a session. The runner registration has been deleted from the server, please re-configure. Runner registrations are automatically deleted for runners that have not connected to the service recently.");
|
_term.WriteError("Failed to create a session. The runner registration has been deleted from the server, please re-configure. Runner registrations are automatically deleted for runners that have not connected to the service recently.");
|
||||||
return false;
|
return CreateSessionResult.Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether we get 401 because the runner registration already removed by the service.
|
// Check whether we get 401 because the runner registration already removed by the service.
|
||||||
@@ -134,14 +134,18 @@ namespace GitHub.Runner.Listener
|
|||||||
if (string.Equals(authError, "invalid_client", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(authError, "invalid_client", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_term.WriteError("Failed to create a session. The runner registration has been deleted from the server, please re-configure. Runner registrations are automatically deleted for runners that have not connected to the service recently.");
|
_term.WriteError("Failed to create a session. The runner registration has been deleted from the server, please re-configure. Runner registrations are automatically deleted for runners that have not connected to the service recently.");
|
||||||
return false;
|
return CreateSessionResult.Failure;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsSessionCreationExceptionRetriable(ex))
|
if (!IsSessionCreationExceptionRetriable(ex))
|
||||||
{
|
{
|
||||||
_term.WriteError($"Failed to create session. {ex.Message}");
|
_term.WriteError($"Failed to create session. {ex.Message}");
|
||||||
return false;
|
if (ex is TaskAgentSessionConflictException)
|
||||||
|
{
|
||||||
|
return CreateSessionResult.SessionConflict;
|
||||||
|
}
|
||||||
|
return CreateSessionResult.Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!encounteringError) //print the message only on the first error
|
if (!encounteringError) //print the message only on the first error
|
||||||
|
|||||||
@@ -1155,18 +1155,13 @@ namespace GitHub.Runner.Listener
|
|||||||
TimelineRecord jobRecord = timeline.Records.FirstOrDefault(x => x.Id == message.JobId && x.RecordType == "Job");
|
TimelineRecord jobRecord = timeline.Records.FirstOrDefault(x => x.Id == message.JobId && x.RecordType == "Job");
|
||||||
ArgUtil.NotNull(jobRecord, nameof(jobRecord));
|
ArgUtil.NotNull(jobRecord, nameof(jobRecord));
|
||||||
|
|
||||||
|
|
||||||
jobRecord.ErrorCount++;
|
jobRecord.ErrorCount++;
|
||||||
jobRecord.Issues.Add(issue);
|
jobRecord.Issues.Add(issue);
|
||||||
|
|
||||||
if (message.Variables.TryGetValue("DistributedTask.MarkJobAsFailedOnWorkerCrash", out var markJobAsFailedOnWorkerCrash) &&
|
Trace.Info("Mark the job as failed since the worker crashed");
|
||||||
StringUtil.ConvertToBoolean(markJobAsFailedOnWorkerCrash?.Value))
|
jobRecord.Result = TaskResult.Failed;
|
||||||
{
|
// mark the job as completed so service will pickup the result
|
||||||
Trace.Info("Mark the job as failed since the worker crashed");
|
jobRecord.State = TimelineRecordState.Completed;
|
||||||
jobRecord.Result = TaskResult.Failed;
|
|
||||||
// mark the job as completed so service will pickup the result
|
|
||||||
jobRecord.State = TimelineRecordState.Completed;
|
|
||||||
}
|
|
||||||
|
|
||||||
await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, new TimelineRecord[] { jobRecord }, CancellationToken.None);
|
await jobServer.UpdateTimelineRecordsAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, new TimelineRecord[] { jobRecord }, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,17 @@ using GitHub.Services.WebApi;
|
|||||||
|
|
||||||
namespace GitHub.Runner.Listener
|
namespace GitHub.Runner.Listener
|
||||||
{
|
{
|
||||||
|
public enum CreateSessionResult
|
||||||
|
{
|
||||||
|
Success,
|
||||||
|
Failure,
|
||||||
|
SessionConflict
|
||||||
|
}
|
||||||
|
|
||||||
[ServiceLocator(Default = typeof(MessageListener))]
|
[ServiceLocator(Default = typeof(MessageListener))]
|
||||||
public interface IMessageListener : IRunnerService
|
public interface IMessageListener : IRunnerService
|
||||||
{
|
{
|
||||||
Task<Boolean> CreateSessionAsync(CancellationToken token);
|
Task<CreateSessionResult> CreateSessionAsync(CancellationToken token);
|
||||||
Task DeleteSessionAsync();
|
Task DeleteSessionAsync();
|
||||||
Task<TaskAgentMessage> GetNextMessageAsync(CancellationToken token);
|
Task<TaskAgentMessage> GetNextMessageAsync(CancellationToken token);
|
||||||
Task DeleteMessageAsync(TaskAgentMessage message);
|
Task DeleteMessageAsync(TaskAgentMessage message);
|
||||||
@@ -59,7 +66,7 @@ namespace GitHub.Runner.Listener
|
|||||||
_brokerServer = hostContext.GetService<IBrokerServer>();
|
_brokerServer = hostContext.GetService<IBrokerServer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Boolean> CreateSessionAsync(CancellationToken token)
|
public async Task<CreateSessionResult> CreateSessionAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
|
|
||||||
@@ -123,7 +130,7 @@ namespace GitHub.Runner.Listener
|
|||||||
encounteringError = false;
|
encounteringError = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return CreateSessionResult.Success;
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@@ -147,7 +154,7 @@ namespace GitHub.Runner.Listener
|
|||||||
if (string.Equals(vssOAuthEx.Error, "invalid_client", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(vssOAuthEx.Error, "invalid_client", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_term.WriteError("Failed to create a session. The runner registration has been deleted from the server, please re-configure. Runner registrations are automatically deleted for runners that have not connected to the service recently.");
|
_term.WriteError("Failed to create a session. The runner registration has been deleted from the server, please re-configure. Runner registrations are automatically deleted for runners that have not connected to the service recently.");
|
||||||
return false;
|
return CreateSessionResult.Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether we get 401 because the runner registration already removed by the service.
|
// Check whether we get 401 because the runner registration already removed by the service.
|
||||||
@@ -158,14 +165,18 @@ namespace GitHub.Runner.Listener
|
|||||||
if (string.Equals(authError, "invalid_client", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(authError, "invalid_client", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_term.WriteError("Failed to create a session. The runner registration has been deleted from the server, please re-configure. Runner registrations are automatically deleted for runners that have not connected to the service recently.");
|
_term.WriteError("Failed to create a session. The runner registration has been deleted from the server, please re-configure. Runner registrations are automatically deleted for runners that have not connected to the service recently.");
|
||||||
return false;
|
return CreateSessionResult.Failure;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsSessionCreationExceptionRetriable(ex))
|
if (!IsSessionCreationExceptionRetriable(ex))
|
||||||
{
|
{
|
||||||
_term.WriteError($"Failed to create session. {ex.Message}");
|
_term.WriteError($"Failed to create session. {ex.Message}");
|
||||||
return false;
|
if (ex is TaskAgentSessionConflictException)
|
||||||
|
{
|
||||||
|
return CreateSessionResult.SessionConflict;
|
||||||
|
}
|
||||||
|
return CreateSessionResult.Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!encounteringError) //print the message only on the first error
|
if (!encounteringError) //print the message only on the first error
|
||||||
@@ -188,12 +199,12 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
using (var ts = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
|
using (var ts = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
|
||||||
{
|
{
|
||||||
|
await _runnerServer.DeleteAgentSessionAsync(_settings.PoolId, _session.SessionId, ts.Token);
|
||||||
|
|
||||||
if (_isBrokerSession)
|
if (_isBrokerSession)
|
||||||
{
|
{
|
||||||
await _brokerServer.DeleteSessionAsync(ts.Token);
|
await _brokerServer.DeleteSessionAsync(ts.Token);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
await _runnerServer.DeleteAgentSessionAsync(_settings.PoolId, _session.SessionId, ts.Token);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -225,6 +236,7 @@ namespace GitHub.Runner.Listener
|
|||||||
ArgUtil.NotNull(_settings, nameof(_settings));
|
ArgUtil.NotNull(_settings, nameof(_settings));
|
||||||
bool encounteringError = false;
|
bool encounteringError = false;
|
||||||
int continuousError = 0;
|
int continuousError = 0;
|
||||||
|
int continuousEmptyMessage = 0;
|
||||||
string errorMessage = string.Empty;
|
string errorMessage = string.Empty;
|
||||||
Stopwatch heartbeat = new();
|
Stopwatch heartbeat = new();
|
||||||
heartbeat.Restart();
|
heartbeat.Restart();
|
||||||
@@ -303,7 +315,7 @@ namespace GitHub.Runner.Listener
|
|||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
|
|
||||||
// don't retry if SkipSessionRecover = true, DT service will delete agent session to stop agent from taking more jobs.
|
// don't retry if SkipSessionRecover = true, DT service will delete agent session to stop agent from taking more jobs.
|
||||||
if (ex is TaskAgentSessionExpiredException && !_settings.SkipSessionRecover && await CreateSessionAsync(token))
|
if (ex is TaskAgentSessionExpiredException && !_settings.SkipSessionRecover && (await CreateSessionAsync(token) == CreateSessionResult.Success))
|
||||||
{
|
{
|
||||||
Trace.Info($"{nameof(TaskAgentSessionExpiredException)} received, recovered by recreate session.");
|
Trace.Info($"{nameof(TaskAgentSessionExpiredException)} received, recovered by recreate session.");
|
||||||
}
|
}
|
||||||
@@ -348,16 +360,27 @@ namespace GitHub.Runner.Listener
|
|||||||
|
|
||||||
if (message == null)
|
if (message == null)
|
||||||
{
|
{
|
||||||
|
continuousEmptyMessage++;
|
||||||
if (heartbeat.Elapsed > TimeSpan.FromMinutes(30))
|
if (heartbeat.Elapsed > TimeSpan.FromMinutes(30))
|
||||||
{
|
{
|
||||||
Trace.Info($"No message retrieved from session '{_session.SessionId}' within last 30 minutes.");
|
Trace.Info($"No message retrieved from session '{_session.SessionId}' within last 30 minutes.");
|
||||||
heartbeat.Restart();
|
heartbeat.Restart();
|
||||||
|
continuousEmptyMessage = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Trace.Verbose($"No message retrieved from session '{_session.SessionId}'.");
|
Trace.Verbose($"No message retrieved from session '{_session.SessionId}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (continuousEmptyMessage > 50)
|
||||||
|
{
|
||||||
|
// retried more than 50 times in less than 30mins and still getting empty message
|
||||||
|
// something is not right on the service side, backoff for 15-30s before retry
|
||||||
|
_getNextMessageRetryInterval = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(30), _getNextMessageRetryInterval);
|
||||||
|
Trace.Info("Sleeping for {0} seconds before retrying.", _getNextMessageRetryInterval.TotalSeconds);
|
||||||
|
await HostContext.Delay(_getNextMessageRetryInterval, token);
|
||||||
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -359,7 +359,12 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
Trace.Info(nameof(RunAsync));
|
Trace.Info(nameof(RunAsync));
|
||||||
_listener = GetMesageListener(settings);
|
_listener = GetMesageListener(settings);
|
||||||
if (!await _listener.CreateSessionAsync(HostContext.RunnerShutdownToken))
|
CreateSessionResult createSessionResult = await _listener.CreateSessionAsync(HostContext.RunnerShutdownToken);
|
||||||
|
if (createSessionResult == CreateSessionResult.SessionConflict)
|
||||||
|
{
|
||||||
|
return Constants.Runner.ReturnCode.SessionConflict;
|
||||||
|
}
|
||||||
|
else if (createSessionResult == CreateSessionResult.Failure)
|
||||||
{
|
{
|
||||||
return Constants.Runner.ReturnCode.TerminatedError;
|
return Constants.Runner.ReturnCode.TerminatedError;
|
||||||
}
|
}
|
||||||
@@ -567,6 +572,11 @@ namespace GitHub.Runner.Listener
|
|||||||
Trace.Info("Job is already acquired, skip this message.");
|
Trace.Info("Job is already acquired, skip this message.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Caught exception from acquiring job message: {ex}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jobDispatcher.Run(jobRequestMessage, runOnce);
|
jobDispatcher.Run(jobRequestMessage, runOnce);
|
||||||
|
|||||||
@@ -459,6 +459,34 @@ namespace GitHub.Runner.Sdk
|
|||||||
File.WriteAllText(path, null);
|
File.WriteAllText(path, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces invalid file name characters with '_'
|
||||||
|
/// </summary>
|
||||||
|
public static string ReplaceInvalidFileNameChars(string fileName)
|
||||||
|
{
|
||||||
|
var result = new StringBuilder();
|
||||||
|
var invalidChars = Path.GetInvalidFileNameChars();
|
||||||
|
|
||||||
|
var current = 0; // Current index
|
||||||
|
while (current < fileName?.Length)
|
||||||
|
{
|
||||||
|
var next = fileName.IndexOfAny(invalidChars, current);
|
||||||
|
if (next >= 0)
|
||||||
|
{
|
||||||
|
result.Append(fileName.Substring(current, next - current));
|
||||||
|
result.Append('_');
|
||||||
|
current = next + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Append(fileName.Substring(current));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recursively enumerates a directory without following directory reparse points.
|
/// Recursively enumerates a directory without following directory reparse points.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -7,129 +7,6 @@ namespace GitHub.Runner.Sdk
|
|||||||
public static class WhichUtil
|
public static class WhichUtil
|
||||||
{
|
{
|
||||||
public static string Which(string command, bool require = false, ITraceWriter trace = null, string prependPath = null)
|
public static string Which(string command, bool require = false, ITraceWriter trace = null, string prependPath = null)
|
||||||
{
|
|
||||||
ArgUtil.NotNullOrEmpty(command, nameof(command));
|
|
||||||
trace?.Info($"Which: '{command}'");
|
|
||||||
if (Path.IsPathFullyQualified(command) && File.Exists(command))
|
|
||||||
{
|
|
||||||
trace?.Info($"Fully qualified path: '{command}'");
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
string path = Environment.GetEnvironmentVariable(PathUtil.PathVariable);
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
trace?.Info("PATH environment variable not defined.");
|
|
||||||
path = path ?? string.Empty;
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(prependPath))
|
|
||||||
{
|
|
||||||
path = PathUtil.PrependPath(prependPath, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] pathSegments = path.Split(new Char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
for (int i = 0; i < pathSegments.Length; i++)
|
|
||||||
{
|
|
||||||
pathSegments[i] = Environment.ExpandEnvironmentVariables(pathSegments[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string pathSegment in pathSegments)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(pathSegment) && Directory.Exists(pathSegment))
|
|
||||||
{
|
|
||||||
string[] matches = null;
|
|
||||||
#if OS_WINDOWS
|
|
||||||
string pathExt = Environment.GetEnvironmentVariable("PATHEXT");
|
|
||||||
if (string.IsNullOrEmpty(pathExt))
|
|
||||||
{
|
|
||||||
// XP's system default value for PATHEXT system variable
|
|
||||||
pathExt = ".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh";
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] pathExtSegments = pathExt.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
// if command already has an extension.
|
|
||||||
if (pathExtSegments.Any(ext => command.EndsWith(ext, StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
matches = Directory.GetFiles(pathSegment, command);
|
|
||||||
}
|
|
||||||
catch (UnauthorizedAccessException ex)
|
|
||||||
{
|
|
||||||
trace?.Info("Ignore UnauthorizedAccess exception during Which.");
|
|
||||||
trace?.Verbose(ex.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matches != null && matches.Length > 0 && IsPathValid(matches.First(), trace))
|
|
||||||
{
|
|
||||||
trace?.Info($"Location: '{matches.First()}'");
|
|
||||||
return matches.First();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string searchPattern;
|
|
||||||
searchPattern = StringUtil.Format($"{command}.*");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
matches = Directory.GetFiles(pathSegment, searchPattern);
|
|
||||||
}
|
|
||||||
catch (UnauthorizedAccessException ex)
|
|
||||||
{
|
|
||||||
trace?.Info("Ignore UnauthorizedAccess exception during Which.");
|
|
||||||
trace?.Verbose(ex.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matches != null && matches.Length > 0)
|
|
||||||
{
|
|
||||||
// add extension.
|
|
||||||
for (int i = 0; i < pathExtSegments.Length; i++)
|
|
||||||
{
|
|
||||||
string fullPath = Path.Combine(pathSegment, $"{command}{pathExtSegments[i]}");
|
|
||||||
if (matches.Any(p => p.Equals(fullPath, StringComparison.OrdinalIgnoreCase)) && IsPathValid(fullPath, trace))
|
|
||||||
{
|
|
||||||
trace?.Info($"Location: '{fullPath}'");
|
|
||||||
return fullPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
try
|
|
||||||
{
|
|
||||||
matches = Directory.GetFiles(pathSegment, command);
|
|
||||||
}
|
|
||||||
catch (UnauthorizedAccessException ex)
|
|
||||||
{
|
|
||||||
trace?.Info("Ignore UnauthorizedAccess exception during Which.");
|
|
||||||
trace?.Verbose(ex.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matches != null && matches.Length > 0 && IsPathValid(matches.First(), trace))
|
|
||||||
{
|
|
||||||
trace?.Info($"Location: '{matches.First()}'");
|
|
||||||
return matches.First();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if OS_WINDOWS
|
|
||||||
trace?.Info($"{command}: command not found. Make sure '{command}' is installed and its location included in the 'Path' environment variable.");
|
|
||||||
#else
|
|
||||||
trace?.Info($"{command}: command not found. Make sure '{command}' is installed and its location included in the 'PATH' environment variable.");
|
|
||||||
#endif
|
|
||||||
if (require)
|
|
||||||
{
|
|
||||||
throw new FileNotFoundException(
|
|
||||||
message: $"{command}: command not found",
|
|
||||||
fileName: command);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Which2(string command, bool require = false, ITraceWriter trace = null, string prependPath = null)
|
|
||||||
{
|
{
|
||||||
ArgUtil.NotNullOrEmpty(command, nameof(command));
|
ArgUtil.NotNullOrEmpty(command, nameof(command));
|
||||||
trace?.Info($"Which2: '{command}'");
|
trace?.Info($"Which2: '{command}'");
|
||||||
|
|||||||
@@ -483,10 +483,6 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
// Load stored Ids for later load actions
|
// Load stored Ids for later load actions
|
||||||
compositeAction.Steps[i].Id = _cachedEmbeddedStepIds[action.Id][i];
|
compositeAction.Steps[i].Id = _cachedEmbeddedStepIds[action.Id][i];
|
||||||
if (string.IsNullOrEmpty(executionContext.Global.Variables.Get("DistributedTask.EnableCompositeActions")) && compositeAction.Steps[i].Reference.Type != Pipelines.ActionSourceType.Script)
|
|
||||||
{
|
|
||||||
throw new Exception("`uses:` keyword is not currently supported.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -703,11 +699,12 @@ namespace GitHub.Runner.Worker
|
|||||||
catch (Exception ex) when (!executionContext.CancellationToken.IsCancellationRequested) // Do not retry if the run is cancelled.
|
catch (Exception ex) when (!executionContext.CancellationToken.IsCancellationRequested) // Do not retry if the run is cancelled.
|
||||||
{
|
{
|
||||||
// UnresolvableActionDownloadInfoException is a 422 client error, don't retry
|
// UnresolvableActionDownloadInfoException is a 422 client error, don't retry
|
||||||
|
// NonRetryableActionDownloadInfoException is an non-retryable exception from Actions
|
||||||
// Some possible cases are:
|
// Some possible cases are:
|
||||||
// * Repo is rate limited
|
// * Repo is rate limited
|
||||||
// * Repo or tag doesn't exist, or isn't public
|
// * Repo or tag doesn't exist, or isn't public
|
||||||
// * Policy validation failed
|
// * Policy validation failed
|
||||||
if (attempt < 3 && !(ex is WebApi.UnresolvableActionDownloadInfoException))
|
if (attempt < 3 && !(ex is WebApi.UnresolvableActionDownloadInfoException) && !(ex is WebApi.NonRetryableActionDownloadInfoException))
|
||||||
{
|
{
|
||||||
executionContext.Output($"Failed to resolve action download info. Error: {ex.Message}");
|
executionContext.Output($"Failed to resolve action download info. Error: {ex.Message}");
|
||||||
executionContext.Debug(ex.ToString());
|
executionContext.Debug(ex.ToString());
|
||||||
@@ -796,43 +793,40 @@ namespace GitHub.Runner.Worker
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var useActionArchiveCache = false;
|
var useActionArchiveCache = false;
|
||||||
if (executionContext.Global.Variables.GetBoolean("DistributedTask.UseActionArchiveCache") == true)
|
var hasActionArchiveCache = false;
|
||||||
|
var actionArchiveCacheDir = Environment.GetEnvironmentVariable(Constants.Variables.Agent.ActionArchiveCacheDirectory);
|
||||||
|
if (!string.IsNullOrEmpty(actionArchiveCacheDir) &&
|
||||||
|
Directory.Exists(actionArchiveCacheDir))
|
||||||
{
|
{
|
||||||
var hasActionArchiveCache = false;
|
hasActionArchiveCache = true;
|
||||||
var actionArchiveCacheDir = Environment.GetEnvironmentVariable(Constants.Variables.Agent.ActionArchiveCacheDirectory);
|
Trace.Info($"Check if action archive '{downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha}' already exists in cache directory '{actionArchiveCacheDir}'");
|
||||||
if (!string.IsNullOrEmpty(actionArchiveCacheDir) &&
|
|
||||||
Directory.Exists(actionArchiveCacheDir))
|
|
||||||
{
|
|
||||||
hasActionArchiveCache = true;
|
|
||||||
Trace.Info($"Check if action archive '{downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha}' already exists in cache directory '{actionArchiveCacheDir}'");
|
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
var cacheArchiveFile = Path.Combine(actionArchiveCacheDir, downloadInfo.ResolvedNameWithOwner.Replace(Path.DirectorySeparatorChar, '_').Replace(Path.AltDirectorySeparatorChar, '_'), $"{downloadInfo.ResolvedSha}.zip");
|
var cacheArchiveFile = Path.Combine(actionArchiveCacheDir, downloadInfo.ResolvedNameWithOwner.Replace(Path.DirectorySeparatorChar, '_').Replace(Path.AltDirectorySeparatorChar, '_'), $"{downloadInfo.ResolvedSha}.zip");
|
||||||
#else
|
#else
|
||||||
var cacheArchiveFile = Path.Combine(actionArchiveCacheDir, downloadInfo.ResolvedNameWithOwner.Replace(Path.DirectorySeparatorChar, '_').Replace(Path.AltDirectorySeparatorChar, '_'), $"{downloadInfo.ResolvedSha}.tar.gz");
|
var cacheArchiveFile = Path.Combine(actionArchiveCacheDir, downloadInfo.ResolvedNameWithOwner.Replace(Path.DirectorySeparatorChar, '_').Replace(Path.AltDirectorySeparatorChar, '_'), $"{downloadInfo.ResolvedSha}.tar.gz");
|
||||||
#endif
|
#endif
|
||||||
if (File.Exists(cacheArchiveFile))
|
if (File.Exists(cacheArchiveFile))
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
Trace.Info($"Found action archive '{cacheArchiveFile}' in cache directory '{actionArchiveCacheDir}'");
|
||||||
{
|
File.Copy(cacheArchiveFile, archiveFile);
|
||||||
Trace.Info($"Found action archive '{cacheArchiveFile}' in cache directory '{actionArchiveCacheDir}'");
|
useActionArchiveCache = true;
|
||||||
File.Copy(cacheArchiveFile, archiveFile);
|
executionContext.Debug($"Copied action archive '{cacheArchiveFile}' to '{archiveFile}'");
|
||||||
useActionArchiveCache = true;
|
}
|
||||||
executionContext.Debug($"Copied action archive '{cacheArchiveFile}' to '{archiveFile}'");
|
catch (Exception ex)
|
||||||
}
|
{
|
||||||
catch (Exception ex)
|
Trace.Error($"Failed to copy action archive '{cacheArchiveFile}' to '{archiveFile}'. Error: {ex}");
|
||||||
{
|
|
||||||
Trace.Error($"Failed to copy action archive '{cacheArchiveFile}' to '{archiveFile}'. Error: {ex}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
|
||||||
{
|
|
||||||
Type = JobTelemetryType.General,
|
|
||||||
Message = $"Action archive cache usage: {downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha} use cache {useActionArchiveCache} has cache {hasActionArchiveCache}"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||||
|
{
|
||||||
|
Type = JobTelemetryType.General,
|
||||||
|
Message = $"Action archive cache usage: {downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha} use cache {useActionArchiveCache} has cache {hasActionArchiveCache}"
|
||||||
|
});
|
||||||
|
|
||||||
if (!useActionArchiveCache)
|
if (!useActionArchiveCache)
|
||||||
{
|
{
|
||||||
await DownloadRepositoryArchive(executionContext, link, downloadInfo.Authentication?.Token, archiveFile);
|
await DownloadRepositoryArchive(executionContext, link, downloadInfo.Authentication?.Token, archiveFile);
|
||||||
@@ -878,16 +872,9 @@ namespace GitHub.Runner.Worker
|
|||||||
int exitCode = await processInvoker.ExecuteAsync(stagingDirectory, tar, $"-xzf \"{archiveFile}\"", null, executionContext.CancellationToken);
|
int exitCode = await processInvoker.ExecuteAsync(stagingDirectory, tar, $"-xzf \"{archiveFile}\"", null, executionContext.CancellationToken);
|
||||||
if (exitCode != 0)
|
if (exitCode != 0)
|
||||||
{
|
{
|
||||||
if (executionContext.Global.Variables.GetBoolean("DistributedTask.DetailUntarFailure") == true)
|
var fileInfo = new FileInfo(archiveFile);
|
||||||
{
|
var sha256hash = await IOUtil.GetFileContentSha256HashAsync(archiveFile);
|
||||||
var fileInfo = new FileInfo(archiveFile);
|
throw new InvalidActionArchiveException($"Can't use 'tar -xzf' extract archive file: {archiveFile} (SHA256 '{sha256hash}', size '{fileInfo.Length}' bytes, tar outputs '{string.Join(' ', tarOutputs)}'). Action being checked out: {downloadInfo.NameWithOwner}@{downloadInfo.Ref}. return code: {exitCode}.");
|
||||||
var sha256hash = await IOUtil.GetFileContentSha256HashAsync(archiveFile);
|
|
||||||
throw new InvalidActionArchiveException($"Can't use 'tar -xzf' extract archive file: {archiveFile} (SHA256 '{sha256hash}', size '{fileInfo.Length}' bytes, tar outputs '{string.Join(' ', tarOutputs)}'). Action being checked out: {downloadInfo.NameWithOwner}@{downloadInfo.Ref}. return code: {exitCode}.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidActionArchiveException($"Can't use 'tar -xzf' extract archive file: {archiveFile}. Action being checked out: {downloadInfo.NameWithOwner}@{downloadInfo.Ref}. return code: {exitCode}.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1031,13 +1018,6 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var step in compositeAction.Steps)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(executionContext.Global.Variables.Get("DistributedTask.EnableCompositeActions")) && step.Reference.Type != Pipelines.ActionSourceType.Script)
|
|
||||||
{
|
|
||||||
throw new Exception("`uses:` keyword is not currently supported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return setupInfo;
|
return setupInfo;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -91,13 +91,13 @@ namespace GitHub.Runner.Worker
|
|||||||
string phaseName = executionContext.Global.Variables.System_PhaseDisplayName ?? "UnknownPhaseName";
|
string phaseName = executionContext.Global.Variables.System_PhaseDisplayName ?? "UnknownPhaseName";
|
||||||
|
|
||||||
// zip the files
|
// zip the files
|
||||||
string diagnosticsZipFileName = $"{buildName}-{phaseName}.zip";
|
string diagnosticsZipFileName = $"{buildName}-{IOUtil.ReplaceInvalidFileNameChars(phaseName)}.zip";
|
||||||
string diagnosticsZipFilePath = Path.Combine(supportRootFolder, diagnosticsZipFileName);
|
string diagnosticsZipFilePath = Path.Combine(supportRootFolder, diagnosticsZipFileName);
|
||||||
ZipFile.CreateFromDirectory(supportFilesFolder, diagnosticsZipFilePath);
|
ZipFile.CreateFromDirectory(supportFilesFolder, diagnosticsZipFilePath);
|
||||||
|
|
||||||
// upload the json metadata file
|
// upload the json metadata file
|
||||||
executionContext.Debug("Uploading diagnostic metadata file.");
|
executionContext.Debug("Uploading diagnostic metadata file.");
|
||||||
string metadataFileName = $"diagnostics-{buildName}-{phaseName}.json";
|
string metadataFileName = $"diagnostics-{buildName}-{IOUtil.ReplaceInvalidFileNameChars(phaseName)}.json";
|
||||||
string metadataFilePath = Path.Combine(supportFilesFolder, metadataFileName);
|
string metadataFilePath = Path.Combine(supportFilesFolder, metadataFileName);
|
||||||
string phaseResult = GetTaskResultAsString(executionContext.Result);
|
string phaseResult = GetTaskResultAsString(executionContext.Result);
|
||||||
|
|
||||||
|
|||||||
@@ -223,6 +223,10 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
Environment["ACTIONS_CACHE_URL"] = cacheUrl;
|
Environment["ACTIONS_CACHE_URL"] = cacheUrl;
|
||||||
}
|
}
|
||||||
|
if (systemConnection.Data.TryGetValue("PipelinesServiceUrl", out var pipelinesServiceUrl) && !string.IsNullOrEmpty(pipelinesServiceUrl))
|
||||||
|
{
|
||||||
|
Environment["ACTIONS_RUNTIME_URL"] = pipelinesServiceUrl;
|
||||||
|
}
|
||||||
if (systemConnection.Data.TryGetValue("GenerateIdTokenUrl", out var generateIdTokenUrl) && !string.IsNullOrEmpty(generateIdTokenUrl))
|
if (systemConnection.Data.TryGetValue("GenerateIdTokenUrl", out var generateIdTokenUrl) && !string.IsNullOrEmpty(generateIdTokenUrl))
|
||||||
{
|
{
|
||||||
Environment["ACTIONS_ID_TOKEN_REQUEST_URL"] = generateIdTokenUrl;
|
Environment["ACTIONS_ID_TOKEN_REQUEST_URL"] = generateIdTokenUrl;
|
||||||
|
|||||||
@@ -84,6 +84,45 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
}
|
}
|
||||||
nodeData.NodeVersion = "node16";
|
nodeData.NodeVersion = "node16";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var localForceActionsToNode20 = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable(Constants.Variables.Agent.ManualForceActionsToNode20));
|
||||||
|
executionContext.Global.EnvironmentVariables.TryGetValue(Constants.Variables.Actions.ManualForceActionsToNode20, out var workflowForceActionsToNode20);
|
||||||
|
var enforceNode20Locally = !string.IsNullOrWhiteSpace(workflowForceActionsToNode20) ? StringUtil.ConvertToBoolean(workflowForceActionsToNode20) : localForceActionsToNode20;
|
||||||
|
if (string.Equals(nodeData.NodeVersion, "node16")
|
||||||
|
&& ((executionContext.Global.Variables.GetBoolean("DistributedTask.ForceGithubJavascriptActionsToNode20") ?? false) || enforceNode20Locally))
|
||||||
|
{
|
||||||
|
executionContext.Global.EnvironmentVariables.TryGetValue(Constants.Variables.Actions.AllowActionsUseUnsecureNodeVersion, out var workflowOptOut);
|
||||||
|
var isWorkflowOptOutSet = !string.IsNullOrWhiteSpace(workflowOptOut);
|
||||||
|
var isLocalOptOut = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable(Constants.Variables.Actions.AllowActionsUseUnsecureNodeVersion));
|
||||||
|
bool isOptOut = isWorkflowOptOutSet ? StringUtil.ConvertToBoolean(workflowOptOut) : isLocalOptOut;
|
||||||
|
|
||||||
|
if (!isOptOut)
|
||||||
|
{
|
||||||
|
var repoAction = action as Pipelines.RepositoryPathReference;
|
||||||
|
if (repoAction != null)
|
||||||
|
{
|
||||||
|
var warningActions = new HashSet<string>();
|
||||||
|
if (executionContext.Global.Variables.TryGetValue(Constants.Runner.EnforcedNode16DetectedAfterEndOfLifeEnvVariable, out var node20ForceWarnings))
|
||||||
|
{
|
||||||
|
warningActions = StringUtil.ConvertFromJson<HashSet<string>>(node20ForceWarnings);
|
||||||
|
}
|
||||||
|
|
||||||
|
string repoActionFullName;
|
||||||
|
if (string.IsNullOrEmpty(repoAction.Name))
|
||||||
|
{
|
||||||
|
repoActionFullName = repoAction.Path; // local actions don't have a 'Name'
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
repoActionFullName = $"{repoAction.Name}/{repoAction.Path ?? string.Empty}".TrimEnd('/') + $"@{repoAction.Ref}";
|
||||||
|
}
|
||||||
|
|
||||||
|
warningActions.Add(repoActionFullName);
|
||||||
|
executionContext.Global.Variables.Set(Constants.Runner.EnforcedNode16DetectedAfterEndOfLifeEnvVariable, StringUtil.ConvertToJson(warningActions));
|
||||||
|
}
|
||||||
|
nodeData.NodeVersion = "node20";
|
||||||
|
}
|
||||||
|
}
|
||||||
(handler as INodeScriptActionHandler).Data = nodeData;
|
(handler as INodeScriptActionHandler).Data = nodeData;
|
||||||
}
|
}
|
||||||
else if (data.ExecutionType == ActionExecutionType.Script)
|
else if (data.ExecutionType == ActionExecutionType.Script)
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
Environment["ACTIONS_CACHE_URL"] = cacheUrl;
|
Environment["ACTIONS_CACHE_URL"] = cacheUrl;
|
||||||
}
|
}
|
||||||
|
if (systemConnection.Data.TryGetValue("PipelinesServiceUrl", out var pipelinesServiceUrl) && !string.IsNullOrEmpty(pipelinesServiceUrl))
|
||||||
|
{
|
||||||
|
Environment["ACTIONS_RUNTIME_URL"] = pipelinesServiceUrl;
|
||||||
|
}
|
||||||
if (systemConnection.Data.TryGetValue("GenerateIdTokenUrl", out var generateIdTokenUrl) && !string.IsNullOrEmpty(generateIdTokenUrl))
|
if (systemConnection.Data.TryGetValue("GenerateIdTokenUrl", out var generateIdTokenUrl) && !string.IsNullOrEmpty(generateIdTokenUrl))
|
||||||
{
|
{
|
||||||
Environment["ACTIONS_ID_TOKEN_REQUEST_URL"] = generateIdTokenUrl;
|
Environment["ACTIONS_ID_TOKEN_REQUEST_URL"] = generateIdTokenUrl;
|
||||||
@@ -114,6 +118,11 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
Data.NodeVersion = "node16";
|
Data.NodeVersion = "node16";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (forcedNodeVersion == "node20" && Data.NodeVersion != "node20")
|
||||||
|
{
|
||||||
|
Data.NodeVersion = "node20";
|
||||||
|
}
|
||||||
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext, Data.NodeVersion);
|
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext, Data.NodeVersion);
|
||||||
string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
|
string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
|
||||||
|
|
||||||
|
|||||||
@@ -83,40 +83,19 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
shellCommand = "pwsh";
|
shellCommand = "pwsh";
|
||||||
if (validateShellOnHost)
|
if (validateShellOnHost)
|
||||||
{
|
{
|
||||||
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
shellCommandPath = WhichUtil.Which(shellCommand, require: false, Trace, prependPath);
|
||||||
{
|
|
||||||
shellCommandPath = WhichUtil.Which2(shellCommand, require: false, Trace, prependPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shellCommandPath = WhichUtil.Which(shellCommand, require: false, Trace, prependPath);
|
|
||||||
}
|
|
||||||
if (string.IsNullOrEmpty(shellCommandPath))
|
if (string.IsNullOrEmpty(shellCommandPath))
|
||||||
{
|
{
|
||||||
shellCommand = "powershell";
|
shellCommand = "powershell";
|
||||||
Trace.Info($"Defaulting to {shellCommand}");
|
Trace.Info($"Defaulting to {shellCommand}");
|
||||||
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
shellCommandPath = WhichUtil.Which(shellCommand, require: true, Trace, prependPath);
|
||||||
{
|
|
||||||
shellCommandPath = WhichUtil.Which2(shellCommand, require: true, Trace, prependPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shellCommandPath = WhichUtil.Which(shellCommand, require: true, Trace, prependPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
shellCommand = "sh";
|
shellCommand = "sh";
|
||||||
if (validateShellOnHost)
|
if (validateShellOnHost)
|
||||||
{
|
{
|
||||||
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
shellCommandPath = WhichUtil.Which("bash", false, Trace, prependPath) ?? WhichUtil.Which("sh", true, Trace, prependPath);
|
||||||
{
|
|
||||||
shellCommandPath = WhichUtil.Which2("bash", false, Trace, prependPath) ?? WhichUtil.Which2("sh", true, Trace, prependPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shellCommandPath = WhichUtil.Which("bash", false, Trace, prependPath) ?? WhichUtil.Which("sh", true, Trace, prependPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
argFormat = ScriptHandlerHelpers.GetScriptArgumentsFormat(shellCommand);
|
argFormat = ScriptHandlerHelpers.GetScriptArgumentsFormat(shellCommand);
|
||||||
@@ -127,14 +106,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
shellCommand = parsed.shellCommand;
|
shellCommand = parsed.shellCommand;
|
||||||
if (validateShellOnHost)
|
if (validateShellOnHost)
|
||||||
{
|
{
|
||||||
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
shellCommandPath = WhichUtil.Which(parsed.shellCommand, true, Trace, prependPath);
|
||||||
{
|
|
||||||
shellCommandPath = WhichUtil.Which2(parsed.shellCommand, true, Trace, prependPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shellCommandPath = WhichUtil.Which(parsed.shellCommand, true, Trace, prependPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
argFormat = $"{parsed.shellArgs}".TrimStart();
|
argFormat = $"{parsed.shellArgs}".TrimStart();
|
||||||
@@ -216,38 +188,17 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
shellCommand = "pwsh";
|
shellCommand = "pwsh";
|
||||||
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
commandPath = WhichUtil.Which(shellCommand, require: false, Trace, prependPath);
|
||||||
{
|
|
||||||
commandPath = WhichUtil.Which2(shellCommand, require: false, Trace, prependPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
commandPath = WhichUtil.Which(shellCommand, require: false, Trace, prependPath);
|
|
||||||
}
|
|
||||||
if (string.IsNullOrEmpty(commandPath))
|
if (string.IsNullOrEmpty(commandPath))
|
||||||
{
|
{
|
||||||
shellCommand = "powershell";
|
shellCommand = "powershell";
|
||||||
Trace.Info($"Defaulting to {shellCommand}");
|
Trace.Info($"Defaulting to {shellCommand}");
|
||||||
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
commandPath = WhichUtil.Which(shellCommand, require: true, Trace, prependPath);
|
||||||
{
|
|
||||||
commandPath = WhichUtil.Which2(shellCommand, require: true, Trace, prependPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
commandPath = WhichUtil.Which(shellCommand, require: true, Trace, prependPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ArgUtil.NotNullOrEmpty(commandPath, "Default Shell");
|
ArgUtil.NotNullOrEmpty(commandPath, "Default Shell");
|
||||||
#else
|
#else
|
||||||
shellCommand = "sh";
|
shellCommand = "sh";
|
||||||
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
commandPath = WhichUtil.Which("bash", false, Trace, prependPath) ?? WhichUtil.Which("sh", true, Trace, prependPath);
|
||||||
{
|
|
||||||
commandPath = WhichUtil.Which2("bash", false, Trace, prependPath) ?? WhichUtil.Which2("sh", true, Trace, prependPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
commandPath = WhichUtil.Which("bash", false, Trace, prependPath) ?? WhichUtil.Which("sh", true, Trace, prependPath);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
argFormat = ScriptHandlerHelpers.GetScriptArgumentsFormat(shellCommand);
|
argFormat = ScriptHandlerHelpers.GetScriptArgumentsFormat(shellCommand);
|
||||||
}
|
}
|
||||||
@@ -258,14 +209,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
if (!IsActionStep && systemShells.Contains(shell))
|
if (!IsActionStep && systemShells.Contains(shell))
|
||||||
{
|
{
|
||||||
shellCommand = shell;
|
shellCommand = shell;
|
||||||
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
commandPath = WhichUtil.Which(shell, !isContainerStepHost, Trace, prependPath);
|
||||||
{
|
|
||||||
commandPath = WhichUtil.Which2(shell, !isContainerStepHost, Trace, prependPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
commandPath = WhichUtil.Which(shell, !isContainerStepHost, Trace, prependPath);
|
|
||||||
}
|
|
||||||
if (shell == "bash")
|
if (shell == "bash")
|
||||||
{
|
{
|
||||||
argFormat = ScriptHandlerHelpers.GetScriptArgumentsFormat("sh");
|
argFormat = ScriptHandlerHelpers.GetScriptArgumentsFormat("sh");
|
||||||
@@ -280,14 +224,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
var parsed = ScriptHandlerHelpers.ParseShellOptionString(shell);
|
var parsed = ScriptHandlerHelpers.ParseShellOptionString(shell);
|
||||||
shellCommand = parsed.shellCommand;
|
shellCommand = parsed.shellCommand;
|
||||||
// For non-ContainerStepHost, the command must be located on the host by Which
|
// For non-ContainerStepHost, the command must be located on the host by Which
|
||||||
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
commandPath = WhichUtil.Which(parsed.shellCommand, !isContainerStepHost, Trace, prependPath);
|
||||||
{
|
|
||||||
commandPath = WhichUtil.Which2(parsed.shellCommand, !isContainerStepHost, Trace, prependPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
commandPath = WhichUtil.Which(parsed.shellCommand, !isContainerStepHost, Trace, prependPath);
|
|
||||||
}
|
|
||||||
argFormat = $"{parsed.shellArgs}".TrimStart();
|
argFormat = $"{parsed.shellArgs}".TrimStart();
|
||||||
if (string.IsNullOrEmpty(argFormat))
|
if (string.IsNullOrEmpty(argFormat))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ namespace GitHub.Runner.Worker
|
|||||||
Trace.Info("Job ID {0}", message.JobId);
|
Trace.Info("Job ID {0}", message.JobId);
|
||||||
|
|
||||||
DateTime jobStartTimeUtc = DateTime.UtcNow;
|
DateTime jobStartTimeUtc = DateTime.UtcNow;
|
||||||
|
_runnerSettings = HostContext.GetService<IConfigurationStore>().GetSettings();
|
||||||
IRunnerService server = null;
|
IRunnerService server = null;
|
||||||
|
|
||||||
// add orchestration id to useragent for better correlation.
|
// add orchestration id to useragent for better correlation.
|
||||||
@@ -54,13 +55,6 @@ namespace GitHub.Runner.Worker
|
|||||||
VssUtil.InitializeVssClientSettings(HostContext.UserAgents, HostContext.WebProxy);
|
VssUtil.InitializeVssClientSettings(HostContext.UserAgents, HostContext.WebProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
var jobServerQueueTelemetry = false;
|
|
||||||
if (message.Variables.TryGetValue("DistributedTask.EnableJobServerQueueTelemetry", out VariableValue enableJobServerQueueTelemetry) &&
|
|
||||||
!string.IsNullOrEmpty(enableJobServerQueueTelemetry?.Value))
|
|
||||||
{
|
|
||||||
jobServerQueueTelemetry = StringUtil.ConvertToBoolean(enableJobServerQueueTelemetry.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
ServiceEndpoint systemConnection = message.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
ServiceEndpoint systemConnection = message.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||||
if (MessageUtil.IsRunServiceJob(message.MessageType))
|
if (MessageUtil.IsRunServiceJob(message.MessageType))
|
||||||
{
|
{
|
||||||
@@ -82,7 +76,7 @@ namespace GitHub.Runner.Worker
|
|||||||
launchServer.InitializeLaunchClient(new Uri(launchReceiverEndpoint), accessToken);
|
launchServer.InitializeLaunchClient(new Uri(launchReceiverEndpoint), accessToken);
|
||||||
}
|
}
|
||||||
_jobServerQueue = HostContext.GetService<IJobServerQueue>();
|
_jobServerQueue = HostContext.GetService<IJobServerQueue>();
|
||||||
_jobServerQueue.Start(message, resultsServiceOnly: true, enableTelemetry: jobServerQueueTelemetry);
|
_jobServerQueue.Start(message, resultsServiceOnly: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -104,7 +98,7 @@ namespace GitHub.Runner.Worker
|
|||||||
VssConnection jobConnection = VssUtil.CreateConnection(jobServerUrl, jobServerCredential, delegatingHandlers);
|
VssConnection jobConnection = VssUtil.CreateConnection(jobServerUrl, jobServerCredential, delegatingHandlers);
|
||||||
await jobServer.ConnectAsync(jobConnection);
|
await jobServer.ConnectAsync(jobConnection);
|
||||||
|
|
||||||
_jobServerQueue.Start(message, enableTelemetry: jobServerQueueTelemetry);
|
_jobServerQueue.Start(message);
|
||||||
server = jobServer;
|
server = jobServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,8 +158,6 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
jobContext.SetRunnerContext("os", VarUtil.OS);
|
jobContext.SetRunnerContext("os", VarUtil.OS);
|
||||||
jobContext.SetRunnerContext("arch", VarUtil.OSArchitecture);
|
jobContext.SetRunnerContext("arch", VarUtil.OSArchitecture);
|
||||||
|
|
||||||
_runnerSettings = HostContext.GetService<IConfigurationStore>().GetSettings();
|
|
||||||
jobContext.SetRunnerContext("name", _runnerSettings.AgentName);
|
jobContext.SetRunnerContext("name", _runnerSettings.AgentName);
|
||||||
|
|
||||||
if (jobContext.Global.Variables.TryGetValue(WellKnownDistributedTaskVariables.RunnerEnvironment, out var runnerEnvironment))
|
if (jobContext.Global.Variables.TryGetValue(WellKnownDistributedTaskVariables.RunnerEnvironment, out var runnerEnvironment))
|
||||||
@@ -298,6 +290,12 @@ namespace GitHub.Runner.Worker
|
|||||||
jobContext.Warning(string.Format(Constants.Runner.EnforcedNode12DetectedAfterEndOfLife, actions));
|
jobContext.Warning(string.Format(Constants.Runner.EnforcedNode12DetectedAfterEndOfLife, actions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (jobContext.Global.Variables.TryGetValue(Constants.Runner.EnforcedNode16DetectedAfterEndOfLifeEnvVariable, out var node20ForceWarnings) && (jobContext.Global.Variables.GetBoolean("DistributedTask.ForceGithubJavascriptActionsToNode20") ?? false))
|
||||||
|
{
|
||||||
|
var actions = string.Join(", ", StringUtil.ConvertFromJson<HashSet<string>>(node20ForceWarnings));
|
||||||
|
jobContext.Warning(string.Format(Constants.Runner.EnforcedNode16DetectedAfterEndOfLife, actions));
|
||||||
|
}
|
||||||
|
|
||||||
await ShutdownQueue(throwOnFailure: false);
|
await ShutdownQueue(throwOnFailure: false);
|
||||||
|
|
||||||
// Make sure to clean temp after file upload since they may be pending fileupload still use the TEMP dir.
|
// Make sure to clean temp after file upload since they may be pending fileupload still use the TEMP dir.
|
||||||
@@ -405,6 +403,12 @@ namespace GitHub.Runner.Worker
|
|||||||
jobContext.Warning(string.Format(Constants.Runner.EnforcedNode12DetectedAfterEndOfLife, actions));
|
jobContext.Warning(string.Format(Constants.Runner.EnforcedNode12DetectedAfterEndOfLife, actions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (jobContext.Global.Variables.TryGetValue(Constants.Runner.EnforcedNode16DetectedAfterEndOfLifeEnvVariable, out var node20ForceWarnings))
|
||||||
|
{
|
||||||
|
var actions = string.Join(", ", StringUtil.ConvertFromJson<HashSet<string>>(node20ForceWarnings));
|
||||||
|
jobContext.Warning(string.Format(Constants.Runner.EnforcedNode16DetectedAfterEndOfLife, actions));
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var jobQueueTelemetry = await ShutdownQueue(throwOnFailure: true);
|
var jobQueueTelemetry = await ShutdownQueue(throwOnFailure: true);
|
||||||
|
|||||||
95
src/Sdk/DTWebApi/WebApi/ActionsRunServerHttpClient.cs
Normal file
95
src/Sdk/DTWebApi/WebApi/ActionsRunServerHttpClient.cs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using GitHub.Services.Common;
|
||||||
|
using GitHub.Services.Common.Diagnostics;
|
||||||
|
using GitHub.Services.WebApi;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace GitHub.DistributedTask.WebApi
|
||||||
|
{
|
||||||
|
[ResourceArea(TaskResourceIds.AreaId)]
|
||||||
|
public class ActionsRunServerHttpClient : TaskAgentHttpClient
|
||||||
|
{
|
||||||
|
private static readonly JsonSerializerSettings s_serializerSettings;
|
||||||
|
|
||||||
|
static ActionsRunServerHttpClient()
|
||||||
|
{
|
||||||
|
s_serializerSettings = new VssJsonMediaTypeFormatter().SerializerSettings;
|
||||||
|
s_serializerSettings.DateParseHandling = DateParseHandling.None;
|
||||||
|
s_serializerSettings.FloatParseHandling = FloatParseHandling.Double;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionsRunServerHttpClient(
|
||||||
|
Uri baseUrl,
|
||||||
|
VssCredentials credentials)
|
||||||
|
: base(baseUrl, credentials)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionsRunServerHttpClient(
|
||||||
|
Uri baseUrl,
|
||||||
|
VssCredentials credentials,
|
||||||
|
VssHttpRequestSettings settings)
|
||||||
|
: base(baseUrl, credentials, settings)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionsRunServerHttpClient(
|
||||||
|
Uri baseUrl,
|
||||||
|
VssCredentials credentials,
|
||||||
|
params DelegatingHandler[] handlers)
|
||||||
|
: base(baseUrl, credentials, handlers)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionsRunServerHttpClient(
|
||||||
|
Uri baseUrl,
|
||||||
|
VssCredentials credentials,
|
||||||
|
VssHttpRequestSettings settings,
|
||||||
|
params DelegatingHandler[] handlers)
|
||||||
|
: base(baseUrl, credentials, settings, handlers)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionsRunServerHttpClient(
|
||||||
|
Uri baseUrl,
|
||||||
|
HttpMessageHandler pipeline,
|
||||||
|
Boolean disposeHandler)
|
||||||
|
: base(baseUrl, pipeline, disposeHandler)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Pipelines.AgentJobRequestMessage> GetJobMessageAsync(
|
||||||
|
string messageId,
|
||||||
|
object userState = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
HttpMethod httpMethod = new HttpMethod("GET");
|
||||||
|
Guid locationId = new Guid("25adab70-1379-4186-be8e-b643061ebe3a");
|
||||||
|
object routeValues = new { messageId = messageId };
|
||||||
|
|
||||||
|
return SendAsync<Pipelines.AgentJobRequestMessage>(
|
||||||
|
httpMethod,
|
||||||
|
locationId,
|
||||||
|
routeValues: routeValues,
|
||||||
|
version: new ApiResourceVersion(6.0, 1),
|
||||||
|
userState: userState,
|
||||||
|
cancellationToken: cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task<T> ReadJsonContentAsync<T>(HttpResponseMessage response, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
return JsonConvert.DeserializeObject<T>(json, s_serializerSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2498,6 +2498,25 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class NonRetryableActionDownloadInfoException : DistributedTaskException
|
||||||
|
{
|
||||||
|
public NonRetryableActionDownloadInfoException(String message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public NonRetryableActionDownloadInfoException(String message, Exception innerException)
|
||||||
|
: base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected NonRetryableActionDownloadInfoException(SerializationInfo info, StreamingContext context)
|
||||||
|
: base(info, context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public sealed class FailedToResolveActionDownloadInfoException : DistributedTaskException
|
public sealed class FailedToResolveActionDownloadInfoException : DistributedTaskException
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -141,24 +141,6 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
return ReplaceAgentAsync(poolId, agent.Id, agent, userState, cancellationToken);
|
return ReplaceAgentAsync(poolId, agent.Id, agent, userState, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Pipelines.AgentJobRequestMessage> GetJobMessageAsync(
|
|
||||||
string messageId,
|
|
||||||
object userState = null,
|
|
||||||
CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
HttpMethod httpMethod = new HttpMethod("GET");
|
|
||||||
Guid locationId = new Guid("25adab70-1379-4186-be8e-b643061ebe3a");
|
|
||||||
object routeValues = new { messageId = messageId };
|
|
||||||
|
|
||||||
return SendAsync<Pipelines.AgentJobRequestMessage>(
|
|
||||||
httpMethod,
|
|
||||||
locationId,
|
|
||||||
routeValues: routeValues,
|
|
||||||
version: new ApiResourceVersion(6.0, 1),
|
|
||||||
userState: userState,
|
|
||||||
cancellationToken: cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Task<T> SendAsync<T>(
|
protected Task<T> SendAsync<T>(
|
||||||
HttpMethod method,
|
HttpMethod method,
|
||||||
Guid locationId,
|
Guid locationId,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using GitHub.DistributedTask.WebApi;
|
|||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
using GitHub.Services.OAuth;
|
using GitHub.Services.OAuth;
|
||||||
using GitHub.Services.WebApi;
|
using GitHub.Services.WebApi;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Sdk.RSWebApi.Contracts;
|
using Sdk.RSWebApi.Contracts;
|
||||||
using Sdk.WebApi.WebApi;
|
using Sdk.WebApi.WebApi;
|
||||||
|
|
||||||
@@ -16,6 +17,15 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
{
|
{
|
||||||
public class RunServiceHttpClient : RawHttpClientBase
|
public class RunServiceHttpClient : RawHttpClientBase
|
||||||
{
|
{
|
||||||
|
private static readonly JsonSerializerSettings s_serializerSettings;
|
||||||
|
|
||||||
|
static RunServiceHttpClient()
|
||||||
|
{
|
||||||
|
s_serializerSettings = new VssJsonMediaTypeFormatter().SerializerSettings;
|
||||||
|
s_serializerSettings.DateParseHandling = DateParseHandling.None;
|
||||||
|
s_serializerSettings.FloatParseHandling = FloatParseHandling.Double;
|
||||||
|
}
|
||||||
|
|
||||||
public RunServiceHttpClient(
|
public RunServiceHttpClient(
|
||||||
Uri baseUrl,
|
Uri baseUrl,
|
||||||
VssOAuthCredential credentials)
|
VssOAuthCredential credentials)
|
||||||
@@ -174,5 +184,11 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
throw new Exception($"Failed to renew job: {result.Error}");
|
throw new Exception($"Failed to renew job: {result.Error}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override async Task<T> ReadJsonContentAsync<T>(HttpResponseMessage response, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
return JsonConvert.DeserializeObject<T>(json, s_serializerSettings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
trace.Info("Parsed");
|
trace.Info("Parsed");
|
||||||
|
|
||||||
trace.Info("Commands: {0}", clp.Commands.Count);
|
trace.Info("Commands: {0}", clp.Commands.Count);
|
||||||
Assert.True(clp.Commands.Count == 2);
|
Assert.Equal(2, clp.Commands.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
trace.Info("Parsed");
|
trace.Info("Parsed");
|
||||||
|
|
||||||
trace.Info("Args: {0}", clp.Args.Count);
|
trace.Info("Args: {0}", clp.Args.Count);
|
||||||
Assert.True(clp.Args.Count == 2);
|
Assert.Equal(2, clp.Args.Count);
|
||||||
Assert.True(clp.Args.ContainsKey("arg1"));
|
Assert.True(clp.Args.ContainsKey("arg1"));
|
||||||
Assert.Equal("arg1val", clp.Args["arg1"]);
|
Assert.Equal("arg1val", clp.Args["arg1"]);
|
||||||
Assert.True(clp.Args.ContainsKey("arg2"));
|
Assert.True(clp.Args.ContainsKey("arg2"));
|
||||||
@@ -112,7 +112,7 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
trace.Info("Parsed");
|
trace.Info("Parsed");
|
||||||
|
|
||||||
trace.Info("Args: {0}", clp.Flags.Count);
|
trace.Info("Args: {0}", clp.Flags.Count);
|
||||||
Assert.True(clp.Flags.Count == 2);
|
Assert.Equal(2, clp.Flags.Count);
|
||||||
Assert.Contains("flag1", clp.Flags);
|
Assert.Contains("flag1", clp.Flags);
|
||||||
Assert.Contains("flag2", clp.Flags);
|
Assert.Contains("flag2", clp.Flags);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
"osx-arm64"
|
"osx-arm64"
|
||||||
};
|
};
|
||||||
|
|
||||||
Assert.True(BuildConstants.Source.CommitHash.Length == 40, $"CommitHash should be SHA-1 hash {BuildConstants.Source.CommitHash}");
|
Assert.Equal(40, BuildConstants.Source.CommitHash.Length);
|
||||||
Assert.True(validPackageNames.Contains(BuildConstants.RunnerPackage.PackageName), $"PackageName should be one of the following '{string.Join(", ", validPackageNames)}', current PackageName is '{BuildConstants.RunnerPackage.PackageName}'");
|
Assert.True(validPackageNames.Contains(BuildConstants.RunnerPackage.PackageName), $"PackageName should be one of the following '{string.Join(", ", validPackageNames)}', current PackageName is '{BuildConstants.RunnerPackage.PackageName}'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,11 +56,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
BrokerMessageListener listener = new();
|
BrokerMessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
trace.Info("result: {0}", result);
|
trace.Info("result: {0}", result);
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.True(result);
|
Assert.Equal(CreateSessionResult.Success, result);
|
||||||
_brokerServer
|
_brokerServer
|
||||||
.Verify(x => x.CreateSessionAsync(
|
.Verify(x => x.CreateSessionAsync(
|
||||||
It.Is<TaskAgentSession>(y => y != null),
|
It.Is<TaskAgentSession>(y => y != null),
|
||||||
|
|||||||
@@ -806,7 +806,7 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
"test runner" });
|
"test runner" });
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.True(command.Validate().Count == 0);
|
Assert.Equal(0, command.Validate().Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -844,7 +844,7 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
var command = new CommandSettings(hc, args: new string[] { validCommand, $"--{flag}" });
|
var command = new CommandSettings(hc, args: new string[] { validCommand, $"--{flag}" });
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.True(command.Validate().Count == 0);
|
Assert.Equal(0, command.Validate().Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -874,7 +874,7 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
var command = new CommandSettings(hc, args: new string[] { validCommand, $"--{arg}", argValue });
|
var command = new CommandSettings(hc, args: new string[] { validCommand, $"--{arg}", argValue });
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.True(command.Validate().Count == 0);
|
Assert.Equal(0, command.Validate().Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -190,11 +190,11 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
|||||||
trace.Info("Configured, verifying all the parameter value");
|
trace.Info("Configured, verifying all the parameter value");
|
||||||
var s = configManager.LoadSettings();
|
var s = configManager.LoadSettings();
|
||||||
Assert.NotNull(s);
|
Assert.NotNull(s);
|
||||||
Assert.True(s.ServerUrl.Equals(_expectedServerUrl));
|
Assert.Equal(_expectedServerUrl, s.ServerUrl);
|
||||||
Assert.True(s.AgentName.Equals(_expectedAgentName));
|
Assert.Equal(_expectedAgentName, s.AgentName);
|
||||||
Assert.True(s.PoolId.Equals(_secondRunnerGroupId));
|
Assert.Equal(_secondRunnerGroupId, s.PoolId);
|
||||||
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
|
Assert.Equal(_expectedWorkFolder, s.WorkFolder);
|
||||||
Assert.True(s.Ephemeral.Equals(true));
|
Assert.True(s.Ephemeral);
|
||||||
|
|
||||||
// validate GetAgentPoolsAsync gets called twice with automation pool type
|
// validate GetAgentPoolsAsync gets called twice with automation pool type
|
||||||
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));
|
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));
|
||||||
@@ -292,11 +292,11 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
|||||||
trace.Info("Configured, verifying all the parameter value");
|
trace.Info("Configured, verifying all the parameter value");
|
||||||
var s = configManager.LoadSettings();
|
var s = configManager.LoadSettings();
|
||||||
Assert.NotNull(s);
|
Assert.NotNull(s);
|
||||||
Assert.True(s.ServerUrl.Equals(_expectedServerUrl));
|
Assert.Equal(_expectedServerUrl, s.ServerUrl);
|
||||||
Assert.True(s.AgentName.Equals(_expectedAgentName));
|
Assert.Equal(_expectedAgentName, s.AgentName);
|
||||||
Assert.True(s.PoolId.Equals(_secondRunnerGroupId));
|
Assert.Equal(_secondRunnerGroupId, s.PoolId);
|
||||||
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
|
Assert.Equal(_expectedWorkFolder, s.WorkFolder);
|
||||||
Assert.True(s.Ephemeral.Equals(true));
|
Assert.True(s.Ephemeral);
|
||||||
|
|
||||||
// validate GetAgentPoolsAsync gets called twice with automation pool type
|
// validate GetAgentPoolsAsync gets called twice with automation pool type
|
||||||
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));
|
_runnerServer.Verify(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.Is<TaskAgentPoolType>(p => p == TaskAgentPoolType.Automation)), Times.Exactly(2));
|
||||||
|
|||||||
@@ -734,7 +734,10 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
await jobDispatcher.WaitAsync(CancellationToken.None);
|
await jobDispatcher.WaitAsync(CancellationToken.None);
|
||||||
|
|
||||||
Assert.True(jobDispatcher.RunOnceJobCompleted.Task.IsCompleted, "JobDispatcher should set task complete token for one time agent.");
|
Assert.True(jobDispatcher.RunOnceJobCompleted.Task.IsCompleted, "JobDispatcher should set task complete token for one time agent.");
|
||||||
Assert.True(jobDispatcher.RunOnceJobCompleted.Task.Result, "JobDispatcher should set task complete token to 'TRUE' for one time agent.");
|
if (jobDispatcher.RunOnceJobCompleted.Task.IsCompleted)
|
||||||
|
{
|
||||||
|
Assert.True(await jobDispatcher.RunOnceJobCompleted.Task, "JobDispatcher should set task complete token to 'TRUE' for one time agent.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,11 +75,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
MessageListener listener = new();
|
MessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
trace.Info("result: {0}", result);
|
trace.Info("result: {0}", result);
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.True(result);
|
Assert.Equal(CreateSessionResult.Success, result);
|
||||||
_runnerServer
|
_runnerServer
|
||||||
.Verify(x => x.CreateAgentSessionAsync(
|
.Verify(x => x.CreateAgentSessionAsync(
|
||||||
_settings.PoolId,
|
_settings.PoolId,
|
||||||
@@ -135,11 +135,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
MessageListener listener = new();
|
MessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
trace.Info("result: {0}", result);
|
trace.Info("result: {0}", result);
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.True(result);
|
Assert.Equal(CreateSessionResult.Success, result);
|
||||||
|
|
||||||
_runnerServer
|
_runnerServer
|
||||||
.Verify(x => x.CreateAgentSessionAsync(
|
.Verify(x => x.CreateAgentSessionAsync(
|
||||||
@@ -185,8 +185,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
MessageListener listener = new();
|
MessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
Assert.True(result);
|
Assert.Equal(CreateSessionResult.Success, result);
|
||||||
|
|
||||||
_runnerServer
|
_runnerServer
|
||||||
.Setup(x => x.DeleteAgentSessionAsync(
|
.Setup(x => x.DeleteAgentSessionAsync(
|
||||||
@@ -245,10 +245,10 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
MessageListener listener = new();
|
MessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
trace.Info("result: {0}", result);
|
trace.Info("result: {0}", result);
|
||||||
|
|
||||||
Assert.True(result);
|
Assert.Equal(CreateSessionResult.Success, result);
|
||||||
|
|
||||||
_runnerServer
|
_runnerServer
|
||||||
.Verify(x => x.CreateAgentSessionAsync(
|
.Verify(x => x.CreateAgentSessionAsync(
|
||||||
@@ -272,7 +272,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
//Assert
|
//Assert
|
||||||
_runnerServer
|
_runnerServer
|
||||||
.Verify(x => x.DeleteAgentSessionAsync(
|
.Verify(x => x.DeleteAgentSessionAsync(
|
||||||
_settings.PoolId, expectedSession.SessionId, It.IsAny<CancellationToken>()), Times.Never());
|
_settings.PoolId, expectedBrokerSession.SessionId, It.IsAny<CancellationToken>()), Times.Once());
|
||||||
_brokerServer
|
_brokerServer
|
||||||
.Verify(x => x.DeleteSessionAsync(It.IsAny<CancellationToken>()), Times.Once());
|
.Verify(x => x.DeleteSessionAsync(It.IsAny<CancellationToken>()), Times.Once());
|
||||||
}
|
}
|
||||||
@@ -309,8 +309,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
MessageListener listener = new();
|
MessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
Assert.True(result);
|
Assert.Equal(CreateSessionResult.Success, result);
|
||||||
|
|
||||||
var arMessages = new TaskAgentMessage[]
|
var arMessages = new TaskAgentMessage[]
|
||||||
{
|
{
|
||||||
@@ -390,8 +390,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
MessageListener listener = new();
|
MessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
Assert.True(result);
|
Assert.Equal(CreateSessionResult.Success, result);
|
||||||
|
|
||||||
var brokerMigrationMesage = new BrokerMigrationMessage(new Uri("https://actions.broker.com"));
|
var brokerMigrationMesage = new BrokerMigrationMessage(new Uri("https://actions.broker.com"));
|
||||||
|
|
||||||
@@ -497,11 +497,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
MessageListener listener = new();
|
MessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
trace.Info("result: {0}", result);
|
trace.Info("result: {0}", result);
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.True(result);
|
Assert.Equal(CreateSessionResult.Success, result);
|
||||||
_runnerServer
|
_runnerServer
|
||||||
.Verify(x => x.CreateAgentSessionAsync(
|
.Verify(x => x.CreateAgentSessionAsync(
|
||||||
_settings.PoolId,
|
_settings.PoolId,
|
||||||
@@ -541,8 +541,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
MessageListener listener = new();
|
MessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
Assert.True(result);
|
Assert.Equal(CreateSessionResult.Success, result);
|
||||||
|
|
||||||
_runnerServer
|
_runnerServer
|
||||||
.Setup(x => x.GetAgentMessageAsync(
|
.Setup(x => x.GetAgentMessageAsync(
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_configurationManager.Setup(x => x.IsConfigured())
|
_configurationManager.Setup(x => x.IsConfigured())
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(Task.FromResult<bool>(true));
|
.Returns(Task.FromResult<CreateSessionResult>(CreateSessionResult.Success));
|
||||||
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(async () =>
|
.Returns(async () =>
|
||||||
{
|
{
|
||||||
@@ -126,7 +126,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
//wait for the runner to run one job
|
//wait for the runner to run one job
|
||||||
if (!await signalWorkerComplete.WaitAsync(2000))
|
if (!await signalWorkerComplete.WaitAsync(2000))
|
||||||
{
|
{
|
||||||
Assert.True(false, $"{nameof(_messageListener.Object.GetNextMessageAsync)} was not invoked.");
|
Assert.Fail($"{nameof(_messageListener.Object.GetNextMessageAsync)} was not invoked.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -184,7 +184,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_configStore.Setup(x => x.IsServiceConfigured()).Returns(configureAsService);
|
_configStore.Setup(x => x.IsServiceConfigured()).Returns(configureAsService);
|
||||||
|
|
||||||
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(Task.FromResult(false));
|
.Returns(Task.FromResult<CreateSessionResult>(CreateSessionResult.Failure));
|
||||||
|
|
||||||
var runner = new Runner.Listener.Runner();
|
var runner = new Runner.Listener.Runner();
|
||||||
runner.Initialize(hc);
|
runner.Initialize(hc);
|
||||||
@@ -217,7 +217,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
.Returns(false);
|
.Returns(false);
|
||||||
|
|
||||||
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(Task.FromResult(false));
|
.Returns(Task.FromResult<CreateSessionResult>(CreateSessionResult.Failure));
|
||||||
|
|
||||||
var runner = new Runner.Listener.Runner();
|
var runner = new Runner.Listener.Runner();
|
||||||
runner.Initialize(hc);
|
runner.Initialize(hc);
|
||||||
@@ -263,7 +263,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_configurationManager.Setup(x => x.IsConfigured())
|
_configurationManager.Setup(x => x.IsConfigured())
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(Task.FromResult<bool>(true));
|
.Returns(Task.FromResult<CreateSessionResult>(CreateSessionResult.Success));
|
||||||
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(async () =>
|
.Returns(async () =>
|
||||||
{
|
{
|
||||||
@@ -305,8 +305,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
await Task.WhenAny(runnerTask, Task.Delay(30000));
|
await Task.WhenAny(runnerTask, Task.Delay(30000));
|
||||||
|
|
||||||
Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out.");
|
Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out.");
|
||||||
Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString());
|
Assert.False(runnerTask.IsFaulted, runnerTask.Exception?.ToString());
|
||||||
Assert.True(runnerTask.Result == Constants.Runner.ReturnCode.Success);
|
if (runnerTask.IsCompleted)
|
||||||
|
{
|
||||||
|
Assert.Equal(Constants.Runner.ReturnCode.Success, await runnerTask);
|
||||||
|
}
|
||||||
|
|
||||||
_jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Once(),
|
_jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Once(),
|
||||||
$"{nameof(_jobDispatcher.Object.Run)} was not invoked.");
|
$"{nameof(_jobDispatcher.Object.Run)} was not invoked.");
|
||||||
@@ -363,7 +366,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_configurationManager.Setup(x => x.IsConfigured())
|
_configurationManager.Setup(x => x.IsConfigured())
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(Task.FromResult<bool>(true));
|
.Returns(Task.FromResult<CreateSessionResult>(CreateSessionResult.Success));
|
||||||
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(async () =>
|
.Returns(async () =>
|
||||||
{
|
{
|
||||||
@@ -406,7 +409,10 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
|
|
||||||
Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out.");
|
Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out.");
|
||||||
Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString());
|
Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString());
|
||||||
Assert.True(runnerTask.Result == Constants.Runner.ReturnCode.Success);
|
if (runnerTask.IsCompleted)
|
||||||
|
{
|
||||||
|
Assert.Equal(Constants.Runner.ReturnCode.Success, await runnerTask);
|
||||||
|
}
|
||||||
|
|
||||||
_jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Once(),
|
_jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Once(),
|
||||||
$"{nameof(_jobDispatcher.Object.Run)} was not invoked.");
|
$"{nameof(_jobDispatcher.Object.Run)} was not invoked.");
|
||||||
@@ -458,7 +464,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_configurationManager.Setup(x => x.IsConfigured())
|
_configurationManager.Setup(x => x.IsConfigured())
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.CreateSessionAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(Task.FromResult<bool>(true));
|
.Returns(Task.FromResult<CreateSessionResult>(CreateSessionResult.Success));
|
||||||
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(async () =>
|
.Returns(async () =>
|
||||||
{
|
{
|
||||||
@@ -492,7 +498,10 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
|
|
||||||
Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out.");
|
Assert.True(runnerTask.IsCompleted, $"{nameof(runner.ExecuteCommand)} timed out.");
|
||||||
Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString());
|
Assert.True(!runnerTask.IsFaulted, runnerTask.Exception?.ToString());
|
||||||
Assert.True(runnerTask.Result == Constants.Runner.ReturnCode.RunOnceRunnerUpdating);
|
if (runnerTask.IsCompleted)
|
||||||
|
{
|
||||||
|
Assert.Equal(Constants.Runner.ReturnCode.RunOnceRunnerUpdating, await runnerTask);
|
||||||
|
}
|
||||||
|
|
||||||
_updater.Verify(x => x.SelfUpdate(It.IsAny<AgentRefreshMessage>(), It.IsAny<IJobDispatcher>(), false, It.IsAny<CancellationToken>()), Times.Once);
|
_updater.Verify(x => x.SelfUpdate(It.IsAny<AgentRefreshMessage>(), It.IsAny<IJobDispatcher>(), false, It.IsAny<CancellationToken>()), Times.Once);
|
||||||
_jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Never());
|
_jobDispatcher.Verify(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), true), Times.Never());
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
trace.Error(ex);
|
trace.Error(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.True(false, "Fail to retrive process environment variable.");
|
Assert.Fail("Failed to retrieve process environment variable.");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -65,7 +65,14 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.True(badCode.Count == 0, $"The following code is using Raw HttpClientHandler() which will not follow the proxy setting agent have. Please use HostContext.CreateHttpClientHandler() instead.\n {string.Join("\n", badCode)}");
|
if (badCode.Count > 0)
|
||||||
|
{
|
||||||
|
Assert.Fail($"The following code is using Raw HttpClientHandler() which will not follow the proxy setting agent have. Please use HostContext.CreateHttpClientHandler() instead.\n {string.Join("\n", badCode)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.True(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -112,7 +119,14 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.True(badCode.Count == 0, $"The following code is using Raw HttpClient() which will not follow the proxy setting agent have. Please use New HttpClient(HostContext.CreateHttpClientHandler()) instead.\n {string.Join("\n", badCode)}");
|
if (badCode.Count > 0)
|
||||||
|
{
|
||||||
|
Assert.Fail($"The following code is using Raw HttpClient() which will not follow the proxy setting agent have. Please use New HttpClient(HostContext.CreateHttpClientHandler()) instead.\n {string.Join("\n", badCode)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.True(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -960,6 +960,33 @@ namespace GitHub.Runner.Common.Tests.Util
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Common")]
|
||||||
|
public void ReplaceInvalidFileNameChars()
|
||||||
|
{
|
||||||
|
Assert.Equal(string.Empty, IOUtil.ReplaceInvalidFileNameChars(null));
|
||||||
|
Assert.Equal(string.Empty, IOUtil.ReplaceInvalidFileNameChars(string.Empty));
|
||||||
|
Assert.Equal("hello.txt", IOUtil.ReplaceInvalidFileNameChars("hello.txt"));
|
||||||
|
#if OS_WINDOWS
|
||||||
|
// Refer https://github.com/dotnet/runtime/blob/ce84f1d8a3f12711bad678a33efbc37b461f684f/src/libraries/System.Private.CoreLib/src/System/IO/Path.Windows.cs#L15
|
||||||
|
Assert.Equal(
|
||||||
|
"1_ 2_ 3_ 4_ 5_ 6_ 7_ 8_ 9_ 10_ 11_ 12_ 13_ 14_ 15_ 16_ 17_ 18_ 19_ 20_ 21_ 22_ 23_ 24_ 25_ 26_ 27_ 28_ 29_ 30_ 31_ 32_ 33_ 34_ 35_ 36_ 37_ 38_ 39_ 40_ 41_",
|
||||||
|
IOUtil.ReplaceInvalidFileNameChars($"1\" 2< 3> 4| 5\0 6{(char)1} 7{(char)2} 8{(char)3} 9{(char)4} 10{(char)5} 11{(char)6} 12{(char)7} 13{(char)8} 14{(char)9} 15{(char)10} 16{(char)11} 17{(char)12} 18{(char)13} 19{(char)14} 20{(char)15} 21{(char)16} 22{(char)17} 23{(char)18} 24{(char)19} 25{(char)20} 26{(char)21} 27{(char)22} 28{(char)23} 29{(char)24} 30{(char)25} 31{(char)26} 32{(char)27} 33{(char)28} 34{(char)29} 35{(char)30} 36{(char)31} 37: 38* 39? 40\\ 41/"));
|
||||||
|
#else
|
||||||
|
// Refer https://github.com/dotnet/runtime/blob/ce84f1d8a3f12711bad678a33efbc37b461f684f/src/libraries/System.Private.CoreLib/src/System/IO/Path.Unix.cs#L12
|
||||||
|
Assert.Equal("1_ 2_", IOUtil.ReplaceInvalidFileNameChars("1\0 2/"));
|
||||||
|
#endif
|
||||||
|
Assert.Equal("_leading", IOUtil.ReplaceInvalidFileNameChars("/leading"));
|
||||||
|
Assert.Equal("__consecutive leading", IOUtil.ReplaceInvalidFileNameChars("//consecutive leading"));
|
||||||
|
Assert.Equal("trailing_", IOUtil.ReplaceInvalidFileNameChars("trailing/"));
|
||||||
|
Assert.Equal("consecutive trailing__", IOUtil.ReplaceInvalidFileNameChars("consecutive trailing//"));
|
||||||
|
Assert.Equal("middle_middle", IOUtil.ReplaceInvalidFileNameChars("middle/middle"));
|
||||||
|
Assert.Equal("consecutive middle__consecutive middle", IOUtil.ReplaceInvalidFileNameChars("consecutive middle//consecutive middle"));
|
||||||
|
Assert.Equal("_leading_middle_trailing_", IOUtil.ReplaceInvalidFileNameChars("/leading/middle/trailing/"));
|
||||||
|
Assert.Equal("__consecutive leading__consecutive middle__consecutive trailing__", IOUtil.ReplaceInvalidFileNameChars("//consecutive leading//consecutive middle//consecutive trailing//"));
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task CreateDirectoryReparsePoint(IHostContext context, string link, string target)
|
private static async Task CreateDirectoryReparsePoint(IHostContext context, string link, string target)
|
||||||
{
|
{
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
|
|||||||
@@ -212,210 +212,5 @@ namespace GitHub.Runner.Common.Tests.Util
|
|||||||
File.Delete(brokenSymlink);
|
File.Delete(brokenSymlink);
|
||||||
Environment.SetEnvironmentVariable(PathUtil.PathVariable, oldValue);
|
Environment.SetEnvironmentVariable(PathUtil.PathVariable, oldValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Common")]
|
|
||||||
public void UseWhich2FindGit()
|
|
||||||
{
|
|
||||||
using (TestHostContext hc = new(this))
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
Tracing trace = hc.GetTrace();
|
|
||||||
|
|
||||||
// Act.
|
|
||||||
string gitPath = WhichUtil.Which2("git", trace: trace);
|
|
||||||
|
|
||||||
trace.Info($"Which(\"git\") returns: {gitPath ?? string.Empty}");
|
|
||||||
|
|
||||||
// Assert.
|
|
||||||
Assert.True(!string.IsNullOrEmpty(gitPath) && File.Exists(gitPath), $"Unable to find Git through: {nameof(WhichUtil.Which)}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Common")]
|
|
||||||
public void Which2ReturnsNullWhenNotFound()
|
|
||||||
{
|
|
||||||
using (TestHostContext hc = new(this))
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
Tracing trace = hc.GetTrace();
|
|
||||||
|
|
||||||
// Act.
|
|
||||||
string nosuch = WhichUtil.Which2("no-such-file-cf7e351f", trace: trace);
|
|
||||||
|
|
||||||
trace.Info($"result: {nosuch ?? string.Empty}");
|
|
||||||
|
|
||||||
// Assert.
|
|
||||||
Assert.True(string.IsNullOrEmpty(nosuch), "Path should not be resolved");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Common")]
|
|
||||||
public void Which2ThrowsWhenRequireAndNotFound()
|
|
||||||
{
|
|
||||||
using (TestHostContext hc = new(this))
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
Tracing trace = hc.GetTrace();
|
|
||||||
|
|
||||||
// Act.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
WhichUtil.Which2("no-such-file-cf7e351f", require: true, trace: trace);
|
|
||||||
throw new Exception("which should have thrown");
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException ex)
|
|
||||||
{
|
|
||||||
Assert.Equal("no-such-file-cf7e351f", ex.FileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Common")]
|
|
||||||
public void Which2HandleFullyQualifiedPath()
|
|
||||||
{
|
|
||||||
using (TestHostContext hc = new(this))
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
Tracing trace = hc.GetTrace();
|
|
||||||
|
|
||||||
// Act.
|
|
||||||
var gitPath = WhichUtil.Which2("git", require: true, trace: trace);
|
|
||||||
var gitPath2 = WhichUtil.Which2(gitPath, require: true, trace: trace);
|
|
||||||
|
|
||||||
// Assert.
|
|
||||||
Assert.Equal(gitPath, gitPath2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Common")]
|
|
||||||
public void Which2HandlesSymlinkToTargetFullPath()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
using TestHostContext hc = new TestHostContext(this);
|
|
||||||
Tracing trace = hc.GetTrace();
|
|
||||||
string oldValue = Environment.GetEnvironmentVariable(PathUtil.PathVariable);
|
|
||||||
#if OS_WINDOWS
|
|
||||||
string newValue = oldValue + @$";{Path.GetTempPath()}";
|
|
||||||
string symlinkName = $"symlink-{Guid.NewGuid()}";
|
|
||||||
string symlink = Path.GetTempPath() + $"{symlinkName}.exe";
|
|
||||||
string target = Path.GetTempPath() + $"target-{Guid.NewGuid()}.exe";
|
|
||||||
#else
|
|
||||||
string newValue = oldValue + @$":{Path.GetTempPath()}";
|
|
||||||
string symlinkName = $"symlink-{Guid.NewGuid()}";
|
|
||||||
string symlink = Path.GetTempPath() + $"{symlinkName}";
|
|
||||||
string target = Path.GetTempPath() + $"target-{Guid.NewGuid()}";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Environment.SetEnvironmentVariable(PathUtil.PathVariable, newValue);
|
|
||||||
|
|
||||||
|
|
||||||
using (File.Create(target))
|
|
||||||
{
|
|
||||||
File.CreateSymbolicLink(symlink, target);
|
|
||||||
|
|
||||||
// Act.
|
|
||||||
var result = WhichUtil.Which2(symlinkName, require: true, trace: trace);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(!string.IsNullOrEmpty(result) && File.Exists(result), $"Unable to find symlink through: {nameof(WhichUtil.Which)}");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
File.Delete(symlink);
|
|
||||||
File.Delete(target);
|
|
||||||
Environment.SetEnvironmentVariable(PathUtil.PathVariable, oldValue);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Common")]
|
|
||||||
public void Which2HandlesSymlinkToTargetRelativePath()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
using TestHostContext hc = new TestHostContext(this);
|
|
||||||
Tracing trace = hc.GetTrace();
|
|
||||||
string oldValue = Environment.GetEnvironmentVariable(PathUtil.PathVariable);
|
|
||||||
#if OS_WINDOWS
|
|
||||||
string newValue = oldValue + @$";{Path.GetTempPath()}";
|
|
||||||
string symlinkName = $"symlink-{Guid.NewGuid()}";
|
|
||||||
string symlink = Path.GetTempPath() + $"{symlinkName}.exe";
|
|
||||||
string targetName = $"target-{Guid.NewGuid()}.exe";
|
|
||||||
string target = Path.GetTempPath() + targetName;
|
|
||||||
#else
|
|
||||||
string newValue = oldValue + @$":{Path.GetTempPath()}";
|
|
||||||
string symlinkName = $"symlink-{Guid.NewGuid()}";
|
|
||||||
string symlink = Path.GetTempPath() + $"{symlinkName}";
|
|
||||||
string targetName = $"target-{Guid.NewGuid()}";
|
|
||||||
string target = Path.GetTempPath() + targetName;
|
|
||||||
#endif
|
|
||||||
Environment.SetEnvironmentVariable(PathUtil.PathVariable, newValue);
|
|
||||||
|
|
||||||
|
|
||||||
using (File.Create(target))
|
|
||||||
{
|
|
||||||
File.CreateSymbolicLink(symlink, targetName);
|
|
||||||
|
|
||||||
// Act.
|
|
||||||
var result = WhichUtil.Which2(symlinkName, require: true, trace: trace);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.True(!string.IsNullOrEmpty(result) && File.Exists(result), $"Unable to find {symlinkName} through: {nameof(WhichUtil.Which)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
File.Delete(symlink);
|
|
||||||
File.Delete(target);
|
|
||||||
Environment.SetEnvironmentVariable(PathUtil.PathVariable, oldValue);
|
|
||||||
|
|
||||||
}
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Common")]
|
|
||||||
public void Which2ThrowsWhenSymlinkBroken()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
using TestHostContext hc = new TestHostContext(this);
|
|
||||||
Tracing trace = hc.GetTrace();
|
|
||||||
string oldValue = Environment.GetEnvironmentVariable(PathUtil.PathVariable);
|
|
||||||
|
|
||||||
#if OS_WINDOWS
|
|
||||||
string newValue = oldValue + @$";{Path.GetTempPath()}";
|
|
||||||
string brokenSymlinkName = $"broken-symlink-{Guid.NewGuid()}";
|
|
||||||
string brokenSymlink = Path.GetTempPath() + $"{brokenSymlinkName}.exe";
|
|
||||||
#else
|
|
||||||
string newValue = oldValue + @$":{Path.GetTempPath()}";
|
|
||||||
string brokenSymlinkName = $"broken-symlink-{Guid.NewGuid()}";
|
|
||||||
string brokenSymlink = Path.GetTempPath() + $"{brokenSymlinkName}";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
string target = "no-such-file-cf7e351f";
|
|
||||||
Environment.SetEnvironmentVariable(PathUtil.PathVariable, newValue);
|
|
||||||
|
|
||||||
File.CreateSymbolicLink(brokenSymlink, target);
|
|
||||||
|
|
||||||
// Act.
|
|
||||||
var exception = Assert.Throws<FileNotFoundException>(() => WhichUtil.Which2(brokenSymlinkName, require: true, trace: trace));
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(brokenSymlinkName, exception.FileName);
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
File.Delete(brokenSymlink);
|
|
||||||
Environment.SetEnvironmentVariable(PathUtil.PathVariable, oldValue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -382,8 +382,6 @@ runs:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_ec.Object.Global.Variables.Set("DistributedTask.UseActionArchiveCache", bool.TrueString);
|
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||||
|
|
||||||
@@ -462,7 +460,7 @@ runs:
|
|||||||
//Act
|
//Act
|
||||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||||
|
|
||||||
Assert.True(steps.Count == 0);
|
Assert.Equal(0, steps.Count);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -917,7 +915,7 @@ runs:
|
|||||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||||
|
|
||||||
// node.js based action doesn't need any extra steps to build/pull containers.
|
// node.js based action doesn't need any extra steps to build/pull containers.
|
||||||
Assert.True(steps.Count == 0);
|
Assert.Equal(0, steps.Count);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -1053,7 +1051,7 @@ runs:
|
|||||||
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
|
||||||
|
|
||||||
// node.js based action doesn't need any extra steps to build/pull containers.
|
// node.js based action doesn't need any extra steps to build/pull containers.
|
||||||
Assert.True(steps.Count == 0);
|
Assert.Equal(0, steps.Count);
|
||||||
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang/runner_L0", "CompositeBasic.completed");
|
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang/runner_L0", "CompositeBasic.completed");
|
||||||
Assert.True(File.Exists(watermarkFile));
|
Assert.True(File.Exists(watermarkFile));
|
||||||
// Comes from the composite action
|
// Comes from the composite action
|
||||||
@@ -1247,7 +1245,7 @@ runs:
|
|||||||
// Assert.
|
// Assert.
|
||||||
Assert.NotNull(definition);
|
Assert.NotNull(definition);
|
||||||
Assert.NotNull(definition.Data);
|
Assert.NotNull(definition.Data);
|
||||||
Assert.True(definition.Data.Execution.ExecutionType == ActionExecutionType.Script);
|
Assert.Equal(ActionExecutionType.Script, definition.Data.Execution.ExecutionType);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -2375,10 +2373,6 @@ runs:
|
|||||||
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
||||||
_ec.Setup(x => x.Root).Returns(new GitHub.Runner.Worker.ExecutionContext());
|
_ec.Setup(x => x.Root).Returns(new GitHub.Runner.Worker.ExecutionContext());
|
||||||
var variables = new Dictionary<string, VariableValue>();
|
var variables = new Dictionary<string, VariableValue>();
|
||||||
if (enableComposite)
|
|
||||||
{
|
|
||||||
variables["DistributedTask.EnableCompositeActions"] = "true";
|
|
||||||
}
|
|
||||||
_ec.Object.Global.Variables = new Variables(_hc, variables);
|
_ec.Object.Global.Variables = new Variables(_hc, variables);
|
||||||
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.7.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||||
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.4.0" />
|
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.4.0" />
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ LAYOUT_DIR="$SCRIPT_DIR/../_layout"
|
|||||||
DOWNLOAD_DIR="$SCRIPT_DIR/../_downloads/netcore2x"
|
DOWNLOAD_DIR="$SCRIPT_DIR/../_downloads/netcore2x"
|
||||||
PACKAGE_DIR="$SCRIPT_DIR/../_package"
|
PACKAGE_DIR="$SCRIPT_DIR/../_package"
|
||||||
DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk"
|
DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk"
|
||||||
DOTNETSDK_VERSION="6.0.419"
|
DOTNETSDK_VERSION="6.0.421"
|
||||||
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
|
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
|
||||||
RUNNER_VERSION=$(cat runnerversion)
|
RUNNER_VERSION=$(cat runnerversion)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "6.0.419"
|
"version": "6.0.421"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.314.1
|
2.317.0
|
||||||
|
|||||||
Reference in New Issue
Block a user