Compare commits

..

1 Commits

Author SHA1 Message Date
Allan Guigou
39f6c643d3 Update release version to 2.331.0 2026-01-09 12:22:08 -05:00
17 changed files with 46 additions and 194 deletions

View File

@@ -4,7 +4,7 @@
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:1": {},
"ghcr.io/devcontainers/features/dotnet": {
"version": "8.0.417"
"version": "8.0.416"
},
"ghcr.io/devcontainers/features/node:1": {
"version": "20"

View File

@@ -5,8 +5,8 @@ ARG TARGETOS
ARG TARGETARCH
ARG RUNNER_VERSION
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.7.0
ARG DOCKER_VERSION=29.2.0
ARG BUILDX_VERSION=0.31.1
ARG DOCKER_VERSION=29.0.2
ARG BUILDX_VERSION=0.30.1
RUN apt update -y && apt install curl unzip -y

View File

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

View File

@@ -6,8 +6,8 @@ NODE_URL=https://nodejs.org/dist
NODE_ALPINE_URL=https://github.com/actions/alpine_nodejs/releases/download
# When you update Node versions you must also create a new release of alpine_nodejs at that updated version.
# Follow the instructions here: https://github.com/actions/alpine_nodejs?tab=readme-ov-file#getting-started
NODE20_VERSION="20.20.0"
NODE24_VERSION="24.13.0"
NODE20_VERSION="20.19.6"
NODE24_VERSION="24.12.0"
get_abs_path() {
# exploits the fact that pwd will print abs path when no args

View File

@@ -173,7 +173,6 @@ namespace GitHub.Runner.Common
public static readonly string SnapshotPreflightImageGenPoolCheck = "actions_snapshot_preflight_image_gen_pool_check";
public static readonly string CompareWorkflowParser = "actions_runner_compare_workflow_parser";
public static readonly string SetOrchestrationIdEnvForActions = "actions_set_orchestration_id_env_for_actions";
public static readonly string SendJobLevelAnnotations = "actions_send_job_level_annotations";
}
// Node version migration related constants

View File

@@ -24,7 +24,7 @@ namespace GitHub.Runner.Listener
public interface IJobDispatcher : IRunnerService
{
bool Busy { get; }
TaskCompletionSource<int> RunOnceJobCompleted { get; }
TaskCompletionSource<bool> RunOnceJobCompleted { get; }
void Run(Pipelines.AgentJobRequestMessage message, bool runOnce = false);
bool Cancel(JobCancelMessage message);
Task WaitAsync(CancellationToken token);
@@ -56,7 +56,7 @@ namespace GitHub.Runner.Listener
// timeout limit can be overwritten by environment GITHUB_ACTIONS_RUNNER_CHANNEL_TIMEOUT
private TimeSpan _channelTimeout;
private TaskCompletionSource<int> _runOnceJobCompleted = new();
private TaskCompletionSource<bool> _runOnceJobCompleted = new();
public event EventHandler<JobStatusEventArgs> JobStatus;
@@ -82,7 +82,7 @@ namespace GitHub.Runner.Listener
Trace.Info($"Set runner/worker IPC timeout to {_channelTimeout.TotalSeconds} seconds.");
}
public TaskCompletionSource<int> RunOnceJobCompleted => _runOnceJobCompleted;
public TaskCompletionSource<bool> RunOnceJobCompleted => _runOnceJobCompleted;
public bool Busy { get; private set; }
@@ -340,19 +340,18 @@ namespace GitHub.Runner.Listener
private async Task RunOnceAsync(Pipelines.AgentJobRequestMessage message, string orchestrationId, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
{
int returnCode = 0;
try
{
returnCode = await RunAsync(message, orchestrationId, previousJobDispatch, jobRequestCancellationToken, workerCancelTimeoutKillToken);
await RunAsync(message, orchestrationId, previousJobDispatch, jobRequestCancellationToken, workerCancelTimeoutKillToken);
}
finally
{
Trace.Info("Fire signal for one time used runner.");
_runOnceJobCompleted.TrySetResult(returnCode);
_runOnceJobCompleted.TrySetResult(true);
}
}
private async Task<int> RunAsync(Pipelines.AgentJobRequestMessage message, string orchestrationId, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
private async Task RunAsync(Pipelines.AgentJobRequestMessage message, string orchestrationId, WorkerDispatcher previousJobDispatch, CancellationToken jobRequestCancellationToken, CancellationToken workerCancelTimeoutKillToken)
{
Busy = true;
try
@@ -400,7 +399,7 @@ namespace GitHub.Runner.Listener
{
// renew job request task complete means we run out of retry for the first job request renew.
Trace.Info($"Unable to renew job request for job {message.JobId} for the first time, stop dispatching job to worker.");
return 0;
return;
}
if (jobRequestCancellationToken.IsCancellationRequested)
@@ -413,7 +412,7 @@ namespace GitHub.Runner.Listener
// complete job request with result Cancelled
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, TaskResult.Canceled);
return 0;
return;
}
HostContext.WritePerfCounter($"JobRequestRenewed_{requestId.ToString()}");
@@ -524,7 +523,7 @@ namespace GitHub.Runner.Listener
await renewJobRequest;
// not finish the job request since the job haven't run on worker at all, we will not going to set a result to server.
return 0;
return;
}
// we get first jobrequest renew succeed and start the worker process with the job message.
@@ -605,7 +604,7 @@ namespace GitHub.Runner.Listener
Trace.Error(detailInfo);
}
return returnCode;
return;
}
else if (completedTask == renewJobRequest)
{
@@ -707,8 +706,6 @@ namespace GitHub.Runner.Listener
// complete job request
await CompleteJobRequestAsync(_poolId, message, systemConnection, lockToken, resultOnAbandonOrCancel);
return TaskResultUtil.TranslateToReturnCode(resultOnAbandonOrCancel);
}
finally
{

View File

@@ -5,8 +5,8 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Claims;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -406,27 +406,12 @@ namespace GitHub.Runner.Listener
try
{
Trace.Info(nameof(RunAsync));
// Validate directory permissions.
string workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);
Trace.Info($"Validating directory permissions for: '{workDirectory}'");
try
{
Directory.CreateDirectory(workDirectory);
IOUtil.ValidateExecutePermission(workDirectory);
}
catch (Exception ex)
{
Trace.Error(ex);
_term.WriteError($"Fail to create and validate runner's work directory '{workDirectory}'.");
return Constants.Runner.ReturnCode.TerminatedError;
}
// First try using migrated settings if available
var configManager = HostContext.GetService<IConfigurationManager>();
RunnerSettings migratedSettings = null;
try
try
{
migratedSettings = configManager.LoadMigratedSettings();
Trace.Info("Loaded migrated settings from .runner_migrated file");
@@ -437,15 +422,15 @@ namespace GitHub.Runner.Listener
// If migrated settings file doesn't exist or can't be loaded, we'll use the provided settings
Trace.Info($"Failed to load migrated settings: {ex.Message}");
}
bool usedMigratedSettings = false;
if (migratedSettings != null)
{
// Try to create session with migrated settings first
Trace.Info("Attempting to create session using migrated settings");
_listener = GetMessageListener(migratedSettings, isMigratedSettings: true);
try
{
CreateSessionResult createSessionResult = await _listener.CreateSessionAsync(HostContext.RunnerShutdownToken);
@@ -465,7 +450,7 @@ namespace GitHub.Runner.Listener
Trace.Error($"Exception when creating session with migrated settings: {ex}");
}
}
// If migrated settings weren't used or session creation failed, use original settings
if (!usedMigratedSettings)
{
@@ -518,7 +503,7 @@ namespace GitHub.Runner.Listener
restartSession = true;
break;
}
TaskAgentMessage message = null;
bool skipMessageDeletion = false;
try
@@ -580,20 +565,6 @@ namespace GitHub.Runner.Listener
Trace.Info($"Ignore any exception after cancel message loop. {ex}");
}
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("ACTIONS_RUNNER_RETURN_RUN_ONCE_JOB_RESULT")))
{
try
{
var jobResult = await jobDispatcher.RunOnceJobCompleted.Task;
return jobResult;
}
catch (Exception ex)
{
Trace.Error("run once job finished with error.");
Trace.Error(ex);
}
}
return Constants.Runner.ReturnCode.Success;
}
}
@@ -888,7 +859,7 @@ namespace GitHub.Runner.Listener
{
restart = false;
returnCode = await RunAsync(settings, runOnce);
if (returnCode == Constants.Runner.ReturnCode.RunnerConfigurationRefreshed)
{
Trace.Info("Runner configuration was refreshed, restarting session...");

View File

@@ -379,14 +379,7 @@ namespace GitHub.Runner.Worker
{
prefix = PipelineTemplateConstants.RunDisplayPrefix;
var repositoryReference = action.Reference as RepositoryPathReference;
var pathString = string.Empty;
if (!string.IsNullOrEmpty(repositoryReference.Path))
{
// For local actions (Name is empty), don't prepend "/" to avoid "/./"
pathString = string.IsNullOrEmpty(repositoryReference.Name)
? repositoryReference.Path
: $"/{repositoryReference.Path}";
}
var pathString = string.IsNullOrEmpty(repositoryReference.Path) ? string.Empty : $"/{repositoryReference.Path}";
var repoString = string.IsNullOrEmpty(repositoryReference.Ref) ? $"{repositoryReference.Name}{pathString}" :
$"{repositoryReference.Name}{pathString}@{repositoryReference.Ref}";
tokenToParse = new StringToken(null, null, null, repoString);

View File

@@ -499,7 +499,7 @@ namespace GitHub.Runner.Worker
PublishStepTelemetry();
if (_record.RecordType == ExecutionContextType.Task)
if (_record.RecordType == "Task")
{
var stepResult = new StepResult
{
@@ -532,25 +532,6 @@ namespace GitHub.Runner.Worker
Global.StepsResult.Add(stepResult);
}
if (Global.Variables.GetBoolean(Constants.Runner.Features.SendJobLevelAnnotations) ?? false)
{
if (_record.RecordType == ExecutionContextType.Job)
{
_record.Issues?.ForEach(issue =>
{
var annotation = issue.ToAnnotation();
if (annotation != null)
{
Global.JobAnnotations.Add(annotation.Value);
if (annotation.Value.IsInfrastructureIssue && string.IsNullOrEmpty(Global.InfrastructureFailureCategory))
{
Global.InfrastructureFailureCategory = issue.Category;
}
}
});
}
}
if (Root != this)
{
// only dispose TokenSource for step level ExecutionContext

View File

@@ -421,7 +421,7 @@
"mapping": {
"properties": {
"image": "string",
"options": "string",
"options": "non-empty-string",
"env": "container-env",
"ports": "sequence-of-non-empty-string",
"volumes": "sequence-of-non-empty-string",

View File

@@ -23,14 +23,14 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
<PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" />
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="10.0.2" />
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="8.0.0" />
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
<PackageReference Include="Minimatch" Version="2.0.0" />
<PackageReference Include="YamlDotNet.Signed" Version="5.3.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="System.Private.Uri" Version="4.3.2" />
<PackageReference Include="System.Formats.Asn1" Version="10.0.2" />
<PackageReference Include="System.Formats.Asn1" Version="8.0.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -2593,7 +2593,7 @@
"description": "Use `jobs.<job_id>.container.image` to define the Docker image to use as the container to run the action. The value can be the Docker Hub image or a registry name."
},
"options": {
"type": "string",
"type": "non-empty-string",
"description": "Use `jobs.<job_id>.container.options` to configure additional Docker container resource options."
},
"env": "container-env",

View File

@@ -739,8 +739,7 @@ namespace GitHub.Runner.Common.Tests.Listener
Assert.True(jobDispatcher.RunOnceJobCompleted.Task.IsCompleted, "JobDispatcher should set task complete token for one time agent.");
if (jobDispatcher.RunOnceJobCompleted.Task.IsCompleted)
{
var result = await jobDispatcher.RunOnceJobCompleted.Task;
Assert.Equal(0, result);
Assert.True(await jobDispatcher.RunOnceJobCompleted.Task, "JobDispatcher should set task complete token to 'TRUE' for one time agent.");
}
}
}

View File

@@ -295,13 +295,13 @@ namespace GitHub.Runner.Common.Tests.Listener
_messageListener.Setup(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()))
.Returns(Task.CompletedTask);
var runOnceJobCompleted = new TaskCompletionSource<int>();
var runOnceJobCompleted = new TaskCompletionSource<bool>();
_jobDispatcher.Setup(x => x.RunOnceJobCompleted)
.Returns(runOnceJobCompleted);
_jobDispatcher.Setup(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), It.IsAny<bool>()))
.Callback(() =>
{
runOnceJobCompleted.TrySetResult(0);
runOnceJobCompleted.TrySetResult(true);
});
_jobNotification.Setup(x => x.StartClient(It.IsAny<String>()))
.Callback(() =>
@@ -399,13 +399,13 @@ namespace GitHub.Runner.Common.Tests.Listener
_messageListener.Setup(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()))
.Returns(Task.CompletedTask);
var runOnceJobCompleted = new TaskCompletionSource<int>();
var runOnceJobCompleted = new TaskCompletionSource<bool>();
_jobDispatcher.Setup(x => x.RunOnceJobCompleted)
.Returns(runOnceJobCompleted);
_jobDispatcher.Setup(x => x.Run(It.IsAny<Pipelines.AgentJobRequestMessage>(), It.IsAny<bool>()))
.Callback(() =>
{
runOnceJobCompleted.TrySetResult(0);
runOnceJobCompleted.TrySetResult(true);
});
_jobNotification.Setup(x => x.StartClient(It.IsAny<String>()))
.Callback(() =>
@@ -733,8 +733,8 @@ namespace GitHub.Runner.Common.Tests.Listener
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
var completedTask = new TaskCompletionSource<int>();
completedTask.SetResult(0);
var completedTask = new TaskCompletionSource<bool>();
completedTask.SetResult(true);
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
//Act
@@ -834,8 +834,8 @@ namespace GitHub.Runner.Common.Tests.Listener
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
var completedTask = new TaskCompletionSource<int>();
completedTask.SetResult(0);
var completedTask = new TaskCompletionSource<bool>();
completedTask.SetResult(true);
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
//Act
@@ -954,8 +954,8 @@ namespace GitHub.Runner.Common.Tests.Listener
_configStore.Setup(x => x.IsServiceConfigured()).Returns(false);
var completedTask = new TaskCompletionSource<int>();
completedTask.SetResult(0);
var completedTask = new TaskCompletionSource<bool>();
completedTask.SetResult(true);
_jobDispatcher.Setup(x => x.RunOnceJobCompleted).Returns(completedTask);
//Act

View File

@@ -316,94 +316,6 @@ namespace GitHub.Runner.Common.Tests.Worker
Assert.Equal("${{ matrix.node }}", _actionRunner.DisplayName);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void EvaluateDisplayNameForLocalAction()
{
// Arrange
Setup();
var actionId = Guid.NewGuid();
var action = new Pipelines.ActionStep()
{
Name = "action",
Id = actionId,
Reference = new Pipelines.RepositoryPathReference()
{
RepositoryType = Pipelines.PipelineConstants.SelfAlias,
Path = "./"
}
};
_actionRunner.Action = action;
// Act
var validDisplayName = _actionRunner.EvaluateDisplayName(_context, _actionRunner.ExecutionContext, out bool updated);
// Assert
Assert.True(validDisplayName);
Assert.True(updated);
Assert.Equal("Run ./", _actionRunner.DisplayName); // NOT "Run /./"
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void EvaluateDisplayNameForLocalActionWithPath()
{
// Arrange
Setup();
var actionId = Guid.NewGuid();
var action = new Pipelines.ActionStep()
{
Name = "action",
Id = actionId,
Reference = new Pipelines.RepositoryPathReference()
{
RepositoryType = Pipelines.PipelineConstants.SelfAlias,
Path = "./.github/actions/my-action"
}
};
_actionRunner.Action = action;
// Act
var validDisplayName = _actionRunner.EvaluateDisplayName(_context, _actionRunner.ExecutionContext, out bool updated);
// Assert
Assert.True(validDisplayName);
Assert.True(updated);
Assert.Equal("Run ./.github/actions/my-action", _actionRunner.DisplayName);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void EvaluateDisplayNameForRemoteActionWithPath()
{
// Arrange
Setup();
var actionId = Guid.NewGuid();
var action = new Pipelines.ActionStep()
{
Name = "action",
Id = actionId,
Reference = new Pipelines.RepositoryPathReference()
{
Name = "owner/repo",
Path = "subdir",
Ref = "v1"
}
};
_actionRunner.Action = action;
// Act
var validDisplayName = _actionRunner.EvaluateDisplayName(_context, _actionRunner.ExecutionContext, out bool updated);
// Assert
Assert.True(validDisplayName);
Assert.True(updated);
Assert.Equal("Run owner/repo/subdir@v1", _actionRunner.DisplayName);
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
@@ -547,7 +459,7 @@ namespace GitHub.Runner.Common.Tests.Worker
_handlerFactory = new Mock<IHandlerFactory>();
_defaultStepHost = new Mock<IDefaultStepHost>();
var actionManifestLegacy = new ActionManifestManagerLegacy();
actionManifestLegacy.Initialize(_hc);
_hc.SetSingleton<IActionManifestManagerLegacy>(actionManifestLegacy);

View File

@@ -17,7 +17,7 @@ LAYOUT_DIR="$SCRIPT_DIR/../_layout"
DOWNLOAD_DIR="$SCRIPT_DIR/../_downloads/netcore2x"
PACKAGE_DIR="$SCRIPT_DIR/../_package"
DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk"
DOTNETSDK_VERSION="8.0.417"
DOTNETSDK_VERSION="8.0.416"
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
RUNNER_VERSION=$(cat runnerversion)

View File

@@ -1,5 +1,5 @@
{
"sdk": {
"version": "8.0.417"
"version": "8.0.416"
}
}