mirror of
https://github.com/actions/runner.git
synced 2025-12-11 04:46:58 +00:00
Compare commits
17 Commits
users/eric
...
automate
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6122975a2 | ||
|
|
21d5973243 | ||
|
|
f4c58235e2 | ||
|
|
e8581d562c | ||
|
|
1d88f86230 | ||
|
|
ce43e5043a | ||
|
|
2d97cef1d2 | ||
|
|
52ff9b4f59 | ||
|
|
055585d8cd | ||
|
|
7e7e6c6568 | ||
|
|
ccdf27e0d4 | ||
|
|
de317dba60 | ||
|
|
1410fe000e | ||
|
|
8e78c7ba11 | ||
|
|
74cc31524c | ||
|
|
0fdbfaf862 | ||
|
|
de914793e6 |
@@ -40,7 +40,7 @@ Debian based OS (Debian, Ubuntu, Linux Mint)
|
|||||||
- libssl1.1, libssl1.0.2 or libssl1.0.0
|
- libssl1.1, libssl1.0.2 or libssl1.0.0
|
||||||
- libicu63, libicu60, libicu57 or libicu55
|
- libicu63, libicu60, libicu57 or libicu55
|
||||||
|
|
||||||
Fedora based OS (Fedora, Red Hat Enterprise Linux, CentOS, Oracle Linux 7)
|
Fedora based OS (Fedora, Redhat, Centos, Oracle Linux 7)
|
||||||
|
|
||||||
- lttng-ust
|
- lttng-ust
|
||||||
- openssl-libs
|
- openssl-libs
|
||||||
|
|||||||
@@ -8,15 +8,13 @@
|
|||||||
- N/A
|
- N/A
|
||||||
|
|
||||||
## 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`:
|
// Create a folder under the drive root
|
||||||
``` powershell
|
|
||||||
# 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
|
||||||
Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-win-x64-<RUNNER_VERSION>.zip -OutFile actions-runner-win-x64-<RUNNER_VERSION>.zip
|
Invoke-WebRequest -Uri https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-win-x64-<RUNNER_VERSION>.zip -OutFile actions-runner-win-x64-<RUNNER_VERSION>.zip
|
||||||
# Extract the installer
|
// Extract the installer
|
||||||
Add-Type -AssemblyName System.IO.Compression.FileSystem ;
|
Add-Type -AssemblyName System.IO.Compression.FileSystem ;
|
||||||
[System.IO.Compression.ZipFile]::ExtractToDirectory("$PWD\actions-runner-win-x64-<RUNNER_VERSION>.zip", "$PWD")
|
[System.IO.Compression.ZipFile]::ExtractToDirectory("$PWD\actions-runner-win-x64-<RUNNER_VERSION>.zip", "$PWD")
|
||||||
```
|
```
|
||||||
@@ -24,44 +22,44 @@ Add-Type -AssemblyName System.IO.Compression.FileSystem ;
|
|||||||
## OSX
|
## OSX
|
||||||
|
|
||||||
``` 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
|
||||||
curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz
|
curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz
|
||||||
# Extract the installer
|
// Extract the installer
|
||||||
tar xzf ./actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz
|
tar xzf ./actions-runner-osx-x64-<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
|
||||||
curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz
|
curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz
|
||||||
# Extract the installer
|
// Extract the installer
|
||||||
tar xzf ./actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz
|
tar xzf ./actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
## Linux arm64 (Pre-release)
|
## Linux arm64 (Pre-release)
|
||||||
|
|
||||||
``` 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
|
||||||
curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz
|
curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz
|
||||||
# Extract the installer
|
// Extract the installer
|
||||||
tar xzf ./actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz
|
tar xzf ./actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
## Linux arm (Pre-release)
|
## Linux arm (Pre-release)
|
||||||
|
|
||||||
``` 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
|
||||||
curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz
|
curl -O -L https://github.com/actions/runner/releases/download/v<RUNNER_VERSION>/actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz
|
||||||
# Extract the installer
|
// Extract the installer
|
||||||
tar xzf ./actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz
|
tar xzf ./actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fi
|
|||||||
|
|
||||||
# Determine OS type
|
# Determine OS type
|
||||||
# Debian based OS (Debian, Ubuntu, Linux Mint) has /etc/debian_version
|
# Debian based OS (Debian, Ubuntu, Linux Mint) has /etc/debian_version
|
||||||
# Fedora based OS (Fedora, Red Hat Enterprise Linux, CentOS, Oracle Linux 7) has /etc/redhat-release
|
# Fedora based OS (Fedora, Redhat, Centos, Oracle Linux 7) has /etc/redhat-release
|
||||||
# SUSE based OS (OpenSUSE, SUSE Enterprise) has ID_LIKE=suse in /etc/os-release
|
# SUSE based OS (OpenSUSE, SUSE Enterprise) has ID_LIKE=suse in /etc/os-release
|
||||||
|
|
||||||
function print_errormessage()
|
function print_errormessage()
|
||||||
@@ -116,12 +116,12 @@ then
|
|||||||
elif [ -e /etc/redhat-release ]
|
elif [ -e /etc/redhat-release ]
|
||||||
then
|
then
|
||||||
echo "The current OS is Fedora based"
|
echo "The current OS is Fedora based"
|
||||||
echo "--Fedora/RHEL/CentOS Version--"
|
echo "--------Redhat Version--------"
|
||||||
cat /etc/redhat-release
|
cat /etc/redhat-release
|
||||||
echo "------------------------------"
|
echo "------------------------------"
|
||||||
|
|
||||||
# use dnf on fedora
|
# use dnf on fedora
|
||||||
# use yum on centos and rhel
|
# use yum on centos and redhat
|
||||||
if [ -e /etc/fedora-release ]
|
if [ -e /etc/fedora-release ]
|
||||||
then
|
then
|
||||||
command -v dnf
|
command -v dnf
|
||||||
@@ -191,7 +191,7 @@ then
|
|||||||
redhatRelease=$(</etc/redhat-release)
|
redhatRelease=$(</etc/redhat-release)
|
||||||
if [[ $redhatRelease == "CentOS release 6."* || $redhatRelease == "Red Hat Enterprise Linux Server release 6."* ]]
|
if [[ $redhatRelease == "CentOS release 6."* || $redhatRelease == "Red Hat Enterprise Linux Server release 6."* ]]
|
||||||
then
|
then
|
||||||
echo "The current OS is Red Hat Enterprise Linux 6 or CentOS 6"
|
echo "The current OS is Red Hat Enterprise Linux 6 or Centos 6"
|
||||||
|
|
||||||
# Install known dependencies, as a best effort.
|
# Install known dependencies, as a best effort.
|
||||||
# The remaining dependencies are covered by the GitHub doc that will be shown by `print_rhel6message`
|
# The remaining dependencies are covered by the GitHub doc that will be shown by `print_rhel6message`
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
using System;
|
using GitHub.Runner.Common.Util;
|
||||||
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Diagnostics.Tracing;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Loader;
|
using System.Runtime.Loader;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Diagnostics.Tracing;
|
||||||
using GitHub.DistributedTask.Logging;
|
using GitHub.DistributedTask.Logging;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
@@ -88,7 +89,6 @@ namespace GitHub.Runner.Common
|
|||||||
this.SecretMasker.AddValueEncoder(ValueEncoders.JsonStringEscape);
|
this.SecretMasker.AddValueEncoder(ValueEncoders.JsonStringEscape);
|
||||||
this.SecretMasker.AddValueEncoder(ValueEncoders.UriDataEscape);
|
this.SecretMasker.AddValueEncoder(ValueEncoders.UriDataEscape);
|
||||||
this.SecretMasker.AddValueEncoder(ValueEncoders.XmlDataEscape);
|
this.SecretMasker.AddValueEncoder(ValueEncoders.XmlDataEscape);
|
||||||
this.SecretMasker.AddValueEncoder(ValueEncoders.TrimDoubleQuotes);
|
|
||||||
|
|
||||||
// Create the trace manager.
|
// Create the trace manager.
|
||||||
if (string.IsNullOrEmpty(logFile))
|
if (string.IsNullOrEmpty(logFile))
|
||||||
@@ -614,8 +614,9 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
public static HttpClientHandler CreateHttpClientHandler(this IHostContext context)
|
public static HttpClientHandler CreateHttpClientHandler(this IHostContext context)
|
||||||
{
|
{
|
||||||
var handlerFactory = context.GetService<IHttpClientHandlerFactory>();
|
HttpClientHandler clientHandler = new HttpClientHandler();
|
||||||
return handlerFactory.CreateClientHandler(context.WebProxy);
|
clientHandler.Proxy = context.WebProxy;
|
||||||
|
return clientHandler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
using System.Net.Http;
|
|
||||||
using GitHub.Runner.Sdk;
|
|
||||||
|
|
||||||
namespace GitHub.Runner.Common
|
|
||||||
{
|
|
||||||
[ServiceLocator(Default = typeof(HttpClientHandlerFactory))]
|
|
||||||
public interface IHttpClientHandlerFactory : IRunnerService
|
|
||||||
{
|
|
||||||
HttpClientHandler CreateClientHandler(RunnerWebProxy webProxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class HttpClientHandlerFactory : RunnerService, IHttpClientHandlerFactory
|
|
||||||
{
|
|
||||||
public HttpClientHandler CreateClientHandler(RunnerWebProxy webProxy)
|
|
||||||
{
|
|
||||||
return new HttpClientHandler() { Proxy = webProxy };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||||
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
|
using GitHub.Runner.Common.Util;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using GitHub.Runner.Worker.Container;
|
using GitHub.Runner.Worker.Container;
|
||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants;
|
using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplating.PipelineTemplateConstants;
|
||||||
|
|
||||||
@@ -71,7 +73,13 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear the cache (for self-hosted runners)
|
// Clear the cache (for self-hosted runners)
|
||||||
IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken);
|
// Note, temporarily avoid this step for the on-premises product, to avoid rate limiting.
|
||||||
|
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
||||||
|
var isHostedServer = configurationStore.GetSettings().IsHostedServer;
|
||||||
|
if (isHostedServer)
|
||||||
|
{
|
||||||
|
IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var action in actions)
|
foreach (var action in actions)
|
||||||
{
|
{
|
||||||
@@ -482,7 +490,7 @@ namespace GitHub.Runner.Worker
|
|||||||
ArgUtil.NotNullOrEmpty(repositoryReference.Ref, nameof(repositoryReference.Ref));
|
ArgUtil.NotNullOrEmpty(repositoryReference.Ref, nameof(repositoryReference.Ref));
|
||||||
|
|
||||||
string destDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), repositoryReference.Name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), repositoryReference.Ref);
|
string destDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), repositoryReference.Name.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar), repositoryReference.Ref);
|
||||||
string watermarkFile = GetWatermarkFilePath(destDirectory);
|
string watermarkFile = destDirectory + ".completed";
|
||||||
if (File.Exists(watermarkFile))
|
if (File.Exists(watermarkFile))
|
||||||
{
|
{
|
||||||
executionContext.Debug($"Action '{repositoryReference.Name}@{repositoryReference.Ref}' already downloaded at '{destDirectory}'.");
|
executionContext.Debug($"Action '{repositoryReference.Name}@{repositoryReference.Ref}' already downloaded at '{destDirectory}'.");
|
||||||
@@ -496,84 +504,27 @@ namespace GitHub.Runner.Worker
|
|||||||
executionContext.Output($"Download action repository '{repositoryReference.Name}@{repositoryReference.Ref}'");
|
executionContext.Output($"Download action repository '{repositoryReference.Name}@{repositoryReference.Ref}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
|
||||||
var isHostedServer = configurationStore.GetSettings().IsHostedServer;
|
|
||||||
if (isHostedServer)
|
|
||||||
{
|
|
||||||
string apiUrl = GetApiUrl(executionContext);
|
|
||||||
string archiveLink = BuildLinkToActionArchive(apiUrl, repositoryReference.Name, repositoryReference.Ref);
|
|
||||||
Trace.Info($"Download archive '{archiveLink}' to '{destDirectory}'.");
|
|
||||||
await DownloadRepositoryActionAsync(executionContext, archiveLink, destDirectory);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string apiUrl = GetApiUrl(executionContext);
|
|
||||||
|
|
||||||
// URLs to try:
|
|
||||||
var archiveLinks = new List<string> {
|
|
||||||
// A built-in action or an action the user has created, on their GHES instance
|
|
||||||
// Example: https://my-ghes/api/v3/repos/my-org/my-action/tarball/v1
|
|
||||||
BuildLinkToActionArchive(apiUrl, repositoryReference.Name, repositoryReference.Ref),
|
|
||||||
|
|
||||||
// A community action, synced to their GHES instance
|
|
||||||
// Example: https://my-ghes/api/v3/repos/actions-community/some-org-some-action/tarball/v1
|
|
||||||
BuildLinkToActionArchive(apiUrl, $"actions-community/{repositoryReference.Name.Replace("/", "-")}", repositoryReference.Ref)
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var archiveLink in archiveLinks)
|
|
||||||
{
|
|
||||||
Trace.Info($"Download archive '{archiveLink}' to '{destDirectory}'.");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await DownloadRepositoryActionAsync(executionContext, archiveLink, destDirectory);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (ActionNotFoundException)
|
|
||||||
{
|
|
||||||
Trace.Info($"Failed to find the action '{repositoryReference.Name}' at ref '{repositoryReference.Ref}' at {archiveLink}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new ActionNotFoundException($"Failed to find the action '{repositoryReference.Name}' at ref '{repositoryReference.Ref}'. Paths attempted: {string.Join(", ", archiveLinks)}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetApiUrl(IExecutionContext executionContext)
|
|
||||||
{
|
|
||||||
string apiUrl = executionContext.GetGitHubContext("api_url");
|
|
||||||
if (!string.IsNullOrEmpty(apiUrl))
|
|
||||||
{
|
|
||||||
return apiUrl;
|
|
||||||
}
|
|
||||||
// Once the api_url is set for hosted, we can remove this fallback (it doesn't make sense for GHES)
|
|
||||||
return "https://api.github.com";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string BuildLinkToActionArchive(string apiUrl, string repository, string @ref)
|
|
||||||
{
|
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
return $"{apiUrl}/repos/{repository}/zipball/{@ref}";
|
string archiveLink = $"https://api.github.com/repos/{repositoryReference.Name}/zipball/{repositoryReference.Ref}";
|
||||||
#else
|
#else
|
||||||
return $"{apiUrl}/repos/{repository}/tarball/{@ref}";
|
string archiveLink = $"https://api.github.com/repos/{repositoryReference.Name}/tarball/{repositoryReference.Ref}";
|
||||||
#endif
|
#endif
|
||||||
}
|
Trace.Info($"Download archive '{archiveLink}' to '{destDirectory}'.");
|
||||||
|
|
||||||
private async Task DownloadRepositoryActionAsync(IExecutionContext executionContext, string link, string destDirectory)
|
|
||||||
{
|
|
||||||
//download and extract action in a temp folder and rename it on success
|
//download and extract action in a temp folder and rename it on success
|
||||||
string tempDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), "_temp_" + Guid.NewGuid());
|
string tempDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), "_temp_" + Guid.NewGuid());
|
||||||
Directory.CreateDirectory(tempDirectory);
|
Directory.CreateDirectory(tempDirectory);
|
||||||
|
|
||||||
|
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
string archiveFile = Path.Combine(tempDirectory, $"{Guid.NewGuid()}.zip");
|
string archiveFile = Path.Combine(tempDirectory, $"{Guid.NewGuid()}.zip");
|
||||||
#else
|
#else
|
||||||
string archiveFile = Path.Combine(tempDirectory, $"{Guid.NewGuid()}.tar.gz");
|
string archiveFile = Path.Combine(tempDirectory, $"{Guid.NewGuid()}.tar.gz");
|
||||||
#endif
|
#endif
|
||||||
|
Trace.Info($"Save archive '{archiveLink}' into {archiveFile}.");
|
||||||
Trace.Info($"Save archive '{link}' into {archiveFile}.");
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
int retryCount = 0;
|
int retryCount = 0;
|
||||||
|
|
||||||
// Allow up to 20 * 60s for any action to be downloaded from github graph.
|
// Allow up to 20 * 60s for any action to be downloaded from github graph.
|
||||||
@@ -590,76 +541,64 @@ namespace GitHub.Runner.Worker
|
|||||||
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
||||||
using (var httpClient = new HttpClient(httpClientHandler))
|
using (var httpClient = new HttpClient(httpClientHandler))
|
||||||
{
|
{
|
||||||
var authToken = Environment.GetEnvironmentVariable("_GITHUB_ACTION_TOKEN");
|
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
||||||
if (string.IsNullOrEmpty(authToken))
|
var isHostedServer = configurationStore.GetSettings().IsHostedServer;
|
||||||
|
if (isHostedServer)
|
||||||
{
|
{
|
||||||
// TODO: Deprecate the PREVIEW_ACTION_TOKEN
|
var authToken = Environment.GetEnvironmentVariable("_GITHUB_ACTION_TOKEN");
|
||||||
authToken = executionContext.Variables.Get("PREVIEW_ACTION_TOKEN");
|
if (string.IsNullOrEmpty(authToken))
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(authToken))
|
|
||||||
{
|
|
||||||
HostContext.SecretMasker.AddValue(authToken);
|
|
||||||
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"PAT:{authToken}"));
|
|
||||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var accessToken = executionContext.GetGitHubContext("token");
|
|
||||||
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"x-access-token:{accessToken}"));
|
|
||||||
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
|
||||||
using (var response = await httpClient.GetAsync(link))
|
|
||||||
{
|
|
||||||
if (response.IsSuccessStatusCode)
|
|
||||||
{
|
{
|
||||||
using (var result = await response.Content.ReadAsStreamAsync())
|
// TODO: Deprecate the PREVIEW_ACTION_TOKEN
|
||||||
{
|
authToken = executionContext.Variables.Get("PREVIEW_ACTION_TOKEN");
|
||||||
await result.CopyToAsync(fs, _defaultCopyBufferSize, actionDownloadCancellation.Token);
|
|
||||||
await fs.FlushAsync(actionDownloadCancellation.Token);
|
|
||||||
|
|
||||||
// download succeed, break out the retry loop.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (response.StatusCode == HttpStatusCode.NotFound)
|
|
||||||
|
if (!string.IsNullOrEmpty(authToken))
|
||||||
{
|
{
|
||||||
// It doesn't make sense to retry in this case, so just stop
|
HostContext.SecretMasker.AddValue(authToken);
|
||||||
throw new ActionNotFoundException(new Uri(link));
|
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"PAT:{authToken}"));
|
||||||
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Something else bad happened, let's go to our retry logic
|
var accessToken = executionContext.GetGitHubContext("token");
|
||||||
response.EnsureSuccessStatusCode();
|
var base64EncodingToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"x-access-token:{accessToken}"));
|
||||||
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodingToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Intentionally empty. Temporary for GHES alpha release, download from dotcom unauthenticated.
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
||||||
|
using (var result = await httpClient.GetStreamAsync(archiveLink))
|
||||||
|
{
|
||||||
|
await result.CopyToAsync(fs, _defaultCopyBufferSize, actionDownloadCancellation.Token);
|
||||||
|
await fs.FlushAsync(actionDownloadCancellation.Token);
|
||||||
|
|
||||||
|
// download succeed, break out the retry loop.
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) when (executionContext.CancellationToken.IsCancellationRequested)
|
catch (OperationCanceledException) when (executionContext.CancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
Trace.Info("Action download has been cancelled.");
|
Trace.Info($"Action download has been cancelled.");
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (ActionNotFoundException)
|
|
||||||
{
|
|
||||||
Trace.Info($"The action at '{link}' does not exist");
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (retryCount < 2)
|
catch (Exception ex) when (retryCount < 2)
|
||||||
{
|
{
|
||||||
retryCount++;
|
retryCount++;
|
||||||
Trace.Error($"Fail to download archive '{link}' -- Attempt: {retryCount}");
|
Trace.Error($"Fail to download archive '{archiveLink}' -- Attempt: {retryCount}");
|
||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
if (actionDownloadTimeout.Token.IsCancellationRequested)
|
if (actionDownloadTimeout.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
// action download didn't finish within timeout
|
// action download didn't finish within timeout
|
||||||
executionContext.Warning($"Action '{link}' didn't finish download within {timeoutSeconds} seconds.");
|
executionContext.Warning($"Action '{archiveLink}' didn't finish download within {timeoutSeconds} seconds.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
executionContext.Warning($"Failed to download action '{link}'. Error: {ex.Message}");
|
executionContext.Warning($"Failed to download action '{archiveLink}'. Error {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -673,7 +612,7 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
ArgUtil.NotNullOrEmpty(archiveFile, nameof(archiveFile));
|
ArgUtil.NotNullOrEmpty(archiveFile, nameof(archiveFile));
|
||||||
executionContext.Debug($"Download '{link}' to '{archiveFile}'");
|
executionContext.Debug($"Download '{archiveLink}' to '{archiveFile}'");
|
||||||
|
|
||||||
var stagingDirectory = Path.Combine(tempDirectory, "_staging");
|
var stagingDirectory = Path.Combine(tempDirectory, "_staging");
|
||||||
Directory.CreateDirectory(stagingDirectory);
|
Directory.CreateDirectory(stagingDirectory);
|
||||||
@@ -723,7 +662,6 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
Trace.Verbose("Create watermark file indicate action download succeed.");
|
Trace.Verbose("Create watermark file indicate action download succeed.");
|
||||||
string watermarkFile = GetWatermarkFilePath(destDirectory);
|
|
||||||
File.WriteAllText(watermarkFile, DateTime.UtcNow.ToString());
|
File.WriteAllText(watermarkFile, DateTime.UtcNow.ToString());
|
||||||
|
|
||||||
executionContext.Debug($"Archive '{archiveFile}' has been unzipped into '{destDirectory}'.");
|
executionContext.Debug($"Archive '{archiveFile}' has been unzipped into '{destDirectory}'.");
|
||||||
@@ -748,8 +686,6 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetWatermarkFilePath(string directory) => directory + ".completed";
|
|
||||||
|
|
||||||
private ActionContainer PrepareRepositoryActionAsync(IExecutionContext executionContext, Pipelines.ActionStep repositoryAction)
|
private ActionContainer PrepareRepositoryActionAsync(IExecutionContext executionContext, Pipelines.ActionStep repositoryAction)
|
||||||
{
|
{
|
||||||
var repositoryReference = repositoryAction.Reference as Pipelines.RepositoryPathReference;
|
var repositoryReference = repositoryAction.Reference as Pipelines.RepositoryPathReference;
|
||||||
@@ -995,3 +931,4 @@ namespace GitHub.Runner.Worker
|
|||||||
public string ActionRepository { get; set; }
|
public string ActionRepository { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker
|
|
||||||
{
|
|
||||||
public class ActionNotFoundException : Exception
|
|
||||||
{
|
|
||||||
public ActionNotFoundException(Uri actionUri)
|
|
||||||
: base(FormatMessage(actionUri))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionNotFoundException(string message)
|
|
||||||
: base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionNotFoundException(string message, System.Exception inner)
|
|
||||||
: base(message, inner)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ActionNotFoundException(SerializationInfo info, StreamingContext context)
|
|
||||||
: base(info, context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string FormatMessage(Uri actionUri)
|
|
||||||
{
|
|
||||||
return $"An action could not be found at the URI '{actionUri}'";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -143,10 +143,8 @@ namespace GitHub.Runner.Worker
|
|||||||
var templateEvaluator = ExecutionContext.ToPipelineTemplateEvaluator();
|
var templateEvaluator = ExecutionContext.ToPipelineTemplateEvaluator();
|
||||||
var inputs = templateEvaluator.EvaluateStepInputs(Action.Inputs, ExecutionContext.ExpressionValues, ExecutionContext.ExpressionFunctions);
|
var inputs = templateEvaluator.EvaluateStepInputs(Action.Inputs, ExecutionContext.ExpressionValues, ExecutionContext.ExpressionFunctions);
|
||||||
|
|
||||||
var userInputs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (KeyValuePair<string, string> input in inputs)
|
foreach (KeyValuePair<string, string> input in inputs)
|
||||||
{
|
{
|
||||||
userInputs.Add(input.Key);
|
|
||||||
string message = "";
|
string message = "";
|
||||||
if (definition.Data?.Deprecated?.TryGetValue(input.Key, out message) == true)
|
if (definition.Data?.Deprecated?.TryGetValue(input.Key, out message) == true)
|
||||||
{
|
{
|
||||||
@@ -154,15 +152,13 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var validInputs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
// Merge the default inputs from the definition
|
// Merge the default inputs from the definition
|
||||||
if (definition.Data?.Inputs != null)
|
if (definition.Data?.Inputs != null)
|
||||||
{
|
{
|
||||||
var manifestManager = HostContext.GetService<IActionManifestManager>();
|
var manifestManager = HostContext.GetService<IActionManifestManager>();
|
||||||
foreach (var input in definition.Data.Inputs)
|
foreach (var input in (definition.Data?.Inputs))
|
||||||
{
|
{
|
||||||
string key = input.Key.AssertString("action input name").Value;
|
string key = input.Key.AssertString("action input name").Value;
|
||||||
validInputs.Add(key);
|
|
||||||
if (!inputs.ContainsKey(key))
|
if (!inputs.ContainsKey(key))
|
||||||
{
|
{
|
||||||
inputs[key] = manifestManager.EvaluateDefaultInput(ExecutionContext, key, input.Value);
|
inputs[key] = manifestManager.EvaluateDefaultInput(ExecutionContext, key, input.Value);
|
||||||
@@ -170,14 +166,6 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var input in userInputs)
|
|
||||||
{
|
|
||||||
if (!validInputs.Contains(input))
|
|
||||||
{
|
|
||||||
ExecutionContext.Warning($"Unexpected input '{input}', valid inputs are ['{string.Join("', '", validInputs)}']");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the action environment.
|
// Load the action environment.
|
||||||
ExecutionContext.Debug("Loading env");
|
ExecutionContext.Debug("Loading env");
|
||||||
var environment = new Dictionary<String, String>(VarUtil.EnvironmentVariableKeyComparer);
|
var environment = new Dictionary<String, String>(VarUtil.EnvironmentVariableKeyComparer);
|
||||||
|
|||||||
@@ -149,14 +149,14 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
throw new NotSupportedException(msg);
|
throw new NotSupportedException(msg);
|
||||||
}
|
}
|
||||||
nodeExternal = "node12_alpine";
|
nodeExternal = "node12_alpine";
|
||||||
executionContext.Debug($"Container distribution is alpine. Running JavaScript Action with external tool: {nodeExternal}");
|
executionContext.Output($"Container distribution is alpine. Running JavaScript Action with external tool: {nodeExternal}");
|
||||||
return nodeExternal;
|
return nodeExternal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Optimistically use the default
|
// Optimistically use the default
|
||||||
nodeExternal = "node12";
|
nodeExternal = "node12";
|
||||||
executionContext.Debug($"Running JavaScript Action with default external tool: {nodeExternal}");
|
executionContext.Output($"Running JavaScript Action with default external tool: {nodeExternal}");
|
||||||
return nodeExternal;
|
return nodeExternal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,20 +60,6 @@ namespace GitHub.DistributedTask.Logging
|
|||||||
return SecurityElement.Escape(value);
|
return SecurityElement.Escape(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String TrimDoubleQuotes(String value)
|
|
||||||
{
|
|
||||||
var trimmed = string.Empty;
|
|
||||||
if (!string.IsNullOrEmpty(value) &&
|
|
||||||
value.Length > 8 &&
|
|
||||||
value.StartsWith('"') &&
|
|
||||||
value.EndsWith('"'))
|
|
||||||
{
|
|
||||||
trimmed = value.Substring(1, value.Length - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return trimmed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string Base64StringEscapeShift(String value, int shift)
|
private static string Base64StringEscapeShift(String value, int shift)
|
||||||
{
|
{
|
||||||
var bytes = Encoding.UTF8.GetBytes(value);
|
var bytes = Encoding.UTF8.GetBytes(value);
|
||||||
|
|||||||
@@ -288,6 +288,12 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere
|
||||||
|
public void SetJobSidecarContainers(IDictionary<String, String> value)
|
||||||
|
{
|
||||||
|
m_jobSidecarContainers = value;
|
||||||
|
}
|
||||||
|
|
||||||
public TaskAgentMessage GetAgentMessage()
|
public TaskAgentMessage GetAgentMessage()
|
||||||
{
|
{
|
||||||
var body = JsonUtility.ToString(this);
|
var body = JsonUtility.ToString(this);
|
||||||
@@ -299,6 +305,89 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere
|
||||||
|
internal static TemplateToken ConvertToTemplateToken(ContainerResource resource)
|
||||||
|
{
|
||||||
|
var result = new MappingToken(null, null, null);
|
||||||
|
|
||||||
|
var image = resource.Image;
|
||||||
|
if (!string.IsNullOrEmpty(image))
|
||||||
|
{
|
||||||
|
result.Add(new StringToken(null, null, null, "image"), new StringToken(null, null, null, image));
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = resource.Options;
|
||||||
|
if (!string.IsNullOrEmpty(options))
|
||||||
|
{
|
||||||
|
result.Add(new StringToken(null, null, null, "options"), new StringToken(null, null, null, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
var environment = resource.Environment;
|
||||||
|
if (environment?.Count > 0)
|
||||||
|
{
|
||||||
|
var mapping = new MappingToken(null, null, null);
|
||||||
|
foreach (var pair in environment)
|
||||||
|
{
|
||||||
|
mapping.Add(new StringToken(null, null, null, pair.Key), new StringToken(null, null, null, pair.Value));
|
||||||
|
}
|
||||||
|
result.Add(new StringToken(null, null, null, "env"), mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ports = resource.Ports;
|
||||||
|
if (ports?.Count > 0)
|
||||||
|
{
|
||||||
|
var sequence = new SequenceToken(null, null, null);
|
||||||
|
foreach (var item in ports)
|
||||||
|
{
|
||||||
|
sequence.Add(new StringToken(null, null, null, item));
|
||||||
|
}
|
||||||
|
result.Add(new StringToken(null, null, null, "ports"), sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
var volumes = resource.Volumes;
|
||||||
|
if (volumes?.Count > 0)
|
||||||
|
{
|
||||||
|
var sequence = new SequenceToken(null, null, null);
|
||||||
|
foreach (var item in volumes)
|
||||||
|
{
|
||||||
|
sequence.Add(new StringToken(null, null, null, item));
|
||||||
|
}
|
||||||
|
result.Add(new StringToken(null, null, null, "volumes"), sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[OnDeserialized]
|
||||||
|
private void OnDeserialized(StreamingContext context)
|
||||||
|
{
|
||||||
|
// todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere
|
||||||
|
if (JobContainer is StringToken jobContainerStringToken)
|
||||||
|
{
|
||||||
|
var resourceAlias = jobContainerStringToken.Value;
|
||||||
|
var resource = Resources?.Containers.SingleOrDefault(x => string.Equals(x.Alias, resourceAlias, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (resource != null)
|
||||||
|
{
|
||||||
|
JobContainer = ConvertToTemplateToken(resource);
|
||||||
|
m_jobContainerResourceAlias = resourceAlias;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere
|
||||||
|
if (m_jobSidecarContainers?.Count > 0 && (JobServiceContainers == null || JobServiceContainers.Type == TokenType.Null))
|
||||||
|
{
|
||||||
|
var services = new MappingToken(null, null, null);
|
||||||
|
foreach (var pair in m_jobSidecarContainers)
|
||||||
|
{
|
||||||
|
var networkAlias = pair.Key;
|
||||||
|
var serviceResourceAlias = pair.Value;
|
||||||
|
var serviceResource = Resources.Containers.Single(x => string.Equals(x.Alias, serviceResourceAlias, StringComparison.OrdinalIgnoreCase));
|
||||||
|
services.Add(new StringToken(null, null, null, networkAlias), ConvertToTemplateToken(serviceResource));
|
||||||
|
}
|
||||||
|
JobServiceContainers = services;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[OnSerializing]
|
[OnSerializing]
|
||||||
private void OnSerializing(StreamingContext context)
|
private void OnSerializing(StreamingContext context)
|
||||||
{
|
{
|
||||||
@@ -335,6 +424,12 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
{
|
{
|
||||||
m_variables = null;
|
m_variables = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere
|
||||||
|
if (!string.IsNullOrEmpty(m_jobContainerResourceAlias))
|
||||||
|
{
|
||||||
|
JobContainer = new StringToken(null, null, null, m_jobContainerResourceAlias);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataMember(Name = "EnvironmentVariables", EmitDefaultValue = false)]
|
[DataMember(Name = "EnvironmentVariables", EmitDefaultValue = false)]
|
||||||
@@ -357,5 +452,13 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
|
|
||||||
[DataMember(Name = "Variables", EmitDefaultValue = false)]
|
[DataMember(Name = "Variables", EmitDefaultValue = false)]
|
||||||
private IDictionary<String, VariableValue> m_variables;
|
private IDictionary<String, VariableValue> m_variables;
|
||||||
|
|
||||||
|
// todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere
|
||||||
|
[DataMember(Name = "JobSidecarContainers", EmitDefaultValue = false)]
|
||||||
|
private IDictionary<String, String> m_jobSidecarContainers;
|
||||||
|
|
||||||
|
// todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere
|
||||||
|
[IgnoreDataMember]
|
||||||
|
private string m_jobContainerResourceAlias;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,8 +85,6 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
_hc.SecretMasker.AddValue("Pass word 123!");
|
_hc.SecretMasker.AddValue("Pass word 123!");
|
||||||
_hc.SecretMasker.AddValue("Pass<word>123!");
|
_hc.SecretMasker.AddValue("Pass<word>123!");
|
||||||
_hc.SecretMasker.AddValue("Pass'word'123!");
|
_hc.SecretMasker.AddValue("Pass'word'123!");
|
||||||
_hc.SecretMasker.AddValue("\"Password123!!\"");
|
|
||||||
_hc.SecretMasker.AddValue("\"short\"");
|
|
||||||
|
|
||||||
// Assert.
|
// Assert.
|
||||||
Assert.Equal("123***123", _hc.SecretMasker.MaskSecrets("123Password123!123"));
|
Assert.Equal("123***123", _hc.SecretMasker.MaskSecrets("123Password123!123"));
|
||||||
@@ -101,9 +99,6 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
Assert.Equal("YWJjOlBh***", _hc.SecretMasker.MaskSecrets(Convert.ToBase64String(Encoding.UTF8.GetBytes($"abc:Password123!"))));
|
Assert.Equal("YWJjOlBh***", _hc.SecretMasker.MaskSecrets(Convert.ToBase64String(Encoding.UTF8.GetBytes($"abc:Password123!"))));
|
||||||
Assert.Equal("YWJjZDpQ***", _hc.SecretMasker.MaskSecrets(Convert.ToBase64String(Encoding.UTF8.GetBytes($"abcd:Password123!"))));
|
Assert.Equal("YWJjZDpQ***", _hc.SecretMasker.MaskSecrets(Convert.ToBase64String(Encoding.UTF8.GetBytes($"abcd:Password123!"))));
|
||||||
Assert.Equal("YWJjZGU6***", _hc.SecretMasker.MaskSecrets(Convert.ToBase64String(Encoding.UTF8.GetBytes($"abcde:Password123!"))));
|
Assert.Equal("YWJjZGU6***", _hc.SecretMasker.MaskSecrets(Convert.ToBase64String(Encoding.UTF8.GetBytes($"abcde:Password123!"))));
|
||||||
Assert.Equal("123***123", _hc.SecretMasker.MaskSecrets("123Password123!!123"));
|
|
||||||
Assert.Equal("123short123", _hc.SecretMasker.MaskSecrets("123short123"));
|
|
||||||
Assert.Equal("123***123", _hc.SecretMasker.MaskSecrets("123\"short\"123"));
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,9 +16,7 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
private static readonly List<string> SkippedFiles = new List<string>()
|
private static readonly List<string> SkippedFiles = new List<string>()
|
||||||
{
|
{
|
||||||
"Runner.Common\\HostContext.cs",
|
"Runner.Common\\HostContext.cs",
|
||||||
"Runner.Common/HostContext.cs",
|
"Runner.Common/HostContext.cs"
|
||||||
"Runner.Common\\HttpClientHandlerFactory.cs",
|
|
||||||
"Runner.Common/HttpClientHandlerFactory.cs"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
using System;
|
using GitHub.DistributedTask.Expressions2;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using GitHub.DistributedTask.Expressions2;
|
|
||||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
using GitHub.DistributedTask.Pipelines.ContextData;
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Common.Util;
|
||||||
using GitHub.Runner.Worker;
|
using GitHub.Runner.Worker;
|
||||||
using GitHub.Runner.Worker.Container;
|
using GitHub.Runner.Worker.Container;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Moq.Protected;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
|
|
||||||
@@ -116,175 +114,47 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
[Fact]
|
[Fact]
|
||||||
[Trait("Level", "L0")]
|
[Trait("Level", "L0")]
|
||||||
[Trait("Category", "Worker")]
|
[Trait("Category", "Worker")]
|
||||||
public async void PrepareActions_DownloadBuiltInActionFromGraph_OnPremises()
|
public async void PrepareActions_SkipDownloadActionFromGraphWhenCached_OnPremises()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Setup();
|
Setup();
|
||||||
const string ActionName = "actions/sample-action";
|
var actionId = Guid.NewGuid();
|
||||||
var actions = new List<Pipelines.ActionStep>
|
var actions = new List<Pipelines.ActionStep>
|
||||||
{
|
{
|
||||||
new Pipelines.ActionStep()
|
new Pipelines.ActionStep()
|
||||||
{
|
{
|
||||||
Name = "action",
|
Name = "action",
|
||||||
Id = Guid.NewGuid(),
|
Id = actionId,
|
||||||
Reference = new Pipelines.RepositoryPathReference()
|
Reference = new Pipelines.RepositoryPathReference()
|
||||||
{
|
{
|
||||||
Name = ActionName,
|
Name = "actions/no-such-action",
|
||||||
Ref = "master",
|
Ref = "master",
|
||||||
RepositoryType = "GitHub"
|
RepositoryType = "GitHub"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return a valid action from GHES via mock
|
|
||||||
const string ApiUrl = "https://ghes.example.com/api/v3";
|
|
||||||
string expectedArchiveLink = GetLinkToActionArchive(ApiUrl, ActionName, "master");
|
|
||||||
string archiveFile = await CreateRepoArchive();
|
|
||||||
using var stream = File.OpenRead(archiveFile);
|
|
||||||
var mockClientHandler = new Mock<HttpClientHandler>();
|
|
||||||
mockClientHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.Is<HttpRequestMessage>(m => m.RequestUri == new Uri(expectedArchiveLink)), ItExpr.IsAny<CancellationToken>())
|
|
||||||
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StreamContent(stream) });
|
|
||||||
|
|
||||||
var mockHandlerFactory = new Mock<IHttpClientHandlerFactory>();
|
|
||||||
mockHandlerFactory.Setup(p => p.CreateClientHandler(It.IsAny<RunnerWebProxy>())).Returns(mockClientHandler.Object);
|
|
||||||
_hc.SetSingleton(mockHandlerFactory.Object);
|
|
||||||
|
|
||||||
_ec.Setup(x => x.GetGitHubContext("api_url")).Returns(ApiUrl);
|
|
||||||
_configurationStore.Object.GetSettings().IsHostedServer = false;
|
_configurationStore.Object.GetSettings().IsHostedServer = false;
|
||||||
|
var actionDirectory = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "actions/no-such-action", "master");
|
||||||
|
Directory.CreateDirectory(actionDirectory);
|
||||||
|
var watermarkFile = $"{actionDirectory}.completed";
|
||||||
|
File.WriteAllText(watermarkFile, DateTime.UtcNow.ToString());
|
||||||
|
var actionFile = Path.Combine(actionDirectory, "action.yml");
|
||||||
|
File.WriteAllText(actionFile, @"
|
||||||
|
name: ""no-such-action""
|
||||||
|
runs:
|
||||||
|
using: node12
|
||||||
|
main: no-such-action.js
|
||||||
|
");
|
||||||
|
var testFile = Path.Combine(actionDirectory, "test-file");
|
||||||
|
File.WriteAllText(testFile, "asdf");
|
||||||
|
|
||||||
//Act
|
// Act
|
||||||
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||||
|
|
||||||
//Assert
|
// Assert
|
||||||
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master.completed");
|
Assert.True(File.Exists(testFile));
|
||||||
Assert.True(File.Exists(watermarkFile));
|
|
||||||
|
|
||||||
var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master", "action.yml");
|
|
||||||
Assert.True(File.Exists(actionYamlFile));
|
|
||||||
_hc.GetTrace().Info(File.ReadAllText(actionYamlFile));
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Teardown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Worker")]
|
|
||||||
public async void PrepareActions_DownloadCommunityActionFromGraph_OnPremises()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
Setup();
|
|
||||||
const string ActionName = "ownerName/sample-action";
|
|
||||||
const string MungedActionName = "actions-community/ownerName-sample-action";
|
|
||||||
var actions = new List<Pipelines.ActionStep>
|
|
||||||
{
|
|
||||||
new Pipelines.ActionStep()
|
|
||||||
{
|
|
||||||
Name = "action",
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Reference = new Pipelines.RepositoryPathReference()
|
|
||||||
{
|
|
||||||
Name = ActionName,
|
|
||||||
Ref = "master",
|
|
||||||
RepositoryType = "GitHub"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return a valid action from GHES via mock
|
|
||||||
const string ApiUrl = "https://ghes.example.com/api/v3";
|
|
||||||
string builtInArchiveLink = GetLinkToActionArchive(ApiUrl, ActionName, "master");
|
|
||||||
string mungedArchiveLink = GetLinkToActionArchive(ApiUrl, MungedActionName, "master");
|
|
||||||
string archiveFile = await CreateRepoArchive();
|
|
||||||
using var stream = File.OpenRead(archiveFile);
|
|
||||||
var mockClientHandler = new Mock<HttpClientHandler>();
|
|
||||||
mockClientHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.Is<HttpRequestMessage>(m => m.RequestUri == new Uri(builtInArchiveLink)), ItExpr.IsAny<CancellationToken>())
|
|
||||||
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.NotFound));
|
|
||||||
mockClientHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.Is<HttpRequestMessage>(m => m.RequestUri == new Uri(mungedArchiveLink)), ItExpr.IsAny<CancellationToken>())
|
|
||||||
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StreamContent(stream) });
|
|
||||||
|
|
||||||
var mockHandlerFactory = new Mock<IHttpClientHandlerFactory>();
|
|
||||||
mockHandlerFactory.Setup(p => p.CreateClientHandler(It.IsAny<RunnerWebProxy>())).Returns(mockClientHandler.Object);
|
|
||||||
_hc.SetSingleton(mockHandlerFactory.Object);
|
|
||||||
|
|
||||||
_ec.Setup(x => x.GetGitHubContext("api_url")).Returns(ApiUrl);
|
|
||||||
_configurationStore.Object.GetSettings().IsHostedServer = false;
|
|
||||||
|
|
||||||
//Act
|
|
||||||
await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master.completed");
|
|
||||||
Assert.True(File.Exists(watermarkFile));
|
|
||||||
|
|
||||||
var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master", "action.yml");
|
|
||||||
Assert.True(File.Exists(actionYamlFile));
|
|
||||||
_hc.GetTrace().Info(File.ReadAllText(actionYamlFile));
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Teardown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Worker")]
|
|
||||||
public async void PrepareActions_DownloadUnknownActionFromGraph_OnPremises()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
Setup();
|
|
||||||
const string ActionName = "ownerName/sample-action";
|
|
||||||
var actions = new List<Pipelines.ActionStep>
|
|
||||||
{
|
|
||||||
new Pipelines.ActionStep()
|
|
||||||
{
|
|
||||||
Name = "action",
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
Reference = new Pipelines.RepositoryPathReference()
|
|
||||||
{
|
|
||||||
Name = ActionName,
|
|
||||||
Ref = "master",
|
|
||||||
RepositoryType = "GitHub"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return a valid action from GHES via mock
|
|
||||||
const string ApiUrl = "https://ghes.example.com/api/v3";
|
|
||||||
string archiveLink = GetLinkToActionArchive(ApiUrl, ActionName, "master");
|
|
||||||
string archiveFile = await CreateRepoArchive();
|
|
||||||
using var stream = File.OpenRead(archiveFile);
|
|
||||||
var mockClientHandler = new Mock<HttpClientHandler>();
|
|
||||||
mockClientHandler.Protected().Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
|
|
||||||
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.NotFound));
|
|
||||||
|
|
||||||
var mockHandlerFactory = new Mock<IHttpClientHandlerFactory>();
|
|
||||||
mockHandlerFactory.Setup(p => p.CreateClientHandler(It.IsAny<RunnerWebProxy>())).Returns(mockClientHandler.Object);
|
|
||||||
_hc.SetSingleton(mockHandlerFactory.Object);
|
|
||||||
|
|
||||||
_ec.Setup(x => x.GetGitHubContext("api_url")).Returns(ApiUrl);
|
|
||||||
_configurationStore.Object.GetSettings().IsHostedServer = false;
|
|
||||||
|
|
||||||
//Act
|
|
||||||
Func<Task> action = async () => await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
await Assert.ThrowsAsync<ActionNotFoundException>(action);
|
|
||||||
|
|
||||||
var watermarkFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master.completed");
|
|
||||||
Assert.False(File.Exists(watermarkFile));
|
|
||||||
|
|
||||||
var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "master", "action.yml");
|
|
||||||
Assert.False(File.Exists(actionYamlFile));
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -992,7 +862,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
name: 'Hello World'
|
name: 'Hello World'
|
||||||
description: 'Greet the world and record the time'
|
description: 'Greet the world and record the time'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
greeting: # id of input
|
greeting: # id of input
|
||||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||||
required: true
|
required: true
|
||||||
@@ -1092,7 +962,7 @@ runs:
|
|||||||
name: 'Hello World'
|
name: 'Hello World'
|
||||||
description: 'Greet the world and record the time'
|
description: 'Greet the world and record the time'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
greeting: # id of input
|
greeting: # id of input
|
||||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||||
required: true
|
required: true
|
||||||
@@ -1191,7 +1061,7 @@ runs:
|
|||||||
name: 'Hello World'
|
name: 'Hello World'
|
||||||
description: 'Greet the world and record the time'
|
description: 'Greet the world and record the time'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
greeting: # id of input
|
greeting: # id of input
|
||||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||||
required: true
|
required: true
|
||||||
@@ -1259,7 +1129,7 @@ runs:
|
|||||||
name: 'Hello World'
|
name: 'Hello World'
|
||||||
description: 'Greet the world and record the time'
|
description: 'Greet the world and record the time'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
greeting: # id of input
|
greeting: # id of input
|
||||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||||
required: true
|
required: true
|
||||||
@@ -1341,7 +1211,7 @@ runs:
|
|||||||
name: 'Hello World'
|
name: 'Hello World'
|
||||||
description: 'Greet the world and record the time'
|
description: 'Greet the world and record the time'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
greeting: # id of input
|
greeting: # id of input
|
||||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||||
required: true
|
required: true
|
||||||
@@ -1440,7 +1310,7 @@ runs:
|
|||||||
name: 'Hello World'
|
name: 'Hello World'
|
||||||
description: 'Greet the world and record the time'
|
description: 'Greet the world and record the time'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
greeting: # id of input
|
greeting: # id of input
|
||||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||||
required: true
|
required: true
|
||||||
@@ -1538,7 +1408,7 @@ runs:
|
|||||||
name: 'Hello World'
|
name: 'Hello World'
|
||||||
description: 'Greet the world and record the time'
|
description: 'Greet the world and record the time'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
greeting: # id of input
|
greeting: # id of input
|
||||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||||
required: true
|
required: true
|
||||||
@@ -1606,7 +1476,7 @@ runs:
|
|||||||
name: 'Hello World'
|
name: 'Hello World'
|
||||||
description: 'Greet the world and record the time'
|
description: 'Greet the world and record the time'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
greeting: # id of input
|
greeting: # id of input
|
||||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||||
required: true
|
required: true
|
||||||
@@ -1677,7 +1547,7 @@ runs:
|
|||||||
name: 'Hello World'
|
name: 'Hello World'
|
||||||
description: 'Greet the world and record the time'
|
description: 'Greet the world and record the time'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
greeting: # id of input
|
greeting: # id of input
|
||||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||||
required: true
|
required: true
|
||||||
@@ -1777,7 +1647,7 @@ runs:
|
|||||||
name: 'Hello World'
|
name: 'Hello World'
|
||||||
description: 'Greet the world and record the time'
|
description: 'Greet the world and record the time'
|
||||||
author: 'Test Corporation'
|
author: 'Test Corporation'
|
||||||
inputs:
|
inputs:
|
||||||
greeting: # id of input
|
greeting: # id of input
|
||||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||||
required: true
|
required: true
|
||||||
@@ -1867,82 +1737,6 @@ runs:
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a sample action in an archive on disk, similar to the archive
|
|
||||||
/// retrieved from GitHub's or GHES' repository API.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The path on disk to the archive.</returns>
|
|
||||||
#if OS_WINDOWS
|
|
||||||
private Task<string> CreateRepoArchive()
|
|
||||||
#else
|
|
||||||
private async Task<string> CreateRepoArchive()
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
const string Content = @"
|
|
||||||
# Container action
|
|
||||||
name: 'Hello World'
|
|
||||||
description: 'Greet the world'
|
|
||||||
author: 'GitHub'
|
|
||||||
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
|
|
||||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
|
||||||
runs:
|
|
||||||
using: 'node12'
|
|
||||||
main: 'task.js'
|
|
||||||
";
|
|
||||||
CreateAction(yamlContent: Content, instance: out _, directory: out string directory);
|
|
||||||
|
|
||||||
var tempDir = _hc.GetDirectory(WellKnownDirectory.Temp);
|
|
||||||
Directory.CreateDirectory(tempDir);
|
|
||||||
var archiveFile = Path.Combine(tempDir, Path.GetRandomFileName());
|
|
||||||
var trace = _hc.GetTrace();
|
|
||||||
|
|
||||||
#if OS_WINDOWS
|
|
||||||
ZipFile.CreateFromDirectory(directory, archiveFile, CompressionLevel.Fastest, includeBaseDirectory: true);
|
|
||||||
return Task.FromResult(archiveFile);
|
|
||||||
#else
|
|
||||||
string tar = WhichUtil.Which("tar", require: true, trace: trace);
|
|
||||||
|
|
||||||
// tar -xzf
|
|
||||||
using (var processInvoker = new ProcessInvokerWrapper())
|
|
||||||
{
|
|
||||||
processInvoker.Initialize(_hc);
|
|
||||||
processInvoker.OutputDataReceived += new EventHandler<ProcessDataReceivedEventArgs>((sender, args) =>
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(args.Data))
|
|
||||||
{
|
|
||||||
trace.Info(args.Data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
processInvoker.ErrorDataReceived += new EventHandler<ProcessDataReceivedEventArgs>((sender, args) =>
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(args.Data))
|
|
||||||
{
|
|
||||||
trace.Error(args.Data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
string cwd = Path.GetDirectoryName(directory);
|
|
||||||
string inputDirectory = Path.GetFileName(directory);
|
|
||||||
int exitCode = await processInvoker.ExecuteAsync(_hc.GetDirectory(WellKnownDirectory.Bin), tar, $"-czf \"{archiveFile}\" -C \"{cwd}\" \"{inputDirectory}\"", null, CancellationToken.None);
|
|
||||||
if (exitCode != 0)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException($"Can't use 'tar -czf' to create archive file: {archiveFile}. return code: {exitCode}.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return archiveFile;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetLinkToActionArchive(string apiUrl, string repository, string @ref)
|
|
||||||
{
|
|
||||||
#if OS_WINDOWS
|
|
||||||
return $"{apiUrl}/repos/{repository}/zipball/{@ref}";
|
|
||||||
#else
|
|
||||||
return $"{apiUrl}/repos/{repository}/tarball/{@ref}";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Setup([CallerMemberName] string name = "")
|
private void Setup([CallerMemberName] string name = "")
|
||||||
{
|
{
|
||||||
_ecTokenSource?.Dispose();
|
_ecTokenSource?.Dispose();
|
||||||
@@ -1978,7 +1772,6 @@ runs:
|
|||||||
_hc.SetSingleton<IDockerCommandManager>(_dockerManager.Object);
|
_hc.SetSingleton<IDockerCommandManager>(_dockerManager.Object);
|
||||||
_hc.SetSingleton<IRunnerPluginManager>(_pluginManager.Object);
|
_hc.SetSingleton<IRunnerPluginManager>(_pluginManager.Object);
|
||||||
_hc.SetSingleton<IActionManifestManager>(actionManifest);
|
_hc.SetSingleton<IActionManifestManager>(actionManifest);
|
||||||
_hc.SetSingleton<IHttpClientHandlerFactory>(new HttpClientHandlerFactory());
|
|
||||||
|
|
||||||
_configurationStore = new Mock<IConfigurationStore>();
|
_configurationStore = new Mock<IConfigurationStore>();
|
||||||
_configurationStore
|
_configurationStore
|
||||||
|
|||||||
@@ -278,59 +278,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
Assert.Equal("${{ matrix.node }}", _actionRunner.DisplayName);
|
Assert.Equal("${{ matrix.node }}", _actionRunner.DisplayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
[Trait("Level", "L0")]
|
|
||||||
[Trait("Category", "Worker")]
|
|
||||||
public async void WarnInvalidInputs()
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
Setup();
|
|
||||||
var actionId = Guid.NewGuid();
|
|
||||||
var actionInputs = new MappingToken(null, null, null);
|
|
||||||
actionInputs.Add(new StringToken(null, null, null, "input1"), new StringToken(null, null, null, "test1"));
|
|
||||||
actionInputs.Add(new StringToken(null, null, null, "input2"), new StringToken(null, null, null, "test2"));
|
|
||||||
actionInputs.Add(new StringToken(null, null, null, "invalid1"), new StringToken(null, null, null, "invalid1"));
|
|
||||||
actionInputs.Add(new StringToken(null, null, null, "invalid2"), new StringToken(null, null, null, "invalid2"));
|
|
||||||
var action = new Pipelines.ActionStep()
|
|
||||||
{
|
|
||||||
Name = "action",
|
|
||||||
Id = actionId,
|
|
||||||
Reference = new Pipelines.ContainerRegistryReference()
|
|
||||||
{
|
|
||||||
Image = "ubuntu:16.04"
|
|
||||||
},
|
|
||||||
Inputs = actionInputs
|
|
||||||
};
|
|
||||||
|
|
||||||
_actionRunner.Action = action;
|
|
||||||
|
|
||||||
Dictionary<string, string> finialInputs = new Dictionary<string, string>();
|
|
||||||
_handlerFactory.Setup(x => x.Create(It.IsAny<IExecutionContext>(), It.IsAny<ActionStepDefinitionReference>(), It.IsAny<IStepHost>(), It.IsAny<ActionExecutionData>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<Variables>(), It.IsAny<string>()))
|
|
||||||
.Callback((IExecutionContext executionContext, Pipelines.ActionStepDefinitionReference actionReference, IStepHost stepHost, ActionExecutionData data, Dictionary<string, string> inputs, Dictionary<string, string> environment, Variables runtimeVariables, string taskDirectory) =>
|
|
||||||
{
|
|
||||||
finialInputs = inputs;
|
|
||||||
})
|
|
||||||
.Returns(new Mock<IHandler>().Object);
|
|
||||||
|
|
||||||
//Act
|
|
||||||
await _actionRunner.RunAsync();
|
|
||||||
|
|
||||||
foreach (var input in finialInputs)
|
|
||||||
{
|
|
||||||
_hc.GetTrace().Info($"Input: {input.Key}={input.Value}");
|
|
||||||
}
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
Assert.Equal("test1", finialInputs["input1"]);
|
|
||||||
Assert.Equal("test2", finialInputs["input2"]);
|
|
||||||
Assert.Equal("github", finialInputs["input3"]);
|
|
||||||
Assert.Equal("invalid1", finialInputs["invalid1"]);
|
|
||||||
Assert.Equal("invalid2", finialInputs["invalid2"]);
|
|
||||||
|
|
||||||
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Unexpected input 'invalid1'")), It.IsAny<string>()), Times.Once);
|
|
||||||
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Unexpected input 'invalid2'")), It.IsAny<string>()), Times.Once);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Setup([CallerMemberName] string name = "")
|
private void Setup([CallerMemberName] string name = "")
|
||||||
{
|
{
|
||||||
_ecTokenSource?.Dispose();
|
_ecTokenSource?.Dispose();
|
||||||
|
|||||||
Reference in New Issue
Block a user