fix(cicd): 收紧 managed repository 配置与状态合同
This commit is contained in:
@@ -246,6 +246,8 @@ controlPlane:
|
||||
deploymentName: agentrun-nc01-v02-managed-repository-reconciler
|
||||
configMapName: agentrun-nc01-v02-managed-repository-reconciler
|
||||
serviceAccountName: agentrun-nc01-v02-managed-repository-reconciler
|
||||
hostNetwork: true
|
||||
dnsPolicy: ClusterFirstWithHostNet
|
||||
imagePullPolicy: IfNotPresent
|
||||
cacheMountPath: /cache
|
||||
stateDirectory: .agentrun-managed-repositories/nc01-v02
|
||||
@@ -264,6 +266,8 @@ controlPlane:
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 6
|
||||
status:
|
||||
defaultRepositoryLimit: 8
|
||||
lifecycle:
|
||||
undeclaredRepositoryPolicy: retain
|
||||
managedRefs: source-branch-only
|
||||
|
||||
@@ -30,6 +30,8 @@ export interface AgentRunManagedRepositoryReconcilerSpec {
|
||||
readonly deploymentName: string;
|
||||
readonly configMapName: string;
|
||||
readonly serviceAccountName: string;
|
||||
readonly hostNetwork: boolean;
|
||||
readonly dnsPolicy: "ClusterFirst" | "ClusterFirstWithHostNet" | "Default";
|
||||
readonly imagePullPolicy: "Always" | "IfNotPresent" | "Never";
|
||||
readonly cacheMountPath: string;
|
||||
readonly stateDirectory: string;
|
||||
@@ -51,6 +53,9 @@ export interface AgentRunManagedRepositoryReconcilerSpec {
|
||||
readonly timeoutSeconds: number;
|
||||
readonly failureThreshold: number;
|
||||
};
|
||||
readonly status: {
|
||||
readonly defaultRepositoryLimit: number;
|
||||
};
|
||||
readonly lifecycle: {
|
||||
readonly undeclaredRepositoryPolicy: "retain";
|
||||
readonly managedRefs: "source-branch-only";
|
||||
@@ -1126,14 +1131,25 @@ function parseManagedRepositoryReconciler(input: Record<string, unknown>, path:
|
||||
const retry = recordField(input, "retry", path);
|
||||
const freshness = recordField(input, "freshness", path);
|
||||
const readiness = recordField(input, "readiness", path);
|
||||
const status = recordField(input, "status", path);
|
||||
const lifecycle = recordField(input, "lifecycle", path);
|
||||
const stateDirectory = relativePathField(input, "stateDirectory", path);
|
||||
const hostNetwork = booleanField(input, "hostNetwork", path);
|
||||
const dnsPolicy = enumField(input, "dnsPolicy", path, ["ClusterFirst", "ClusterFirstWithHostNet", "Default"]);
|
||||
if (stateDirectory === "." || stateDirectory.length === 0) throw new Error(`${path}.stateDirectory must name a dedicated relative directory`);
|
||||
if (hostNetwork && dnsPolicy !== "ClusterFirstWithHostNet") {
|
||||
throw new Error(`${path}.dnsPolicy must be ClusterFirstWithHostNet when ${path}.hostNetwork is true`);
|
||||
}
|
||||
if (!hostNetwork && dnsPolicy === "ClusterFirstWithHostNet") {
|
||||
throw new Error(`${path}.dnsPolicy must not be ClusterFirstWithHostNet when ${path}.hostNetwork is false`);
|
||||
}
|
||||
return {
|
||||
enabled: booleanField(input, "enabled", path),
|
||||
deploymentName: kubernetesNameField(input, "deploymentName", path),
|
||||
configMapName: kubernetesNameField(input, "configMapName", path),
|
||||
serviceAccountName: kubernetesNameField(input, "serviceAccountName", path),
|
||||
hostNetwork,
|
||||
dnsPolicy,
|
||||
imagePullPolicy: enumField(input, "imagePullPolicy", path, ["Always", "IfNotPresent", "Never"]),
|
||||
cacheMountPath: absolutePathField(input, "cacheMountPath", path),
|
||||
stateDirectory,
|
||||
@@ -1155,6 +1171,9 @@ function parseManagedRepositoryReconciler(input: Record<string, unknown>, path:
|
||||
timeoutSeconds: positiveIntegerField(readiness, "timeoutSeconds", `${path}.readiness`),
|
||||
failureThreshold: positiveIntegerField(readiness, "failureThreshold", `${path}.readiness`),
|
||||
},
|
||||
status: {
|
||||
defaultRepositoryLimit: positiveIntegerField(status, "defaultRepositoryLimit", `${path}.status`),
|
||||
},
|
||||
lifecycle: {
|
||||
undeclaredRepositoryPolicy: enumField(lifecycle, "undeclaredRepositoryPolicy", `${path}.lifecycle`, ["retain"]),
|
||||
managedRefs: enumField(lifecycle, "managedRefs", `${path}.lifecycle`, ["source-branch-only"]),
|
||||
|
||||
@@ -11,9 +11,12 @@ import { join } from "node:path";
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import {
|
||||
agentRunManagedRepositoryDesiredConfig,
|
||||
agentRunManagedRepositoryRenderHash,
|
||||
renderAgentRunManagedRepositoryReconcilerFragment,
|
||||
} from "./agentrun-managed-repository-reconciler";
|
||||
import { resolveAgentRunLaneTarget } from "./agentrun-lanes";
|
||||
import { resolveCicdDeliveryAuthority } from "./cicd-delivery-authority";
|
||||
import { renderGitMirrorStatusEnvelope } from "./agentrun/git-mirror";
|
||||
import { yamlLaneGitMirrorStatusScript } from "./agentrun/secrets";
|
||||
import { renderPlatformInfraGiteaDesiredFragments } from "./platform-infra-gitea-desired-fragments";
|
||||
|
||||
@@ -43,6 +46,8 @@ describe("AgentRun YAML-owned managed repository reconciler", () => {
|
||||
expect(JSON.parse(documents[1].data["config.json"]).repositories).toEqual(desired.repositories);
|
||||
expect(documents[2].spec.strategy.type).toBe("Recreate");
|
||||
expect(documents[2].spec.template.spec.automountServiceAccountToken).toBe(false);
|
||||
expect(documents[2].spec.template.spec.hostNetwork).toBe(spec.gitMirror.repositoryReconciler.hostNetwork);
|
||||
expect(documents[2].spec.template.spec.dnsPolicy).toBe(spec.gitMirror.repositoryReconciler.dnsPolicy);
|
||||
expect(documents[2].spec.template.spec.containers[0].readinessProbe.exec.command).toContain("--health");
|
||||
expect(renderAgentRunManagedRepositoryReconcilerFragment("JD01")).toBe("");
|
||||
|
||||
@@ -112,6 +117,62 @@ describe("AgentRun YAML-owned managed repository reconciler", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("renders host networking only from the explicit reconciler fields", () => {
|
||||
const temporary = mkdtempSync("/tmp/unidesk-agentrun-repository-network-");
|
||||
try {
|
||||
const source = readFileSync(new URL("../../config/agentrun.yaml", import.meta.url), "utf8");
|
||||
const nonLoopbackProxy = source.replace(
|
||||
"githubProxy:\n host: 127.0.0.1\n port: 10808",
|
||||
"githubProxy:\n host: proxy.internal.example\n port: 10808",
|
||||
);
|
||||
const nonLoopbackPath = join(temporary, "non-loopback.yaml");
|
||||
writeFileSync(nonLoopbackPath, nonLoopbackProxy);
|
||||
const nonLoopbackEnv = { ...process.env, AGENTRUN_CONTROL_PLANE_CONFIG: nonLoopbackPath };
|
||||
const nonLoopbackDeployment = renderAgentRunManagedRepositoryReconcilerFragment("NC01", nonLoopbackEnv)
|
||||
.split(/^---\s*$/mu)
|
||||
.map((item) => Bun.YAML.parse(item) as any)
|
||||
.find((item) => item.kind === "Deployment");
|
||||
expect(nonLoopbackDeployment.spec.template.spec.hostNetwork).toBe(true);
|
||||
expect(nonLoopbackDeployment.spec.template.spec.dnsPolicy).toBe("ClusterFirstWithHostNet");
|
||||
|
||||
const clusterNetwork = source.replace(
|
||||
"hostNetwork: true\n dnsPolicy: ClusterFirstWithHostNet",
|
||||
"hostNetwork: false\n dnsPolicy: ClusterFirst",
|
||||
);
|
||||
const clusterNetworkPath = join(temporary, "cluster-network.yaml");
|
||||
writeFileSync(clusterNetworkPath, clusterNetwork);
|
||||
const clusterNetworkEnv = { ...process.env, AGENTRUN_CONTROL_PLANE_CONFIG: clusterNetworkPath };
|
||||
const clusterNetworkSpec = resolveAgentRunLaneTarget(
|
||||
{ node: "NC01", lane: "nc01-v02" },
|
||||
clusterNetworkEnv,
|
||||
).spec;
|
||||
const clusterNetworkDeployment = renderAgentRunManagedRepositoryReconcilerFragment("NC01", clusterNetworkEnv)
|
||||
.split(/^---\s*$/mu)
|
||||
.map((item) => Bun.YAML.parse(item) as any)
|
||||
.find((item) => item.kind === "Deployment");
|
||||
expect(clusterNetworkDeployment.spec.template.spec.hostNetwork).toBe(false);
|
||||
expect(clusterNetworkDeployment.spec.template.spec.dnsPolicy).toBe("ClusterFirst");
|
||||
const defaultSpec = resolveAgentRunLaneTarget({ node: "NC01", lane: "nc01-v02" }).spec;
|
||||
expect(agentRunManagedRepositoryRenderHash(clusterNetworkSpec)).not.toBe(agentRunManagedRepositoryRenderHash(defaultSpec));
|
||||
|
||||
const invalidHostNetworkPath = join(temporary, "invalid-host-network.yaml");
|
||||
writeFileSync(invalidHostNetworkPath, source.replace("dnsPolicy: ClusterFirstWithHostNet", "dnsPolicy: ClusterFirst"));
|
||||
expect(() => resolveAgentRunLaneTarget(
|
||||
{ node: "NC01", lane: "nc01-v02" },
|
||||
{ ...process.env, AGENTRUN_CONTROL_PLANE_CONFIG: invalidHostNetworkPath },
|
||||
)).toThrow("must be ClusterFirstWithHostNet when");
|
||||
|
||||
const invalidClusterNetworkPath = join(temporary, "invalid-cluster-network.yaml");
|
||||
writeFileSync(invalidClusterNetworkPath, source.replace("hostNetwork: true", "hostNetwork: false"));
|
||||
expect(() => resolveAgentRunLaneTarget(
|
||||
{ node: "NC01", lane: "nc01-v02" },
|
||||
{ ...process.env, AGENTRUN_CONTROL_PLANE_CONFIG: invalidClusterNetworkPath },
|
||||
)).toThrow("must not be ClusterFirstWithHostNet when");
|
||||
} finally {
|
||||
rmSync(temporary, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("materializes source refs, exposes health, and retains undeclared cache content", () => {
|
||||
const temporary = mkdtempSync("/tmp/unidesk-agentrun-managed-repository-");
|
||||
try {
|
||||
@@ -224,6 +285,113 @@ describe("AgentRun YAML-owned managed repository reconciler", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("keeps the legacy default summary contract and bounds the final CLI envelope", () => {
|
||||
const target = resolveAgentRunLaneTarget({ node: "NC01", lane: "nc01-v02" });
|
||||
const spec = target.spec;
|
||||
const authority = resolveCicdDeliveryAuthority({
|
||||
node: spec.nodeId,
|
||||
lane: spec.lane,
|
||||
sourceRepository: spec.source.repository,
|
||||
sourceBranch: spec.source.branch,
|
||||
declaredSourceAuthorityMode: spec.source.sourceAuthority?.mode ?? null,
|
||||
declaredConfigRef: target.configPath,
|
||||
});
|
||||
expect(authority.kind).toBe("pac-pr-merge");
|
||||
const defaultRepositoryLimit = spec.gitMirror.repositoryReconciler.status.defaultRepositoryLimit;
|
||||
const fixtureRepositoryTotal = defaultRepositoryLimit + 2;
|
||||
const repositories = Array.from({ length: fixtureRepositoryTotal }, (_, index) => ({
|
||||
key: `repo-${index}`,
|
||||
repository: `acme/repo-${index}`,
|
||||
sourceBranch: "main",
|
||||
cacheCommit: String(index).repeat(40),
|
||||
cacheRefPresent: true,
|
||||
servedCommit: String(index).repeat(40),
|
||||
servedRefPresent: true,
|
||||
refAligned: true,
|
||||
remoteFingerprintAligned: true,
|
||||
fresh: true,
|
||||
}));
|
||||
const summary = {
|
||||
ok: true,
|
||||
repository: "pikasTech/agentrun",
|
||||
sourceBranch: "v0.2",
|
||||
gitopsBranch: "nc01-v0.2-gitops",
|
||||
sourceCommit: "a".repeat(40),
|
||||
gitopsCommit: "b".repeat(40),
|
||||
controller: {
|
||||
deploymentReady: true,
|
||||
configRendered: true,
|
||||
workloadRendered: true,
|
||||
statusAligned: true,
|
||||
heartbeatFresh: true,
|
||||
},
|
||||
repositories,
|
||||
firstDrift: null,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
const observation = {
|
||||
ok: true,
|
||||
summary,
|
||||
raw: JSON.stringify(summary),
|
||||
result: { exitCode: 0, stdout: JSON.stringify(summary), stderr: "" },
|
||||
} as any;
|
||||
const baseOptions = { node: "NC01", lane: "nc01-v02" };
|
||||
const compact = renderGitMirrorStatusEnvelope(
|
||||
target,
|
||||
authority,
|
||||
observation,
|
||||
{ ...baseOptions, full: false, raw: false },
|
||||
) as any;
|
||||
expect(compact.summary).toMatchObject({
|
||||
repository: "pikasTech/agentrun",
|
||||
sourceBranch: "v0.2",
|
||||
gitopsBranch: "nc01-v0.2-gitops",
|
||||
sourceCommit: "a".repeat(40),
|
||||
gitopsCommit: "b".repeat(40),
|
||||
});
|
||||
expect(compact.summary.repositories).toHaveLength(defaultRepositoryLimit);
|
||||
expect(compact.summary.repositoryView).toEqual({
|
||||
limit: defaultRepositoryLimit,
|
||||
total: fixtureRepositoryTotal,
|
||||
shown: defaultRepositoryLimit,
|
||||
omitted: fixtureRepositoryTotal - defaultRepositoryLimit,
|
||||
});
|
||||
expect(compact.target.repositoryView).toEqual({
|
||||
limit: defaultRepositoryLimit,
|
||||
total: spec.gitMirror.repositories.length,
|
||||
shown: spec.gitMirror.repositories.length,
|
||||
omitted: 0,
|
||||
});
|
||||
expect(compact.probe.stdoutTailOmitted).toBe(true);
|
||||
expect(compact.disclosure.probeTailOmitted).toBe(true);
|
||||
expect(compact.next).toMatchObject({ status: expect.any(String), history: expect.any(String) });
|
||||
expect(compact.next.sync).toBeUndefined();
|
||||
|
||||
for (const options of [
|
||||
{ ...baseOptions, full: true, raw: false },
|
||||
{ ...baseOptions, full: false, raw: true },
|
||||
]) {
|
||||
const expanded = renderGitMirrorStatusEnvelope(target, authority, observation, options) as any;
|
||||
expect(expanded.summary.repositories).toHaveLength(fixtureRepositoryTotal);
|
||||
expect(expanded.summary.repositoryView).toEqual({
|
||||
limit: null,
|
||||
total: fixtureRepositoryTotal,
|
||||
shown: fixtureRepositoryTotal,
|
||||
omitted: 0,
|
||||
});
|
||||
expect(expanded.target.repositories).toHaveLength(spec.gitMirror.repositories.length);
|
||||
expect(expanded.target.repositoryView).toEqual({
|
||||
limit: null,
|
||||
total: spec.gitMirror.repositories.length,
|
||||
shown: spec.gitMirror.repositories.length,
|
||||
omitted: 0,
|
||||
});
|
||||
expect(expanded.probe.stdoutTailOmitted).toBe(!options.raw);
|
||||
expect(expanded.probe.stdoutTail).toBe(options.raw ? JSON.stringify(summary) : undefined);
|
||||
expect(expanded.disclosure.probeTailOmitted).toBe(!options.raw);
|
||||
}
|
||||
});
|
||||
|
||||
test("keeps legacy main-repository fields and bounds per-repository probes", () => {
|
||||
const spec = resolveAgentRunLaneTarget({ node: "NC01", lane: "nc01-v02" }).spec;
|
||||
const script = yamlLaneGitMirrorStatusScript(spec);
|
||||
|
||||
@@ -90,6 +90,7 @@ export function agentRunManagedRepositoryRenderHash(spec: AgentRunLaneSpec): str
|
||||
const config = agentRunManagedRepositoryDesiredConfig(spec);
|
||||
return sha256([
|
||||
JSON.stringify(config),
|
||||
JSON.stringify(managedRepositoryManifestIdentity(spec)),
|
||||
readFileSync(controllerSourcePath, "utf8"),
|
||||
readFileSync(proxySourcePath, "utf8"),
|
||||
].join("\0"));
|
||||
@@ -151,7 +152,6 @@ function managedRepositoryObjects(spec: AgentRunLaneSpec): Record<string, unknow
|
||||
const cacheVolume = spec.gitMirror.cacheHostPath === null
|
||||
? { name: "cache", persistentVolumeClaim: { claimName: spec.gitMirror.cachePvc } }
|
||||
: { name: "cache", hostPath: { path: spec.gitMirror.cacheHostPath, type: "DirectoryOrCreate" } };
|
||||
const loopbackProxy = spec.gitMirror.githubProxy.host === "127.0.0.1" || spec.gitMirror.githubProxy.host === "localhost";
|
||||
return [
|
||||
{
|
||||
apiVersion: "v1",
|
||||
@@ -208,7 +208,8 @@ function managedRepositoryObjects(spec: AgentRunLaneSpec): Record<string, unknow
|
||||
serviceAccountName: reconciler.serviceAccountName,
|
||||
automountServiceAccountToken: false,
|
||||
terminationGracePeriodSeconds: Math.ceil(reconciler.shutdownGraceMs / 1000),
|
||||
...(loopbackProxy ? { hostNetwork: true, dnsPolicy: "ClusterFirstWithHostNet" } : {}),
|
||||
hostNetwork: reconciler.hostNetwork,
|
||||
dnsPolicy: reconciler.dnsPolicy,
|
||||
containers: [{
|
||||
name: "managed-repository-reconciler",
|
||||
image: spec.gitMirror.toolsImage,
|
||||
@@ -253,6 +254,25 @@ function managedRepositoryObjects(spec: AgentRunLaneSpec): Record<string, unknow
|
||||
];
|
||||
}
|
||||
|
||||
function managedRepositoryManifestIdentity(spec: AgentRunLaneSpec): Record<string, unknown> {
|
||||
const reconciler = spec.gitMirror.repositoryReconciler;
|
||||
return {
|
||||
namespace: spec.gitMirror.namespace,
|
||||
deploymentName: reconciler.deploymentName,
|
||||
configMapName: reconciler.configMapName,
|
||||
serviceAccountName: reconciler.serviceAccountName,
|
||||
hostNetwork: reconciler.hostNetwork,
|
||||
dnsPolicy: reconciler.dnsPolicy,
|
||||
image: spec.gitMirror.toolsImage,
|
||||
imagePullPolicy: reconciler.imagePullPolicy,
|
||||
cachePvc: spec.gitMirror.cachePvc,
|
||||
cacheHostPath: spec.gitMirror.cacheHostPath,
|
||||
sshSecretName: spec.gitMirror.sshSecretName,
|
||||
readiness: reconciler.readiness,
|
||||
resources: reconciler.resources,
|
||||
};
|
||||
}
|
||||
|
||||
function sha256(value: string): string {
|
||||
return createHash("sha256").update(value).digest("hex");
|
||||
}
|
||||
|
||||
@@ -482,18 +482,33 @@ export async function gitMirrorStatus(config: UniDeskConfig, options: GitMirrorS
|
||||
declaredConfigRef: target.configPath,
|
||||
});
|
||||
const observation = await readGitMirrorStatus(config, target);
|
||||
return renderGitMirrorStatusEnvelope(target, deliveryAuthority, observation, options);
|
||||
}
|
||||
|
||||
export function renderGitMirrorStatusEnvelope(
|
||||
target: ReturnType<typeof resolveAgentRunLaneTarget>,
|
||||
deliveryAuthority: ReturnType<typeof resolveCicdDeliveryAuthority>,
|
||||
observation: Awaited<ReturnType<typeof readGitMirrorStatus>>,
|
||||
options: GitMirrorStatusOptions,
|
||||
): Record<string, unknown> {
|
||||
const spec = target.spec;
|
||||
const summary = observation.summary;
|
||||
const outputSummary = options.full || options.raw ? summary : compactManagedRepositoryStatus(summary);
|
||||
const expanded = options.full || options.raw;
|
||||
const repositoryLimit = spec.gitMirror.repositoryReconciler.status.defaultRepositoryLimit;
|
||||
const compactTargetRepositories = spec.gitMirror.repositories
|
||||
.slice(0, repositoryLimit)
|
||||
.map((repository) => ({ key: repository.key, sourceBranch: repository.sourceBranch }));
|
||||
const compactTarget = {
|
||||
node: spec.nodeId,
|
||||
lane: spec.lane,
|
||||
namespace: spec.gitMirror.namespace,
|
||||
controller: spec.gitMirror.repositoryReconciler.deploymentName,
|
||||
repositories: spec.gitMirror.repositories.map((repository) => ({ key: repository.key, sourceBranch: repository.sourceBranch })),
|
||||
repositories: compactTargetRepositories,
|
||||
repositoryView: repositoryListView(spec.gitMirror.repositories.length, compactTargetRepositories.length, repositoryLimit),
|
||||
};
|
||||
const fullTarget = {
|
||||
...compactTarget,
|
||||
repositories: spec.gitMirror.repositories.map((repository) => ({ key: repository.key, sourceBranch: repository.sourceBranch })),
|
||||
version: spec.version,
|
||||
sourceAuthority: spec.source.sourceAuthority,
|
||||
gitMirror: {
|
||||
@@ -515,7 +530,15 @@ export async function gitMirrorStatus(config: UniDeskConfig, options: GitMirrorS
|
||||
remoteConfigured: true,
|
||||
})),
|
||||
},
|
||||
repositoryView: repositoryListView(spec.gitMirror.repositories.length, spec.gitMirror.repositories.length, null),
|
||||
};
|
||||
const summaryRepositories = Array.isArray(summary.repositories) ? summary.repositories : [];
|
||||
const outputSummary = expanded
|
||||
? {
|
||||
...summary,
|
||||
repositoryView: repositoryListView(summaryRepositories.length, summaryRepositories.length, null),
|
||||
}
|
||||
: compactManagedRepositoryStatus(summary, repositoryLimit);
|
||||
const outputNext = deliveryAuthority.kind === "pac-pr-merge"
|
||||
? {
|
||||
status: `bun scripts/cli.ts platform-infra pipelines-as-code status --target ${deliveryAuthority.consumer.node} --consumer ${deliveryAuthority.consumer.consumerId}`,
|
||||
@@ -549,7 +572,7 @@ export async function gitMirrorStatus(config: UniDeskConfig, options: GitMirrorS
|
||||
full: options.full,
|
||||
raw: options.raw,
|
||||
rawOmitted: !options.raw,
|
||||
probeTailOmitted: !(options.full || options.raw),
|
||||
probeTailOmitted: !options.raw,
|
||||
expandWith: `bun scripts/cli.ts agentrun git-mirror status --node ${spec.nodeId} --lane ${spec.lane} --full`,
|
||||
rawWith: `bun scripts/cli.ts agentrun git-mirror status --node ${spec.nodeId} --lane ${spec.lane} --raw`,
|
||||
},
|
||||
@@ -578,9 +601,9 @@ function compactDeliveryAuthority(authority: ReturnType<typeof resolveCicdDelive
|
||||
};
|
||||
}
|
||||
|
||||
function compactManagedRepositoryStatus(summary: Record<string, unknown>): Record<string, unknown> {
|
||||
function compactManagedRepositoryStatus(summary: Record<string, unknown>, limit: number): Record<string, unknown> {
|
||||
const controller = record(summary.controller);
|
||||
const repositories = Array.isArray(summary.repositories)
|
||||
const allRepositories = Array.isArray(summary.repositories)
|
||||
? summary.repositories.map((value) => record(value)).map((repository) => ({
|
||||
key: repository.key ?? null,
|
||||
sourceBranch: repository.sourceBranch ?? null,
|
||||
@@ -592,8 +615,14 @@ function compactManagedRepositoryStatus(summary: Record<string, unknown>): Recor
|
||||
aligned: repository.refAligned === true && repository.remoteFingerprintAligned === true,
|
||||
}))
|
||||
: [];
|
||||
const repositories = allRepositories.slice(0, limit);
|
||||
return {
|
||||
ok: summary.ok === true,
|
||||
repository: summary.repository ?? null,
|
||||
sourceBranch: summary.sourceBranch ?? null,
|
||||
gitopsBranch: summary.gitopsBranch ?? null,
|
||||
sourceCommit: summary.sourceCommit ?? null,
|
||||
gitopsCommit: summary.gitopsCommit ?? null,
|
||||
controller: {
|
||||
deploymentReady: controller.deploymentReady === true,
|
||||
desiredRendered: controller.configRendered === true && controller.workloadRendered === true,
|
||||
@@ -601,11 +630,21 @@ function compactManagedRepositoryStatus(summary: Record<string, unknown>): Recor
|
||||
heartbeatFresh: controller.heartbeatFresh === true,
|
||||
},
|
||||
repositories,
|
||||
repositoryView: repositoryListView(allRepositories.length, repositories.length, limit),
|
||||
firstDrift: summary.firstDrift ?? null,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
function repositoryListView(total: number, shown: number, limit: number | null): Record<string, number | null> {
|
||||
return {
|
||||
limit,
|
||||
total,
|
||||
shown,
|
||||
omitted: Math.max(0, total - shown),
|
||||
};
|
||||
}
|
||||
|
||||
function shortCommit(value: unknown): string | null {
|
||||
return typeof value === "string" && /^[0-9a-f]{40}$/u.test(value) ? value.slice(0, 12) : null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user