editing jobs

This commit is contained in:
Francesco Renzi
2026-01-21 22:30:19 +00:00
committed by GitHub
parent 9bc9aff86f
commit 008594a3ee
14 changed files with 6450 additions and 9 deletions

View File

@@ -13,6 +13,7 @@ using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk;
using GitHub.Runner.Worker.Dap.StepCommands;
using Newtonsoft.Json;
namespace GitHub.Runner.Worker.Dap
@@ -282,9 +283,21 @@ namespace GitHub.Runner.Worker.Dap
private readonly List<CompletedStepInfo> _completedSteps = new List<CompletedStepInfo>();
private int _nextCompletedFrameId = CompletedFrameIdBase;
// Track completed IStep objects for the step manipulator
private readonly List<IStep> _completedStepsTracker = new List<IStep>();
// Variable provider for converting contexts to DAP variables
private DapVariableProvider _variableProvider;
// Step command parser for !step REPL commands
private IStepCommandParser _stepCommandParser;
// Step command handler for executing step commands
private IStepCommandHandler _stepCommandHandler;
// Step manipulator for queue operations
private IStepManipulator _stepManipulator;
// Checkpoint storage for step-back (time-travel) debugging
private readonly List<StepCheckpoint> _checkpoints = new List<StepCheckpoint>();
private const int MaxCheckpoints = 50;
@@ -319,6 +332,9 @@ namespace GitHub.Runner.Worker.Dap
{
base.Initialize(hostContext);
_variableProvider = new DapVariableProvider(hostContext);
_stepCommandParser = hostContext.GetService<IStepCommandParser>();
_stepCommandHandler = hostContext.GetService<IStepCommandHandler>();
_stepManipulator = hostContext.GetService<IStepManipulator>();
Trace.Info("DapDebugSession initialized");
}
@@ -661,6 +677,12 @@ namespace GitHub.Runner.Worker.Dap
return HandleDebugCommand(expression);
}
// Check for !step command (step manipulation commands)
if (_stepCommandParser.IsStepCommand(expression))
{
return await HandleStepCommandAsync(expression);
}
// Get the current execution context
var executionContext = _currentStep?.ExecutionContext ?? _jobContext;
if (executionContext == null)
@@ -1096,6 +1118,81 @@ namespace GitHub.Runner.Worker.Dap
});
}
/// <summary>
/// Handles step manipulation commands (!step ...).
/// Parses and executes commands using the StepCommandHandler.
/// </summary>
private async Task<Response> HandleStepCommandAsync(string expression)
{
Trace.Info($"Handling step command: {expression}");
try
{
var command = _stepCommandParser.Parse(expression);
// Ensure manipulator is initialized with current context
EnsureManipulatorInitialized();
// Execute the command
var result = await _stepCommandHandler.HandleAsync(command, _jobContext);
if (result.Success)
{
// Return appropriate response format based on input type
if (command.WasJsonInput)
{
return CreateSuccessResponse(new EvaluateResponseBody
{
Result = Newtonsoft.Json.JsonConvert.SerializeObject(result),
Type = "json",
VariablesReference = 0
});
}
else
{
return CreateSuccessResponse(new EvaluateResponseBody
{
Result = result.Message,
Type = "string",
VariablesReference = 0
});
}
}
else
{
return CreateErrorResponse($"[{result.Error}] {result.Message}");
}
}
catch (StepCommandException ex)
{
Trace.Warning($"Step command error: {ex.ErrorCode} - {ex.Message}");
return CreateErrorResponse($"[{ex.ErrorCode}] {ex.Message}");
}
catch (Exception ex)
{
Trace.Error($"Step command failed: {ex}");
return CreateErrorResponse($"Step command failed: {ex.Message}");
}
}
/// <summary>
/// Ensures the step manipulator is initialized with the current job context.
/// </summary>
private void EnsureManipulatorInitialized()
{
if (_jobContext == null)
{
throw new StepCommandException(StepCommandErrors.NoContext,
"No job context available. Wait for the first step to start.");
}
// The manipulator should already be initialized from OnStepStartingAsync
// but just ensure the handler has the reference
_stepCommandHandler.SetManipulator(_stepManipulator);
}
private Response HandleSetBreakpoints(Request request)
{
// Stub - breakpoints not implemented in demo
@@ -1187,6 +1284,9 @@ namespace GitHub.Runner.Worker.Dap
_jobContext = jobContext;
_jobCancellationToken = cancellationToken; // Store for REPL commands
// Initialize or update the step manipulator
InitializeStepManipulator(step, isFirstStep);
// Hook up StepsContext debug logging (do this once when we first get jobContext)
if (jobContext.Global.StepsContext.OnDebugLog == null)
{
@@ -1233,6 +1333,28 @@ namespace GitHub.Runner.Worker.Dap
await WaitForCommandAsync(cancellationToken);
}
/// <summary>
/// Initializes or updates the step manipulator with the current state.
/// </summary>
private void InitializeStepManipulator(IStep currentStep, bool isFirstStep)
{
if (isFirstStep)
{
// First step - initialize fresh
_stepManipulator.Initialize(_jobContext, 0);
(_stepManipulator as StepManipulator)?.SetCurrentStep(currentStep);
_stepCommandHandler.SetManipulator(_stepManipulator);
_stepManipulator.RecordOriginalState();
Trace.Info("Step manipulator initialized for debug session");
}
else
{
// Update current step reference
(_stepManipulator as StepManipulator)?.SetCurrentStep(currentStep);
_stepManipulator.UpdateCurrentIndex(_completedStepsTracker.Count + 1);
}
}
public void OnStepCompleted(IStep step)
{
if (!IsActive)
@@ -1269,6 +1391,10 @@ namespace GitHub.Runner.Worker.Dap
FrameId = _nextCompletedFrameId++
});
// Track IStep for the step manipulator
_completedStepsTracker.Add(step);
_stepManipulator?.AddCompletedStep(step);
// Clear current step reference since it's done
// (will be set again when next step starts)
}
@@ -1604,6 +1730,16 @@ namespace GitHub.Runner.Worker.Dap
_completedSteps.RemoveAt(_completedSteps.Count - 1);
}
// Also clear the step tracker for manipulator sync
while (_completedStepsTracker.Count > checkpointIndex)
{
_completedStepsTracker.RemoveAt(_completedStepsTracker.Count - 1);
}
// Reset the step manipulator to match the restored state
// It will be re-initialized when the restored step starts
_stepManipulator?.ClearChanges();
// Store restored checkpoint for StepsRunner to consume
_restoredCheckpoint = checkpoint;