feat: 改为真实后端接口模式
- devopsApi: 移除前端 mock 降级和本地发布单/refs 伪造逻辑 - views: 同步发布、运行记录、配置和顶部状态文案为真实接口模式 - docs: 更新前端维护规则,禁止新增 mock 数据降级
This commit is contained in:
+193
-281
@@ -1,21 +1,9 @@
|
||||
/**
|
||||
* 前端唯一 DevOps API 入口;优先联调后端,失败时降级到本地 mock 保持页面可用。
|
||||
* 前端唯一 DevOps API 入口;所有运维数据都来自后端真实接口。
|
||||
*/
|
||||
import {
|
||||
agentCards,
|
||||
dashboardSummary,
|
||||
integrationConfigStatus as mockIntegrationConfigStatus,
|
||||
integrations,
|
||||
projects as mockProjects,
|
||||
releaseProcessXml,
|
||||
runs as mockRuns,
|
||||
settings,
|
||||
} from '../mocks/devops'
|
||||
import type {
|
||||
AgentCard,
|
||||
AgentInvocation,
|
||||
AgentMode,
|
||||
AgentPurpose,
|
||||
CreateAgentInvocationPayload,
|
||||
CreateDeployRunPayload,
|
||||
DashboardSummary,
|
||||
@@ -36,7 +24,7 @@ import type {
|
||||
} from '../types/devops'
|
||||
import { http } from './http'
|
||||
|
||||
type DataSource = 'backend' | 'mock'
|
||||
type DataSource = 'backend'
|
||||
type BackendHealthStatus = 'ok' | 'not_configured' | 'unavailable'
|
||||
type BackendRunStatus = 'pending' | 'queued' | 'running' | 'success' | 'failed' | 'canceled'
|
||||
|
||||
@@ -57,6 +45,8 @@ interface BackendProject {
|
||||
name: EnvironmentName
|
||||
displayName: string
|
||||
jenkinsJobPath: string
|
||||
releasePolicy?: 'branch' | 'tag' | 'master'
|
||||
refPattern?: string
|
||||
}>
|
||||
}
|
||||
|
||||
@@ -108,9 +98,7 @@ interface BackendProcessDefinition {
|
||||
nodeIds: string[]
|
||||
}
|
||||
|
||||
let lastDataSource: DataSource = 'mock'
|
||||
|
||||
const wait = async () => new Promise((resolve) => window.setTimeout(resolve, 180))
|
||||
let lastDataSource: DataSource = 'backend'
|
||||
const integrationConfigKeys: IntegrationConfigKey[] = [
|
||||
'jenkins',
|
||||
'gitea',
|
||||
@@ -123,221 +111,195 @@ const integrationConfigKeys: IntegrationConfigKey[] = [
|
||||
]
|
||||
const redactedVariableName = '非变量名内容已隐藏'
|
||||
|
||||
const agentCards: AgentCard[] = [
|
||||
{
|
||||
mode: 'risk',
|
||||
title: '发布风险摘要',
|
||||
description: '基于 ref diff、项目配置、历史失败记录生成检查项。',
|
||||
inputScope: ['项目', '环境', 'ref/tag', '最近失败 run'],
|
||||
outputPreview: ['配置变更影响范围', '构建和迁移风险', '发布前人工确认项'],
|
||||
},
|
||||
{
|
||||
mode: 'diagnosis',
|
||||
title: '失败诊断',
|
||||
description: '只读取 Jenkins 日志片段、run steps 和已知 runbook。',
|
||||
inputScope: ['失败 run', '失败节点', '脱敏日志片段'],
|
||||
outputPreview: ['失败原因摘要', '下一步排查命令', '建议责任边界'],
|
||||
},
|
||||
{
|
||||
mode: 'runbook',
|
||||
title: 'Runbook 问答',
|
||||
description: '围绕平台文档、项目 README、历史发布记录回答运维问题。',
|
||||
inputScope: ['白名单文档', '项目', '环境'],
|
||||
outputPreview: ['操作步骤', '风险提醒', '需要升级给后端的事项'],
|
||||
},
|
||||
{
|
||||
mode: 'releaseNotes',
|
||||
title: '发布说明草稿',
|
||||
description: '根据 commit、PR 和 tag 生成生产发布说明草稿。',
|
||||
inputScope: ['commit 列表', 'PR 信息', 'tag'],
|
||||
outputPreview: ['变更摘要', '验证范围', '回滚提示'],
|
||||
},
|
||||
]
|
||||
|
||||
const settings: SystemSetting[] = [
|
||||
{
|
||||
key: 'api-contract',
|
||||
name: 'API 契约来源',
|
||||
status: 'warning',
|
||||
owner: '后端 OpenAPI',
|
||||
contract: '所有项目、发布、refs、流程和集成状态均通过 devops-platform-api 获取。',
|
||||
boundary: '前端不写死真实 token,不直接访问 Jenkins/Gitea/LLM。',
|
||||
},
|
||||
{
|
||||
key: 'bpmn',
|
||||
name: 'BPMN 流程定义',
|
||||
status: 'healthy',
|
||||
owner: '平台流程配置',
|
||||
contract: 'step.bpmnNodeId 必须和 BPMN XML 节点 id 一致。',
|
||||
boundary: '前端只展示流程和状态,不推进状态机。',
|
||||
},
|
||||
{
|
||||
key: 'agent',
|
||||
name: 'Agent 边界',
|
||||
status: 'warning',
|
||||
owner: '后端 Agent proxy',
|
||||
contract: '只支持发布风险、失败诊断、Runbook、发布说明四类用途。',
|
||||
boundary: '无泛聊天入口;模型 key/baseURL/model 仅后端保存。',
|
||||
},
|
||||
{
|
||||
key: 'notification',
|
||||
name: '通知平台',
|
||||
status: 'warning',
|
||||
owner: '后端 notification adapter',
|
||||
contract: '通知模板由后端返回,后续可接企微、飞书或通用 webhook。',
|
||||
boundary: 'webhook 不进入前端,不在浏览器保存。',
|
||||
},
|
||||
]
|
||||
|
||||
export const devopsApi = {
|
||||
getLastDataSource(): DataSource {
|
||||
return lastDataSource
|
||||
},
|
||||
|
||||
async getDashboard(): Promise<DashboardSummary> {
|
||||
return withBackendFallback(
|
||||
async () => {
|
||||
const [health, backendProjects, backendRuns, integrationConfig] = await Promise.all([
|
||||
fetchHealth(),
|
||||
fetchProjects(),
|
||||
fetchRuns(),
|
||||
fetchIntegrationConfigStatusWithFallback(),
|
||||
])
|
||||
const adaptedProjects = toProjectConfigs(backendProjects)
|
||||
const [health, backendProjects, backendRuns, integrationConfig] = await Promise.all([
|
||||
fetchHealth(),
|
||||
fetchProjects(),
|
||||
fetchRuns(),
|
||||
fetchIntegrationConfigStatus(),
|
||||
])
|
||||
const adaptedProjects = toProjectConfigs(backendProjects)
|
||||
lastDataSource = 'backend'
|
||||
|
||||
return {
|
||||
integrations: toIntegrationStatuses(health),
|
||||
projects: adaptedProjects,
|
||||
runs: backendRuns.map((run) => toReleaseRun(run, adaptedProjects)),
|
||||
agentCards,
|
||||
settings: toSystemSettings(health, integrationConfig),
|
||||
integrationConfig,
|
||||
}
|
||||
},
|
||||
() => dashboardSummary,
|
||||
)
|
||||
return {
|
||||
integrations: toIntegrationStatuses(health),
|
||||
projects: adaptedProjects,
|
||||
runs: backendRuns.map((run) => toReleaseRun(run, adaptedProjects)),
|
||||
agentCards,
|
||||
settings: toSystemSettings(health, integrationConfig),
|
||||
integrationConfig,
|
||||
}
|
||||
},
|
||||
|
||||
async getIntegrations(): Promise<IntegrationStatus[]> {
|
||||
return withBackendFallback(
|
||||
async () => toIntegrationStatuses(await fetchHealth()),
|
||||
() => integrations,
|
||||
)
|
||||
lastDataSource = 'backend'
|
||||
return toIntegrationStatuses(await fetchHealth())
|
||||
},
|
||||
|
||||
async getProjects(): Promise<ProjectConfig[]> {
|
||||
return withBackendFallback(
|
||||
async () => toProjectConfigs(await fetchProjects()),
|
||||
() => mockProjects,
|
||||
)
|
||||
lastDataSource = 'backend'
|
||||
return toProjectConfigs(await fetchProjects())
|
||||
},
|
||||
|
||||
async getProjectRefs(projectKey: string, projectList = mockProjects): Promise<ProjectRefs> {
|
||||
return withBackendFallback(
|
||||
async () => {
|
||||
const { data } = await http.get<ProjectRefs>(`/projects/${projectKey}/refs`)
|
||||
return data
|
||||
},
|
||||
() => createFallbackRefs(projectKey, projectList),
|
||||
)
|
||||
async getProjectRefs(projectKey: string): Promise<ProjectRefs> {
|
||||
const { data } = await http.get<ProjectRefs>(`/projects/${projectKey}/refs`)
|
||||
lastDataSource = 'backend'
|
||||
return data
|
||||
},
|
||||
|
||||
async getRuns(projectList = mockProjects): Promise<ReleaseRun[]> {
|
||||
return withBackendFallback(
|
||||
async () => (await fetchRuns()).map((run) => toReleaseRun(run, projectList)),
|
||||
() => mockRuns,
|
||||
)
|
||||
async getRuns(projectList: ProjectConfig[] = []): Promise<ReleaseRun[]> {
|
||||
lastDataSource = 'backend'
|
||||
return (await fetchRuns()).map((run) => toReleaseRun(run, projectList))
|
||||
},
|
||||
|
||||
async createDeployRun(
|
||||
payload: CreateDeployRunPayload,
|
||||
projectList = mockProjects,
|
||||
projectList: ProjectConfig[] = [],
|
||||
): Promise<ReleaseRun> {
|
||||
return withBackendFallback(
|
||||
async () => {
|
||||
const { data } = await http.post<BackendDeployRun>('/deploy-runs', payload)
|
||||
return toReleaseRun(data, projectList)
|
||||
},
|
||||
() => createFallbackRun(payload, projectList, 'queued'),
|
||||
)
|
||||
const { data } = await http.post<BackendDeployRun>('/deploy-runs', payload)
|
||||
lastDataSource = 'backend'
|
||||
return toReleaseRun(data, projectList)
|
||||
},
|
||||
|
||||
async cancelDeployRun(runId: string, source?: ReleaseRun): Promise<ReleaseRun> {
|
||||
return withBackendFallback(
|
||||
async () => {
|
||||
const { data } = await http.post<BackendDeployRun>(`/deploy-runs/${runId}/cancel`)
|
||||
return toReleaseRun(data, mockProjects)
|
||||
},
|
||||
() => ({
|
||||
...(source ?? mockRuns[0]),
|
||||
id: source?.id ?? runId,
|
||||
status: 'canceled',
|
||||
duration: source?.duration ?? '-',
|
||||
steps: (source?.steps ?? mockRuns[0].steps).map((step) => ({
|
||||
...step,
|
||||
status: step.status === 'success' ? 'success' : 'canceled',
|
||||
summary: step.status === 'success' ? step.summary : '人工取消,本节点未继续执行。',
|
||||
})),
|
||||
}),
|
||||
)
|
||||
async cancelDeployRun(
|
||||
runId: string,
|
||||
projectList: ProjectConfig[] = [],
|
||||
): Promise<ReleaseRun> {
|
||||
const { data } = await http.post<BackendDeployRun>(`/deploy-runs/${runId}/cancel`)
|
||||
lastDataSource = 'backend'
|
||||
return toReleaseRun(data, projectList)
|
||||
},
|
||||
|
||||
async retryDeployRun(runId: string, source?: ReleaseRun): Promise<ReleaseRun> {
|
||||
return withBackendFallback(
|
||||
async () => {
|
||||
const { data } = await http.post<BackendDeployRun>(`/deploy-runs/${runId}/retry`)
|
||||
return toReleaseRun(data, mockProjects)
|
||||
},
|
||||
() =>
|
||||
createFallbackRun(
|
||||
{
|
||||
projectKey: source?.projectKey ?? 'access-manage',
|
||||
environment: source?.environment ?? 'test',
|
||||
ref: source?.ref ?? 'develop',
|
||||
operator: source?.actor ?? 'ops-admin',
|
||||
remark: source ? `重试 ${source.id}` : `重试 ${runId}`,
|
||||
},
|
||||
mockProjects,
|
||||
'queued',
|
||||
),
|
||||
)
|
||||
async retryDeployRun(
|
||||
runId: string,
|
||||
projectList: ProjectConfig[] = [],
|
||||
): Promise<ReleaseRun> {
|
||||
const { data } = await http.post<BackendDeployRun>(`/deploy-runs/${runId}/retry`)
|
||||
lastDataSource = 'backend'
|
||||
return toReleaseRun(data, projectList)
|
||||
},
|
||||
|
||||
async syncJenkinsRun(
|
||||
runId: string,
|
||||
source?: ReleaseRun,
|
||||
projectList = mockProjects,
|
||||
projectList: ProjectConfig[] = [],
|
||||
): Promise<ReleaseRun> {
|
||||
return withBackendFallback(
|
||||
async () => {
|
||||
const { data } = await http.post<BackendDeployRun>(`/deploy-runs/${runId}/sync-jenkins`)
|
||||
return toReleaseRun(data, projectList)
|
||||
},
|
||||
() => source ?? mockRuns.find((run) => run.id === runId) ?? mockRuns[0],
|
||||
)
|
||||
const { data } = await http.post<BackendDeployRun>(`/deploy-runs/${runId}/sync-jenkins`)
|
||||
lastDataSource = 'backend'
|
||||
return toReleaseRun(data, projectList)
|
||||
},
|
||||
|
||||
async syncJenkinsRuns(projectList = mockProjects): Promise<JenkinsSyncSummary> {
|
||||
return withBackendFallback(
|
||||
async () => {
|
||||
const { data } = await http.post<BackendJenkinsSyncSummary>(
|
||||
'/deploy-runs/sync-jenkins',
|
||||
)
|
||||
return toJenkinsSyncSummary(data, projectList)
|
||||
},
|
||||
() => ({
|
||||
checkedAt: new Date().toISOString(),
|
||||
totalCandidates: 0,
|
||||
syncedRuns: [],
|
||||
failedRuns: [],
|
||||
}),
|
||||
async syncJenkinsRuns(projectList: ProjectConfig[] = []): Promise<JenkinsSyncSummary> {
|
||||
const { data } = await http.post<BackendJenkinsSyncSummary>(
|
||||
'/deploy-runs/sync-jenkins',
|
||||
)
|
||||
lastDataSource = 'backend'
|
||||
return toJenkinsSyncSummary(data, projectList)
|
||||
},
|
||||
|
||||
async getAgentCards(): Promise<AgentCard[]> {
|
||||
await wait()
|
||||
return agentCards
|
||||
},
|
||||
|
||||
async getIntegrationConfigStatus(): Promise<IntegrationConfigStatus> {
|
||||
return withBackendFallback(
|
||||
async () => fetchIntegrationConfigStatus(),
|
||||
() => mockIntegrationConfigStatus,
|
||||
)
|
||||
lastDataSource = 'backend'
|
||||
return fetchIntegrationConfigStatus()
|
||||
},
|
||||
|
||||
async createAgentInvocation(
|
||||
payload: CreateAgentInvocationPayload,
|
||||
): Promise<AgentInvocation> {
|
||||
return withBackendFallback(
|
||||
async () => {
|
||||
const { data } = await http.post<AgentInvocation>('/agent/invocations', payload)
|
||||
return data
|
||||
},
|
||||
() => createFallbackAgentInvocation(payload),
|
||||
)
|
||||
const { data } = await http.post<AgentInvocation>('/agent/invocations', payload)
|
||||
lastDataSource = 'backend'
|
||||
return data
|
||||
},
|
||||
|
||||
async getSettings(): Promise<SystemSetting[]> {
|
||||
return withBackendFallback(
|
||||
async () => {
|
||||
const [health, integrationConfig] = await Promise.all([
|
||||
fetchHealth(),
|
||||
fetchIntegrationConfigStatusWithFallback(),
|
||||
])
|
||||
|
||||
return toSystemSettings(health, integrationConfig)
|
||||
},
|
||||
() => settings,
|
||||
)
|
||||
const [health, integrationConfig] = await Promise.all([
|
||||
fetchHealth(),
|
||||
fetchIntegrationConfigStatus(),
|
||||
])
|
||||
lastDataSource = 'backend'
|
||||
return toSystemSettings(health, integrationConfig)
|
||||
},
|
||||
|
||||
async getReleaseProcessXml(): Promise<string> {
|
||||
return withBackendFallback(
|
||||
async () => {
|
||||
const { data } = await http.get<BackendProcessDefinition>(
|
||||
'/process-definitions/release',
|
||||
)
|
||||
return data.xml
|
||||
},
|
||||
() => releaseProcessXml,
|
||||
const { data } = await http.get<BackendProcessDefinition>(
|
||||
'/process-definitions/release',
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
async function withBackendFallback<T>(
|
||||
request: () => Promise<T>,
|
||||
fallback: () => T | Promise<T>,
|
||||
): Promise<T> {
|
||||
if (import.meta.env.VITE_DEVOPS_USE_MOCK === 'true') {
|
||||
lastDataSource = 'mock'
|
||||
await wait()
|
||||
return fallback()
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await request()
|
||||
lastDataSource = 'backend'
|
||||
return result
|
||||
} catch {
|
||||
lastDataSource = 'mock'
|
||||
await wait()
|
||||
return fallback()
|
||||
}
|
||||
return data.xml
|
||||
},
|
||||
}
|
||||
|
||||
async function fetchHealth(): Promise<BackendHealth> {
|
||||
@@ -360,14 +322,6 @@ async function fetchIntegrationConfigStatus(): Promise<IntegrationConfigStatus>
|
||||
return sanitizeIntegrationConfigStatus(data)
|
||||
}
|
||||
|
||||
async function fetchIntegrationConfigStatusWithFallback(): Promise<IntegrationConfigStatus> {
|
||||
try {
|
||||
return await fetchIntegrationConfigStatus()
|
||||
} catch {
|
||||
return mockIntegrationConfigStatus
|
||||
}
|
||||
}
|
||||
|
||||
function toIntegrationStatuses(health: BackendHealth): IntegrationStatus[] {
|
||||
return [
|
||||
toIntegrationStatus('jenkins', 'Jenkins', health),
|
||||
@@ -464,7 +418,6 @@ function sanitizeIntegrationNote(note?: string): string | undefined {
|
||||
|
||||
function toProjectConfigs(backendProjects: BackendProject[]): ProjectConfig[] {
|
||||
return backendProjects.map((project) => {
|
||||
const mockProject = mockProjects.find((item) => item.key === project.key)
|
||||
const repo = parseRepository(project.repositoryUrl, project.key)
|
||||
|
||||
return {
|
||||
@@ -473,30 +426,24 @@ function toProjectConfigs(backendProjects: BackendProject[]): ProjectConfig[] {
|
||||
repository: project.repositoryUrl,
|
||||
owner: repo.owner,
|
||||
repo: repo.repo,
|
||||
serviceType: mockProject?.serviceType ?? '服务',
|
||||
serviceType: serviceTypeLabel(project.key),
|
||||
defaultBranch: project.defaultBranch,
|
||||
tagPolicy: mockProject?.tagPolicy ?? '生产发布优先选择 v* tag',
|
||||
tagPolicy: tagPolicyLabel(project.environments),
|
||||
environments: project.environments.map((environment) => {
|
||||
const mockEnvironment = mockProject?.environments.find(
|
||||
(item) => item.name === environment.name,
|
||||
)
|
||||
|
||||
return {
|
||||
name: environment.name,
|
||||
label: environment.displayName,
|
||||
branchPolicy:
|
||||
mockEnvironment?.branchPolicy ??
|
||||
(environment.name === 'production' ? 'v* tag' : project.defaultBranch),
|
||||
status: mockEnvironment?.status ?? 'idle',
|
||||
version: mockEnvironment?.version ?? '-',
|
||||
lastRunId: mockEnvironment?.lastRunId ?? '-',
|
||||
lastReleasedAt: mockEnvironment?.lastReleasedAt ?? '-',
|
||||
lastActor: mockEnvironment?.lastActor ?? '-',
|
||||
branchPolicy: releasePolicyLabel(environment.releasePolicy, project.defaultBranch),
|
||||
status: 'idle' as const,
|
||||
version: '-',
|
||||
lastRunId: '-',
|
||||
lastReleasedAt: '-',
|
||||
lastActor: '-',
|
||||
jenkinsJob: environment.jenkinsJobPath,
|
||||
wecomRobot: mockEnvironment?.wecomRobot ?? '后端通知配置',
|
||||
wecomRobot: '后端通知配置',
|
||||
}
|
||||
}),
|
||||
integrations: mockProject?.integrations ?? {
|
||||
integrations: {
|
||||
jenkins: 'warning',
|
||||
gitea: 'warning',
|
||||
wecom: 'warning',
|
||||
@@ -571,66 +518,6 @@ function toPipelineStep(step: BackendDeployRunStep): PipelineStep {
|
||||
}
|
||||
}
|
||||
|
||||
function createFallbackRun(
|
||||
payload: CreateDeployRunPayload,
|
||||
projectList: ProjectConfig[],
|
||||
status: ReleaseStatus,
|
||||
): ReleaseRun {
|
||||
const project = projectList.find((item) => item.key === payload.projectKey)
|
||||
const now = new Date().toISOString()
|
||||
|
||||
return {
|
||||
id: payload.idempotencyKey || `local-${Date.now()}`,
|
||||
projectKey: payload.projectKey,
|
||||
projectName: project?.name ?? payload.projectKey,
|
||||
environment: payload.environment,
|
||||
ref: payload.ref,
|
||||
status,
|
||||
actor: payload.operator,
|
||||
startedAt: formatDateTime(now),
|
||||
duration: status === 'queued' ? '等待 Jenkins queue' : '-',
|
||||
note: payload.remark || '本地降级发布单',
|
||||
jenkinsSummary: '等待 Jenkins 返回',
|
||||
jenkinsBuildUrl: '',
|
||||
commitSummary: '本地降级:后端不可用时保留操作上下文',
|
||||
steps: mockRuns[0].steps.map((step) => ({
|
||||
...step,
|
||||
status: step.bpmnNodeId === 'start-release' ? 'success' : 'pending',
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
function createFallbackAgentInvocation(
|
||||
payload: CreateAgentInvocationPayload,
|
||||
): AgentInvocation {
|
||||
return {
|
||||
id: `local-agent-${Date.now()}`,
|
||||
runId: payload.runId,
|
||||
projectKey: payload.projectKey,
|
||||
type: payload.type,
|
||||
status: 'success',
|
||||
promptSummary: payload.promptSummary,
|
||||
resultMarkdown: fallbackAgentResult(payload.type),
|
||||
createdAt: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
|
||||
function createFallbackRefs(projectKey: string, projectList: ProjectConfig[]): ProjectRefs {
|
||||
const project = projectList.find((item) => item.key === projectKey)
|
||||
const defaultBranch = project?.defaultBranch ?? 'develop'
|
||||
|
||||
return {
|
||||
source: 'mock',
|
||||
branches: Array.from(new Set([defaultBranch, 'develop', 'master']))
|
||||
.filter(Boolean)
|
||||
.map((name) => ({ name })),
|
||||
tags: [
|
||||
{ name: 'v2026.06.05-envsplit.7' },
|
||||
{ name: 'v2026.06.11-api.1' },
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
function toHealthState(status: BackendHealthStatus): HealthState {
|
||||
if (status === 'ok') {
|
||||
return 'healthy'
|
||||
@@ -760,6 +647,47 @@ function parseRepository(repositoryUrl: string, fallbackRepo: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function serviceTypeLabel(projectKey: string): string {
|
||||
if (projectKey.endsWith('-web') || projectKey === 'role-admin' || projectKey === 'role-user') {
|
||||
return '前端应用'
|
||||
}
|
||||
|
||||
if (projectKey === 'my-resume') {
|
||||
return '静态站点'
|
||||
}
|
||||
|
||||
return '后端服务'
|
||||
}
|
||||
|
||||
function tagPolicyLabel(environments: BackendProject['environments']): string {
|
||||
const production = environments.find((environment) => environment.name === 'production')
|
||||
|
||||
if (production?.releasePolicy === 'master') {
|
||||
return '生产发布固定 master'
|
||||
}
|
||||
|
||||
if (production?.releasePolicy === 'tag') {
|
||||
return '生产发布优先选择 v* tag'
|
||||
}
|
||||
|
||||
return '按环境发布策略执行'
|
||||
}
|
||||
|
||||
function releasePolicyLabel(
|
||||
policy: BackendProject['environments'][number]['releasePolicy'],
|
||||
defaultBranch: string,
|
||||
): string {
|
||||
if (policy === 'tag') {
|
||||
return 'v* tag'
|
||||
}
|
||||
|
||||
if (policy === 'master') {
|
||||
return 'master'
|
||||
}
|
||||
|
||||
return defaultBranch
|
||||
}
|
||||
|
||||
function formatDateTime(value?: string): string {
|
||||
if (!value) {
|
||||
return '-'
|
||||
@@ -814,7 +742,7 @@ function formatDuration(
|
||||
|
||||
function toSystemSettings(
|
||||
health: BackendHealth,
|
||||
integrationConfig = mockIntegrationConfigStatus,
|
||||
integrationConfig: IntegrationConfigStatus,
|
||||
): SystemSetting[] {
|
||||
const integrationByKey = new Map(
|
||||
integrationConfig.integrations.map((item) => [item.key, item]),
|
||||
@@ -825,7 +753,7 @@ function toSystemSettings(
|
||||
return {
|
||||
...item,
|
||||
status: toHealthState(health.status),
|
||||
contract: '已优先读取 devops-platform-api 的统一 envelope;后端不可用时降级到本地 mock。',
|
||||
contract: '已通过 devops-platform-api 的统一 envelope 读取真实接口数据。',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -860,19 +788,3 @@ function toHealthStateFromConfig(state?: IntegrationConfigState): HealthState {
|
||||
|
||||
return 'error'
|
||||
}
|
||||
|
||||
function fallbackAgentResult(type: AgentPurpose): string {
|
||||
const mode: Record<AgentPurpose, AgentMode> = {
|
||||
'release-risk': 'risk',
|
||||
'failure-diagnosis': 'diagnosis',
|
||||
'runbook-qa': 'runbook',
|
||||
'release-note': 'releaseNotes',
|
||||
'incident-review': 'diagnosis',
|
||||
}
|
||||
const card = agentCards.find((item) => item.mode === mode[type])
|
||||
|
||||
return [
|
||||
`${card?.title ?? 'Agent'} 本地降级结果`,
|
||||
'后端或 LLM 未配置时,前端只保留 DevOps 场景输入,不保存任何模型密钥。',
|
||||
].join('\n\n')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user