OPC # 0006: OPC Git Trunk-Based management

This commit is contained in:
amadzarak
2026-04-26 14:40:05 -04:00
parent bb0c6e08c7
commit 2badb5264b
2 changed files with 134 additions and 51 deletions
+101 -15
View File
@@ -804,30 +804,116 @@ body {
.opc-sdlc-pipeline {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.2rem;
margin-bottom: 0.35rem;
}
.opc-sdlc-stage-item {
display: flex;
align-items: center;
gap: 0.2rem;
align-items: flex-start;
flex-wrap: nowrap;
gap: 0;
overflow-x: auto;
padding-bottom: 0.25rem;
}
.opc-sdlc-arrow {
color: #8f99a8;
font-size: 0.8rem;
font-size: 1rem;
font-weight: 600;
margin: 0 0.1rem;
flex-shrink: 0;
align-self: center;
margin: 0 0.4rem;
user-select: none;
}
.opc-sdlc-furthest {
font-size: 0.75rem;
/* Individual branch box */
.opc-sdlc-box {
flex: 1 1 140px;
min-width: 130px;
max-width: 200px;
display: flex;
flex-direction: column;
border: 1px solid #dce0e6;
border-radius: 6px;
background: #fff;
overflow: hidden;
flex-shrink: 0;
}
.opc-sdlc-box--reached {
border-width: 2px;
}
.opc-sdlc-box-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.3rem 0.5rem;
background: #f6f7f9;
border-bottom: 1px solid #e5e8eb;
flex-shrink: 0;
}
.opc-sdlc-box-count {
font-size: 0.68rem;
color: #738091;
margin-top: 0.3rem;
background: #e5e8eb;
border-radius: 10px;
padding: 0 6px;
line-height: 1.5;
}
/* Scrollable body */
.opc-sdlc-box-body {
flex: 1;
overflow-y: auto;
max-height: 140px;
min-height: 60px;
padding: 0.3rem 0.4rem;
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.opc-sdlc-sha-row {
display: flex;
align-items: baseline;
gap: 0.35rem;
padding: 0.1rem 0.2rem;
border-radius: 3px;
opacity: 0.35;
}
.opc-sdlc-sha-row--reached {
opacity: 1;
}
.opc-sdlc-sha {
font-family: 'Consolas', 'Courier New', monospace;
font-size: 0.7rem;
color: #2d72d2;
flex-shrink: 0;
}
.opc-sdlc-sha-msg {
font-size: 0.68rem;
color: #4a5568;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
min-width: 0;
}
.opc-sdlc-box-empty {
font-size: 0.7rem;
color: #a3acb6;
font-style: italic;
padding: 0.2rem 0;
}
.opc-sdlc-box-pending {
font-size: 0.68rem;
color: #a3acb6;
font-style: italic;
margin-top: auto;
padding-top: 0.25rem;
border-top: 1px dashed #e5e8eb;
}
/* Commits section labels */
+33 -36
View File
@@ -1,4 +1,4 @@
import { useState, useMemo, useEffect, useCallback } from 'react';
import { useState, useMemo, useEffect, useCallback, Fragment } from 'react';
import { GitCommitDrawer } from '../components/GitCommitDrawer';
import {
Button, Callout, Divider, Drawer, FormGroup,
@@ -79,15 +79,6 @@ const SDLC_STAGES: { branch: string; label: string; intent: Intent }[] = [
{ branch: 'main', label: 'Production', intent: Intent.SUCCESS },
];
function deriveSdlcSummary(coverage: BranchCoverage[]): { label: string; intent: Intent } | null {
for (let i = SDLC_STAGES.length - 1; i >= 0; i--) {
const stage = SDLC_STAGES[i];
const hit = coverage.find(c => c.branch === stage.branch);
if (hit?.contains) return { label: stage.label, intent: stage.intent };
}
return null;
}
// Aggregate per-repo branch coverage into a single view.
// A stage is "reached" only when every repo that recognised at least one hash
// reports contains=true for that branch. Repos that recognised no hashes are
@@ -487,42 +478,48 @@ function CommitsTab({ opc, isActive }: { opc: Opc; isActive: boolean }) {
{/* SDLC Delivery Chain */}
{coverage.length > 0 && (() => {
const summary = deriveSdlcSummary(coverage);
const allCommits = [
...autoCommits,
...pinned.map(p => ({ repoKey: 'pinned', hash: p.hash, shortHash: p.shortHash, author: p.pinnedBy, date: p.pinnedAt, subject: p.subject, files: [] })),
].filter((c, i, a) => a.findIndex(x => x.hash === c.hash) === i);
return (
<div className="opc-delivery-chain">
<div className="opc-field-label" style={{ marginBottom: '0.6rem' }}>Delivery Chain</div>
<div className="opc-field-label" style={{ marginBottom: '0.75rem' }}>Delivery Chain</div>
<div className="opc-sdlc-pipeline">
{SDLC_STAGES.map((stage, i) => {
const hit = coverage.find(c => c.branch === stage.branch);
const hit = coverage.find(c => c.branch === stage.branch);
const reached = hit?.contains ?? false;
return (
<div key={stage.branch} className="opc-sdlc-stage-item">
<Fragment key={stage.branch}>
{i > 0 && <span className="opc-sdlc-arrow"></span>}
<Tooltip content={
reached
? `All linked commits have reached ${stage.label}`
: hit
? `Not all linked commits have reached ${stage.label} yet`
: `${stage.label} branch not found locally`
}>
<Tag
intent={reached ? stage.intent : Intent.NONE}
icon={reached ? 'tick-circle' : 'circle'}
minimal={!reached}
round
>
{stage.label}
</Tag>
</Tooltip>
</div>
<div className={`opc-sdlc-box${reached ? ' opc-sdlc-box--reached' : ''}`} style={{ borderColor: reached ? SDLC_STAGES[i].intent === 'primary' ? '#2d72d2' : SDLC_STAGES[i].intent === 'warning' ? '#c87619' : SDLC_STAGES[i].intent === 'danger' ? '#ac2f33' : '#1c6e42' : '#dce0e6' }}>
{/* Box header */}
<div className="opc-sdlc-box-header">
<Tag intent={reached ? stage.intent : Intent.NONE} minimal={!reached} round style={{ fontWeight: 600, fontSize: '0.72rem' }}>
{stage.label}
</Tag>
{reached && <span className="opc-sdlc-box-count">{allCommits.length}</span>}
</div>
{/* Scrollable SHA list */}
<div className="opc-sdlc-box-body">
{allCommits.length === 0 ? (
<span className="opc-sdlc-box-empty">No linked commits</span>
) : allCommits.map(c => (
<div key={c.hash} className={`opc-sdlc-sha-row${reached ? ' opc-sdlc-sha-row--reached' : ''}`} title={c.subject}>
<code className="opc-sdlc-sha">{c.shortHash}</code>
<span className="opc-sdlc-sha-msg">{c.subject}</span>
</div>
))}
{!reached && allCommits.length > 0 && (
<div className="opc-sdlc-box-pending">Not yet promoted</div>
)}
</div>
</div>
</Fragment>
);
})}
</div>
{summary && (
<div className="opc-sdlc-furthest">
Furthest: <strong>{summary.label}</strong>
</div>
)}
</div>
);
})()}