From f25c9dfba336aac9085f02072858fb8a5f03a047 Mon Sep 17 00:00:00 2001 From: Ferenc Hammerl <31069338+fhammerl@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:32:30 +0200 Subject: [PATCH] Fixes `if:cancelled()` composite steps not running and normal composite steps not interrupting when the job is cancelled. (#2638) * Set composite step's action_status when job is cancelled Also make composite step inherit timeout from parent * Fix eof line --- src/Runner.Worker/ExecutionContext.cs | 37 ++++++++++++++++++- .../Handlers/CompositeActionHandler.cs | 3 ++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/Runner.Worker/ExecutionContext.cs b/src/Runner.Worker/ExecutionContext.cs index 24cc52444..7f111fd52 100644 --- a/src/Runner.Worker/ExecutionContext.cs +++ b/src/Runner.Worker/ExecutionContext.cs @@ -78,6 +78,7 @@ namespace GitHub.Runner.Worker List StepEnvironmentOverrides { get; } ExecutionContext Root { get; } + ExecutionContext Parent { get; } // Initialize void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token); @@ -264,6 +265,14 @@ namespace GitHub.Runner.Worker } } + public ExecutionContext Parent + { + get + { + return _parentExecutionContext; + } + } + public JobContext JobContext { get @@ -406,7 +415,7 @@ namespace GitHub.Runner.Worker /// /// An embedded execution context shares the same record ID, record name, logger, - /// and a linked cancellation token. + /// but NOT the cancellation token (just like workflow steps contexts - they don't share a token) /// public IExecutionContext CreateEmbeddedChild( string scopeName, @@ -416,7 +425,7 @@ namespace GitHub.Runner.Worker Dictionary intraActionState = null, string siblingScopeName = null) { - return Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, contextName, stage, logger: _logger, isEmbedded: true, cancellationTokenSource: CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token), intraActionState: intraActionState, embeddedId: embeddedId, siblingScopeName: siblingScopeName); + return Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, contextName, stage, logger: _logger, isEmbedded: true, cancellationTokenSource: null, intraActionState: intraActionState, embeddedId: embeddedId, siblingScopeName: siblingScopeName); } public void Start(string currentOperation = null) @@ -597,9 +606,33 @@ namespace GitHub.Runner.Worker if (timeout != null) { _cancellationTokenSource.CancelAfter(timeout.Value); + m_timeoutStartedAt = DateTime.UtcNow; + m_timeout = timeout.Value; } } + DateTime? m_timeoutStartedAt; + TimeSpan? m_timeout; + public TimeSpan? GetRemainingTimeout() + { + if (m_timeoutStartedAt != null && m_timeout != null) + { + var elapsedSinceTimeoutSet = DateTime.UtcNow - m_timeoutStartedAt.Value; + var remainingTimeout = m_timeout.Value - elapsedSinceTimeoutSet; + if (remainingTimeout.Ticks > 0) + { + return remainingTimeout; + } + else + { + // there was a timeout and it has expired + return TimeSpan.Zero; + } + } + // no timeout was ever set + return null; + } + public void Progress(int percentage, string currentOperation = null) { if (percentage > 100 || percentage < 0) diff --git a/src/Runner.Worker/Handlers/CompositeActionHandler.cs b/src/Runner.Worker/Handlers/CompositeActionHandler.cs index ee746cfc8..dbdd15d99 100644 --- a/src/Runner.Worker/Handlers/CompositeActionHandler.cs +++ b/src/Runner.Worker/Handlers/CompositeActionHandler.cs @@ -310,6 +310,7 @@ namespace GitHub.Runner.Worker.Handlers // Mark job as cancelled ExecutionContext.Root.Result = TaskResult.Canceled; ExecutionContext.Root.JobContext.Status = ExecutionContext.Root.Result?.ToActionResult(); + step.ExecutionContext.SetGitHubContext("action_status", (ExecutionContext.Root.Result?.ToActionResult() ?? ActionResult.Cancelled).ToString().ToLowerInvariant()); step.ExecutionContext.Debug($"Re-evaluate condition on job cancellation for step: '{step.DisplayName}'."); var conditionReTestTraceWriter = new ConditionTraceWriter(Trace, null); // host tracing only @@ -420,6 +421,8 @@ namespace GitHub.Runner.Worker.Handlers { Trace.Info($"Starting: {step.DisplayName}"); step.ExecutionContext.Debug($"Starting: {step.DisplayName}"); + // composite steps inherit the timeout from the parent, set by https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes + step.ExecutionContext.SetTimeout(step.ExecutionContext.Parent.GetRemainingTimeout()); await Common.Util.EncodingUtil.SetEncoding(HostContext, Trace, step.ExecutionContext.CancellationToken);