openxiangda-devkit-core 2.71.5 → 2.71.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1147,8 +1147,8 @@ export class OpenXiangdaApplicationServices {
1147
1147
  if (diagnostics.length > 0) {
1148
1148
  return this.result("deployment.plan", workspace.context.workspace, undefined, diagnostics);
1149
1149
  }
1150
- const source = await operationStage('source', '核对远端主线与源码', () => publishedDeliverySource(workspace.root));
1151
- const lifecycle = developmentLifecycle(workspace.root, this.appSpecContractIndex(workspace, true));
1150
+ const source = await operationStage('source', '核对远端主线与源码(网络不可达时保留本地来源)', () => publishedDeliverySource(workspace.root));
1151
+ const lifecycle = developmentLifecycle(workspace.root, this.appSpecContractIndex(workspace, true), undefined, input.appSpecChangeId, 'warning-first');
1152
1152
  if (!lifecycle.readyForTest)
1153
1153
  return this.result('deployment.plan', workspace.context.workspace, { lifecycle: summarizeLifecycle(lifecycle) }, lifecycle.diagnostics);
1154
1154
  const sources = compileApplicationSources(workspace.config, this.toolchainVersion);
@@ -1158,6 +1158,7 @@ export class OpenXiangdaApplicationServices {
1158
1158
  return this.result("deployment.plan", workspace.context.workspace, {
1159
1159
  runtimeCapacity,
1160
1160
  workflowActivation,
1161
+ lifecycle: summarizeLifecycle(lifecycle),
1161
1162
  environment: input.environment,
1162
1163
  appCode: workspace.config.app.code,
1163
1164
  sealed: false,
@@ -1168,7 +1169,7 @@ export class OpenXiangdaApplicationServices {
1168
1169
  backendRoot: workspace.config.backend.root,
1169
1170
  configDigest: sources.config.digest,
1170
1171
  contractDigest: sources.contracts.digest,
1171
- }, workflowActivation);
1172
+ }, [...workflowActivation, ...lifecycle.diagnostics]);
1172
1173
  }
1173
1174
  async deploy(input) {
1174
1175
  const workspace = await this.workspace(input.root);
@@ -1181,14 +1182,18 @@ export class OpenXiangdaApplicationServices {
1181
1182
  this.diagnostic("DEPLOY_PREPRODUCTION_ONLY", "Native 2.0 只允许直接部署到预发环境", String(input.environment), "使用 openxiangda deploy;生产环境必须显式复用成功的测试 DeploymentRun"),
1182
1183
  ]);
1183
1184
  }
1184
- const source = await operationStage('source', '核对远端主线与源码', () => publishedDeliverySource(workspace.root));
1185
+ const source = await operationStage('source', '核对远端主线与源码(网络不可达时保留本地来源)', () => publishedDeliverySource(workspace.root));
1185
1186
  const client = await this.client(workspace.root);
1186
1187
  const capabilities = await client.capabilities();
