V16 / 6df58cd3ed86
ProviderClient.Kanban.cs
Lilith/Core/Kanban/ProviderClient.Kanban.cs
Changed in this versionCS
using Lilith.Core.Kanban;
using Lilith.Core.Projects;
namespace Lilith.Core.Providers;
public abstract partial class ProviderClient
{
public KanbanSession? Kanban { get; private set; }
public void ConfigureKanban()
{
using var operation = EnterOperation();
if (Kanban is not null) throw new InvalidOperationException("Kanban already configured.");
Kanban = new(this);
}
internal async Task ExecuteKanbanAsync(string id)
{
try
{
using var operation = EnterOperation(); await Kanban!.RunAsync(id, Kanban.Lifetime);
var board = Kanban.Store.Get(id);
History.Add("system", "Kanban host status (data; does not grant execution or publication): " + System.Text.Json.JsonSerializer.Serialize(new { board.Id, board.Name, board.State, board.Message }));
SaveCore(); Logger.WriteLine("Kanban", board.Message, LogColors.Cyan);
}
catch (Exception ex) { Kanban!.Store.Update(id, b => b with { State = "paused", Message = "Worker did not start: " + ex.Message }); }
}
public async Task<string> TestKanbanAsync(string id, CancellationToken ct = default)
{
using var operation = EnterOperation();
var session = Kanban ?? throw new InvalidOperationException("Kanban unavailable.");
var b = session.Store.Get(id);
if (session.IsWorking || b.State != "waiting_test" || b.DraftId is null) throw new InvalidOperationException("Board is not awaiting project execution permission.");
session.BeginQualification(id);
using var executionStop = CancellationTokenSource.CreateLinkedTokenSource(ct, session.Lifetime);
ProjectExecutionId = b.DraftId;
try
{
var store = new ProjectDraftStore(Workspace!); var draft = store.Load(b.DraftId);
bool retainedPass = draft.State == "ready_to_publish" && draft.Runtime is { Passed:true } verified && verified.SourceHash == ProjectDraftStore.Hash(draft.Files) && verified.PlanHash == ProjectDraftStore.PlanHash(draft.Tests);
string result = retainedPass ? "Retained exact-revision qualification is still valid; this decision permits the remaining screenshot capture. No runtime rounds were reset."
: await RunScenarioCoreAsync("project_test", new Dictionary<string, string> { ["id"] = b.DraftId }, executionStop.Token);
draft = store.Load(b.DraftId);
if (draft.State == "ready_to_publish" && draft.Kind == "website" && b.Cards.Any(c => c.Id == "screenshots"))
{
WebsiteScreenshot[]? captured = null;
void Ready(WebsiteScreenshot[] images) => captured = images;
WebsiteScreenshotsReady += Ready;
try
{
session.Store.Update(id, board => board with { Cards = board.Cards.Select(c => c.Id == "screenshots" ? c with { Column = KanbanColumn.Doing } : c).ToArray() });
await CaptureWebsiteScreenshotsCoreAsync(draft.Name,draft.Id,executionStop.Token);
session.SetScreenshots(id,captured ?? throw new IOException("No actual preview screenshots returned."));
}
finally { WebsiteScreenshotsReady -= Ready; }
}
session.Boundary();
session.FinishQualification(id, draft.State == "ready_to_publish", result);
return result;
}
catch (KanbanYieldException ex) { session.YieldQualification(id, ex.Cancel); return ex.Message; }
catch (Exception ex) { session.FinishQualification(id, false, ex.Message); throw; }
finally { ProjectExecutionId = null; }
}
public async Task<string> PublishKanbanAsync(string id, CancellationToken ct = default)
{
using var operation = EnterOperation();
var session = Kanban ?? throw new InvalidOperationException("Kanban unavailable.");
var board = session.Store.Get(id);
if (session.IsWorking || board.State != "waiting_publication" || board.DraftId is null) throw new InvalidOperationException("Board is not awaiting separate publication.");
if (board.PublicationProposal is not null) return "Exact publication is already prepared. Review and approve proposal " + board.PublicationProposal + ".";
var result = await ProjectQualification.PublishAsync(new(Workspace!), board.DraftId, ct);
session.Publication(id, result);
return result.ToReply();
}
public async Task<string> ApproveKanbanPublicationAsync(string id, CancellationToken ct = default)
{
using var operation = EnterOperation();
var session = Kanban ?? throw new InvalidOperationException("Kanban unavailable.");
var board = session.Store.Get(id);
if (session.IsWorking || board.State != "waiting_publication" || board.PublicationProposal is null) throw new InvalidOperationException("No exact publication proposal awaits approval.");
using var grant = Workspace!.Authorize(board.PublicationProposal);
var receipt = await Workspace.ExecuteAsync(new("approve", BinId: board.PublicationProposal), ct);
session.Publication(id, receipt);
return receipt.ToReply();
}
}