OPC # 0006: OPC Git Trunk-Based management
This commit is contained in:
@@ -804,30 +804,116 @@ body {
|
|||||||
|
|
||||||
.opc-sdlc-pipeline {
|
.opc-sdlc-pipeline {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
gap: 0.2rem;
|
gap: 0;
|
||||||
margin-bottom: 0.35rem;
|
overflow-x: auto;
|
||||||
}
|
padding-bottom: 0.25rem;
|
||||||
|
|
||||||
.opc-sdlc-stage-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.2rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.opc-sdlc-arrow {
|
.opc-sdlc-arrow {
|
||||||
color: #8f99a8;
|
color: #8f99a8;
|
||||||
font-size: 0.8rem;
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin: 0 0.1rem;
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
|
margin: 0 0.4rem;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.opc-sdlc-furthest {
|
/* Individual branch box */
|
||||||
font-size: 0.75rem;
|
.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;
|
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 */
|
/* Commits section labels */
|
||||||
|
|||||||
@@ -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 { GitCommitDrawer } from '../components/GitCommitDrawer';
|
||||||
import {
|
import {
|
||||||
Button, Callout, Divider, Drawer, FormGroup,
|
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 },
|
{ 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.
|
// Aggregate per-repo branch coverage into a single view.
|
||||||
// A stage is "reached" only when every repo that recognised at least one hash
|
// 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
|
// 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 */}
|
{/* SDLC Delivery Chain */}
|
||||||
{coverage.length > 0 && (() => {
|
{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 (
|
return (
|
||||||
<div className="opc-delivery-chain">
|
<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">
|
<div className="opc-sdlc-pipeline">
|
||||||
{SDLC_STAGES.map((stage, i) => {
|
{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;
|
const reached = hit?.contains ?? false;
|
||||||
return (
|
return (
|
||||||
<div key={stage.branch} className="opc-sdlc-stage-item">
|
<Fragment key={stage.branch}>
|
||||||
{i > 0 && <span className="opc-sdlc-arrow">→</span>}
|
{i > 0 && <span className="opc-sdlc-arrow">→</span>}
|
||||||
<Tooltip content={
|
<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' }}>
|
||||||
reached
|
{/* Box header */}
|
||||||
? `All linked commits have reached ${stage.label}`
|
<div className="opc-sdlc-box-header">
|
||||||
: hit
|
<Tag intent={reached ? stage.intent : Intent.NONE} minimal={!reached} round style={{ fontWeight: 600, fontSize: '0.72rem' }}>
|
||||||
? `Not all linked commits have reached ${stage.label} yet`
|
{stage.label}
|
||||||
: `${stage.label} branch not found locally`
|
</Tag>
|
||||||
}>
|
{reached && <span className="opc-sdlc-box-count">{allCommits.length}</span>}
|
||||||
<Tag
|
</div>
|
||||||
intent={reached ? stage.intent : Intent.NONE}
|
{/* Scrollable SHA list */}
|
||||||
icon={reached ? 'tick-circle' : 'circle'}
|
<div className="opc-sdlc-box-body">
|
||||||
minimal={!reached}
|
{allCommits.length === 0 ? (
|
||||||
round
|
<span className="opc-sdlc-box-empty">No linked commits</span>
|
||||||
>
|
) : allCommits.map(c => (
|
||||||
{stage.label}
|
<div key={c.hash} className={`opc-sdlc-sha-row${reached ? ' opc-sdlc-sha-row--reached' : ''}`} title={c.subject}>
|
||||||
</Tag>
|
<code className="opc-sdlc-sha">{c.shortHash}</code>
|
||||||
</Tooltip>
|
<span className="opc-sdlc-sha-msg">{c.subject}</span>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
{!reached && allCommits.length > 0 && (
|
||||||
|
<div className="opc-sdlc-box-pending">Not yet promoted</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
{summary && (
|
|
||||||
<div className="opc-sdlc-furthest">
|
|
||||||
Furthest: <strong>{summary.label}</strong>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
|
|||||||
Reference in New Issue
Block a user