Compare commits

..

1 Commits

Author SHA1 Message Date
Thomas Boop
48e9fd1a88 Update releaseVersion 2022-09-26 11:42:31 -04:00
13 changed files with 42 additions and 266 deletions

View File

@@ -1,11 +1,13 @@
## Features
- Service containers startup error logs are now included in workflow's logs (#2110)
- Created prerelease runner package for win-arm64 architecture (#2022)
- Added `GITHUB_STATE` and `GITHUB_OUTPUT` environment file commands (#2118)
<!-- ## Bugs -->
## Bugs
- Fixed an issue where self hosted environments had their docker env's overwritten (#2107)
- Fixed an issue where step summaries for composite actions got overwritten (#2077)
## Misc
- Added a feature flag to start warning on `save-state` and `set-output` deprecation (#2164)
- Prepare supporting `vars` in workflow templates (#2096)
- Bumped `actions/core` dependency (#2123)
## 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.

View File

@@ -1 +1 @@
2.298.0
2.297.0

View File

@@ -159,7 +159,6 @@ namespace GitHub.Runner.Common
public static readonly string WorkerCrash = "WORKER_CRASH";
public static readonly string LowDiskSpace = "LOW_DISK_SPACE";
public static readonly string UnsupportedCommand = "UNSUPPORTED_COMMAND";
public static readonly string UnsupportedCommandMessage = "The `{0}` command is deprecated and will be disabled soon. Please upgrade to using Environment Files. For more information see: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/";
public static readonly string UnsupportedCommandMessageDisabled = "The `{0}` command is disabled. Please upgrade to using Environment Files or opt into unsecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_COMMANDS` environment variable to `true`. For more information see: https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/";
public static readonly string UnsupportedStopCommandTokenDisabled = "You cannot use a endToken that is an empty string, the string 'pause-logging', or another workflow command. For more information see: https://docs.github.com/actions/learn-github-actions/workflow-commands-for-github-actions#example-stopping-and-starting-workflow-commands or opt into insecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_STOPCOMMAND_TOKENS` environment variable to `true`.";
public static readonly string UnsupportedSummarySize = "$GITHUB_STEP_SUMMARY upload aborted, supports content up to a size of {0}k, got {1}k. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";

View File

@@ -307,17 +307,6 @@ namespace GitHub.Runner.Worker
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{
if (context.Global.Variables.GetBoolean("DistributedTask.DeprecateStepOutputCommands") ?? false)
{
var issue = new Issue()
{
Type = IssueType.Warning,
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
};
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
context.AddIssue(issue);
}
if (!command.Properties.TryGetValue(SetOutputCommandProperties.Name, out string outputName) || string.IsNullOrEmpty(outputName))
{
throw new Exception("Required field 'name' is missing in ##[set-output] command.");
@@ -342,17 +331,6 @@ namespace GitHub.Runner.Worker
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command, ContainerInfo container)
{
if (context.Global.Variables.GetBoolean("DistributedTask.DeprecateStepOutputCommands") ?? false)
{
var issue = new Issue()
{
Type = IssueType.Warning,
Message = String.Format(Constants.Runner.UnsupportedCommandMessage, this.Command)
};
issue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.UnsupportedCommand;
context.AddIssue(issue);
}
if (!command.Properties.TryGetValue(SaveStateCommandProperties.Name, out string stateName) || string.IsNullOrEmpty(stateName))
{
throw new Exception("Required field 'name' is missing in ##[save-state] command.");
@@ -608,7 +586,7 @@ namespace GitHub.Runner.Worker
public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command, ContainerInfo container)
{
ValidateLinesAndColumns(command, context);
command.Properties.TryGetValue(IssueCommandProperties.File, out string file);
command.Properties.TryGetValue(IssueCommandProperties.Line, out string line);
command.Properties.TryGetValue(IssueCommandProperties.Column, out string column);

View File

@@ -92,8 +92,6 @@ namespace GitHub.Runner.Worker.Container
public bool IsJobContainer { get; set; }
public bool IsAlpine { get; set; }
public bool FailedInitialization { get; set; }
public IDictionary<string, string> ContainerEnvironmentVariables
{
get

View File

@@ -98,41 +98,12 @@ namespace GitHub.Runner.Worker
await StartContainerAsync(executionContext, container);
}
await RunContainersHealthcheck(executionContext, containers);
}
public async Task RunContainersHealthcheck(IExecutionContext executionContext, List<ContainerInfo> containers)
{
executionContext.Output("##[group]Waiting for all services to be ready");
var unhealthyContainers = new List<ContainerInfo>();
foreach (var container in containers.Where(c => !c.IsJobContainer))
{
var healthcheck = await ContainerHealthcheck(executionContext, container);
if (!string.Equals(healthcheck, "healthy", StringComparison.OrdinalIgnoreCase))
{
unhealthyContainers.Add(container);
}
else
{
executionContext.Output($"{container.ContainerNetworkAlias} service is healthy.");
}
await ContainerHealthcheck(executionContext, container);
}
executionContext.Output("##[endgroup]");
if (unhealthyContainers.Count > 0)
{
foreach (var container in unhealthyContainers)
{
executionContext.Output($"##[group]Service container {container.ContainerNetworkAlias} failed.");
await _dockerManager.DockerLogs(context: executionContext, containerId: container.ContainerId);
executionContext.Error($"Failed to initialize container {container.ContainerImage}");
container.FailedInitialization = true;
executionContext.Output("##[endgroup]");
}
throw new InvalidOperationException("One or more containers failed to start.");
}
}
public async Task StopContainersAsync(IExecutionContext executionContext, object data)
@@ -328,15 +299,16 @@ namespace GitHub.Runner.Worker
if (!string.IsNullOrEmpty(container.ContainerId))
{
if (!container.IsJobContainer && !container.FailedInitialization)
if (!container.IsJobContainer)
{
executionContext.Output($"Print service container logs: {container.ContainerDisplayName}");
// Print logs for service container jobs (not the "action" job itself b/c that's already logged).
executionContext.Output($"Print service container logs: {container.ContainerDisplayName}");
int logsExitCode = await _dockerManager.DockerLogs(executionContext, container.ContainerId);
if (logsExitCode != 0)
{
executionContext.Warning($"Docker logs fail with exit code {logsExitCode}");
}
int logsExitCode = await _dockerManager.DockerLogs(executionContext, container.ContainerId);
if (logsExitCode != 0)
{
executionContext.Warning($"Docker logs fail with exit code {logsExitCode}");
}
}
executionContext.Output($"Stop and remove container: {container.ContainerDisplayName}");
@@ -423,14 +395,14 @@ namespace GitHub.Runner.Worker
}
}
private async Task<string> ContainerHealthcheck(IExecutionContext executionContext, ContainerInfo container)
private async Task ContainerHealthcheck(IExecutionContext executionContext, ContainerInfo container)
{
string healthCheck = "--format=\"{{if .Config.Healthcheck}}{{print .State.Health.Status}}{{end}}\"";
string serviceHealth = (await _dockerManager.DockerInspect(context: executionContext, dockerObject: container.ContainerId, options: healthCheck)).FirstOrDefault();
if (string.IsNullOrEmpty(serviceHealth))
{
// Container has no HEALTHCHECK
return String.Empty;
return;
}
var retryCount = 0;
while (string.Equals(serviceHealth, "starting", StringComparison.OrdinalIgnoreCase))
@@ -441,7 +413,14 @@ namespace GitHub.Runner.Worker
serviceHealth = (await _dockerManager.DockerInspect(context: executionContext, dockerObject: container.ContainerId, options: healthCheck)).FirstOrDefault();
retryCount++;
}
return serviceHealth;
if (string.Equals(serviceHealth, "healthy", StringComparison.OrdinalIgnoreCase))
{
executionContext.Output($"{container.ContainerNetworkAlias} service is healthy.");
}
else
{
throw new InvalidOperationException($"Failed to initialize, {container.ContainerNetworkAlias} service is {serviceHealth}.");
}
}
private async Task<string> ContainerRegistryLogin(IExecutionContext executionContext, ContainerInfo container)

View File

@@ -73,7 +73,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
public const String TimeoutMinutes = "timeout-minutes";
public const String Username = "username";
public const String Uses = "uses";
public const String Vars = "vars";
public const String VmImage = "vmImage";
public const String Volumes = "volumes";
public const String With = "with";

View File

@@ -631,7 +631,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
{
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.GitHub),
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Needs),
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Vars),
};
private static readonly INamedValueInfo[] s_stepNamedValues = new INamedValueInfo[]
{
@@ -644,7 +643,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Runner),
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Env),
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Needs),
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Vars),
};
private static readonly IFunctionInfo[] s_stepConditionFunctions = new IFunctionInfo[]
{

View File

@@ -465,7 +465,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
PipelineTemplateConstants.Job,
PipelineTemplateConstants.Runner,
PipelineTemplateConstants.Env,
PipelineTemplateConstants.Vars,
};
private readonly String[] s_expressionFunctionNames = new[]
{

View File

@@ -36,8 +36,7 @@
"workflow-env": {
"context": [
"github",
"secrets",
"vars"
"secrets"
],
"mapping": {
"loose-key-type": "non-empty-string",
@@ -87,7 +86,6 @@
"context": [
"github",
"needs",
"vars",
"always(0,0)",
"failure(0,MAX)",
"cancelled(0,0)",
@@ -100,7 +98,6 @@
"context": [
"github",
"needs",
"vars",
"always(0,0)",
"failure(0,MAX)",
"cancelled(0,0)",
@@ -119,8 +116,7 @@
"strategy": {
"context": [
"github",
"needs",
"vars"
"needs"
],
"mapping": {
"properties": {
@@ -160,8 +156,7 @@
"github",
"needs",
"strategy",
"matrix",
"vars"
"matrix"
],
"one-of": [
"non-empty-string",
@@ -187,8 +182,7 @@
"needs",
"strategy",
"matrix",
"secrets",
"vars"
"secrets"
],
"mapping": {
"loose-key-type": "non-empty-string",
@@ -210,8 +204,7 @@
"strategy",
"matrix",
"needs",
"env",
"vars"
"env"
],
"mapping": {
"properties": {
@@ -288,7 +281,6 @@
"job",
"runner",
"env",
"vars",
"always(0,0)",
"failure(0,0)",
"cancelled(0,0)",
@@ -307,7 +299,6 @@
"job",
"runner",
"env",
"vars",
"always(0,0)",
"failure(0,0)",
"cancelled(0,0)",
@@ -335,7 +326,6 @@
"job",
"runner",
"env",
"vars",
"hashFiles(1,255)"
],
"mapping": {
@@ -355,7 +345,6 @@
"job",
"runner",
"env",
"vars",
"hashFiles(1,255)"
],
"mapping": {
@@ -369,8 +358,7 @@
"github",
"needs",
"strategy",
"matrix",
"vars"
"matrix"
],
"one-of": [
"string",
@@ -396,8 +384,7 @@
"github",
"needs",
"strategy",
"matrix",
"vars"
"matrix"
],
"mapping": {
"loose-key-type": "non-empty-string",
@@ -410,8 +397,7 @@
"github",
"needs",
"strategy",
"matrix",
"vars"
"matrix"
],
"one-of": [
"non-empty-string",
@@ -423,8 +409,7 @@
"context": [
"secrets",
"env",
"github",
"vars"
"github"
],
"mapping": {
"properties": {
@@ -458,8 +443,7 @@
"github",
"needs",
"strategy",
"matrix",
"vars"
"matrix"
],
"boolean": {}
},
@@ -469,8 +453,7 @@
"github",
"needs",
"strategy",
"matrix",
"vars"
"matrix"
],
"number": {}
},
@@ -480,8 +463,7 @@
"github",
"needs",
"strategy",
"matrix",
"vars"
"matrix"
],
"string": {}
},
@@ -497,7 +479,6 @@
"job",
"runner",
"env",
"vars",
"hashFiles(1,255)"
],
"boolean": {}
@@ -514,7 +495,6 @@
"job",
"runner",
"env",
"vars",
"hashFiles(1,255)"
],
"number": {}
@@ -530,8 +510,7 @@
"steps",
"job",
"runner",
"env",
"vars"
"env"
],
"string": {}
},
@@ -545,8 +524,7 @@
"steps",
"job",
"runner",
"env",
"vars"
"env"
],
"string": {}
},
@@ -562,7 +540,6 @@
"job",
"runner",
"env",
"vars",
"hashFiles(1,255)"
],
"string": {}

