OPC # 0006: OPC Git Trunk-Based management
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
using ControlPlane.Core.Models;
|
using ControlPlane.Core.Models;
|
||||||
using ControlPlane.Core.Services;
|
using ControlPlane.Core.Services;
|
||||||
|
using LibGit2Sharp;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -24,9 +25,14 @@ public class ProjectBuildService(
|
|||||||
|
|
||||||
return
|
return
|
||||||
[
|
[
|
||||||
new("Clarity.Server", BuildKind.DotnetProject, "Clarity.Server/Clarity.Server.csproj"),
|
// ── Solution-level builds (primary targets) ──────────────────────
|
||||||
new("Clarity.ServiceDefaults", BuildKind.DotnetProject, "Clarity.ServiceDefaults/Clarity.ServiceDefaults.csproj"),
|
new("Clarity Solution", BuildKind.SolutionBuild, "Clarity/Clarity.slnx"),
|
||||||
new("frontend (Clarity.Server)", BuildKind.NpmProject, "frontend"),
|
new("ControlPlane Solution", BuildKind.SolutionBuild, "OPC/ControlPlane.slnx"),
|
||||||
|
|
||||||
|
// ── Individual Clarity projects ───────────────────────────────────
|
||||||
|
new("Clarity.Server", BuildKind.DotnetProject, "Clarity/Clarity.Server/Clarity.Server.csproj"),
|
||||||
|
new("Clarity.ServiceDefaults", BuildKind.DotnetProject, "Clarity/Clarity.ServiceDefaults/Clarity.ServiceDefaults.csproj"),
|
||||||
|
new("frontend (Clarity.Server)", BuildKind.NpmProject, "Clarity/frontend"),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +59,16 @@ public class ProjectBuildService(
|
|||||||
record.Log.Add("──────────────────────────────────────");
|
record.Log.Add("──────────────────────────────────────");
|
||||||
onLine($"▶ Building {def.Name}");
|
onLine($"▶ Building {def.Name}");
|
||||||
|
|
||||||
|
// Capture HEAD SHA so the build is traceable to a specific commit
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var gitRepo = new Repository(RepoRoot);
|
||||||
|
record.CommitSha = gitRepo.Head.Tip?.Sha;
|
||||||
|
if (record.CommitSha is not null)
|
||||||
|
record.Log.Add($" Commit: {record.CommitSha[..8]}");
|
||||||
|
}
|
||||||
|
catch { /* not a git repo or no commits yet */ }
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var (exe, args, workDir) = def.Kind == BuildKind.NpmProject
|
var (exe, args, workDir) = def.Kind == BuildKind.NpmProject
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Text.Json.Serialization;
|
|||||||
namespace ControlPlane.Core.Models;
|
namespace ControlPlane.Core.Models;
|
||||||
|
|
||||||
public enum BuildStatus { Running, Succeeded, Failed }
|
public enum BuildStatus { Running, Succeeded, Failed }
|
||||||
public enum BuildKind { DockerImage, DotnetProject, NpmProject }
|
public enum BuildKind { DockerImage, DotnetProject, NpmProject, SolutionBuild }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Persisted record of a single build run — image build, dotnet build, or npm build.
|
/// Persisted record of a single build run — image build, dotnet build, or npm build.
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ function GitHistoryPanel({ relativePath }: { relativePath: string }) {
|
|||||||
<p style={{ fontSize: '0.75rem', color: '#8f99a8', marginTop: '0.5rem' }}>No commits found for this path.</p>
|
<p style={{ fontSize: '0.75rem', color: '#8f99a8', marginTop: '0.5rem' }}>No commits found for this path.</p>
|
||||||
)}
|
)}
|
||||||
{commits.length > 0 && (
|
{commits.length > 0 && (
|
||||||
<HTMLTable className="bp5-html-table-condensed bp5-html-table-striped" style={{ width: '100%', marginTop: '0.5rem', fontSize: '0.72rem' }}>
|
<HTMLTable className="bp6-html-table-condensed bp6-html-table-striped" style={{ width: '100%', marginTop: '0.5rem', fontSize: '0.72rem' }}>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style={{ width: 60 }}>Commit</th>
|
<th style={{ width: 60 }}>Commit</th>
|
||||||
@@ -250,9 +250,9 @@ export default function BuildMonitorPage() {
|
|||||||
|
|
||||||
useEffect(() => { (async () => { await load(); })(); }, []);
|
useEffect(() => { (async () => { await load(); })(); }, []);
|
||||||
|
|
||||||
// Find latest build per project
|
// Find latest build per project — match exactly by relativePath (= build target)
|
||||||
const lastBuildFor = (name: string): BuildRecord | undefined =>
|
const lastBuildFor = (project: ProjectDefinition): BuildRecord | undefined =>
|
||||||
history.find((b) => b.target.includes(name.split(' ')[0]) || b.target.endsWith(name));
|
history.find((b) => b.target === project.relativePath);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -292,7 +292,7 @@ export default function BuildMonitorPage() {
|
|||||||
<ProjectCard
|
<ProjectCard
|
||||||
key={p.name}
|
key={p.name}
|
||||||
project={p}
|
project={p}
|
||||||
lastBuild={lastBuildFor(p.name)}
|
lastBuild={lastBuildFor(p)}
|
||||||
onBuilt={load}
|
onBuilt={load}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ export default function PipelinesPage() {
|
|||||||
try {
|
try {
|
||||||
const [r, b] = await Promise.all([getReleaseHistory(), getBuildHistory()]);
|
const [r, b] = await Promise.all([getReleaseHistory(), getBuildHistory()]);
|
||||||
setReleases(r);
|
setReleases(r);
|
||||||
setBuilds(b.filter((b) => b.kind === 'DockerImage'));
|
setBuilds(b.filter((b) => b.kind === 'SolutionBuild'));
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -252,7 +252,7 @@ export default function PipelinesPage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h3 style={{ margin: '0 0 0.5rem' }}>Image Build History</h3>
|
<h3 style={{ margin: '0 0 0.5rem' }}>Solution Build History</h3>
|
||||||
{loading ? <Spinner size={20} /> : <BuildHistoryTable records={builds} />}
|
{loading ? <Spinner size={20} /> : <BuildHistoryTable records={builds} />}
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user