mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Compare commits
13 Commits
features/n
...
v2.285.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f6b9e55d6 | ||
|
|
7046439877 | ||
|
|
b7fff90e29 | ||
|
|
50afba61b4 | ||
|
|
66ee648c13 | ||
|
|
16e83b0e84 | ||
|
|
c75a77df66 | ||
|
|
6332f9a42f | ||
|
|
5b8ff174c6 | ||
|
|
e3e977fd84 | ||
|
|
4dc8a09db3 | ||
|
|
dcc5d34ad1 | ||
|
|
3e34fb10c1 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
devScript: ./dev.sh
|
||||
|
||||
- runtime: win-x64
|
||||
os: windows-latest
|
||||
os: windows-2019
|
||||
devScript: ./dev
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -72,7 +72,7 @@ jobs:
|
||||
devScript: ./dev.sh
|
||||
|
||||
- runtime: win-x64
|
||||
os: windows-latest
|
||||
os: windows-2019
|
||||
devScript: ./dev
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -164,6 +164,7 @@ jobs:
|
||||
release_name: "v${{ steps.releaseNote.outputs.version }}"
|
||||
body: |
|
||||
${{ steps.releaseNote.outputs.note }}
|
||||
prerelease: true
|
||||
|
||||
# Upload release assets
|
||||
- name: Upload Release Asset (win-x64)
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
## Features
|
||||
|
||||
- Expose GITHUB_REF_* as environment variable (#1314)
|
||||
- Add arch to runner context (#1372)
|
||||
- Support Conditional Steps in Composite Actions (#1438)
|
||||
- Log current runner version in terminal (#1441)
|
||||
- n/a
|
||||
|
||||
## Bugs
|
||||
|
||||
- Makes the user keychains available to the service (#847)
|
||||
- Use Actions Service health and api.github.com endpoints after connection failure on Actions Server and Hosted (#1385)
|
||||
- Fix an issue where nested local composite actions did not correctly register post steps (#1433)
|
||||
- Fixed an issue where container environment variables names or values could escape the docker command (#2108)
|
||||
|
||||
|
||||
## Misc
|
||||
|
||||
- Cleanup Older versions on MacOS now that we recreate node versions as needed (#1410)
|
||||
- n/a
|
||||
|
||||
|
||||
## Windows x64
|
||||
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
|
||||
|
||||
@@ -1 +1 @@
|
||||
<Update to ./src/runnerversion when creating release>
|
||||
2.285.2
|
||||
|
||||
@@ -4,6 +4,7 @@ PRECACHE=$2
|
||||
|
||||
NODE_URL=https://nodejs.org/dist
|
||||
NODE12_VERSION="12.13.1"
|
||||
NODE16_VERSION="16.13.0"
|
||||
|
||||
get_abs_path() {
|
||||
# exploits the fact that pwd will print abs path when no args
|
||||
@@ -126,6 +127,8 @@ function acquireExternalTool() {
|
||||
if [[ "$PACKAGERUNTIME" == "win-x64" || "$PACKAGERUNTIME" == "win-x86" ]]; then
|
||||
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/$PACKAGERUNTIME/node.exe" node12/bin
|
||||
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/$PACKAGERUNTIME/node.lib" node12/bin
|
||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/$PACKAGERUNTIME/node.exe" node16/bin
|
||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/$PACKAGERUNTIME/node.lib" node16/bin
|
||||
if [[ "$PRECACHE" != "" ]]; then
|
||||
acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere
|
||||
fi
|
||||
@@ -134,18 +137,23 @@ fi
|
||||
# Download the external tools only for OSX.
|
||||
if [[ "$PACKAGERUNTIME" == "osx-x64" ]]; then
|
||||
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-darwin-x64.tar.gz" node12 fix_nested_dir
|
||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-darwin-x64.tar.gz" node16 fix_nested_dir
|
||||
fi
|
||||
|
||||
# Download the external tools for Linux PACKAGERUNTIMEs.
|
||||
if [[ "$PACKAGERUNTIME" == "linux-x64" ]]; then
|
||||
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-x64.tar.gz" node12 fix_nested_dir
|
||||
acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/${NODE12_VERSION}/alpine/x64/node-${NODE12_VERSION}-alpine-x64.tar.gz" node12_alpine
|
||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-x64.tar.gz" node16 fix_nested_dir
|
||||
acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/${NODE16_VERSION}/alpine/x64/node-v${NODE16_VERSION}-alpine-x64.tar.gz" node16_alpine
|
||||
fi
|
||||
|
||||
if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then
|
||||
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-arm64.tar.gz" node12 fix_nested_dir
|
||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-arm64.tar.gz" node16 fix_nested_dir
|
||||
fi
|
||||
|
||||
if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then
|
||||
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-armv7l.tar.gz" node12 fix_nested_dir
|
||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-armv7l.tar.gz" node16 fix_nested_dir
|
||||
fi
|
||||
|
||||
@@ -200,9 +200,17 @@ namespace GitHub.Runner.Common
|
||||
if (credData != null &&
|
||||
credData.Data.TryGetValue("clientId", out var clientId))
|
||||
{
|
||||
_userAgents.Add(new ProductInfoHeaderValue($"RunnerId", clientId));
|
||||
_userAgents.Add(new ProductInfoHeaderValue("ClientId", clientId));
|
||||
}
|
||||
}
|
||||
|
||||
var runnerFile = GetConfigFile(WellKnownConfigFile.Runner);
|
||||
if (File.Exists(runnerFile))
|
||||
{
|
||||
var runnerSettings = IOUtil.LoadObject<RunnerSettings>(runnerFile);
|
||||
_userAgents.Add(new ProductInfoHeaderValue("RunnerId", runnerSettings.AgentId.ToString(CultureInfo.InvariantCulture)));
|
||||
_userAgents.Add(new ProductInfoHeaderValue("GroupId", runnerSettings.PoolId.ToString(CultureInfo.InvariantCulture)));
|
||||
}
|
||||
}
|
||||
|
||||
public string GetDirectory(WellKnownDirectory directory)
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace GitHub.Runner.Common
|
||||
Task<PackageMetadata> GetPackageAsync(string packageType, string platform, string version, bool includeToken, CancellationToken cancellationToken);
|
||||
|
||||
// agent update
|
||||
Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState);
|
||||
Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState, string trace);
|
||||
}
|
||||
|
||||
public sealed class RunnerServer : RunnerService, IRunnerServer
|
||||
@@ -341,25 +341,10 @@ namespace GitHub.Runner.Common
|
||||
return _genericTaskAgentClient.GetPackageAsync(packageType, platform, version, includeToken, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
public Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState)
|
||||
public Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, int agentId, string currentState, string trace)
|
||||
{
|
||||
CheckConnection(RunnerConnectionType.Generic);
|
||||
return _genericTaskAgentClient.UpdateAgentUpdateStateAsync(agentPoolId, agentId, currentState);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Runner Auth Url
|
||||
//-----------------------------------------------------------------
|
||||
public Task<string> GetRunnerAuthUrlAsync(int runnerPoolId, int runnerId)
|
||||
{
|
||||
CheckConnection(RunnerConnectionType.MessageQueue);
|
||||
return _messageTaskAgentClient.GetAgentAuthUrlAsync(runnerPoolId, runnerId);
|
||||
}
|
||||
|
||||
public Task ReportRunnerAuthUrlErrorAsync(int runnerPoolId, int runnerId, string error)
|
||||
{
|
||||
CheckConnection(RunnerConnectionType.MessageQueue);
|
||||
return _messageTaskAgentClient.ReportAgentAuthUrlMigrationErrorAsync(runnerPoolId, runnerId, error);
|
||||
return _genericTaskAgentClient.UpdateAgentUpdateStateAsync(agentPoolId, agentId, currentState, trace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using GitHub.Services.WebApi;
|
||||
using GitHub.Services.Common;
|
||||
using GitHub.Runner.Common;
|
||||
using GitHub.Runner.Sdk;
|
||||
using System.Text;
|
||||
|
||||
namespace GitHub.Runner.Listener
|
||||
{
|
||||
@@ -63,23 +64,25 @@ namespace GitHub.Runner.Listener
|
||||
|
||||
// Print console line that warn user not shutdown runner.
|
||||
await UpdateRunnerUpdateStateAsync("Runner update in progress, do not shutdown runner.");
|
||||
await UpdateRunnerUpdateStateAsync($"Downloading {_targetPackage.Version} runner");
|
||||
await UpdateRunnerUpdateStateAsync($"Downloading {_targetPackage.Version} runner", $"RunnerPlatform: {_targetPackage.Platform}");
|
||||
|
||||
await DownloadLatestRunner(token);
|
||||
var downloadTrace = await DownloadLatestRunner(token);
|
||||
Trace.Info($"Download latest runner and unzip into runner root.");
|
||||
|
||||
// wait till all running job finish
|
||||
await UpdateRunnerUpdateStateAsync("Waiting for current job finish running.");
|
||||
await UpdateRunnerUpdateStateAsync("Waiting for current job finish running.", downloadTrace);
|
||||
|
||||
await jobDispatcher.WaitAsync(token);
|
||||
Trace.Info($"All running job has exited.");
|
||||
|
||||
// We need to keep runner backup around for macOS until we fixed https://github.com/actions/runner/issues/743
|
||||
// delete runner backup
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
DeletePreviousVersionRunnerBackup(token);
|
||||
Trace.Info($"Delete old version runner backup.");
|
||||
stopWatch.Stop();
|
||||
// generate update script from template
|
||||
await UpdateRunnerUpdateStateAsync("Generate and execute update script.");
|
||||
await UpdateRunnerUpdateStateAsync("Generate and execute update script.", $"DeleteRunnerBackupTime: {stopWatch.ElapsedMilliseconds}ms");
|
||||
|
||||
string updateScript = GenerateUpdateScript(restartInteractiveRunner);
|
||||
Trace.Info($"Generate update script into: {updateScript}");
|
||||
@@ -96,7 +99,7 @@ namespace GitHub.Runner.Listener
|
||||
invokeScript.Start();
|
||||
Trace.Info($"Update script start running");
|
||||
|
||||
await UpdateRunnerUpdateStateAsync("Runner will exit shortly for update, should be back online within 10 seconds.");
|
||||
await UpdateRunnerUpdateStateAsync("Runner will exit shortly for update, should be back online within 10 seconds.", $"RestartInteractiveRunner: {restartInteractiveRunner}");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -150,8 +153,10 @@ namespace GitHub.Runner.Listener
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
private async Task DownloadLatestRunner(CancellationToken token)
|
||||
private async Task<string> DownloadLatestRunner(CancellationToken token)
|
||||
{
|
||||
var traceStringBuilder = new StringBuilder();
|
||||
traceStringBuilder.AppendLine($"DownloadUrl: {_targetPackage.DownloadUrl}");
|
||||
string latestRunnerDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), Constants.Path.UpdateDirectory);
|
||||
IOUtil.DeleteDirectory(latestRunnerDirectory, token);
|
||||
Directory.CreateDirectory(latestRunnerDirectory);
|
||||
@@ -160,6 +165,7 @@ namespace GitHub.Runner.Listener
|
||||
string archiveFile = null;
|
||||
bool downloadSucceeded = false;
|
||||
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
try
|
||||
{
|
||||
// Download the runner, using multiple attempts in order to be resilient against any networking/CDN issues
|
||||
@@ -210,6 +216,7 @@ namespace GitHub.Runner.Listener
|
||||
try
|
||||
{
|
||||
Trace.Info($"Download runner: begin download");
|
||||
long downloadSize = 0;
|
||||
|
||||
//open zip stream in async mode
|
||||
using (HttpClient httpClient = new HttpClient(HostContext.CreateHttpClientHandler()))
|
||||
@@ -228,11 +235,16 @@ namespace GitHub.Runner.Listener
|
||||
//81920 is the default used by System.IO.Stream.CopyTo and is under the large object heap threshold (85k).
|
||||
await result.CopyToAsync(fs, 81920, downloadCts.Token);
|
||||
await fs.FlushAsync(downloadCts.Token);
|
||||
downloadSize = fs.Length;
|
||||
}
|
||||
}
|
||||
|
||||
Trace.Info($"Download runner: finished download");
|
||||
downloadSucceeded = true;
|
||||
stopWatch.Stop();
|
||||
traceStringBuilder.AppendLine($"PackageDownloadTime: {stopWatch.ElapsedMilliseconds}ms");
|
||||
traceStringBuilder.AppendLine($"Attempts: {attempt}");
|
||||
traceStringBuilder.AppendLine($"PackageSize: {downloadSize / 1024 / 1024}MB");
|
||||
break;
|
||||
}
|
||||
catch (OperationCanceledException) when (token.IsCancellationRequested)
|
||||
@@ -257,6 +269,7 @@ namespace GitHub.Runner.Listener
|
||||
throw new TaskCanceledException($"Runner package '{archiveFile}' failed after {Constants.RunnerDownloadRetryMaxAttempts} download attempts");
|
||||
}
|
||||
|
||||
stopWatch.Restart();
|
||||
// If we got this far, we know that we've successfully downloaded the runner package
|
||||
// Validate Hash Matches if it is provided
|
||||
using (FileStream stream = File.OpenRead(archiveFile))
|
||||
@@ -320,7 +333,9 @@ namespace GitHub.Runner.Listener
|
||||
throw new NotSupportedException($"{archiveFile}");
|
||||
}
|
||||
|
||||
stopWatch.Stop();
|
||||
Trace.Info($"Finished getting latest runner package at: {latestRunnerDirectory}.");
|
||||
traceStringBuilder.AppendLine($"PackageExtractTime: {stopWatch.ElapsedMilliseconds}ms");
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -340,6 +355,7 @@ namespace GitHub.Runner.Listener
|
||||
}
|
||||
}
|
||||
|
||||
stopWatch.Restart();
|
||||
// copy latest runner into runner root folder
|
||||
// copy bin from _work/_update -> bin.version under root
|
||||
string binVersionDir = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), $"{Constants.Path.BinDirectory}.{_targetPackage.Version}");
|
||||
@@ -365,6 +381,11 @@ namespace GitHub.Runner.Listener
|
||||
IOUtil.DeleteFile(destination);
|
||||
file.CopyTo(destination, true);
|
||||
}
|
||||
|
||||
stopWatch.Stop();
|
||||
traceStringBuilder.AppendLine($"CopyRunnerToRootTime: {stopWatch.ElapsedMilliseconds}ms");
|
||||
|
||||
return traceStringBuilder.ToString();
|
||||
}
|
||||
|
||||
private void DeletePreviousVersionRunnerBackup(CancellationToken token)
|
||||
@@ -484,13 +505,18 @@ namespace GitHub.Runner.Listener
|
||||
return updateScript;
|
||||
}
|
||||
|
||||
private async Task UpdateRunnerUpdateStateAsync(string currentState)
|
||||
private async Task UpdateRunnerUpdateStateAsync(string currentState, string trace = "")
|
||||
{
|
||||
_terminal.WriteLine(currentState);
|
||||
|
||||
if (!string.IsNullOrEmpty(trace))
|
||||
{
|
||||
Trace.Info(trace);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _runnerServer.UpdateAgentUpdateStateAsync(_poolId, _agentId, currentState);
|
||||
await _runnerServer.UpdateAgentUpdateStateAsync(_poolId, _agentId, currentState, trace);
|
||||
}
|
||||
catch (VssResourceNotFoundException)
|
||||
{
|
||||
|
||||
@@ -278,7 +278,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
// Clone action so we can modify the condition without affecting the original
|
||||
var clonedAction = action.Clone() as Pipelines.ActionStep;
|
||||
_cachedEmbeddedPostSteps[parentStepId].Push(clonedAction);
|
||||
_cachedEmbeddedPostSteps[parentStepId].Push(clonedAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -443,7 +443,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
for (var i = 0; i < compositeAction.Steps.Count; i++)
|
||||
{
|
||||
// Store Id's for later load actions
|
||||
// Load stored Ids for later load actions
|
||||
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)
|
||||
{
|
||||
@@ -451,6 +451,16 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_cachedEmbeddedStepIds[action.Id] = new List<Guid>();
|
||||
foreach (var compositeStep in compositeAction.Steps)
|
||||
{
|
||||
var guid = Guid.NewGuid();
|
||||
compositeStep.Id = guid;
|
||||
_cachedEmbeddedStepIds[action.Id].Add(guid);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1189,6 +1199,8 @@ namespace GitHub.Runner.Worker
|
||||
public string Pre { get; set; }
|
||||
|
||||
public string Post { get; set; }
|
||||
|
||||
public string NodeVersion { get; set; }
|
||||
}
|
||||
|
||||
public sealed class PluginActionExecutionData : ActionExecutionData
|
||||
|
||||
@@ -451,7 +451,8 @@ namespace GitHub.Runner.Worker
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase))
|
||||
else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase)||
|
||||
string.Equals(usingToken.Value, "node16", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(mainToken?.Value))
|
||||
{
|
||||
@@ -461,6 +462,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
return new NodeJSActionExecutionData()
|
||||
{
|
||||
NodeVersion = usingToken.Value,
|
||||
Script = mainToken.Value,
|
||||
Pre = preToken?.Value,
|
||||
InitCondition = preIfToken?.Value ?? "always()",
|
||||
@@ -490,7 +492,7 @@ namespace GitHub.Runner.Worker
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker' or 'node12' instead.");
|
||||
throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker', 'node12' or 'node16' instead.");
|
||||
}
|
||||
}
|
||||
else if (pluginToken != null)
|
||||
|
||||
@@ -131,11 +131,11 @@ namespace GitHub.Runner.Worker.Container
|
||||
{
|
||||
if (String.IsNullOrEmpty(env.Value))
|
||||
{
|
||||
dockerOptions.Add($"-e \"{env.Key}\"");
|
||||
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
|
||||
}
|
||||
else
|
||||
{
|
||||
dockerOptions.Add($"-e \"{env.Key}={env.Value.Replace("\"", "\\\"")}\"");
|
||||
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key, env.Value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace GitHub.Runner.Worker.Container
|
||||
{
|
||||
// e.g. -e MY_SECRET maps the value into the exec'ed process without exposing
|
||||
// the value directly in the command
|
||||
dockerOptions.Add($"-e {env.Key}");
|
||||
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
|
||||
}
|
||||
|
||||
// Watermark for GitHub Action environment
|
||||
|
||||
@@ -6,6 +6,9 @@ namespace GitHub.Runner.Worker.Container
|
||||
{
|
||||
public class DockerUtil
|
||||
{
|
||||
private static readonly Regex QuoteEscape = new Regex(@"(\\*)" + "\"", RegexOptions.Compiled);
|
||||
private static readonly Regex EndOfStringEscape = new Regex(@"(\\+)$", RegexOptions.Compiled);
|
||||
|
||||
public static List<PortMapping> ParseDockerPort(IList<string> portMappingLines)
|
||||
{
|
||||
const string targetPort = "targetPort";
|
||||
@@ -17,7 +20,7 @@ namespace GitHub.Runner.Worker.Container
|
||||
string pattern = $"^(?<{targetPort}>\\d+)/(?<{proto}>\\w+) -> (?<{host}>.+):(?<{hostPort}>\\d+)$";
|
||||
|
||||
List<PortMapping> portMappings = new List<PortMapping>();
|
||||
foreach(var line in portMappingLines)
|
||||
foreach (var line in portMappingLines)
|
||||
{
|
||||
Match m = Regex.Match(line, pattern, RegexOptions.None, TimeSpan.FromSeconds(1));
|
||||
if (m.Success)
|
||||
@@ -61,5 +64,44 @@ namespace GitHub.Runner.Worker.Container
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static string CreateEscapedOption(string flag, string key)
|
||||
{
|
||||
if (String.IsNullOrEmpty(key))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return $"{flag} {EscapeString(key)}";
|
||||
}
|
||||
|
||||
public static string CreateEscapedOption(string flag, string key, string value)
|
||||
{
|
||||
if (String.IsNullOrEmpty(key))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
var escapedString = EscapeString($"{key}={value}");
|
||||
return $"{flag} {escapedString}";
|
||||
}
|
||||
|
||||
private static string EscapeString(string value)
|
||||
{
|
||||
if (String.IsNullOrEmpty(value))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
// Dotnet escaping rules are weird here, we can only escape \ if it precedes a "
|
||||
// If a double quotation mark follows two or an even number of backslashes, each proceeding backslash pair is replaced with one backslash and the double quotation mark is removed.
|
||||
// If a double quotation mark follows an odd number of backslashes, including just one, each preceding pair is replaced with one backslash and the remaining backslash is removed; however, in this case the double quotation mark is not removed.
|
||||
// https://docs.microsoft.com/en-us/dotnet/api/system.environment.getcommandlineargs?redirectedfrom=MSDN&view=net-6.0#remarks
|
||||
|
||||
// First, find any \ followed by a " and double the number of \ + 1.
|
||||
value = QuoteEscape.Replace(value, @"$1$1\" + "\"");
|
||||
// Next, what if it ends in `\`, it would escape the end quote. So, we need to detect that at the end of the string and perform the same escape
|
||||
// Luckily, we can just use the $ character with detects the end of string in regex
|
||||
value = EndOfStringEscape.Replace(value, @"$1$1");
|
||||
// Finally, wrap it in quotes
|
||||
return $"\"{value}\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
HasPreStep = Data.HasPre,
|
||||
HasPostStep = Data.HasPost,
|
||||
IsEmbedded = ExecutionContext.IsEmbedded,
|
||||
Type = "node12"
|
||||
Type = Data.NodeVersion
|
||||
};
|
||||
ExecutionContext.Root.ActionsStepsTelemetry.Add(telemetry);
|
||||
}
|
||||
@@ -99,7 +99,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);
|
||||
}
|
||||
|
||||
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext);
|
||||
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext, Data.NodeVersion);
|
||||
string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
|
||||
|
||||
// Format the arguments passed to node.
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
|
||||
string ResolvePathForStepHost(string path);
|
||||
|
||||
Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext);
|
||||
Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext, string preferredVersion);
|
||||
|
||||
Task<int> ExecuteAsync(string workingDirectory,
|
||||
string fileName,
|
||||
@@ -58,9 +58,9 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
return path;
|
||||
}
|
||||
|
||||
public Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext)
|
||||
public Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext, string preferredVersion)
|
||||
{
|
||||
return Task.FromResult<string>("node12");
|
||||
return Task.FromResult<string>(preferredVersion);
|
||||
}
|
||||
|
||||
public async Task<int> ExecuteAsync(string workingDirectory,
|
||||
@@ -123,7 +123,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext)
|
||||
public async Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext, string preferredVersion)
|
||||
{
|
||||
// Best effort to determine a compatible node runtime
|
||||
// There may be more variation in which libraries are linked than just musl/glibc,
|
||||
@@ -148,14 +148,14 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
var msg = $"JavaScript Actions in Alpine containers are only supported on x64 Linux runners. Detected {os} {arch}";
|
||||
throw new NotSupportedException(msg);
|
||||
}
|
||||
nodeExternal = "node12_alpine";
|
||||
nodeExternal = $"{preferredVersion}_alpine";
|
||||
executionContext.Debug($"Container distribution is alpine. Running JavaScript Action with external tool: {nodeExternal}");
|
||||
return nodeExternal;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Optimistically use the default
|
||||
nodeExternal = "node12";
|
||||
nodeExternal = preferredVersion;
|
||||
executionContext.Debug($"Running JavaScript Action with default external tool: {nodeExternal}");
|
||||
return nodeExternal;
|
||||
}
|
||||
@@ -188,7 +188,7 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
{
|
||||
// e.g. -e MY_SECRET maps the value into the exec'ed process without exposing
|
||||
// the value directly in the command
|
||||
dockerCommandArgs.Add($"-e {env.Key}");
|
||||
dockerCommandArgs.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(PrependPath))
|
||||
{
|
||||
|
||||
@@ -142,6 +142,12 @@ namespace GitHub.Runner.Worker
|
||||
Trace.Error(ex);
|
||||
}
|
||||
|
||||
var secretSource = context.GetGitHubContext("secret_source");
|
||||
if (!string.IsNullOrEmpty(secretSource))
|
||||
{
|
||||
context.Output($"Secret source: {secretSource}");
|
||||
}
|
||||
|
||||
var repoFullName = context.GetGitHubContext("repository");
|
||||
ArgUtil.NotNull(repoFullName, nameof(repoFullName));
|
||||
context.Debug($"Primary repository: {repoFullName}");
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
"runs": {
|
||||
"one-of": [
|
||||
"container-runs",
|
||||
"node12-runs",
|
||||
"node-runs",
|
||||
"plugin-runs",
|
||||
"composite-runs"
|
||||
]
|
||||
@@ -80,7 +80,7 @@
|
||||
"loose-value-type": "string"
|
||||
}
|
||||
},
|
||||
"node12-runs": {
|
||||
"node-runs": {
|
||||
"mapping": {
|
||||
"properties": {
|
||||
"using": "non-empty-string",
|
||||
@@ -112,10 +112,10 @@
|
||||
"item-type": "composite-step"
|
||||
}
|
||||
},
|
||||
"composite-step":{
|
||||
"composite-step": {
|
||||
"one-of": [
|
||||
"run-step",
|
||||
"uses-step"
|
||||
"uses-step"
|
||||
]
|
||||
},
|
||||
"run-step": {
|
||||
@@ -254,4 +254,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -768,6 +768,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
/// <param name="poolId"></param>
|
||||
/// <param name="agentId"></param>
|
||||
/// <param name="currentState"></param>
|
||||
/// <param name="updateTrace"></param>
|
||||
/// <param name="userState"></param>
|
||||
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
@@ -775,6 +776,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
int poolId,
|
||||
int agentId,
|
||||
string currentState,
|
||||
string updateTrace,
|
||||
object userState = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -784,6 +786,7 @@ namespace GitHub.DistributedTask.WebApi
|
||||
|
||||
List<KeyValuePair<string, string>> queryParams = new List<KeyValuePair<string, string>>();
|
||||
queryParams.Add("currentState", currentState);
|
||||
queryParams.Add("updateTrace", updateTrace);
|
||||
|
||||
return SendAsync<TaskAgent>(
|
||||
httpMethod,
|
||||
@@ -794,65 +797,5 @@ namespace GitHub.DistributedTask.WebApi
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [Preview API]
|
||||
/// </summary>
|
||||
/// <param name="poolId"></param>
|
||||
/// <param name="agentId"></param>
|
||||
/// <param name="userState"></param>
|
||||
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||
public Task<String> GetAgentAuthUrlAsync(
|
||||
int poolId,
|
||||
int agentId,
|
||||
object userState = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
HttpMethod httpMethod = new HttpMethod("GET");
|
||||
Guid locationId = new Guid("a82a119c-1e46-44b6-8d75-c82a79cf975b");
|
||||
object routeValues = new { poolId = poolId, agentId = agentId };
|
||||
|
||||
return SendAsync<String>(
|
||||
httpMethod,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(6.0, 1),
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [Preview API]
|
||||
/// </summary>
|
||||
/// <param name="poolId"></param>
|
||||
/// <param name="agentId"></param>
|
||||
/// <param name="error"></param>
|
||||
/// <param name="userState"></param>
|
||||
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public virtual async Task ReportAgentAuthUrlMigrationErrorAsync(
|
||||
int poolId,
|
||||
int agentId,
|
||||
string error,
|
||||
object userState = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
HttpMethod httpMethod = new HttpMethod("POST");
|
||||
Guid locationId = new Guid("a82a119c-1e46-44b6-8d75-c82a79cf975b");
|
||||
object routeValues = new { poolId = poolId, agentId = agentId };
|
||||
HttpContent content = new ObjectContent<string>(error, new VssJsonMediaTypeFormatter(true));
|
||||
|
||||
using (HttpResponseMessage response = await SendAsync(
|
||||
httpMethod,
|
||||
locationId,
|
||||
routeValues: routeValues,
|
||||
version: new ApiResourceVersion(6.0, 1),
|
||||
userState: userState,
|
||||
cancellationToken: cancellationToken,
|
||||
content: content).ConfigureAwait(false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,5 +144,54 @@ namespace GitHub.Runner.Common.Tests.Worker.Container
|
||||
var actual = DockerUtil.ParseRegistryHostnameFromImageName(input);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
[InlineData("", "")]
|
||||
[InlineData("foo", "foo")]
|
||||
[InlineData("foo \\ bar", "foo \\ bar")]
|
||||
[InlineData("foo \\", "foo \\\\")]
|
||||
[InlineData("foo \\\\", "foo \\\\\\\\")]
|
||||
[InlineData("foo \\\" bar", "foo \\\\\\\" bar")]
|
||||
[InlineData("foo \\\\\" bar", "foo \\\\\\\\\\\" bar")]
|
||||
public void CreateEscapedOption_keyOnly(string input, string escaped)
|
||||
{
|
||||
var flag = "--example";
|
||||
var actual = DockerUtil.CreateEscapedOption(flag, input);
|
||||
string expected;
|
||||
if (String.IsNullOrEmpty(input))
|
||||
{
|
||||
expected = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
expected = $"{flag} \"{escaped}\"";
|
||||
}
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
[InlineData("foo", "bar", "foo=bar")]
|
||||
[InlineData("foo\\", "bar", "foo\\=bar")]
|
||||
[InlineData("foo\\", "bar\\", "foo\\=bar\\\\")]
|
||||
[InlineData("foo \\","bar \\", "foo \\=bar \\\\")]
|
||||
public void CreateEscapedOption_keyValue(string keyInput, string valueInput, string escapedString)
|
||||
{
|
||||
var flag = "--example";
|
||||
var actual = DockerUtil.CreateEscapedOption(flag, keyInput, valueInput);
|
||||
string expected;
|
||||
if (String.IsNullOrEmpty(keyInput))
|
||||
{
|
||||
expected = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
expected = $"{flag} \"{escapedString}\"";
|
||||
}
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -965,7 +965,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
};
|
||||
|
||||
//Act
|
||||
var result = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
var result = await _actionManager.PrepareActionsAsync(_ec.Object, actions);
|
||||
|
||||
//Assert
|
||||
Assert.Equal(1, result.PreStepTracker.Count);
|
||||
@@ -1288,7 +1288,7 @@ runs:
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void LoadsNodeActionDefinition()
|
||||
public void LoadsNode12ActionDefinition()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1344,8 +1344,78 @@ runs:
|
||||
Assert.True(string.IsNullOrEmpty(inputDefaults["entryPoint"]));
|
||||
Assert.NotNull(definition.Data.Execution); // execution
|
||||
|
||||
Assert.NotNull((definition.Data.Execution as NodeJSActionExecutionData));
|
||||
Assert.NotNull(definition.Data.Execution as NodeJSActionExecutionData);
|
||||
Assert.Equal("task.js", (definition.Data.Execution as NodeJSActionExecutionData).Script);
|
||||
Assert.Equal("node12", (definition.Data.Execution as NodeJSActionExecutionData).NodeVersion);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void LoadsNode16ActionDefinition()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Arrange.
|
||||
Setup();
|
||||
const string Content = @"
|
||||
# Container action
|
||||
name: 'Hello World'
|
||||
description: 'Greet the world and record the time'
|
||||
author: 'GitHub'
|
||||
inputs:
|
||||
greeting: # id of input
|
||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||
required: true
|
||||
default: 'Hello'
|
||||
entryPoint: # id of input
|
||||
description: 'optional docker entrypoint overwrite.'
|
||||
required: false
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we did the greeting'
|
||||
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'node16'
|
||||
main: 'task.js'
|
||||
";
|
||||
Pipelines.ActionStep instance;
|
||||
string directory;
|
||||
CreateAction(yamlContent: Content, instance: out instance, directory: out directory);
|
||||
|
||||
// Act.
|
||||
Definition definition = _actionManager.LoadAction(_ec.Object, instance);
|
||||
|
||||
// Assert.
|
||||
Assert.NotNull(definition);
|
||||
Assert.Equal(directory, definition.Directory);
|
||||
Assert.NotNull(definition.Data);
|
||||
Assert.NotNull(definition.Data.Inputs); // inputs
|
||||
Dictionary<string, string> inputDefaults = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var input in definition.Data.Inputs)
|
||||
{
|
||||
var name = input.Key.AssertString("key").Value;
|
||||
var value = input.Value.AssertScalar("value").ToString();
|
||||
|
||||
_hc.GetTrace().Info($"Default: {name} = {value}");
|
||||
inputDefaults[name] = value;
|
||||
}
|
||||
|
||||
Assert.Equal(2, inputDefaults.Count);
|
||||
Assert.True(inputDefaults.ContainsKey("greeting"));
|
||||
Assert.Equal("Hello", inputDefaults["greeting"]);
|
||||
Assert.True(string.IsNullOrEmpty(inputDefaults["entryPoint"]));
|
||||
Assert.NotNull(definition.Data.Execution); // execution
|
||||
|
||||
Assert.NotNull(definition.Data.Execution as NodeJSActionExecutionData);
|
||||
Assert.Equal("task.js", (definition.Data.Execution as NodeJSActionExecutionData).Script);
|
||||
Assert.Equal("node16", (definition.Data.Execution as NodeJSActionExecutionData).NodeVersion);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -408,6 +408,50 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var nodeAction = result.Execution as NodeJSActionExecutionData;
|
||||
|
||||
Assert.Equal("main.js", nodeAction.Script);
|
||||
Assert.Equal("node12", nodeAction.NodeVersion);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Load_Node16Action()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Arrange
|
||||
Setup();
|
||||
|
||||
var actionManifest = new ActionManifestManager();
|
||||
actionManifest.Initialize(_hc);
|
||||
|
||||
//Act
|
||||
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "node16action.yml"));
|
||||
|
||||
//Assert
|
||||
Assert.Equal("Hello World", result.Name);
|
||||
Assert.Equal("Greet the world and record the time", result.Description);
|
||||
Assert.Equal(2, result.Inputs.Count);
|
||||
Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value);
|
||||
Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value);
|
||||
Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value);
|
||||
Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value);
|
||||
Assert.Equal(1, result.Deprecated.Count);
|
||||
|
||||
Assert.True(result.Deprecated.ContainsKey("greeting"));
|
||||
result.Deprecated.TryGetValue("greeting", out string value);
|
||||
Assert.Equal("This property has been deprecated", value);
|
||||
|
||||
Assert.Equal(ActionExecutionType.NodeJS, result.Execution.ExecutionType);
|
||||
|
||||
var nodeAction = result.Execution as NodeJSActionExecutionData;
|
||||
|
||||
Assert.Equal("main.js", nodeAction.Script);
|
||||
Assert.Equal("node16", nodeAction.NodeVersion);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -102,6 +102,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_message = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "test", "test", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), steps, null, null, null, null);
|
||||
GitHubContext github = new GitHubContext();
|
||||
github["repository"] = new Pipelines.ContextData.StringContextData("actions/runner");
|
||||
github["secret_source"] = new Pipelines.ContextData.StringContextData("Actions");
|
||||
_message.ContextData.Add("github", github);
|
||||
|
||||
hc.SetSingleton(_actionManager.Object);
|
||||
|
||||
109
src/Test/L0/Worker/StepHostL0.cs
Normal file
109
src/Test/L0/Worker/StepHostL0.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using GitHub.Runner.Worker;
|
||||
using GitHub.Runner.Worker.Handlers;
|
||||
using GitHub.Runner.Worker.Container;
|
||||
|
||||
namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
public sealed class StepHostL0
|
||||
{
|
||||
private Mock<IExecutionContext> _ec;
|
||||
private Mock<IDockerCommandManager> _dc;
|
||||
private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
|
||||
{
|
||||
var hc = new TestHostContext(this, testName);
|
||||
|
||||
_ec = new Mock<IExecutionContext>();
|
||||
_ec.SetupAllProperties();
|
||||
_ec.Setup(x => x.Global).Returns(new GlobalContext { WriteDebug = true });
|
||||
var trace = hc.GetTrace();
|
||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { trace.Info($"[{tag}]{message}"); });
|
||||
|
||||
_dc = new Mock<IDockerCommandManager>();
|
||||
hc.SetSingleton(_dc.Object);
|
||||
return hc;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task DetermineNodeRuntimeVersionInContainerAsync()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
// Arrange.
|
||||
var sh = new ContainerStepHost();
|
||||
sh.Initialize(hc);
|
||||
sh.Container = new ContainerInfo() { ContainerId = "1234abcd" };
|
||||
|
||||
_dc.Setup(d => d.DockerExec(_ec.Object, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<List<string>>()))
|
||||
.ReturnsAsync(0);
|
||||
|
||||
// Act.
|
||||
var nodeVersion = await sh.DetermineNodeRuntimeVersion(_ec.Object, "node12");
|
||||
|
||||
// Assert.
|
||||
Assert.Equal("node12", nodeVersion);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task DetermineNodeRuntimeVersionInAlpineContainerAsync()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
// Arrange.
|
||||
var sh = new ContainerStepHost();
|
||||
sh.Initialize(hc);
|
||||
sh.Container = new ContainerInfo() { ContainerId = "1234abcd" };
|
||||
|
||||
_dc.Setup(d => d.DockerExec(_ec.Object, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<List<string>>()))
|
||||
.Callback((IExecutionContext ec, string id, string options, string command, List<string> output) =>
|
||||
{
|
||||
output.Add("alpine");
|
||||
})
|
||||
.ReturnsAsync(0);
|
||||
|
||||
// Act.
|
||||
var nodeVersion = await sh.DetermineNodeRuntimeVersion(_ec.Object, "node16");
|
||||
|
||||
// Assert.
|
||||
Assert.Equal("node16_alpine", nodeVersion);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public async Task DetermineNodeRuntimeVersionInUnknowContainerAsync()
|
||||
{
|
||||
using (TestHostContext hc = CreateTestContext())
|
||||
{
|
||||
// Arrange.
|
||||
var sh = new ContainerStepHost();
|
||||
sh.Initialize(hc);
|
||||
sh.Container = new ContainerInfo() { ContainerId = "1234abcd" };
|
||||
|
||||
_dc.Setup(d => d.DockerExec(_ec.Object, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<List<string>>()))
|
||||
.Callback((IExecutionContext ec, string id, string options, string command, List<string> output) =>
|
||||
{
|
||||
output.Add("github");
|
||||
})
|
||||
.ReturnsAsync(0);
|
||||
|
||||
// Act.
|
||||
var nodeVersion = await sh.DetermineNodeRuntimeVersion(_ec.Object, "node16");
|
||||
|
||||
// Assert.
|
||||
Assert.Equal("node16", nodeVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/Test/TestData/node16action.yml
Normal file
20
src/Test/TestData/node16action.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
name: 'Hello World'
|
||||
description: 'Greet the world and record the time'
|
||||
author: 'Test Corporation'
|
||||
inputs:
|
||||
greeting: # id of input
|
||||
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
|
||||
required: true
|
||||
default: 'Hello'
|
||||
deprecationMessage: 'This property has been deprecated'
|
||||
entryPoint: # id of input
|
||||
description: 'optional docker entrypoint overwrite.'
|
||||
required: false
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we did the greeting'
|
||||
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
|
||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||
runs:
|
||||
using: 'node16'
|
||||
main: 'main.js'
|
||||
@@ -1 +1 @@
|
||||
2.284.0
|
||||
2.285.2
|
||||
|
||||
Reference in New Issue
Block a user