View File

@@ -1,108 +0,0 @@
using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Container;
using Xunit;
using Moq;
using GitHub.Runner.Worker.Container.ContainerHooks;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using GitHub.DistributedTask.WebApi;
using System;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class ContainerOperationProviderL0
{
private TestHostContext _hc;
private Mock<IExecutionContext> _ec;
private Mock<IDockerCommandManager> _dockerManager;
private Mock<IContainerHookManager> _containerHookManager;
private ContainerOperationProvider containerOperationProvider;
private Mock<IJobServerQueue> serverQueue;
private Mock<IPagingLogger> pagingLogger;
private List<string> healthyDockerStatus = new List<string> { "healthy" };
private List<string> unhealthyDockerStatus = new List<string> { "unhealthy" };
private List<string> dockerLogs = new List<string> { "log1", "log2", "log3" };
List<ContainerInfo> containers = new List<ContainerInfo>();
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async void RunServiceContainersHealthcheck_UnhealthyServiceContainer_AssertFailedTask()
{
//Arrange
Setup();
_dockerManager.Setup(x => x.DockerInspect(_ec.Object, It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(unhealthyDockerStatus));
//Act
try
{
await containerOperationProvider.RunContainersHealthcheck(_ec.Object, containers);
}
catch (InvalidOperationException)
{
//Assert
Assert.Equal(TaskResult.Failed, _ec.Object.Result ?? TaskResult.Failed);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async void RunServiceContainersHealthcheck_UnhealthyServiceContainer_AssertExceptionThrown()
{
//Arrange
Setup();
_dockerManager.Setup(x => x.DockerInspect(_ec.Object, It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(unhealthyDockerStatus));
//Act and Assert
await Assert.ThrowsAsync<InvalidOperationException>(() => containerOperationProvider.RunContainersHealthcheck(_ec.Object, containers));
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async void RunServiceContainersHealthcheck_healthyServiceContainer_AssertSucceededTask()
{
//Arrange
Setup();
_dockerManager.Setup(x => x.DockerInspect(_ec.Object, It.IsAny<string>(), It.IsAny<string>())).Returns(Task.FromResult(healthyDockerStatus));
//Act
await containerOperationProvider.RunContainersHealthcheck(_ec.Object, containers);
//Assert
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
}
private void Setup([CallerMemberName] string testName = "")
{
containers.Add(new ContainerInfo() { ContainerImage = "ubuntu:16.04" });
_hc = new TestHostContext(this, testName);
_ec = new Mock<IExecutionContext>();
serverQueue = new Mock<IJobServerQueue>();
pagingLogger = new Mock<IPagingLogger>();
_dockerManager = new Mock<IDockerCommandManager>();
_containerHookManager = new Mock<IContainerHookManager>();
containerOperationProvider = new ContainerOperationProvider();
_hc.SetSingleton<IDockerCommandManager>(_dockerManager.Object);
_hc.SetSingleton<IJobServerQueue>(serverQueue.Object);
_hc.SetSingleton<IPagingLogger>(pagingLogger.Object);
_hc.SetSingleton<IDockerCommandManager>(_dockerManager.Object);
_hc.SetSingleton<IContainerHookManager>(_containerHookManager.Object);
_ec.Setup(x => x.Global).Returns(new GlobalContext());
containerOperationProvider.Initialize(_hc);
}
}
}

View File

@@ -844,51 +844,6 @@ namespace GitHub.Runner.Common.Tests.Worker
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void ActionVariables_AddedToVarsContext()
{
using (TestHostContext hc = CreateTestContext())
{
TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference();
TimelineReference timeline = new TimelineReference();
Guid jobId = Guid.NewGuid();
string jobName = "some job name";
var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null);
jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource()
{
Alias = Pipelines.PipelineConstants.SelfAlias,
Id = "github",
Version = "sha1"
});
jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
var inputVarsContext = new DictionaryContextData();
inputVarsContext["VARIABLE_1"] = new StringContextData("value1");
inputVarsContext["VARIABLE_2"] = new StringContextData("value2");
jobRequest.ContextData["vars"] = inputVarsContext;
// Arrange: Setup the paging logger.
var pagingLogger1 = new Mock<IPagingLogger>();
var jobServerQueue = new Mock<IJobServerQueue>();
hc.EnqueueInstance(pagingLogger1.Object);
hc.SetSingleton(jobServerQueue.Object);
var jobContext = new Runner.Worker.ExecutionContext();
jobContext.Initialize(hc);
jobContext.InitializeJob(jobRequest, CancellationToken.None);
var expected = new DictionaryContextData();
expected["VARIABLE_1"] = new StringContextData("value1");
expected["VARIABLE_2"] = new StringContextData("value1");
Assert.True(ExpressionValuesAssertEqual(expected, jobContext.ExpressionValues["vars"] as DictionaryContextData));
}
}
private bool ExpressionValuesAssertEqual(DictionaryContextData expect, DictionaryContextData actual)
{
foreach (var key in expect.Keys.ToList())

View File

@@ -1 +1 @@
2.298.0
2.297.0