1188
+ let prerequisiteWarnings = [];
1187
1189
  if (capabilities.features?.['application.deployment-prerequisites']?.status === 'available') {
1188
- await operationStage('prerequisites', '检查内核、源码托管与镜像仓库', async () => {
1190
+ const prerequisites = await operationStage('prerequisites', '检查内核、源码托管与镜像仓库(源码异常转为警告)', async () => {
1189
1191
  const backend = backendRuntimeRequired(workspace.config);
1190
- assertDeploymentPrerequisites(await client.deploymentPrerequisites(workspace.config.app.code, backend), workspace.config.app.code, backend);
1192
+ const report = await client.deploymentPrerequisites(workspace.config.app.code, backend);
1193
+ const warnings = assertDeploymentPrerequisites(report, workspace.config.app.code, backend);
1194
+ return { report, warnings };
1191
1195
  });
1196
+ prerequisiteWarnings = prerequisites.warnings;
1192
1197
  }
1193
1198
  const sources = compileApplicationSources(workspace.config, this.toolchainVersion);
1194
1199
  await operationStage('target-preflight', '目标平台只读预检', () => this.validateTargetConfigurationCompatibility(workspace, input.environment, compileLocalConfiguration(workspace.config, this.toolchainVersion)));
@@ -1204,8 +1209,8 @@ export class OpenXiangdaApplicationServices {
1204
1209
  const generated = await this.generate({ root: workspace.root, check: true });
1205
1210
  if (!generated.ok)
1206
1211
  return { ...generated, operation: 'deploy' };
1207
- const lifecycle = await operationStage('development-records', '核对需求、架构与测试验收计划', async () => {
1208
- const lifecycle = developmentLifecycle(workspace.root, this.appSpecContractIndex(workspace, true));
1212
+ const lifecycle = await operationStage('development-records', '读取需求、架构与测试验收计划(缺口转为警告)', async () => {
1213
+ const lifecycle = developmentLifecycle(workspace.root, this.appSpecContractIndex(workspace, true), undefined, input.appSpecChangeId, 'warning-first');
1209
1214
  return { ...lifecycle, ok: lifecycle.readyForTest };
1210
1215
  });
1211
1216
  if (!lifecycle.readyForTest)
@@ -1255,7 +1260,11 @@ export class OpenXiangdaApplicationServices {
1255
1260
  ...(input.requestId ? { requestId: input.requestId } : {}),
1256
1261
  ...(input.approvalLink ? { approvalLink: true } : {}),
1257
1262
  });
1258
- return this.result("deploy", workspace.context.workspace, deployment, workflowActivation, [
1263
+ return this.result("deploy", workspace.context.workspace, deployment, [
1264
+ ...workflowActivation,
1265
+ ...prerequisiteWarnings,
1266
+ ...lifecycle.diagnostics,
1267
+ ], [
1259
1268
  {
1260
1269
  code: "status",
1261
1270
  label: "查看部署状态",
@@ -1448,7 +1457,7 @@ export class OpenXiangdaApplicationServices {
1448
1457
  if (!appVersionId)
1449
1458
  throw new Error("DEPLOYMENT_APP_VERSION_NOT_AVAILABLE");
1450
1459
  const diagnostics = await this.productionPromotionDiagnostics(workspace, client, source, appVersionId);
1451
- if (diagnostics.length > 0) {
1460
+ if (diagnostics.some(item => item.severity === 'error')) {
1452
1461
  return this.result("deploy", workspace.context.workspace, undefined, diagnostics);
1453
1462
  }
1454
1463
  const deployment = await operationStage('submit', '提交生产晋级', () => client.promote({
@@ -1457,7 +1466,7 @@ export class OpenXiangdaApplicationServices {
1457
1466
  environmentKind: environment,
1458
1467
  idempotencyKey: `promote:${source.packageDigest}:${environment}`,
1459
1468
  }));
1460
- return this.ok("deploy", workspace.context.workspace, deployment, [
1469
+ return this.result("deploy", workspace.context.workspace, deployment, diagnostics, [
1461
1470
  { code: "status", label: "查看生产部署状态", command: `openxiangda status ${deployment.id}` },
1462
1471
  ]);
1463
1472
  }
@@ -1477,12 +1486,21 @@ export class OpenXiangdaApplicationServices {
1477
1486
  assertProductionPromotionPreflight(version, {
1478
1487
  appCode: workspace.config.app.code, sourceDeploymentId: source.id, appVersionId, packageDigest: source.packageDigest,
1479
1488
  });
1480
- const testedLifecycle = lifecycleAtCommit(workspace.root, version.source.commit, this.appSpecContractIndex(workspace, true));
1489
+ let testedLifecycle;
1490
+ try {
1491
+ testedLifecycle = lifecycleAtCommit(workspace.root, version.source.commit, this.appSpecContractIndex(workspace, true));
1492
+ }
1493
+ catch (error) {
1494
+ // 生产晋级使用已封存的 packageDigest;当前机器无法读取来源仓库时仅提示,
1495
+ // 不把本地 Git、VPN 或远端仓库可达性误判成制品不可发布。
1496
+ diagnostics.push(this.advisoryDiagnostic(this.diagnostic('APPSPEC_TESTED_SOURCE_UNAVAILABLE', '无法从测试版本来源读取 AppSpec 验收计划;仍可按不可变测试制品晋级', 'appspec', '网络恢复后补齐验收记录;本次以测试 DeploymentRun 和 packageDigest 为准', { message: error instanceof Error ? error.message : String(error) })));
1497
+ testedLifecycle = { changeId: null, acceptanceIds: [], design: { diagnostics: [] } };
1498
+ }
1481
1499
  if (!testedLifecycle.changeId || !testedLifecycle.acceptanceIds.length) {
1482
- diagnostics.push(this.diagnostic('APPSPEC_TESTED_PLAN_REQUIRED', '指定测试版本缺少可追溯的需求与验收计划', 'appspec', '补齐真实记录并发布新的测试候选,再执行验收和生产晋级'));
1500
+ diagnostics.push(this.advisoryDiagnostic(this.diagnostic('APPSPEC_TESTED_PLAN_REQUIRED', '指定测试版本缺少可追溯的需求与验收计划;仍可继续生产晋级,请在后续补齐记录', 'appspec', '补齐真实记录并在下一次发布前复核;本次仅作为发布风险提示')));
1483
1501
  }
1484
1502
  // 原测试提交中的设计基线也必须有效,当前主线的补写不能补签旧版本。
1485
- diagnostics.push(...testedLifecycle.design.diagnostics);
1503
+ diagnostics.push(...this.advisoryDiagnostics(testedLifecycle.design.diagnostics));
1486
1504
  const acceptance = await operationStage('business-acceptance', '核对原测试版本的业务验收报告', async () => {
1487
1505
  const result = verifyBusinessAcceptance(workspace.root, source, testedLifecycle);
1488
1506
  if (result.ok)
@@ -1493,13 +1511,13 @@ export class OpenXiangdaApplicationServices {
1493
1511
  });
1494
1512
  return result;
1495
1513
  });
1496
- diagnostics.push(...acceptance.diagnostics);
1514
+ diagnostics.push(...this.advisoryDiagnostics(acceptance.diagnostics));
1497
1515
  }
1498
1516
  catch (error) {
1499
1517
  return [this.configurationCompatibilityDiagnostic(error)];
1500
1518
  }
1501
1519
  if (workspace.context.workspace.dirty || version.source.dirty) {
1502
- diagnostics.push(this.diagnostic("PRODUCTION_SOURCE_DIRTY", "生产晋级只接受来源干净且当前工作区无未提交变更的 AppVersion", "git", "提交并推送当前改动,或在干净主线目录中晋级该测试版本"));
1520
+ diagnostics.push(this.advisoryDiagnostic(this.diagnostic("PRODUCTION_SOURCE_DIRTY", "测试版本或当前工作区存在未提交变更;本次仍复用已封存测试制品晋级", "git", "后续发布前补齐来源快照;当前制品仍按不可变 packageDigest 晋级")));
1503
1521
  }
1504
1522
  const repository = git(workspace.root, ["config", "--get", "remote.origin.url"]);
1505
1523
  const currentRepository = this.repositoryIdentity(repository);
@@ -1507,9 +1525,9 @@ export class OpenXiangdaApplicationServices {
1507
1525
  if (!currentRepository ||
1508
1526
  !sourceRepository ||
1509
1527
  currentRepository !== sourceRepository) {
1510
- diagnostics.push(this.diagnostic("PRODUCTION_SOURCE_REPOSITORY_MISMATCH", "当前工作区仓库与 AppVersion 来源仓库不一致", "git.remote.origin.url", "在构建该 AppVersion 的同一仓库中执行晋级", { current: repository || null, source: version.source.repository }));
1528
+ diagnostics.push(this.advisoryDiagnostic(this.diagnostic("PRODUCTION_SOURCE_REPOSITORY_MISMATCH", "当前工作区仓库与 AppVersion 来源仓库不一致;仓库归属仅作溯源提示", "git.remote.origin.url", "确认 packageDigest 和测试 DeploymentRun 后继续;如需完整溯源再补充仓库信息", { current: repository || null, source: version.source.repository })));
1511
1529
  }
1512
- if (diagnostics.length)
1530
+ if (diagnostics.some(item => item.severity === 'error'))
1513
1531
  return diagnostics;
1514
1532
  try {
1515
1533
  await operationStage('promotion-source', '核对测试制品的主线来源', () => publishedDeliverySource(workspace.root, version.source.commit));
@@ -1517,7 +1535,7 @@ export class OpenXiangdaApplicationServices {
1517
1535
  catch (error) {
1518
1536
  if (!(error instanceof DeliverySourceError))
1519
1537
  throw error;
1520
- diagnostics.push(this.diagnostic(error.code, error.message, 'git', String(error.data.remediation), error.data));
1538
+ diagnostics.push(this.advisoryDiagnostic(this.diagnostic(error.code, error.message, 'git', String(error.data.remediation), error.data)));
1521
1539
  }
1522
1540
  return diagnostics;
1523
1541
  }
@@ -2081,6 +2099,21 @@ export class OpenXiangdaApplicationServices {
2081
2099
  ...(details ? { details } : {}),
2082
2100
  };
2083
2101
  }
2102
+ advisoryDiagnostic(diagnostic) {
2103
+ return {
2104
+ ...diagnostic,
2105
+ severity: diagnostic.severity === 'error' ? 'warning' : diagnostic.severity,
2106
+ retryable: true,
2107
+ details: {
2108
+ ...diagnostic.details,
2109
+ gateClass: 'advisory',
2110
+ affects: ['release-readiness'],
2111
+ },
2112
+ };
2113
+ }
2114
+ advisoryDiagnostics(diagnostics) {
2115
+ return diagnostics.map(diagnostic => this.advisoryDiagnostic(diagnostic));
2116
+ }
2084
2117
  connectedDevelopmentFailure(error, workspaceRoot) {
2085
2118
  const rawCode = String(error?.code ||
2086
2119
  (error instanceof Error ? error.message.split(":", 1)[0] : "") ||