mirror of
https://github.com/actions/runner.git
synced 2025-12-11 21:06:55 +00:00
Compare commits
1 Commits
revert-291
...
releases/m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86858e39e4 |
@@ -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.421"
|
"version": "6.0.420"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": "16"
|
"version": "16"
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
Make sure the built-in node.js has access to GitHub.com or GitHub Enterprise Server.
|
Make sure the built-in node.js has access to GitHub.com or GitHub Enterprise Server.
|
||||||
|
|
||||||
The runner carries its own copy of node.js executable under `<runner_root>/externals/node20/`.
|
The runner carries its own copy of node.js executable under `<runner_root>/externals/node16/`.
|
||||||
|
|
||||||
All javascript base Actions will get executed by the built-in `node` at `<runner_root>/externals/node20/`.
|
All javascript base Actions will get executed by the built-in `node` at `<runner_root>/externals/node16/`.
|
||||||
|
|
||||||
> Not the `node` from `$PATH`
|
> Not the `node` from `$PATH`
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,33 @@
|
|||||||
## What's Changed
|
## What's Changed
|
||||||
|
* fix summaries for actions results by @SrRyan in https://github.com/actions/runner/pull/3174
|
||||||
|
* Bump runner version to match the latest patch release by @TingluoHuang in https://github.com/actions/runner/pull/3175
|
||||||
|
* don't crash listener on getting job exceptions for run-service by @yaananth in https://github.com/actions/runner/pull/3177
|
||||||
|
* Remove -f flag in wait when manually trap signal by @nikola-jokic in https://github.com/actions/runner/pull/3182
|
||||||
|
* consume new pipelines service url in handlers by @patrickcarnahan in https://github.com/actions/runner/pull/3185
|
||||||
|
* Add ability to enforce actions to run on node20 by @takost in https://github.com/actions/runner/pull/3192
|
||||||
|
* Bump hook version to 0.6.0 by @nikola-jokic in https://github.com/actions/runner/pull/3203
|
||||||
|
* Update dotnet sdk to latest version @6.0.420 by @github-actions in https://github.com/actions/runner/pull/3211
|
||||||
|
* Bump docker version and docker buildx version by @nikola-jokic in https://github.com/actions/runner/pull/3208
|
||||||
|
* Handle new non-retryable exception type by @thyeggman in https://github.com/actions/runner/pull/3191
|
||||||
|
* Always Delete Actions Service Session by @luketomlinson in https://github.com/actions/runner/pull/3214
|
||||||
|
|
||||||
- Preserve dates when deserializing job message from Run Service by @ericsciple in https://github.com/actions/runner/pull/3269
|
## New Contributors
|
||||||
|
* @SrRyan made their first contribution in https://github.com/actions/runner/pull/3174
|
||||||
|
* @patrickcarnahan made their first contribution in https://github.com/actions/runner/pull/3185
|
||||||
|
|
||||||
**Full Changelog**: https://github.com/actions/runner/compare/v2.316.0...v2.316.1
|
**Full Changelog**: https://github.com/actions/runner/compare/v2.314.1...v2.315.0
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/actions/runner/compare/v2.313.0...v2.314.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
|
||||||
@@ -25,14 +38,12 @@ 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
|
||||||
@@ -44,7 +55,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
|
||||||
@@ -55,7 +66,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
|
||||||
@@ -66,7 +77,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
|
||||||
@@ -77,7 +88,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
|
||||||
@@ -88,7 +99,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
|
||||||
@@ -98,7 +109,6 @@ 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 @@
|
|||||||
<Update to ./src/runnerversion when creating release>
|
2.315.0
|
||||||
@@ -114,11 +114,6 @@ 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++;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ if [ -f ".path" ]; then
|
|||||||
echo ".path=${PATH}"
|
echo ".path=${PATH}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
nodever=${GITHUB_ACTIONS_RUNNER_FORCED_NODE_VERSION:-node20}
|
nodever=${GITHUB_ACTIONS_RUNNER_FORCED_NODE_VERSION:-node16}
|
||||||
|
|
||||||
# insert anything to setup env when running as a service
|
# insert anything to setup env when running as a service
|
||||||
# run the host process which keep the listener alive
|
# run the host process which keep the listener alive
|
||||||
|
|||||||
@@ -135,17 +135,12 @@ if [[ "$currentplatform" == 'darwin' && restartinteractiverunner -eq 0 ]]; then
|
|||||||
then
|
then
|
||||||
# inspect the open file handles to find the node process
|
# inspect the open file handles to find the node process
|
||||||
# we can't actually inspect the process using ps because it uses relative paths and doesn't follow symlinks
|
# we can't actually inspect the process using ps because it uses relative paths and doesn't follow symlinks
|
||||||
nodever="node20"
|
nodever="node16"
|
||||||
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
||||||
if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node16
|
if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node12
|
||||||
then
|
then
|
||||||
nodever="node16"
|
nodever="node12"
|
||||||
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
||||||
if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node12
|
|
||||||
then
|
|
||||||
nodever="node12"
|
|
||||||
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
if [[ $? -eq 0 && -n "$path" ]]
|
if [[ $? -eq 0 && -n "$path" ]]
|
||||||
then
|
then
|
||||||
|
|||||||
@@ -49,10 +49,5 @@ 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,9 +70,6 @@ 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
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
private bool _hasConnection;
|
private bool _hasConnection;
|
||||||
private VssConnection _connection;
|
private VssConnection _connection;
|
||||||
private ActionsRunServerHttpClient _actionsRunServerClient;
|
private TaskAgentHttpClient _taskAgentClient;
|
||||||
|
|
||||||
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));
|
||||||
_actionsRunServerClient = _connection.GetClient<ActionsRunServerHttpClient>();
|
_taskAgentClient = _connection.GetClient<TaskAgentHttpClient>();
|
||||||
_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 _actionsRunServerClient.GetJobMessageAsync(id, cancellationToken);
|
return await _taskAgentClient.GetJobMessageAsync(id, cancellationToken);
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
|
|
||||||
return jobMessage;
|
return jobMessage;
|
||||||
|
|||||||
@@ -153,7 +153,6 @@ 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
|
||||||
@@ -262,7 +261,7 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
public static readonly string ToolsDirectory = "agent.ToolsDirectory";
|
public static readonly string ToolsDirectory = "agent.ToolsDirectory";
|
||||||
|
|
||||||
// Set this env var to "node16" to downgrade the node version for internal functions (e.g hashfiles). This does NOT affect the version of node actions.
|
// Set this env var to "node12" to downgrade the node version for internal functions (e.g hashfiles). This does NOT affect the version of node actions.
|
||||||
public static readonly string ForcedInternalNodeVersion = "ACTIONS_RUNNER_FORCED_INTERNAL_NODE_VERSION";
|
public static readonly string ForcedInternalNodeVersion = "ACTIONS_RUNNER_FORCED_INTERNAL_NODE_VERSION";
|
||||||
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";
|
||||||
|
|||||||
@@ -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);
|
void Start(Pipelines.AgentJobRequestMessage jobRequest, bool resultsServiceOnly = false, bool enableTelemetry = 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,7 +74,6 @@ 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();
|
||||||
|
|
||||||
@@ -105,10 +104,11 @@ namespace GitHub.Runner.Common
|
|||||||
_resultsServer = hostContext.GetService<IResultsServer>();
|
_resultsServer = hostContext.GetService<IResultsServer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(Pipelines.AgentJobRequestMessage jobRequest, bool resultsServiceOnly = false)
|
public void Start(Pipelines.AgentJobRequestMessage jobRequest, bool resultsServiceOnly = false, bool enableTelemetry = 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,12 +139,6 @@ 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.");
|
||||||
@@ -580,9 +574,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 && _resultsServiceExceptionsCount > 3)
|
if (!_resultsServiceOnly)
|
||||||
{
|
{
|
||||||
_resultsClientInitiated = false;
|
_resultsClientInitiated = false;
|
||||||
SendResultsTelemetry(ex);
|
SendResultsTelemetry(ex);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace GitHub.Runner.Common.Util
|
|||||||
{
|
{
|
||||||
public static class NodeUtil
|
public static class NodeUtil
|
||||||
{
|
{
|
||||||
private const string _defaultNodeVersion = "node20";
|
private const string _defaultNodeVersion = "node16";
|
||||||
public static readonly ReadOnlyCollection<string> BuiltInNodeVersions = new(new[] { "node16", "node20" });
|
public static readonly ReadOnlyCollection<string> BuiltInNodeVersions = new(new[] { "node16", "node20" });
|
||||||
public static string GetInternalNodeVersion()
|
public static string GetInternalNodeVersion()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace GitHub.Runner.Listener
|
|||||||
_brokerServer = HostContext.GetService<IBrokerServer>();
|
_brokerServer = HostContext.GetService<IBrokerServer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CreateSessionResult> CreateSessionAsync(CancellationToken token)
|
public async Task<Boolean> CreateSessionAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ namespace GitHub.Runner.Listener
|
|||||||
encounteringError = false;
|
encounteringError = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CreateSessionResult.Success;
|
return true;
|
||||||
}
|
}
|
||||||
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 CreateSessionResult.Failure;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,18 +134,14 @@ 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 CreateSessionResult.Failure;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsSessionCreationExceptionRetriable(ex))
|
if (!IsSessionCreationExceptionRetriable(ex))
|
||||||
{
|
{
|
||||||
_term.WriteError($"Failed to create session. {ex.Message}");
|
_term.WriteError($"Failed to create session. {ex.Message}");
|
||||||
if (ex is TaskAgentSessionConflictException)
|
return false;
|
||||||
{
|
|
||||||
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,13 +1155,18 @@ 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);
|
||||||
|
|
||||||
Trace.Info("Mark the job as failed since the worker crashed");
|
if (message.Variables.TryGetValue("DistributedTask.MarkJobAsFailedOnWorkerCrash", out var markJobAsFailedOnWorkerCrash) &&
|
||||||
jobRecord.Result = TaskResult.Failed;
|
StringUtil.ConvertToBoolean(markJobAsFailedOnWorkerCrash?.Value))
|
||||||
// mark the job as completed so service will pickup the result
|
{
|
||||||
jobRecord.State = TimelineRecordState.Completed;
|
Trace.Info("Mark the job as failed since the worker crashed");
|
||||||
|
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,17 +18,10 @@ 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<CreateSessionResult> CreateSessionAsync(CancellationToken token);
|
Task<Boolean> 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);
|
||||||
@@ -66,7 +59,7 @@ namespace GitHub.Runner.Listener
|
|||||||
_brokerServer = hostContext.GetService<IBrokerServer>();
|
_brokerServer = hostContext.GetService<IBrokerServer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CreateSessionResult> CreateSessionAsync(CancellationToken token)
|
public async Task<Boolean> CreateSessionAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
|
|
||||||
@@ -130,7 +123,7 @@ namespace GitHub.Runner.Listener
|
|||||||
encounteringError = false;
|
encounteringError = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CreateSessionResult.Success;
|
return true;
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@@ -154,7 +147,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 CreateSessionResult.Failure;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -165,18 +158,14 @@ 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 CreateSessionResult.Failure;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsSessionCreationExceptionRetriable(ex))
|
if (!IsSessionCreationExceptionRetriable(ex))
|
||||||
{
|
{
|
||||||
_term.WriteError($"Failed to create session. {ex.Message}");
|
_term.WriteError($"Failed to create session. {ex.Message}");
|
||||||
if (ex is TaskAgentSessionConflictException)
|
return false;
|
||||||
{
|
|
||||||
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
|
||||||
@@ -236,7 +225,6 @@ 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();
|
||||||
@@ -315,7 +303,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) == CreateSessionResult.Success))
|
if (ex is TaskAgentSessionExpiredException && !_settings.SkipSessionRecover && await CreateSessionAsync(token))
|
||||||
{
|
{
|
||||||
Trace.Info($"{nameof(TaskAgentSessionExpiredException)} received, recovered by recreate session.");
|
Trace.Info($"{nameof(TaskAgentSessionExpiredException)} received, recovered by recreate session.");
|
||||||
}
|
}
|
||||||
@@ -360,27 +348,16 @@ 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,12 +359,7 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
Trace.Info(nameof(RunAsync));
|
Trace.Info(nameof(RunAsync));
|
||||||
_listener = GetMesageListener(settings);
|
_listener = GetMesageListener(settings);
|
||||||
CreateSessionResult createSessionResult = await _listener.CreateSessionAsync(HostContext.RunnerShutdownToken);
|
if (!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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -459,34 +459,6 @@ 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,6 +7,129 @@ 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,6 +483,10 @@ 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
|
||||||
@@ -793,39 +797,42 @@ namespace GitHub.Runner.Worker
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var useActionArchiveCache = false;
|
var useActionArchiveCache = false;
|
||||||
var hasActionArchiveCache = false;
|
if (executionContext.Global.Variables.GetBoolean("DistributedTask.UseActionArchiveCache") == true)
|
||||||
var actionArchiveCacheDir = Environment.GetEnvironmentVariable(Constants.Variables.Agent.ActionArchiveCacheDirectory);
|
|
||||||
if (!string.IsNullOrEmpty(actionArchiveCacheDir) &&
|
|
||||||
Directory.Exists(actionArchiveCacheDir))
|
|
||||||
{
|
{
|
||||||
hasActionArchiveCache = true;
|
var hasActionArchiveCache = false;
|
||||||
Trace.Info($"Check if action archive '{downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha}' already exists in cache directory '{actionArchiveCacheDir}'");
|
var actionArchiveCacheDir = Environment.GetEnvironmentVariable(Constants.Variables.Agent.ActionArchiveCacheDirectory);
|
||||||
#if OS_WINDOWS
|
if (!string.IsNullOrEmpty(actionArchiveCacheDir) &&
|
||||||
var cacheArchiveFile = Path.Combine(actionArchiveCacheDir, downloadInfo.ResolvedNameWithOwner.Replace(Path.DirectorySeparatorChar, '_').Replace(Path.AltDirectorySeparatorChar, '_'), $"{downloadInfo.ResolvedSha}.zip");
|
Directory.Exists(actionArchiveCacheDir))
|
||||||
#else
|
|
||||||
var cacheArchiveFile = Path.Combine(actionArchiveCacheDir, downloadInfo.ResolvedNameWithOwner.Replace(Path.DirectorySeparatorChar, '_').Replace(Path.AltDirectorySeparatorChar, '_'), $"{downloadInfo.ResolvedSha}.tar.gz");
|
|
||||||
#endif
|
|
||||||
if (File.Exists(cacheArchiveFile))
|
|
||||||
{
|
{
|
||||||
try
|
hasActionArchiveCache = true;
|
||||||
|
Trace.Info($"Check if action archive '{downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha}' already exists in cache directory '{actionArchiveCacheDir}'");
|
||||||
|
#if OS_WINDOWS
|
||||||
|
var cacheArchiveFile = Path.Combine(actionArchiveCacheDir, downloadInfo.ResolvedNameWithOwner.Replace(Path.DirectorySeparatorChar, '_').Replace(Path.AltDirectorySeparatorChar, '_'), $"{downloadInfo.ResolvedSha}.zip");
|
||||||
|
#else
|
||||||
|
var cacheArchiveFile = Path.Combine(actionArchiveCacheDir, downloadInfo.ResolvedNameWithOwner.Replace(Path.DirectorySeparatorChar, '_').Replace(Path.AltDirectorySeparatorChar, '_'), $"{downloadInfo.ResolvedSha}.tar.gz");
|
||||||
|
#endif
|
||||||
|
if (File.Exists(cacheArchiveFile))
|
||||||
{
|
{
|
||||||
Trace.Info($"Found action archive '{cacheArchiveFile}' in cache directory '{actionArchiveCacheDir}'");
|
try
|
||||||
File.Copy(cacheArchiveFile, archiveFile);
|
{
|
||||||
useActionArchiveCache = true;
|
Trace.Info($"Found action archive '{cacheArchiveFile}' in cache directory '{actionArchiveCacheDir}'");
|
||||||
executionContext.Debug($"Copied action archive '{cacheArchiveFile}' to '{archiveFile}'");
|
File.Copy(cacheArchiveFile, archiveFile);
|
||||||
}
|
useActionArchiveCache = true;
|
||||||
catch (Exception ex)
|
executionContext.Debug($"Copied action archive '{cacheArchiveFile}' to '{archiveFile}'");
|
||||||
{
|
}
|
||||||
Trace.Error($"Failed to copy action archive '{cacheArchiveFile}' to '{archiveFile}'. Error: {ex}");
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Failed to copy action archive '{cacheArchiveFile}' to '{archiveFile}'. Error: {ex}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
|
||||||
{
|
{
|
||||||
Type = JobTelemetryType.General,
|
Type = JobTelemetryType.General,
|
||||||
Message = $"Action archive cache usage: {downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha} use cache {useActionArchiveCache} has cache {hasActionArchiveCache}"
|
Message = $"Action archive cache usage: {downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha} use cache {useActionArchiveCache} has cache {hasActionArchiveCache}"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!useActionArchiveCache)
|
if (!useActionArchiveCache)
|
||||||
{
|
{
|
||||||
@@ -872,9 +879,16 @@ 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)
|
||||||
{
|
{
|
||||||
var fileInfo = new FileInfo(archiveFile);
|
if (executionContext.Global.Variables.GetBoolean("DistributedTask.DetailUntarFailure") == true)
|
||||||
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}.");
|
var fileInfo = new FileInfo(archiveFile);
|
||||||
|
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
|
||||||
@@ -1018,6 +1032,13 @@ 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}-{IOUtil.ReplaceInvalidFileNameChars(phaseName)}.zip";
|
string diagnosticsZipFileName = $"{buildName}-{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}-{IOUtil.ReplaceInvalidFileNameChars(phaseName)}.json";
|
string metadataFileName = $"diagnostics-{buildName}-{phaseName}.json";
|
||||||
string metadataFilePath = Path.Combine(supportFilesFolder, metadataFileName);
|
string metadataFilePath = Path.Combine(supportFilesFolder, metadataFileName);
|
||||||
string phaseResult = GetTaskResultAsString(executionContext.Result);
|
string phaseResult = GetTaskResultAsString(executionContext.Result);
|
||||||
|
|
||||||
|
|||||||
@@ -837,6 +837,7 @@ namespace GitHub.Runner.Worker
|
|||||||
// Actions environment
|
// Actions environment
|
||||||
ActionsEnvironment = message.ActionsEnvironment;
|
ActionsEnvironment = message.ActionsEnvironment;
|
||||||
|
|
||||||
|
|
||||||
// Service container info
|
// Service container info
|
||||||
Global.ServiceContainers = new List<ContainerInfo>();
|
Global.ServiceContainers = new List<ContainerInfo>();
|
||||||
|
|
||||||
|
|||||||
@@ -83,19 +83,40 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
shellCommand = "pwsh";
|
shellCommand = "pwsh";
|
||||||
if (validateShellOnHost)
|
if (validateShellOnHost)
|
||||||
{
|
{
|
||||||
shellCommandPath = WhichUtil.Which(shellCommand, require: false, Trace, prependPath);
|
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
||||||
|
{
|
||||||
|
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}");
|
||||||
shellCommandPath = WhichUtil.Which(shellCommand, require: true, Trace, prependPath);
|
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
||||||
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
shellCommandPath = WhichUtil.Which("bash", false, Trace, prependPath) ?? WhichUtil.Which("sh", true, Trace, prependPath);
|
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
||||||
|
{
|
||||||
|
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);
|
||||||
@@ -106,7 +127,14 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
shellCommand = parsed.shellCommand;
|
shellCommand = parsed.shellCommand;
|
||||||
if (validateShellOnHost)
|
if (validateShellOnHost)
|
||||||
{
|
{
|
||||||
shellCommandPath = WhichUtil.Which(parsed.shellCommand, true, Trace, prependPath);
|
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
||||||
|
{
|
||||||
|
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();
|
||||||
@@ -188,17 +216,38 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
shellCommand = "pwsh";
|
shellCommand = "pwsh";
|
||||||
commandPath = WhichUtil.Which(shellCommand, require: false, Trace, prependPath);
|
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
||||||
|
{
|
||||||
|
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}");
|
||||||
commandPath = WhichUtil.Which(shellCommand, require: true, Trace, prependPath);
|
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
||||||
|
{
|
||||||
|
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";
|
||||||
commandPath = WhichUtil.Which("bash", false, Trace, prependPath) ?? WhichUtil.Which("sh", true, Trace, prependPath);
|
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
||||||
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
@@ -209,7 +258,14 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
if (!IsActionStep && systemShells.Contains(shell))
|
if (!IsActionStep && systemShells.Contains(shell))
|
||||||
{
|
{
|
||||||
shellCommand = shell;
|
shellCommand = shell;
|
||||||
commandPath = WhichUtil.Which(shell, !isContainerStepHost, Trace, prependPath);
|
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
||||||
|
{
|
||||||
|
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");
|
||||||
@@ -224,7 +280,14 @@ 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
|
||||||
commandPath = WhichUtil.Which(parsed.shellCommand, !isContainerStepHost, Trace, prependPath);
|
if (ExecutionContext.Global.Variables.GetBoolean("DistributedTask.UseWhich2") == true)
|
||||||
|
{
|
||||||
|
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,7 +42,6 @@ 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.
|
||||||
@@ -55,6 +54,13 @@ 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))
|
||||||
{
|
{
|
||||||
@@ -76,7 +82,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);
|
_jobServerQueue.Start(message, resultsServiceOnly: true, enableTelemetry: jobServerQueueTelemetry);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -98,7 +104,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);
|
_jobServerQueue.Start(message, enableTelemetry: jobServerQueueTelemetry);
|
||||||
server = jobServer;
|
server = jobServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +164,8 @@ 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))
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -141,6 +141,24 @@ 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,7 +9,6 @@ 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;
|
||||||
|
|
||||||
@@ -17,15 +16,6 @@ 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)
|
||||||
@@ -184,11 +174,5 @@ 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,11 +56,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
BrokerMessageListener listener = new();
|
BrokerMessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
trace.Info("result: {0}", result);
|
trace.Info("result: {0}", result);
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.Equal(CreateSessionResult.Success, result);
|
Assert.True(result);
|
||||||
_brokerServer
|
_brokerServer
|
||||||
.Verify(x => x.CreateSessionAsync(
|
.Verify(x => x.CreateSessionAsync(
|
||||||
It.Is<TaskAgentSession>(y => y != null),
|
It.Is<TaskAgentSession>(y => y != null),
|
||||||
|
|||||||
@@ -75,11 +75,11 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
MessageListener listener = new();
|
MessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
trace.Info("result: {0}", result);
|
trace.Info("result: {0}", result);
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.Equal(CreateSessionResult.Success, result);
|
Assert.True(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);
|
||||||
|
|
||||||
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
trace.Info("result: {0}", result);
|
trace.Info("result: {0}", result);
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.Equal(CreateSessionResult.Success, result);
|
Assert.True(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);
|
||||||
|
|
||||||
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
Assert.Equal(CreateSessionResult.Success, result);
|
Assert.True(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);
|
||||||
|
|
||||||
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
trace.Info("result: {0}", result);
|
trace.Info("result: {0}", result);
|
||||||
|
|
||||||
Assert.Equal(CreateSessionResult.Success, result);
|
Assert.True(result);
|
||||||
|
|
||||||
_runnerServer
|
_runnerServer
|
||||||
.Verify(x => x.CreateAgentSessionAsync(
|
.Verify(x => x.CreateAgentSessionAsync(
|
||||||
@@ -309,8 +309,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
MessageListener listener = new();
|
MessageListener listener = new();
|
||||||
listener.Initialize(tc);
|
listener.Initialize(tc);
|
||||||
|
|
||||||
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
Assert.Equal(CreateSessionResult.Success, result);
|
Assert.True(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);
|
||||||
|
|
||||||
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
Assert.Equal(CreateSessionResult.Success, result);
|
Assert.True(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);
|
||||||
|
|
||||||
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
trace.Info("result: {0}", result);
|
trace.Info("result: {0}", result);
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.Equal(CreateSessionResult.Success, result);
|
Assert.True(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);
|
||||||
|
|
||||||
CreateSessionResult result = await listener.CreateSessionAsync(tokenSource.Token);
|
bool result = await listener.CreateSessionAsync(tokenSource.Token);
|
||||||
Assert.Equal(CreateSessionResult.Success, result);
|
Assert.True(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<CreateSessionResult>(CreateSessionResult.Success));
|
.Returns(Task.FromResult<bool>(true));
|
||||||
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(async () =>
|
.Returns(async () =>
|
||||||
{
|
{
|
||||||
@@ -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<CreateSessionResult>(CreateSessionResult.Failure));
|
.Returns(Task.FromResult(false));
|
||||||
|
|
||||||
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<CreateSessionResult>(CreateSessionResult.Failure));
|
.Returns(Task.FromResult(false));
|
||||||
|
|
||||||
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<CreateSessionResult>(CreateSessionResult.Success));
|
.Returns(Task.FromResult<bool>(true));
|
||||||
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(async () =>
|
.Returns(async () =>
|
||||||
{
|
{
|
||||||
@@ -363,7 +363,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<CreateSessionResult>(CreateSessionResult.Success));
|
.Returns(Task.FromResult<bool>(true));
|
||||||
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(async () =>
|
.Returns(async () =>
|
||||||
{
|
{
|
||||||
@@ -458,7 +458,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<CreateSessionResult>(CreateSessionResult.Success));
|
.Returns(Task.FromResult<bool>(true));
|
||||||
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
_messageListener.Setup(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()))
|
||||||
.Returns(async () =>
|
.Returns(async () =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -960,33 +960,6 @@ 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,5 +212,210 @@ 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,6 +382,8 @@ runs:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_ec.Object.Global.Variables.Set("DistributedTask.UseActionArchiveCache", bool.TrueString);
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||||
|
|
||||||
@@ -2373,6 +2375,10 @@ 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>());
|
||||||
|
|||||||
@@ -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.421"
|
DOTNETSDK_VERSION="6.0.420"
|
||||||
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.421"
|
"version": "6.0.420"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.316.1
|
2.315.0
|
||||||
|
|||||||
Reference in New Issue
Block a user