From df0737e55afeb5c01eb39909605970d1d3a73ffc Mon Sep 17 00:00:00 2001 From: Melodi <62381002+Melodi17@users.noreply.github.com> Date: Sun, 13 Sep 2026 13:22:20 +1000 Subject: refactor: cleanup; feat: connection to agent --- src/marlin.core/functional/WorkflowBuilder.cs | 153 ++++++++++++++++++++++++++ src/marlin.core/functional/WorkflowJson.cs | 17 +++ 2 files changed, 170 insertions(+) create mode 100644 src/marlin.core/functional/WorkflowBuilder.cs create mode 100644 src/marlin.core/functional/WorkflowJson.cs (limited to 'src/marlin.core/functional') diff --git a/src/marlin.core/functional/WorkflowBuilder.cs b/src/marlin.core/functional/WorkflowBuilder.cs new file mode 100644 index 0000000..79da909 --- /dev/null +++ b/src/marlin.core/functional/WorkflowBuilder.cs @@ -0,0 +1,153 @@ +namespace marlin.core.functional; + +using models.meta; +using models.meta.dto; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +public class WorkflowBuilder +{ + private static readonly IDeserializer Deserializer = new DeserializerBuilder() + .WithNamingConvention(HyphenatedNamingConvention.Instance) + .WithEnumNamingConvention(HyphenatedNamingConvention.Instance) + .IgnoreUnmatchedProperties() + .Build(); + + private readonly Workflow _workflow = new(); + + public Workflow Build() => this._workflow; + + public void LoadFromDirectory(string path) + { + var files = Directory.EnumerateFiles(path, "*.yml", SearchOption.AllDirectories); + + foreach (var file in files) + this.LoadFromString(File.ReadAllText(file)); + } + + public void LoadFromString(string content) + { + var file = Deserializer.Deserialize(content); + if (file == null) + return; + + this.ApplyRequirements(file.Requirements); + this.ApplyRecipes(file.Recipes); + this.ApplySteps(file.Steps); + this.ApplyArtifactHandlers(file.ArtifactHandlers); + } + + private void ApplyRequirements(Dictionary? requirements) + { + if (requirements == null) + return; + + foreach (var (id, dto) in requirements) + { + var requirementSet = new RequirementSet(id) + { + Base = dto.Base, + Has = dto.Has != null ? new Dictionary(dto.Has) : new() + }; + + this._workflow.Requirements[id] = requirementSet; + } + } + + private void ApplyRecipes(Dictionary? recipes) + { + if (recipes == null) + return; + + foreach (var (id, dto) in recipes) + { + var recipe = new Recipe(id) + { + Name = dto.Name ?? id, + RunsOnId = dto.RunsOn, + RequireIds = dto.Requires ?? [], + Parameters = dto.Parameters != null ? new Dictionary(dto.Parameters) : new(), + Environment = dto.Environment != null ? new Dictionary(dto.Environment) : new() + }; + + if (dto.Steps != null) + { + foreach (var (stepId, stepDto) in dto.Steps) + recipe.Steps[stepId] = this.BuildRecipeStep(stepId, stepDto); + } + + this._workflow.Recipes[id] = recipe; + } + } + + private void ApplySteps(Dictionary? steps) + { + if (steps == null) + return; + + foreach (var (id, dto) in steps) + this._workflow.Steps[id] = this.BuildRecipeStep(id, dto); + } + + private RecipeStep BuildRecipeStep(string id, StepDto dto) + { + var step = new RecipeStep(id); + ApplyAbstractStep(step, dto); + + step.BaseId = dto.Base; + step.Ingest = dto.Ingest ?? []; + + if (dto.Artifacts != null) + { + foreach (var (artifactId, artifactDto) in dto.Artifacts) + { + step.Artifacts[artifactId] = new Artifact(artifactId) + { + Path = artifactDto.Path!, + Type = artifactDto.As!, + Handle = artifactDto.Handle ?? ArtifactHandle.Defer + }; + } + } + + if (dto.Cleanup != null) + step.Cleanup = BuildCleanupStep(id + "/cleanup", dto.Cleanup); + + return step; + } + + private static CleanupStep BuildCleanupStep(string id, CleanupStepDto dto) + { + var step = new CleanupStep(id) { After = dto.After ?? CleanupAfter.Step }; + ApplyAbstractStep(step, dto); + return step; + } + + private void ApplyArtifactHandlers(Dictionary? handlers) + { + if (handlers == null) + return; + + foreach (var (id, dto) in handlers) + { + var handler = new ArtifactHandler(id); + + if (dto.Handle != null) + { + var handleStep = new ArtifactHandlerStep(id + "/handle"); + ApplyAbstractStep(handleStep, dto.Handle); + handler.Handle = handleStep; + } + + this._workflow.ArtifactHandlers[id] = handler; + } + } + + private static void ApplyAbstractStep(AbstractStep step, StepDtoBase dto) + { + step.Name = dto.Name; + step.Run = dto.Run; + step.With = dto.With != null ? new Dictionary(dto.With) : new(); + step.Fail = dto.Fail; + } +} diff --git a/src/marlin.core/functional/WorkflowJson.cs b/src/marlin.core/functional/WorkflowJson.cs new file mode 100644 index 0000000..011edae --- /dev/null +++ b/src/marlin.core/functional/WorkflowJson.cs @@ -0,0 +1,17 @@ +namespace marlin.core.functional; + +using System.Text.Json; +using System.Text.Json.Serialization; +using models.meta; + +public static class WorkflowJson +{ + private static readonly JsonSerializerOptions Options = new() + { + Converters = { new JsonStringEnumConverter() } + }; + + public static string Serialize(Workflow workflow) => JsonSerializer.Serialize(workflow, Options); + + public static Workflow Deserialize(string json) => JsonSerializer.Deserialize(json, Options)!; +} -- cgit v1.3.1