Forge
csharp3407750f
1using AgentForge.Abstractions;
2using AgentForge.Contracts;
3using AgentForge.Data;
4using AgentForge.Data.Entities;
5using AgentForge.Plugin.Ci.Contracts;
6using AgentForge.Plugin.Ci.Data;
7using AgentForge.Plugin.Ci.Execution;
8using AgentForge.Plugin.Ci.Ir;
9using AgentForge.Plugin.Ci.Pages;
10using AgentForge.Plugin.Sdk;
11using AgentForge.Plugin.View;
12using AgentForge.Services;
13using Microsoft.AspNetCore.Builder;
14using Microsoft.Extensions.DependencyInjection;
15
16namespace AgentForge.Plugin.Ci;
17
18internal static class ForgeCiViewEndpoints
19{
20 internal static void Map(WebApplication app)
21 {
22 app.MapGet($"/view/repos/{ForgeRepoRouting.Pattern}/ci", RenderCiHome);
23 app.MapGet($"/view/repos/{ForgeRepoRouting.Pattern}/ci/new", RenderCreateForm);
24 app.MapPost($"/view/repos/{ForgeRepoRouting.Pattern}/ci/new", CreateFromView);
25 app.MapGet($"/view/repos/{ForgeRepoRouting.Pattern}/ci/definitions/{{definitionName}}/designer", RenderDesigner);
26 app.MapPost($"/view/repos/{ForgeRepoRouting.Pattern}/ci/definitions/{{definitionName}}/add-task", AddTaskFromView);
27 app.MapGet($"/view/repos/{ForgeRepoRouting.Pattern}/ci/runs/{{runId}}", RenderRunTimeline);
28 }
29
30 private static IResult RenderCiHome(
31 string name,
32 ForgeRepository repository,
33 HttpContext http)
34 {
35 var repo = ForgePluginEndpoints.RequireRepo(repository, name, http);
36 var body = ForgeCiRepoTabPage.RenderHome(repo.Name, http.RequestServices);
37 return ForgeViewPresenter.RepoPage($"CI · {repo.Name}", repo, "ci", repository, body, http);
38 }
39
40 private static IResult RenderCreateForm(string name, ForgeRepository repository, HttpContext http)
41 {
42 var repo = ForgePluginEndpoints.RequireRepo(repository, name, http);
43 var body = ForgeCiRepoTabPage.RenderCreateForm(repo.Name);
44 return ForgeViewPresenter.RepoPage($"New pipeline · {repo.Name}", repo, "ci", repository, body, http);
45 }
46
47 private static IResult CreateFromView(
48 string name,
49 HttpContext http,
50 ForgeRepository repository,
51 ForgeCiDefinitionStore definitions)
52 {
53 var repo = ForgePluginEndpoints.RequireRepo(repository, name, http);
54 var form = http.Request.Form;
55 var definitionName = form["name"].ToString().Trim();
56 if (definitionName.Length == 0)
57 return RenderCreateError(repo, repository, http, "Definition name is required.");
58
59 try
60 {
61 var document = ForgeCiTemplates.CreateDefault(definitionName);
62 var createdBy = ForgeActorResolver.ResolveActor(http, null);
63 var record = definitions.Insert(repo.Id, document, createdBy);
64 var redirect = $"/view/repos/{ForgeRepoNames.ToUrlPath(repo.Name)}/ci/definitions/{Uri.EscapeDataString(record.Name)}/designer";
65 return Results.Redirect(redirect);
66 }
67 catch (ForgeCiValidationException ex)
68 {
69 return RenderCreateError(repo, repository, http, ex.Message);
70 }
71 catch (InvalidOperationException ex)
72 {
73 return RenderCreateError(repo, repository, http, ex.Message);
74 }
75 }
76
77 private static IResult RenderDesigner(
78 string name,
79 string definitionName,
80 ForgeRepository repository,
81 ForgeCiDefinitionStore definitions,
82 HttpContext http)
83 {
84 var repo = ForgePluginEndpoints.RequireRepo(repository, name, http);
85 var normalized = ForgeCiTemplates.NormalizeDefinitionName(definitionName);
86 var record = definitions.GetByName(repo.Id, normalized);
87 if (record is null)
88 throw new ForgeApiException(404, $"CI definition '{normalized}' not found.");
89
90 var document = definitions.LoadDocument(record);
91 var body = ForgeCiDesignerPage.Render(repo.Name, record, document);
92 return ForgeViewPresenter.RepoPage($"Pipeline · {normalized}", repo, "ci", repository, body, http);
93 }
94
95 private static IResult AddTaskFromView(
96 string name,
97 string definitionName,
98 HttpContext http,
99 ForgeRepository repository,
100 ForgeCiDefinitionStore definitions)
101 {
102 var repo = ForgePluginEndpoints.RequireRepo(repository, name, http);
103 var normalized = ForgeCiTemplates.NormalizeDefinitionName(definitionName);
104 var record = definitions.GetByName(repo.Id, normalized);
105 if (record is null)
106 throw new ForgeApiException(404, $"CI definition '{normalized}' not found.");
107
108 var taskId = http.Request.Form["taskId"].ToString().Trim();
109 if (taskId.Length == 0)
110 return RenderDesignerAfterError(repo, record, definitions, repository, http, "Select a task.");
111
112 try
113 {
114 var document = definitions.LoadDocument(record);
115 ForgeCiDocumentEditor.AddTask(document, taskId);
116 definitions.Update(repo.Id, normalized, document);
117 return Results.Redirect(
118 $"/view/repos/{ForgeRepoNames.ToUrlPath(repo.Name)}/ci/definitions/{Uri.EscapeDataString(normalized)}/designer");
119 }
120 catch (ForgeCiValidationException ex)
121 {
122 return RenderDesignerAfterError(repo, record, definitions, repository, http, ex.Message);
123 }
124 }
125
126 private static IResult RenderCreateError(
127 ForgeRepoEntity repo,
128 ForgeRepository repository,
129 HttpContext http,
130 string error) =>
131 ForgeViewPresenter.RepoPage(
132 $"New pipeline · {repo.Name}",
133 repo,
134 "ci",
135 repository,
136 ForgeCiRepoTabPage.RenderCreateForm(repo.Name, error),
137 http);
138
139 private static IResult RenderDesignerAfterError(
140 ForgeRepoEntity repo,
141 ForgeCiDefinitionRecord record,
142 ForgeCiDefinitionStore definitions,
143 ForgeRepository repository,
144 HttpContext http,
145 string error)
146 {
147 var document = definitions.LoadDocument(record);
148 var body = ForgeCiDesignerPage.Render(repo.Name, record, document, error);
149 return ForgeViewPresenter.RepoPage($"Pipeline · {record.Name}", repo, "ci", repository, body, http);
150 }
151
152 private static IResult RenderRunTimeline(
153 string name,
154 string runId,
155 ForgeRepository repository,
156 ForgeCiRunStore runs,
157 HttpContext http)
158 {
159 var repo = ForgePluginEndpoints.RequireRepo(repository, name, http);
160 var runScope = ForgeCiRunResolve.TryGetRunScope(repo, runs, runId);
161 if (runScope is null)
162 throw new ForgeApiException(404, $"CI run '{runId}' not found.");
163
164 var steps = runScope.Runs.ListSteps(runScope.Run.Id);
165 var detail = ForgeCiRunQueueService.ToDetail(repo.Name, runScope.Run, steps);
166 var body = ForgeCiRunTimelinePage.Render(repo.Name, detail);
167 return ForgeViewPresenter.RepoPage($"CI run · {repo.Name}", repo, "ci", repository, body, http);
168 }
169}
170
View only · write via MCP/CIDE