summaryrefslogtreecommitdiff
path: root/src/marlin.agents.shell
diff options
context:
space:
mode:
authorMelodi <62381002+Melodi17@users.noreply.github.com>2026-10-28 00:04:17 +1000
committerMelodi <62381002+Melodi17@users.noreply.github.com>2026-10-28 00:04:17 +1000
commite566edc0442330c11906da384d589c47f836de37 (patch)
tree55697bf60d402beeae06ea9258a827a931a359e4 /src/marlin.agents.shell
parent64172b677f699b816d76ab1ea13264b4f91bfb46 (diff)
fix: file name; move into loop and uow
Diffstat (limited to 'src/marlin.agents.shell')
-rw-r--r--src/marlin.agents.shell/CommandRunner.cs5
-rw-r--r--src/marlin.agents.shell/JobUnitOfWork.cs55
-rw-r--r--src/marlin.agents.shell/Program.cs42
-rw-r--r--src/marlin.agents.shell/SlaveLoop.cs69
4 files changed, 109 insertions, 62 deletions
diff --git a/src/marlin.agents.shell/CommandRunner.cs b/src/marlin.agents.shell/CommandRunner.cs
index d4fcbb3..90c0a58 100644
--- a/src/marlin.agents.shell/CommandRunner.cs
+++ b/src/marlin.agents.shell/CommandRunner.cs
@@ -2,7 +2,7 @@ using System.Diagnostics;
public static class CommandRunner
{
- public static async Task<int> RunAsync(string command, Action<string> onStdout, Action<string> onStderr)
+ public static async Task<int> RunAsync(string command, string workingDir, Action<string> onStdout, Action<string> onStderr)
{
var psi = new ProcessStartInfo
{
@@ -11,7 +11,8 @@ public static class CommandRunner
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
- CreateNoWindow = true
+ CreateNoWindow = true,
+ WorkingDirectory = workingDir
};
var process = new Process { StartInfo = psi };
diff --git a/src/marlin.agents.shell/JobUnitOfWork.cs b/src/marlin.agents.shell/JobUnitOfWork.cs
index 85df806..5d639c0 100644
--- a/src/marlin.agents.shell/JobUnitOfWork.cs
+++ b/src/marlin.agents.shell/JobUnitOfWork.cs
@@ -12,38 +12,27 @@ using ProtoBuf.Grpc.Client;
public class JobUnitOfWork
{
- private static string _jobInstanceDir = Path.Combine(CommonPaths.AgentRoot, "instance");
+ private readonly IInstanceService _instanceService;
+ private readonly Channel<LogEventRequest> _logQueue;
- private readonly MarlinApiClient _apiClient;
- private readonly IGitClient _gitClient;
- private readonly DispatchJob _job;
- private readonly string _checkoutPath;
+ protected readonly string _workingDir;
+ protected readonly string _recipe;
- private GrpcChannel _channel;
- private IInstanceService _instanceService;
-
- private Channel<LogEventRequest> _logQueue;
-
- public JobUnitOfWork(DispatchJob job, MarlinApiClient apiClient, IGitClient gitClient)
+ public JobUnitOfWork(IInstanceService instanceService, string workingDir, string recipe)
{
- this._job = job;
- this._apiClient = apiClient;
- this._gitClient = gitClient;
+ this._instanceService = instanceService;
+ this._logQueue = Channel.CreateUnbounded<LogEventRequest>();
- this._checkoutPath = Path.Combine(_jobInstanceDir, job.Id.ToString("N"));
+ this._workingDir = workingDir;
+ this._recipe = recipe;
}
public async Task Run()
{
- this._channel = this._apiClient.CreateJobScopedRpc(this._job);
- this._instanceService = this._channel.CreateGrpcService<IInstanceService>();
- this._logQueue = Channel.CreateUnbounded<LogEventRequest>();
-
var drainTask = Task.Run(this.DrainLogEvents);
await StatusUpdate(JobStatus.Active);
- await this.CheckoutSource();
var plan = await this.BuildPlan();
var success = await this.ExecutePlan(plan);
@@ -53,28 +42,13 @@ public class JobUnitOfWork
await drainTask;
}
- private async Task CheckoutSource()
- {
- Directory.CreateDirectory(_jobInstanceDir);
- if (Path.Exists(this._checkoutPath))
- Directory.Delete(this._checkoutPath, true);
-
- await this._gitClient.Checkout(this._checkoutPath, this._job.Vcs, this._job.Ref, this._job.Commit);
- }
-
- private async Task DrainLogEvents()
- {
- await foreach (var evt in this._logQueue.Reader.ReadAllAsync())
- await this._instanceService.SendLogEvent(evt);
- }
-
private async Task<ExecutionPlan> BuildPlan()
{
WorkflowBuilder builder = new();
- builder.LoadFromDirectory(Path.Join(this._checkoutPath, ".marlin"));
+ builder.LoadFromDirectory(Path.Join(this._workingDir, ".marlin"));
Workflow workflow = builder.Build();
- Recipe recipe = workflow.Recipes[this._job.Recipe];
+ Recipe recipe = workflow.Recipes[this._recipe];
ExecutionPlan plan = ExecutionPlan.Create(workflow, recipe);
return plan;
@@ -101,6 +75,7 @@ public class JobUnitOfWork
Console.WriteLine(component);
Task<int> run = CommandRunner.RunAsync(
component.Command,
+ this._workingDir,
o => Log(o, false, component),
o => Log(o, true, component));
@@ -111,6 +86,12 @@ public class JobUnitOfWork
return results.All(x => x.Value == CommandResult.Success);
}
+ private async Task DrainLogEvents()
+ {
+ await foreach (var evt in this._logQueue.Reader.ReadAllAsync())
+ await this._instanceService.SendLogEvent(evt);
+ }
+
private void Log(string message, bool error, ExecutionCommand component)
{
this._logQueue.Writer.TryWrite(
diff --git a/src/marlin.agents.shell/Program.cs b/src/marlin.agents.shell/Program.cs
index 75ea8ad..c895e8d 100644
--- a/src/marlin.agents.shell/Program.cs
+++ b/src/marlin.agents.shell/Program.cs
@@ -8,36 +8,32 @@ using marlin.core.models;
using marlin.core.models.meta;
using marlin.core.models.plan;
using marlin.core.util;
+using ProtoBuf.Grpc;
using ProtoBuf.Grpc.Client;
DotNetEnv.Env.NoClobber().TraversePath().Load();
-var jobInstanceDir = Path.Combine(CommonPaths.AgentRoot, "instance");
-Directory.CreateDirectory(jobInstanceDir);
-
-var client = MarlinApiClient.FromEnvironment();
-
-var capabilities = new CapabilitySet();
-capabilities.Items.Add(KnownCapabilities.Architecture, RuntimeInformation.OSArchitecture.ToString());
-capabilities.Items.Add(KnownCapabilities.CoreCount, Environment.ProcessorCount.ToString());
-capabilities.Items.Add(KnownCapabilities.Memory, (GC.GetGCMemoryInfo().TotalAvailableMemoryBytes / (1024 * 1024)).ToString());
-var status = new AgentStatus(capabilities);
+if (args.Length == 0)
+{
+ SlaveLoop loop = new();
+ await loop.Start();
+}
+else
+{
+ string recipe = args[0];
+ string repoPath = args.Length > 1 ? args[1] : Environment.CurrentDirectory;
-AgentConfiguration config = await client.Handshake(status);
-Console.WriteLine($"IAM: {config.Name}");
-Console.WriteLine($"MCJ: {config.MaxConcurrentJobs}");
+ JobUnitOfWork uow = new(new LocalInstanceService(), repoPath, recipe);
+ await uow.Run();
+}
-while (true)
+public class LocalInstanceService : IInstanceService
{
- var job = await client.QueueNextJob();
- Console.WriteLine(job == null ? "No pending jobs." : $"Claimed job {job.Id}: {job.Recipe}");
-
- if (job != null)
+ public async Task SendLogEvent(LogEventRequest request, CallContext context = default)
{
- GitBinaryClient gitClient = new();
- var uow = new JobUnitOfWork(job, client, gitClient);
- await uow.Run();
+ TextWriter writer = request.Error ? Console.Error : Console.Out;
+ await writer.WriteLineAsync(request.Message);
}
-
- await Task.Delay(TimeSpan.FromSeconds(5));
+
+ public Task SendStatusUpdate(StatusUpdateRequest request, CallContext context = default) => Task.CompletedTask;
} \ No newline at end of file
diff --git a/src/marlin.agents.shell/SlaveLoop.cs b/src/marlin.agents.shell/SlaveLoop.cs
new file mode 100644
index 0000000..82f6ff5
--- /dev/null
+++ b/src/marlin.agents.shell/SlaveLoop.cs
@@ -0,0 +1,69 @@
+namespace marlin.agents.shell;
+
+using System.Runtime.InteropServices;
+using core.contracts;
+using core.functional;
+using core.models;
+using core.util;
+using ProtoBuf.Grpc.Client;
+
+public class SlaveLoop
+{
+ private static readonly string _jobInstanceDir = Path.Combine(CommonPaths.AgentRoot, "instance");
+ private readonly MarlinApiClient _client;
+
+ public SlaveLoop()
+ {
+ this._client = MarlinApiClient.FromEnvironment();
+ }
+
+ public async Task Start()
+ {
+ AgentConfiguration config = await Handshake();
+ Console.WriteLine($"IAM: {config.Name}");
+ Console.WriteLine($"MCJ: {config.MaxConcurrentJobs}");
+
+ while (true)
+ {
+ var job = await this._client.QueueNextJob();
+ if (job == null)
+ {
+ await Task.Delay(TimeSpan.FromSeconds(5));
+ continue;
+ }
+
+ Console.WriteLine($"Claimed job {job.Id}: {job.Recipe}");
+ await FireJob(job);
+ }
+ }
+
+ private async Task<AgentConfiguration> Handshake()
+ {
+ var capabilities = new CapabilitySet();
+ capabilities.Items.Add(KnownCapabilities.Architecture, RuntimeInformation.OSArchitecture.ToString());
+ capabilities.Items.Add(KnownCapabilities.CoreCount, Environment.ProcessorCount.ToString());
+ capabilities.Items.Add(KnownCapabilities.Memory, (GC.GetGCMemoryInfo().TotalAvailableMemoryBytes / (1024 * 1024)).ToString());
+ var status = new AgentStatus(capabilities);
+
+ AgentConfiguration config = await _client.Handshake(status);
+ return config;
+ }
+
+ private async Task FireJob(DispatchJob job)
+ {
+ GitBinaryClient gitClient = new();
+ var channel = this._client.CreateJobScopedRpc(job);
+ var instanceService = channel.CreateGrpcService<IInstanceService>();
+
+ string workingDir = Path.Combine(_jobInstanceDir, job.Id.ToString("N"));
+
+ Directory.CreateDirectory(_jobInstanceDir);
+ if (Path.Exists(workingDir))
+ Directory.Delete(workingDir, true);
+
+ await gitClient.Checkout(workingDir, job.Vcs, job.Ref, job.Commit);
+
+ var uow = new JobUnitOfWork(instanceService, workingDir, job.Recipe);
+ await uow.Run();
+ }
+} \ No newline at end of file