mirror of
https://github.com/actions/runner.git
synced 2026-01-05 09:38:54 +08:00
Compare commits
18 Commits
releases/m
...
brcrista/m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8a53aa04a | ||
|
|
9fe65b872a | ||
|
|
ec420bab24 | ||
|
|
07cda747b2 | ||
|
|
9ca132380c | ||
|
|
a5a0f8df47 | ||
|
|
ccf53338be | ||
|
|
400b2d879c | ||
|
|
c4b6d288d4 | ||
|
|
0699597876 | ||
|
|
a592b14ae3 | ||
|
|
04269f7b1b | ||
|
|
e89d2e84bd | ||
|
|
afe7066e39 | ||
|
|
da79ef4acb | ||
|
|
5afb52b272 | ||
|
|
cf87c55557 | ||
|
|
43fa351980 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
|||||||
devScript: ./dev.sh
|
devScript: ./dev.sh
|
||||||
|
|
||||||
- runtime: win-x64
|
- runtime: win-x64
|
||||||
os: windows-2019
|
os: windows-latest
|
||||||
devScript: ./dev
|
devScript: ./dev
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|||||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -72,7 +72,7 @@ jobs:
|
|||||||
devScript: ./dev.sh
|
devScript: ./dev.sh
|
||||||
|
|
||||||
- runtime: win-x64
|
- runtime: win-x64
|
||||||
os: windows-2019
|
os: windows-latest
|
||||||
devScript: ./dev
|
devScript: ./dev
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
@@ -164,7 +164,6 @@ jobs:
|
|||||||
release_name: "v${{ steps.releaseNote.outputs.version }}"
|
release_name: "v${{ steps.releaseNote.outputs.version }}"
|
||||||
body: |
|
body: |
|
||||||
${{ steps.releaseNote.outputs.note }}
|
${{ steps.releaseNote.outputs.note }}
|
||||||
prerelease: true
|
|
||||||
|
|
||||||
# Upload release assets
|
# Upload release assets
|
||||||
- name: Upload Release Asset (win-x64)
|
- name: Upload Release Asset (win-x64)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
|
||||||
- Fixed an issue where container environment variables names or values could escape the docker command (#2108)
|
- Fixed an issue where ephemeral runners did not restart after upgrading (#1396)
|
||||||
|
|
||||||
## Misc
|
## Misc
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.283.4
|
<Update to ./src/runnerversion when creating release>
|
||||||
|
|||||||
@@ -25,5 +25,7 @@
|
|||||||
</dict>
|
</dict>
|
||||||
<key>ProcessType</key>
|
<key>ProcessType</key>
|
||||||
<string>Interactive</string>
|
<string>Interactive</string>
|
||||||
|
<key>SessionCreate</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using GitHub.Runner.Sdk;
|
||||||
|
using GitHub.Services.Common;
|
||||||
using GitHub.Services.WebApi;
|
using GitHub.Services.WebApi;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
@@ -36,6 +39,9 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
_connection = jobConnection;
|
_connection = jobConnection;
|
||||||
int attemptCount = 5;
|
int attemptCount = 5;
|
||||||
|
var configurationStore = HostContext.GetService<IConfigurationStore>();
|
||||||
|
var runnerSettings = configurationStore.GetSettings();
|
||||||
|
|
||||||
while (!_connection.HasAuthenticated && attemptCount-- > 0)
|
while (!_connection.HasAuthenticated && attemptCount-- > 0)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -45,8 +51,13 @@ namespace GitHub.Runner.Common
|
|||||||
}
|
}
|
||||||
catch (Exception ex) when (attemptCount > 0)
|
catch (Exception ex) when (attemptCount > 0)
|
||||||
{
|
{
|
||||||
Trace.Info($"Catch exception during connect. {attemptCount} attemp left.");
|
Trace.Info($"Catch exception during connect. {attemptCount} attempts left.");
|
||||||
Trace.Error(ex);
|
Trace.Error(ex);
|
||||||
|
|
||||||
|
if (runnerSettings.IsHostedServer)
|
||||||
|
{
|
||||||
|
await CheckNetworkEndpointsAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
@@ -56,6 +67,52 @@ namespace GitHub.Runner.Common
|
|||||||
_hasConnection = true;
|
_hasConnection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task CheckNetworkEndpointsAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Trace.Info("Requesting Actions Service health endpoint status");
|
||||||
|
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
||||||
|
using (var actionsClient = new HttpClient(httpClientHandler))
|
||||||
|
{
|
||||||
|
var baseUri = new Uri(_connection.Uri.GetLeftPart(UriPartial.Authority));
|
||||||
|
|
||||||
|
actionsClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
||||||
|
|
||||||
|
// Call the _apis/health endpoint
|
||||||
|
var response = await actionsClient.GetAsync(new Uri(baseUri, "_apis/health"));
|
||||||
|
Trace.Info($"Actions health status code: {response.StatusCode}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Log error, but continue as this call is best-effort
|
||||||
|
Trace.Info($"Actions Service health endpoint failed due to {ex.GetType().Name}");
|
||||||
|
Trace.Error(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Trace.Info("Requesting Github API endpoint status");
|
||||||
|
// This is a dotcom public API... just call it directly
|
||||||
|
using (var httpClientHandler = HostContext.CreateHttpClientHandler())
|
||||||
|
using (var gitHubClient = new HttpClient(httpClientHandler))
|
||||||
|
{
|
||||||
|
gitHubClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
||||||
|
|
||||||
|
// Call the api.github.com endpoint
|
||||||
|
var response = await gitHubClient.GetAsync("https://api.github.com");
|
||||||
|
Trace.Info($"api.github.com status code: {response.StatusCode}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Log error, but continue as this call is best-effort
|
||||||
|
Trace.Info($"Github API endpoint failed due to {ex.GetType().Name}");
|
||||||
|
Trace.Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void CheckConnection()
|
private void CheckConnection()
|
||||||
{
|
{
|
||||||
if (!_hasConnection)
|
if (!_hasConnection)
|
||||||
|
|||||||
@@ -262,6 +262,16 @@ namespace GitHub.Runner.Worker
|
|||||||
environment[$"STATE_{state.Key}"] = state.Value ?? string.Empty;
|
environment[$"STATE_{state.Key}"] = state.Value ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK
|
||||||
|
if (Action.DisplayName != null && Action.DisplayName.StartsWith("make dependency"))
|
||||||
|
{
|
||||||
|
var target = Action.DisplayName.Split()[2];
|
||||||
|
inputs = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["script"] = $"make {target}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Create the handler.
|
// Create the handler.
|
||||||
IHandler handler = handlerFactory.Create(
|
IHandler handler = handlerFactory.Create(
|
||||||
ExecutionContext,
|
ExecutionContext,
|
||||||
|
|||||||
@@ -131,11 +131,11 @@ namespace GitHub.Runner.Worker.Container
|
|||||||
{
|
{
|
||||||
if (String.IsNullOrEmpty(env.Value))
|
if (String.IsNullOrEmpty(env.Value))
|
||||||
{
|
{
|
||||||
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
|
dockerOptions.Add($"-e \"{env.Key}\"");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key, env.Value));
|
dockerOptions.Add($"-e \"{env.Key}={env.Value.Replace("\"", "\\\"")}\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ namespace GitHub.Runner.Worker.Container
|
|||||||
{
|
{
|
||||||
// e.g. -e MY_SECRET maps the value into the exec'ed process without exposing
|
// e.g. -e MY_SECRET maps the value into the exec'ed process without exposing
|
||||||
// the value directly in the command
|
// the value directly in the command
|
||||||
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
|
dockerOptions.Add($"-e {env.Key}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watermark for GitHub Action environment
|
// Watermark for GitHub Action environment
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ namespace GitHub.Runner.Worker.Container
|
|||||||
{
|
{
|
||||||
public class DockerUtil
|
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)
|
public static List<PortMapping> ParseDockerPort(IList<string> portMappingLines)
|
||||||
{
|
{
|
||||||
const string targetPort = "targetPort";
|
const string targetPort = "targetPort";
|
||||||
@@ -20,7 +17,7 @@ namespace GitHub.Runner.Worker.Container
|
|||||||
string pattern = $"^(?<{targetPort}>\\d+)/(?<{proto}>\\w+) -> (?<{host}>.+):(?<{hostPort}>\\d+)$";
|
string pattern = $"^(?<{targetPort}>\\d+)/(?<{proto}>\\w+) -> (?<{host}>.+):(?<{hostPort}>\\d+)$";
|
||||||
|
|
||||||
List<PortMapping> portMappings = new List<PortMapping>();
|
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));
|
Match m = Regex.Match(line, pattern, RegexOptions.None, TimeSpan.FromSeconds(1));
|
||||||
if (m.Success)
|
if (m.Success)
|
||||||
@@ -64,44 +61,5 @@ namespace GitHub.Runner.Worker.Container
|
|||||||
}
|
}
|
||||||
return "";
|
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}\"";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
|
|
||||||
Inputs.TryGetValue("script", out string contents);
|
Inputs.TryGetValue("script", out string contents);
|
||||||
contents = contents ?? string.Empty;
|
contents = contents ?? string.Empty;
|
||||||
if (Action.Type == Pipelines.ActionSourceType.Script)
|
// if (Action.Type == Pipelines.ActionSourceType.Script)
|
||||||
{
|
// {
|
||||||
var firstLine = contents.TrimStart(' ', '\t', '\r', '\n');
|
var firstLine = contents.TrimStart(' ', '\t', '\r', '\n');
|
||||||
var firstNewLine = firstLine.IndexOfAny(new[] { '\r', '\n' });
|
var firstNewLine = firstLine.IndexOfAny(new[] { '\r', '\n' });
|
||||||
if (firstNewLine >= 0)
|
if (firstNewLine >= 0)
|
||||||
@@ -41,11 +41,11 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
}
|
}
|
||||||
|
|
||||||
ExecutionContext.Output($"##[group]Run {firstLine}");
|
ExecutionContext.Output($"##[group]Run {firstLine}");
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
throw new InvalidOperationException($"Invalid action type {Action.Type} for {nameof(ScriptHandler)}");
|
// throw new InvalidOperationException($"Invalid action type {Action.Type} for {nameof(ScriptHandler)}");
|
||||||
}
|
// }
|
||||||
|
|
||||||
var multiLines = contents.Replace("\r\n", "\n").TrimEnd('\n').Split('\n');
|
var multiLines = contents.Replace("\r\n", "\n").TrimEnd('\n').Split('\n');
|
||||||
foreach (var line in multiLines)
|
foreach (var line in multiLines)
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
{
|
{
|
||||||
// e.g. -e MY_SECRET maps the value into the exec'ed process without exposing
|
// e.g. -e MY_SECRET maps the value into the exec'ed process without exposing
|
||||||
// the value directly in the command
|
// the value directly in the command
|
||||||
dockerCommandArgs.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
|
dockerCommandArgs.Add($"-e {env.Key}");
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(PrependPath))
|
if (!string.IsNullOrEmpty(PrependPath))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -265,29 +265,91 @@ namespace GitHub.Runner.Worker
|
|||||||
if (step.Type == Pipelines.StepType.Action)
|
if (step.Type == Pipelines.StepType.Action)
|
||||||
{
|
{
|
||||||
var action = step as Pipelines.ActionStep;
|
var action = step as Pipelines.ActionStep;
|
||||||
Trace.Info($"Adding {action.DisplayName}.");
|
|
||||||
var actionRunner = HostContext.CreateService<IActionRunner>();
|
void EmitStep(Pipelines.ActionStep action)
|
||||||
actionRunner.Action = action;
|
|
||||||
actionRunner.Stage = ActionRunStage.Main;
|
|
||||||
actionRunner.Condition = step.Condition;
|
|
||||||
var contextData = new Pipelines.ContextData.DictionaryContextData();
|
|
||||||
if (message.ContextData?.Count > 0)
|
|
||||||
{
|
{
|
||||||
foreach (var pair in message.ContextData)
|
Trace.Info($"Adding {action.DisplayName}.");
|
||||||
|
var actionRunner = HostContext.CreateService<IActionRunner>();
|
||||||
|
actionRunner.Action = action;
|
||||||
|
actionRunner.Stage = ActionRunStage.Main;
|
||||||
|
actionRunner.Condition = step.Condition;
|
||||||
|
var contextData = new Pipelines.ContextData.DictionaryContextData();
|
||||||
|
if (message.ContextData?.Count > 0)
|
||||||
{
|
{
|
||||||
contextData[pair.Key] = pair.Value;
|
foreach (var pair in message.ContextData)
|
||||||
|
{
|
||||||
|
contextData[pair.Key] = pair.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actionRunner.TryEvaluateDisplayName(contextData, context);
|
||||||
|
jobSteps.Add(actionRunner);
|
||||||
|
|
||||||
|
if (prepareResult.PreStepTracker.TryGetValue(step.Id, out var preStep))
|
||||||
|
{
|
||||||
|
Trace.Info($"Adding pre-{action.DisplayName}.");
|
||||||
|
preStep.TryEvaluateDisplayName(contextData, context);
|
||||||
|
preStep.DisplayName = $"Pre {preStep.DisplayName}";
|
||||||
|
preJobSteps.Add(preStep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actionRunner.TryEvaluateDisplayName(contextData, context);
|
// HACK: if the step is "run: make," parse the Makefile and emit $"make dependency {target}"
|
||||||
jobSteps.Add(actionRunner);
|
// Only works for the default target right now.
|
||||||
|
bool isRunMake = false;
|
||||||
if (prepareResult.PreStepTracker.TryGetValue(step.Id, out var preStep))
|
if (action.Reference?.Type == Pipelines.ActionSourceType.Script)
|
||||||
{
|
{
|
||||||
Trace.Info($"Adding pre-{action.DisplayName}.");
|
var inputs = action.Inputs.AssertMapping(null);
|
||||||
preStep.TryEvaluateDisplayName(contextData, context);
|
foreach (var pair in inputs)
|
||||||
preStep.DisplayName = $"Pre {preStep.DisplayName}";
|
{
|
||||||
preJobSteps.Add(preStep);
|
var propertyName = pair.Key.AssertString($"{PipelineTemplateConstants.Steps}");
|
||||||
|
if (string.Equals(propertyName.Value, "script", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var tokenToParse = pair.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Run}");
|
||||||
|
if (tokenToParse.ToString() == "make")
|
||||||
|
{
|
||||||
|
isRunMake = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRunMake)
|
||||||
|
{
|
||||||
|
// Get the path of the Makefile in the repository root.
|
||||||
|
var githubContext = jobContext.ExpressionValues["github"] as GitHubContext;
|
||||||
|
var workspaceDir = githubContext["workspace"] as StringContextData;
|
||||||
|
var makefile = Path.Combine(workspaceDir, "Makefile");
|
||||||
|
if (!File.Exists(makefile))
|
||||||
|
{
|
||||||
|
// Forget about trying to be smart. Just do the normal thing.
|
||||||
|
EmitStep(action);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Assume the default target is named `all`.
|
||||||
|
var targetDependencies = MakefileReader.ReadTargetDependencies(jobContext, makefile, target: "all");
|
||||||
|
if (targetDependencies.Count == 0)
|
||||||
|
{
|
||||||
|
// Forget about trying to be smart. Just do the normal thing.
|
||||||
|
EmitStep(action);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var target in targetDependencies)
|
||||||
|
{
|
||||||
|
action = (Pipelines.ActionStep)action.Clone();
|
||||||
|
action.Id = Guid.NewGuid();
|
||||||
|
action.DisplayName = $"make dependency {target}";
|
||||||
|
EmitStep(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitStep(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/Runner.Worker/MakefileReader.cs
Normal file
24
src/Runner.Worker/MakefileReader.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace GitHub.Runner.Worker
|
||||||
|
{
|
||||||
|
public static class MakefileReader
|
||||||
|
{
|
||||||
|
// Get the dependencies for a target from a Makefile.
|
||||||
|
// Does not recurse into the dependencies of those dependencies.
|
||||||
|
public static List<string> ReadTargetDependencies(IExecutionContext executionContext, string makefile, string target)
|
||||||
|
{
|
||||||
|
var targetToFind = target + ":";
|
||||||
|
var lines = File.ReadLines(makefile);
|
||||||
|
string targetLine = lines.FirstOrDefault(line => line.TrimStart().StartsWith(targetToFind));
|
||||||
|
if (targetLine is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetLine.Split().Skip(1).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -144,54 +144,5 @@ namespace GitHub.Runner.Common.Tests.Worker.Container
|
|||||||
var actual = DockerUtil.ParseRegistryHostnameFromImageName(input);
|
var actual = DockerUtil.ParseRegistryHostnameFromImageName(input);
|
||||||
Assert.Equal(expected, actual);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.283.4
|
2.283.3
|
||||||
|
|||||||
Reference in New Issue
Block a user