Compare commits

..

18 Commits

Author SHA1 Message Date
Brian Cristante
d8a53aa04a Remove dead code 2021-10-14 17:40:49 -04:00
Brian Cristante
9fe65b872a Fix detecting if the step is run: make 2021-10-14 17:16:50 -04:00
Brian Cristante
ec420bab24 Try adding steps in JobExtension 2021-10-14 16:26:00 -04:00
Brian Cristante
07cda747b2 Create a new step host for each sub-step 2021-10-14 13:59:34 -04:00
Brian Cristante
9ca132380c Detect Makefiles and parse out the targets 2021-10-14 11:56:26 -04:00
Brian Cristante
a5a0f8df47 Revert "Add GNUMakeManager"
This reverts commit ccf53338be.
2021-10-14 09:09:58 -04:00
Brian Cristante
ccf53338be Add GNUMakeManager 2021-10-12 14:11:14 -04:00
Raphael Cruzeiro
400b2d879c Makes the user keychains available to the service (#847)
Without creating a session, the service is not able to access the keychains for the user specified under `UserName`. This causes any workflow that deals with code signing to fail as the only keychain loaded with be the system one. This should fix #350
2021-10-06 15:37:45 -04:00
Thomas Boop
c4b6d288d4 fix ephemeral runner upgrade on mac/linux (#1403) 2021-10-05 10:15:19 +02:00
Julio Barba
0699597876 Use Actions Service health and api.github.com endpoints after connection failure on Actions Server and Hosted (#1385) 2021-09-30 13:40:34 -04:00
Thomas Boop
a592b14ae3 Runner 2.283.2 Release (#1389) 2021-09-29 15:49:40 -04:00
Thomas Boop
04269f7b1b Handle keeping previous OSX versions more smoothly on Mac (#1381)
* Handle macOS upgrade smoothly

* cleanup

* misc cleanup

* final updates

* Update src/Misc/layoutbin/update.sh.template

Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>

* Update src/Misc/layoutbin/update.sh.template

Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>

* Upload telemetry and default to old method as needed

* minor fix

* add one more bit of logging

* some more telemetry

* quote variables to handle spaces

* tiny fix for ubuntu

* remove version and move telemetry to diag

* use full path

Co-authored-by: Patrick Ellis <319655+pje@users.noreply.github.com>
2021-09-29 15:49:31 -04:00
Ferenc Hammerl
e89d2e84bd Stop-Commands: stopToken restrictions (#1371)
* Prevent stopTokens that are workflow commands

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>

* Check context for env var too

* Accept true, 1 and $true instead of just "true"

* Setup ExpressionValues in tests

* Update src/Runner.Common/Constants.cs

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>

* Separate success and fail tests for invalid token

* Fix envcontext for tests

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>
2021-09-29 14:44:01 -04:00
Thomas Boop
afe7066e39 only cleanup runner local files on success (#1384) 2021-09-28 18:55:28 -04:00
Ferenc Hammerl
da79ef4acb Fix unconfiguring of runner after group changes (#1359)
* Ignore agentpool when unconfiguring the runner

Runner names and IDs are unique within a ServiceHost
They don't need to be included when unconfiguring the runner.

* Use -1 instead of 0 to highlight how it is ignored

* Use overloads and 0 instead of -1

Using 0 seems to be the convention

* Fix typo calling the wrong method
2021-09-22 15:04:43 +02:00
Tingluo Huang
5afb52b272 Update the comment about the --once in Constants.cs (#1360)
* Update Constants.cs

* feedback.

* Update src/Runner.Listener/Runner.cs

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>

Co-authored-by: Thomas Boop <52323235+thboop@users.noreply.github.com>
2021-09-21 21:31:48 +00:00
Thomas Boop
cf87c55557 Don't retry 422 (#1352) 2021-09-21 09:59:21 -04:00
Ferenc Hammerl
43fa351980 Update telemetry (#1355)
* Track "pause-logging"

* Bump release version
2021-09-20 15:54:20 +02:00
15 changed files with 190 additions and 127 deletions

View File

@@ -37,7 +37,7 @@ jobs:
devScript: ./dev.sh
- runtime: win-x64
os: windows-2019
os: windows-latest
devScript: ./dev
runs-on: ${{ matrix.os }}

View File

@@ -72,7 +72,7 @@ jobs:
devScript: ./dev.sh
- runtime: win-x64
os: windows-2019
os: windows-latest
devScript: ./dev
runs-on: ${{ matrix.os }}
@@ -164,7 +164,6 @@ jobs:
release_name: "v${{ steps.releaseNote.outputs.version }}"
body: |
${{ steps.releaseNote.outputs.note }}
prerelease: true
# Upload release assets
- name: Upload Release Asset (win-x64)

View File

@@ -2,7 +2,7 @@
## 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

View File

@@ -1 +1 @@
2.283.4
<Update to ./src/runnerversion when creating release>

View File

@@ -25,5 +25,7 @@
</dict>
<key>ProcessType</key>
<string>Interactive</string>
<key>SessionCreate</key>
<true/>
</dict>
</plist>

View File

@@ -2,8 +2,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using GitHub.Runner.Sdk;
using GitHub.Services.Common;
using GitHub.Services.WebApi;
namespace GitHub.Runner.Common
@@ -36,6 +39,9 @@ namespace GitHub.Runner.Common
{
_connection = jobConnection;
int attemptCount = 5;
var configurationStore = HostContext.GetService<IConfigurationStore>();
var runnerSettings = configurationStore.GetSettings();
while (!_connection.HasAuthenticated && attemptCount-- > 0)
{
try
@@ -45,8 +51,13 @@ namespace GitHub.Runner.Common
}
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);
if (runnerSettings.IsHostedServer)
{
await CheckNetworkEndpointsAsync();
}
}
await Task.Delay(100);
@@ -56,6 +67,52 @@ namespace GitHub.Runner.Common
_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()
{
if (!_hasConnection)

View File

@@ -262,6 +262,16 @@ namespace GitHub.Runner.Worker
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.
IHandler handler = handlerFactory.Create(
ExecutionContext,

View File

@@ -131,11 +131,11 @@ namespace GitHub.Runner.Worker.Container
{
if (String.IsNullOrEmpty(env.Value))
{
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
dockerOptions.Add($"-e \"{env.Key}\"");
}
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
// the value directly in the command
dockerOptions.Add(DockerUtil.CreateEscapedOption("-e", env.Key));
dockerOptions.Add($"-e {env.Key}");
}
// Watermark for GitHub Action environment

View File

@@ -6,9 +6,6 @@ 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";
@@ -20,7 +17,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)
@@ -64,44 +61,5 @@ 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}\"";
}
}
}

View File

@@ -31,8 +31,8 @@ namespace GitHub.Runner.Worker.Handlers
Inputs.TryGetValue("script", out string contents);
contents = contents ?? string.Empty;
if (Action.Type == Pipelines.ActionSourceType.Script)
{
// if (Action.Type == Pipelines.ActionSourceType.Script)
// {
var firstLine = contents.TrimStart(' ', '\t', '\r', '\n');
var firstNewLine = firstLine.IndexOfAny(new[] { '\r', '\n' });
if (firstNewLine >= 0)
@@ -41,11 +41,11 @@ namespace GitHub.Runner.Worker.Handlers
}
ExecutionContext.Output($"##[group]Run {firstLine}");
}
else
{
throw new InvalidOperationException($"Invalid action type {Action.Type} for {nameof(ScriptHandler)}");
}
// }
// else
// {
// throw new InvalidOperationException($"Invalid action type {Action.Type} for {nameof(ScriptHandler)}");
// }
var multiLines = contents.Replace("\r\n", "\n").TrimEnd('\n').Split('\n');
foreach (var line in multiLines)

View File

@@ -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(DockerUtil.CreateEscapedOption("-e", env.Key));
dockerCommandArgs.Add($"-e {env.Key}");
}
if (!string.IsNullOrEmpty(PrependPath))
{

View File

@@ -265,29 +265,91 @@ namespace GitHub.Runner.Worker
if (step.Type == Pipelines.StepType.Action)
{
var action = step as Pipelines.ActionStep;
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)
void EmitStep(Pipelines.ActionStep action)
{
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);
jobSteps.Add(actionRunner);
if (prepareResult.PreStepTracker.TryGetValue(step.Id, out var preStep))
// HACK: if the step is "run: make," parse the Makefile and emit $"make dependency {target}"
// Only works for the default target right now.
bool isRunMake = false;
if (action.Reference?.Type == Pipelines.ActionSourceType.Script)
{
Trace.Info($"Adding pre-{action.DisplayName}.");
preStep.TryEvaluateDisplayName(contextData, context);
preStep.DisplayName = $"Pre {preStep.DisplayName}";
preJobSteps.Add(preStep);
var inputs = action.Inputs.AssertMapping(null);
foreach (var pair in inputs)
{
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);
}
}
}

View 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();
}
}
}

View File

@@ -144,54 +144,5 @@ 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);
}
}
}

View File

@@ -1 +1 @@
2.283.4
2.283.3