统一 Rust 与 TypeScript 格式化门禁

纳入 AGC Cargo workspace 的统一 rustfmt 检查与格式化入口

完成项目 TypeScript/Prettier 与 Rust 全量格式化

修复 Pingora expected executable 门禁的空白敏感误报

同步开发运维文档与 AGC skill pack 格式化忽略规则
This commit is contained in:
2026-09-01 16:26:20 +08:00
parent ae79d50b14
commit 071faa482c
1051 changed files with 28412 additions and 19834 deletions
+28 -8
View File
@@ -22,10 +22,22 @@ const REQUIRED_ARTIFACTS = [
{ path: 'scripts/check-pingora-canary-access-log-parity.mjs' },
{ path: 'scripts/deploy/pingora-direct-enable.sh', executable: true },
{ path: 'scripts/deploy/pingora-direct-rollback.sh', executable: true },
{ path: 'scripts/deploy/pingora-realpath-canary-enable.sh', executable: true },
{ path: 'scripts/deploy/pingora-realpath-canary-disable.sh', executable: true },
{ path: 'scripts/deploy/pingora-health-patrol-env-switch.mjs', executable: true },
{ path: 'scripts/deploy/pingora-gateway-env-shadow-switch.mjs', executable: true },
{
path: 'scripts/deploy/pingora-realpath-canary-enable.sh',
executable: true,
},
{
path: 'scripts/deploy/pingora-realpath-canary-disable.sh',
executable: true,
},
{
path: 'scripts/deploy/pingora-health-patrol-env-switch.mjs',
executable: true,
},
{
path: 'scripts/deploy/pingora-gateway-env-shadow-switch.mjs',
executable: true,
},
{ path: 'scripts/deploy/pingora-tls-cert-sync.mjs', executable: true },
{ path: 'deploy/systemd/genarrative-pingora-gateway.service' },
{ path: 'deploy/systemd/genarrative-pingora-gateway-direct-entry.conf' },
@@ -478,7 +490,10 @@ async function inspectReleaseManifest(input, pingoraGateway) {
for (const candidate of candidates) {
try {
const content = await readFile(path.join(input.releaseRoot, candidate), 'utf8');
const content = await readFile(
path.join(input.releaseRoot, candidate),
'utf8',
);
manifest = JSON.parse(content);
pathUsed = candidate;
break;
@@ -501,7 +516,9 @@ async function inspectReleaseManifest(input, pingoraGateway) {
diagnostics.length = 0;
const artifacts = Array.isArray(manifest.artifacts) ? manifest.artifacts : [];
const apiArtifact = artifacts.find((artifact) => artifact?.path === 'api-server');
const apiArtifact = artifacts.find(
(artifact) => artifact?.path === 'api-server',
);
const pingoraArtifact = artifacts.find(
(artifact) => artifact?.path === 'pingora-gateway',
);
@@ -695,10 +712,13 @@ function runCommand(command, args, input) {
}
function summarize(statuses) {
const criticalCount = statuses.filter((status) => status === 'CRITICAL').length;
const criticalCount = statuses.filter(
(status) => status === 'CRITICAL',
).length;
const warningCount = statuses.filter((status) => status === 'WARNING').length;
return {
status: criticalCount > 0 ? 'CRITICAL' : warningCount > 0 ? 'WARNING' : 'OK',
status:
criticalCount > 0 ? 'CRITICAL' : warningCount > 0 ? 'WARNING' : 'OK',
criticalCount,
warningCount,
};
@@ -45,10 +45,9 @@ const stdoutPath = path.join(bundleDir, 'command.stdout.txt');
const stderrPath = path.join(bundleDir, 'command.stderr.txt');
const recordPath = path.join(bundleDir, 'command-record.json');
const manifestPath = path.join(bundleDir, 'manifest.json');
const exitCode =
run.signal
? run.exitCode ?? 1
: run.exitCode === null || run.exitCode === undefined
const exitCode = run.signal
? (run.exitCode ?? 1)
: run.exitCode === null || run.exitCode === undefined
? run.error
? 1
: 0
@@ -140,8 +139,7 @@ if (exitCode !== 0) {
function parseArgs(argv) {
const separatorIndex = argv.indexOf('--');
const optionArgs =
separatorIndex < 0 ? argv : argv.slice(0, separatorIndex);
const optionArgs = separatorIndex < 0 ? argv : argv.slice(0, separatorIndex);
if (optionArgs.includes('-h') || optionArgs.includes('--help')) {
printUsage();
process.exit(0);
@@ -153,8 +151,7 @@ function parseArgs(argv) {
const command = argv[separatorIndex + 1] || '';
const commandArgs = argv.slice(separatorIndex + 2);
const result = {
phase:
process.env.GENARRATIVE_PINGORA_CUTOVER_COMMAND_PHASE || 'manual',
phase: process.env.GENARRATIVE_PINGORA_CUTOVER_COMMAND_PHASE || 'manual',
commandName:
process.env.GENARRATIVE_PINGORA_CUTOVER_COMMAND_NAME || 'command',
cutoverRunId: process.env.GENARRATIVE_PINGORA_CUTOVER_RUN_ID || '',
@@ -229,12 +226,18 @@ function validateConfig(config) {
validateSafeName(config.cutoverRunId, '--cutover-run-id');
}
if (config.expectedExecutable) {
validateSafeExecutablePath(config.expectedExecutable, '--expected-executable');
validateSafeExecutablePath(
config.expectedExecutable,
'--expected-executable',
);
}
if (!path.isAbsolute(config.outputRoot)) {
throw new Error('--output-root 必须是绝对路径。');
}
if (path.resolve(config.outputRoot) === path.parse(path.resolve(config.outputRoot)).root) {
if (
path.resolve(config.outputRoot) ===
path.parse(path.resolve(config.outputRoot)).root
) {
throw new Error('--output-root 不能是文件系统根目录。');
}
validateNoControlCharacters(config.outputRoot, '--output-root');
@@ -310,9 +313,7 @@ function validateRequiredArg(value, label) {
function validateSafeName(value, label) {
const text = String(value || '');
if (!/^[0-9A-Za-z._-]+$/u.test(text)) {
throw new Error(
`${label} 只能包含 ASCII 字母、数字、点、下划线或短横线。`,
);
throw new Error(`${label} 只能包含 ASCII 字母、数字、点、下划线或短横线。`);
}
}
@@ -358,10 +359,7 @@ async function validateOutputRootForWriting(outputRoot) {
const target = path.resolve(outputRoot);
const root = path.parse(target).root;
let current = root;
const segments = path
.relative(root, target)
.split(path.sep)
.filter(Boolean);
const segments = path.relative(root, target).split(path.sep).filter(Boolean);
for (const segment of segments) {
current = path.join(current, segment);
@@ -401,7 +399,10 @@ async function writeEvidenceFile(filePath, content) {
}
async function buildEvidenceFileMetadata(filePath) {
const [stats, content] = await Promise.all([stat(filePath), readFile(filePath)]);
const [stats, content] = await Promise.all([
stat(filePath),
readFile(filePath),
]);
return {
path: path.basename(filePath),
sizeBytes: stats.size,
@@ -476,15 +477,18 @@ function redactSecrets(text, secrets) {
}
function formatCommand(command, args, secrets) {
return [redactSecrets(command, secrets), ...redactSecretArgs(args, secrets)].join(' ');
return [
redactSecrets(command, secrets),
...redactSecretArgs(args, secrets),
].join(' ');
}
function redactSecretArgs(args, secrets = []) {
return args.map((arg, index) => {
const redacted =
index > 0 && SECRET_VALUE_FLAGS.has(args[index - 1])
? '<redacted>'
: redactInlineSecretArg(arg);
? '<redacted>'
: redactInlineSecretArg(arg);
return redactSecrets(redacted, secrets);
});
}
+66 -59
View File
@@ -68,8 +68,7 @@ function parseArgs(argv) {
DEFAULT_CUTOVER_TIMELINE_MAX_SPAN_MS,
'GENARRATIVE_PINGORA_CUTOVER_EVIDENCE_TIMELINE_MAX_SPAN_MS',
),
requiredCutoverRunId:
process.env.GENARRATIVE_PINGORA_CUTOVER_RUN_ID || '',
requiredCutoverRunId: process.env.GENARRATIVE_PINGORA_CUTOVER_RUN_ID || '',
allowExtraRootEntries: false,
requiredPhases: [],
requiredPhaseDirectLiveAccessLog: [],
@@ -117,7 +116,9 @@ function parseArgs(argv) {
);
break;
case '--require-command-executable':
result.requiredCommandExecutables.push(requireValue(argv, ++index, arg));
result.requiredCommandExecutables.push(
requireValue(argv, ++index, arg),
);
break;
case '--require-command-arg':
result.requiredCommandArgs.push(requireValue(argv, ++index, arg));
@@ -277,7 +278,9 @@ function normalizeRequiredCommandExecutables(commandExecutables) {
'--require-command-executable commandName',
);
if (!path.isAbsolute(executable)) {
throw new Error('--require-command-executable executable 必须是绝对路径。');
throw new Error(
'--require-command-executable executable 必须是绝对路径。',
);
}
if (isFilesystemRootPath(executable)) {
throw new Error(
@@ -357,7 +360,8 @@ function findRequiredCommandExecutable(requiredCommandExecutables, command) {
return (
requiredCommandExecutables.find(
(item) =>
item.phase === command.phase && item.commandName === command.commandName,
item.phase === command.phase &&
item.commandName === command.commandName,
)?.executable || null
);
}
@@ -366,32 +370,27 @@ function findRequiredCommandArgs(requiredCommandArgs, command) {
return requiredCommandArgs
.filter(
(item) =>
item.phase === command.phase && item.commandName === command.commandName,
item.phase === command.phase &&
item.commandName === command.commandName,
)
.map((item) => item.arg);
}
function validateSafePhase(value, label) {
if (!isSafePhase(value)) {
throw new Error(
`${label} 只能包含 ASCII 字母、数字、点、下划线或短横线。`,
);
throw new Error(`${label} 只能包含 ASCII 字母、数字、点、下划线或短横线。`);
}
}
function validateSafeCommandName(value, label) {
if (!isSafePhase(value)) {
throw new Error(
`${label} 只能包含 ASCII 字母、数字、点、下划线或短横线。`,
);
throw new Error(`${label} 只能包含 ASCII 字母、数字、点、下划线或短横线。`);
}
}
function validateSafeCutoverRunId(value, label) {
if (!isSafePhase(value)) {
throw new Error(
`${label} 只能包含 ASCII 字母、数字、点、下划线或短横线。`,
);
throw new Error(`${label} 只能包含 ASCII 字母、数字、点、下划线或短横线。`);
}
}
@@ -516,7 +515,11 @@ async function discoverEvidence(evidenceRoot, options = {}) {
continue;
}
const manifest = await readJsonManifest(manifestPath, entry.name, diagnostics);
const manifest = await readJsonManifest(
manifestPath,
entry.name,
diagnostics,
);
if (!manifest) {
continue;
}
@@ -742,32 +745,29 @@ async function buildAudit(config, discovery) {
config.requiredPhases.length > 0
? config.requiredPhases
: config.requiredCommands.length > 0
? []
: sortedUnique(
discovery.candidates
.filter((candidate) => candidate.commandName === null)
.map((candidate) => candidate.phase),
);
? []
: sortedUnique(
discovery.candidates
.filter((candidate) => candidate.commandName === null)
.map((candidate) => candidate.phase),
);
const phases = [];
for (const phase of phasesToCheck) {
const requiredAccessLog = config.requiredPhaseDirectLiveAccessLog.includes(
phase,
);
const requiredStaticHeaders = config.requiredPhaseDirectLiveStaticHeaders.includes(
phase,
);
const requiredPingoraEnvShadow = config.requiredPhasePingoraEnvShadow.includes(
phase,
);
const requiredAccessLog =
config.requiredPhaseDirectLiveAccessLog.includes(phase);
const requiredStaticHeaders =
config.requiredPhaseDirectLiveStaticHeaders.includes(phase);
const requiredPingoraEnvShadow =
config.requiredPhasePingoraEnvShadow.includes(phase);
const allPhaseCandidates = discovery.candidates.filter(
(candidate) => candidate.phase === phase && candidate.commandName === null,
(candidate) =>
candidate.phase === phase && candidate.commandName === null,
);
const candidates = filterCandidatesByCutoverRunId(
allPhaseCandidates,
config.requiredCutoverRunId,
)
.sort(compareEvidenceCandidates);
).sort(compareEvidenceCandidates);
if (candidates.length === 0) {
phases.push({
phase,
@@ -856,8 +856,7 @@ async function buildAudit(config, discovery) {
const candidates = filterCandidatesByCutoverRunId(
allCommandCandidates,
config.requiredCutoverRunId,
)
.sort(compareEvidenceCandidates);
).sort(compareEvidenceCandidates);
if (candidates.length === 0) {
commands.push({
phase: requiredCommand.phase,
@@ -1048,8 +1047,8 @@ function buildOperatorSummary(input) {
? phase.manifestCheck?.directLiveAccessLogCheck?.ok === true
: null,
reason: accessLogChecked
? phase.manifestCheck?.directLiveAccessLogCheck?.reason ??
firstDiagnostic
? (phase.manifestCheck?.directLiveAccessLogCheck?.reason ??
firstDiagnostic)
: null,
summary: accessLogSummary,
},
@@ -1059,8 +1058,8 @@ function buildOperatorSummary(input) {
? phase.manifestCheck?.directLiveStaticHeadersCheck?.ok === true
: null,
reason: staticHeadersChecked
? phase.manifestCheck?.directLiveStaticHeadersCheck?.reason ??
firstDiagnostic
? (phase.manifestCheck?.directLiveStaticHeadersCheck?.reason ??
firstDiagnostic)
: null,
summary: staticHeadersSummary,
},
@@ -1140,7 +1139,11 @@ function filterCandidatesByCutoverRunId(candidates, requiredCutoverRunId) {
);
}
function missingEvidenceDiagnostic(label, requiredCutoverRunId, allCandidateCount) {
function missingEvidenceDiagnostic(
label,
requiredCutoverRunId,
allCandidateCount,
) {
if (!requiredCutoverRunId) {
return `没有找到${label}的证据 manifest。`;
}
@@ -1252,7 +1255,9 @@ function checkPingoraEnvShadowSummary(summary) {
);
}
if (summary.tlsListen !== '') {
failures.push(`tlsListen 必须为空,实际为 ${formatNullable(summary.tlsListen)}`);
failures.push(
`tlsListen 必须为空,实际为 ${formatNullable(summary.tlsListen)}`,
);
}
if (summary.httpRedirectListen !== '') {
failures.push(
@@ -1448,8 +1453,12 @@ async function checkCommandManifest(
commandRecordExpectedExecutable:
commandRecordCheck.record?.expectedExecutable ?? null,
commandRecordExecutable: commandRecordCheck.record?.executable ?? null,
commandRecordStartedAt: normalizeIsoTime(commandRecordCheck.record?.startedAt),
commandRecordFinishedAt: normalizeIsoTime(commandRecordCheck.record?.finishedAt),
commandRecordStartedAt: normalizeIsoTime(
commandRecordCheck.record?.startedAt,
),
commandRecordFinishedAt: normalizeIsoTime(
commandRecordCheck.record?.finishedAt,
),
commandRecordDurationMs: commandRecordCheck.record?.durationMs ?? null,
requiredArgs,
diagnostics,
@@ -1463,9 +1472,7 @@ function checkCommandOutputEvidenceFileNames(candidate) {
['stderr', candidate.stderrFileName],
]) {
if (!fileName) {
diagnostics.push(
`最新命令证据 manifest.files.${field}.path 必须存在。`,
);
diagnostics.push(`最新命令证据 manifest.files.${field}.path 必须存在。`);
continue;
}
if (!isSafeEvidenceFileName(fileName)) {
@@ -1484,7 +1491,9 @@ async function readCommandRecord(candidate) {
ok: false,
path: null,
record: null,
diagnostics: ['最新命令证据 manifest.files.commandRecord.path 必须存在。'],
diagnostics: [
'最新命令证据 manifest.files.commandRecord.path 必须存在。',
],
};
}
if (!isSafeEvidenceFileName(fileName)) {
@@ -1524,7 +1533,9 @@ async function readCommandRecord(candidate) {
ok: false,
path: recordPath,
record: null,
diagnostics: ['最新命令证据 command-record.json 顶层必须是 JSON object。'],
diagnostics: [
'最新命令证据 command-record.json 顶层必须是 JSON object。',
],
};
}
return { ok: true, path: recordPath, record, diagnostics: [] };
@@ -1645,9 +1656,7 @@ function checkCommandRecordInvocation(record, label) {
} else if (record.args.some((item) => typeof item !== 'string')) {
diagnostics.push(`最新命令证据 ${label}.args 必须只包含字符串。`);
} else if (record.args.some((item) => /[\0\r\n]/u.test(item))) {
diagnostics.push(
`最新命令证据 ${label}.args 不能包含换行或 NUL 字符。`,
);
diagnostics.push(`最新命令证据 ${label}.args 不能包含换行或 NUL 字符。`);
}
if (typeof record.command !== 'string' || record.command.length === 0) {
diagnostics.push(`最新命令证据 ${label}.command 必须是非空字符串。`);
@@ -1675,9 +1684,7 @@ function checkCommandRecordTimes(record, label, candidate) {
const startedMs = Date.parse(startedAt);
const finishedMs = Date.parse(finishedAt);
if (finishedMs < startedMs) {
diagnostics.push(
`最新命令证据 ${label}.finishedAt 不能早于 startedAt。`,
);
diagnostics.push(`最新命令证据 ${label}.finishedAt 不能早于 startedAt。`);
}
if (candidate.generatedAt) {
const generatedMs = Date.parse(candidate.generatedAt);
@@ -1689,9 +1696,7 @@ function checkCommandRecordTimes(record, label, candidate) {
}
const durationMs = record.durationMs;
if (!Number.isSafeInteger(durationMs) || durationMs < 0) {
diagnostics.push(
`最新命令证据 ${label}.durationMs 必须是非负安全整数。`,
);
diagnostics.push(`最新命令证据 ${label}.durationMs 必须是非负安全整数。`);
} else {
const actualDurationMs = finishedMs - startedMs;
if (durationMs !== actualDurationMs) {
@@ -1767,7 +1772,9 @@ function commandStatus(verify, manifestCheck) {
}
function formatNullable(value) {
return value === null || value === undefined ? '<missing>' : JSON.stringify(value);
return value === null || value === undefined
? '<missing>'
: JSON.stringify(value);
}
function isSafeEvidenceFileName(value) {
+29 -25
View File
@@ -219,9 +219,7 @@ function summarizePingoraEnvShadow(snapshotRun) {
);
}
if (summary.tlsListen !== '') {
summary.diagnostics.push(
`tlsListen 应为空,实际 ${summary.tlsListen}`,
);
summary.diagnostics.push(`tlsListen 应为空,实际 ${summary.tlsListen}`);
}
if (summary.httpRedirectListen !== '') {
summary.diagnostics.push(
@@ -229,14 +227,10 @@ function summarizePingoraEnvShadow(snapshotRun) {
);
}
if (summary.tlsCertFile !== '') {
summary.diagnostics.push(
`tlsCertFile 应为空,实际 ${summary.tlsCertFile}`,
);
summary.diagnostics.push(`tlsCertFile 应为空,实际 ${summary.tlsCertFile}`);
}
if (summary.tlsKeyFile !== '') {
summary.diagnostics.push(
`tlsKeyFile 应为空,实际 ${summary.tlsKeyFile}`,
);
summary.diagnostics.push(`tlsKeyFile 应为空,实际 ${summary.tlsKeyFile}`);
}
if (summary.mode && summary.mode !== 'shadow') {
summary.diagnostics.push(`mode 应为 shadow,实际 ${summary.mode}`);
@@ -278,9 +272,7 @@ function summarizeDirectLiveAccessLog(directLiveRun) {
present: true,
logFile: accessLogCheck.logFile || '',
sinceLines: coerceNonNegativeInteger(accessLogCheck.sinceLines),
scannedLineCount: coerceNonNegativeInteger(
accessLogCheck.scannedLineCount,
),
scannedLineCount: coerceNonNegativeInteger(accessLogCheck.scannedLineCount),
checked: coerceNonNegativeInteger(accessLogCheck.checked),
matchedCount: coerceNonNegativeInteger(accessLogCheck.matchedCount),
missingCount: coerceNonNegativeInteger(accessLogCheck.missingCount),
@@ -398,7 +390,9 @@ function summarizeStaticHeaderEntry(entry, options = {}) {
};
const diagnostics = [];
if (summary.statusCode !== 200) {
diagnostics.push(`GET statusCode 应为 200,实际 ${formatValue(summary.statusCode)}`);
diagnostics.push(
`GET statusCode 应为 200,实际 ${formatValue(summary.statusCode)}`,
);
}
if (!summary.cacheControl) {
diagnostics.push('缺少 Cache-Control');
@@ -763,7 +757,10 @@ function validateConfig(config) {
}
validateEvidenceCommandArgs('状态快照命令参数', buildSnapshotArgs(config));
if (config.runDirectLive) {
validateEvidenceCommandArgs('direct live 命令参数', buildDirectLiveArgs(config));
validateEvidenceCommandArgs(
'direct live 命令参数',
buildDirectLiveArgs(config),
);
}
}
@@ -785,7 +782,10 @@ function validateDirectLiveConfig(config) {
}
validateHostOption(config.directRedirectHost, '--direct-redirect-host');
if (config.directRedirectBaseUrl) {
validateHttpsBaseUrl(config.directRedirectBaseUrl, '--direct-redirect-base-url');
validateHttpsBaseUrl(
config.directRedirectBaseUrl,
'--direct-redirect-base-url',
);
}
if (!config.directSpacetimeDatabase) {
throw new Error('--run-direct-live 必须提供 --direct-spacetime-database。');
@@ -800,7 +800,10 @@ function validateDirectLiveConfig(config) {
);
}
if (config.directProbeToken) {
validateNoControlCharacters(config.directProbeToken, '--direct-probe-token');
validateNoControlCharacters(
config.directProbeToken,
'--direct-probe-token',
);
}
if (!config.directPingoraAccessLog) {
throw new Error('--run-direct-live 必须提供 --direct-pingora-access-log。');
@@ -825,9 +828,7 @@ function validatePhase(value) {
function validateSafeName(value, label) {
const text = String(value || '');
if (!/^[0-9A-Za-z._-]+$/u.test(text)) {
throw new Error(
`${label} 只能包含 ASCII 字母、数字、点、下划线或短横线。`,
);
throw new Error(`${label} 只能包含 ASCII 字母、数字、点、下划线或短横线。`);
}
}
@@ -987,10 +988,7 @@ async function validateOutputRootForWriting(outputRoot) {
const target = path.resolve(outputRoot);
const root = path.parse(target).root;
let current = root;
const segments = path
.relative(root, target)
.split(path.sep)
.filter(Boolean);
const segments = path.relative(root, target).split(path.sep).filter(Boolean);
for (const segment of segments) {
current = path.join(current, segment);
@@ -1030,7 +1028,10 @@ async function writeEvidenceFile(filePath, content) {
}
async function buildEvidenceFileMetadata(filePath) {
const [stats, content] = await Promise.all([stat(filePath), readFile(filePath)]);
const [stats, content] = await Promise.all([
stat(filePath),
readFile(filePath),
]);
return {
path: path.basename(filePath),
sizeBytes: stats.size,
@@ -1130,7 +1131,10 @@ async function runEvidenceCommand({
let jsonMetadata = null;
let parseErrorMetadata = null;
if (parsed.ok) {
await writeEvidenceFile(jsonPath, `${JSON.stringify(parsed.value, null, 2)}\n`);
await writeEvidenceFile(
jsonPath,
`${JSON.stringify(parsed.value, null, 2)}\n`,
);
jsonMetadata = await buildEvidenceFileMetadata(jsonPath);
} else {
await writeEvidenceFile(parseErrorPath, `${parsed.error}\n`);
@@ -12,8 +12,10 @@ await validateBundleDir(bundleDir);
const manifest = await readManifest(manifestPath);
validateManifestSchemaVersion(manifest);
const { verifications, registeredFileNames } =
await verifyManifestFiles(bundleDir, manifest);
const { verifications, registeredFileNames } = await verifyManifestFiles(
bundleDir,
manifest,
);
const extraFiles = config.allowExtraFiles
? []
: await findExtraEvidenceEntries(bundleDir, registeredFileNames);
@@ -237,8 +239,8 @@ async function findExtraEvidenceEntries(bundleDir, registeredFileNames) {
reason: entry.isSymbolicLink()
? 'extra symlink not registered in manifest.files'
: entry.isDirectory()
? 'extra directory not registered in manifest.files'
: 'extra file not registered in manifest.files',
? 'extra directory not registered in manifest.files'
: 'extra file not registered in manifest.files',
});
}
return extras.sort((left, right) => left.path.localeCompare(right.path));
@@ -267,7 +269,10 @@ function validateMetadata(key, value) {
if (!Number.isSafeInteger(value.sizeBytes) || value.sizeBytes < 0) {
throw new Error(`manifest.files.${key}.sizeBytes 必须是非负安全整数。`);
}
if (typeof value.sha256 !== 'string' || !/^[0-9a-f]{64}$/u.test(value.sha256)) {
if (
typeof value.sha256 !== 'string' ||
!/^[0-9a-f]{64}$/u.test(value.sha256)
) {
throw new Error(`manifest.files.${key}.sha256 必须是 64 位小写十六进制。`);
}
}
@@ -276,7 +281,9 @@ async function verifyMetadata(bundleDir, key, metadata) {
const filePath = path.join(bundleDir, metadata.path);
const expectedPath = path.resolve(bundleDir, metadata.path);
if (path.dirname(expectedPath) !== path.resolve(bundleDir)) {
throw new Error(`manifest.files.${key}.path 超出证据目录: ${metadata.path}`);
throw new Error(
`manifest.files.${key}.path 超出证据目录: ${metadata.path}`,
);
}
let stats;
let content;
+21 -21
View File
@@ -357,8 +357,7 @@ async function inspectHealthPatrolEnv(input) {
const values = parsed.values;
const publicHost = values.GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST || '';
const publicBaseUrl =
values.GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL || '';
const publicBaseUrl = values.GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL || '';
const gatewayMode = values.GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE || '';
const diagnostics = [];
let status = 'OK';
@@ -371,14 +370,18 @@ async function inspectHealthPatrolEnv(input) {
}
if (
input.expectedPublicBaseUrl &&
normalizeBaseUrl(publicBaseUrl) !== normalizeBaseUrl(input.expectedPublicBaseUrl)
normalizeBaseUrl(publicBaseUrl) !==
normalizeBaseUrl(input.expectedPublicBaseUrl)
) {
diagnostics.push(
`GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL 应为 ${normalizeBaseUrl(input.expectedPublicBaseUrl)},实际 ${publicBaseUrl || '(空)'}`,
);
status = maxStatus(status, 'CRITICAL');
}
if (input.expectedPublicHost !== null && publicHost !== input.expectedPublicHost) {
if (
input.expectedPublicHost !== null &&
publicHost !== input.expectedPublicHost
) {
diagnostics.push(
`GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST 应为 ${input.expectedPublicHost},实际 ${publicHost || '(空)'}`,
);
@@ -402,8 +405,7 @@ async function inspectHealthPatrolEnv(input) {
apiBaseUrl: values.GENARRATIVE_HEALTH_PATROL_API_BASE_URL || '',
spacetimeBaseUrl:
values.GENARRATIVE_HEALTH_PATROL_SPACETIME_BASE_URL || '',
pingoraBaseUrl:
values.GENARRATIVE_HEALTH_PATROL_PINGORA_BASE_URL || '',
pingoraBaseUrl: values.GENARRATIVE_HEALTH_PATROL_PINGORA_BASE_URL || '',
hasPingoraProbeToken: Boolean(
values.GENARRATIVE_HEALTH_PATROL_PINGORA_PROBE_TOKEN ||
values.GENARRATIVE_PINGORA_GATEWAY_PROBE_TOKEN,
@@ -436,21 +438,18 @@ async function inspectPingoraEnv(input) {
values.GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN || '',
tlsCertFile: values.GENARRATIVE_PINGORA_GATEWAY_TLS_CERT_FILE || '',
tlsKeyFile: values.GENARRATIVE_PINGORA_GATEWAY_TLS_KEY_FILE || '',
forwardedProto:
values.GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO || '',
forwardedProto: values.GENARRATIVE_PINGORA_GATEWAY_FORWARDED_PROTO || '',
trustXForwardedFor:
values.GENARRATIVE_PINGORA_GATEWAY_TRUST_X_FORWARDED_FOR || '',
trustedFrontProxyConfirmed:
values.GENARRATIVE_PINGORA_GATEWAY_TRUSTED_FRONT_PROXY_CONFIRMED || '',
protectionEnabled:
values.GENARRATIVE_PINGORA_GATEWAY_PROTECTION_ENABLED || '',
instanceCount:
values.GENARRATIVE_PINGORA_GATEWAY_INSTANCE_COUNT || '',
instanceCount: values.GENARRATIVE_PINGORA_GATEWAY_INSTANCE_COUNT || '',
sharedProtectionConfirmed:
values.GENARRATIVE_PINGORA_GATEWAY_SHARED_PROTECTION_CONFIRMED || '',
hasProbeToken: Boolean(values.GENARRATIVE_PINGORA_GATEWAY_PROBE_TOKEN),
accessLogFile:
values.GENARRATIVE_PINGORA_GATEWAY_ACCESS_LOG_FILE || '',
accessLogFile: values.GENARRATIVE_PINGORA_GATEWAY_ACCESS_LOG_FILE || '',
};
const posture = summarizePingoraEnvPosture(
envValues,
@@ -753,7 +752,9 @@ async function inspectPingoraUnit(input) {
let status = 'OK';
if (catResult.code !== 0) {
diagnostics.push(`systemctl cat 失败: ${catResult.stderr || catResult.error}`);
diagnostics.push(
`systemctl cat 失败: ${catResult.stderr || catResult.error}`,
);
status = 'CRITICAL';
}
if (showResult.code !== 0) {
@@ -848,7 +849,9 @@ async function runChecks(input, healthPatrolEnv, secrets) {
checks.push(await runHealthPatrolEnvCheck(input, secrets));
}
if (input.runHealthPatrol) {
checks.push(await runProductionHealthPatrol(input, healthPatrolEnv, secrets));
checks.push(
await runProductionHealthPatrol(input, healthPatrolEnv, secrets),
);
}
return checks;
}
@@ -930,14 +933,11 @@ async function runProductionHealthPatrol(input, healthPatrolEnv, secrets) {
const rawEnv = await readEnvFile(input.healthPatrolEnvFile);
const env = {
...process.env,
...readEnvValuesForChild(rawEnv.status === 'CRITICAL' ? healthPatrolEnv : rawEnv),
...readEnvValuesForChild(
rawEnv.status === 'CRITICAL' ? healthPatrolEnv : rawEnv,
),
};
const result = await runCommand(
'node',
['--', script, '--json'],
input,
env,
);
const result = await runCommand('node', ['--', script, '--json'], input, env);
return commandCheck('production-health-patrol', result, secrets);
}
+23 -17
View File
@@ -41,8 +41,7 @@ function parseArgs(argv) {
process.env.GENARRATIVE_PINGORA_REALPATH_CANARY_CONFIG_FILE ||
'/etc/nginx/conf.d/zz-genarrative-pingora-realpath-canary.conf',
expectedPublicGateway:
process.env.GENARRATIVE_PINGORA_REHEARSAL_EXPECT_PUBLIC_GATEWAY ||
'none',
process.env.GENARRATIVE_PINGORA_REHEARSAL_EXPECT_PUBLIC_GATEWAY || 'none',
expectedHealthPatrolGatewayMode:
process.env.GENARRATIVE_HEALTH_PATROL_EXPECTED_GATEWAY_MODE || '',
requireRealpathCanary: readBoolEnv(
@@ -298,12 +297,10 @@ async function inspectHealthPatrolEnv(input) {
secretValues: collectSecretValuesFromEnv(values),
values: {
gatewayMode,
publicBaseUrl:
values.GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL || '',
publicBaseUrl: values.GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL || '',
publicHost: values.GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST || '',
apiBaseUrl: values.GENARRATIVE_HEALTH_PATROL_API_BASE_URL || '',
pingoraBaseUrl:
values.GENARRATIVE_HEALTH_PATROL_PINGORA_BASE_URL || '',
pingoraBaseUrl: values.GENARRATIVE_HEALTH_PATROL_PINGORA_BASE_URL || '',
hasPingoraProbeToken: Boolean(
values.GENARRATIVE_HEALTH_PATROL_PINGORA_PROBE_TOKEN ||
values.GENARRATIVE_PINGORA_GATEWAY_PROBE_TOKEN,
@@ -345,12 +342,9 @@ async function inspectPingoraEnv(input) {
httpRedirectListen:
values.GENARRATIVE_PINGORA_GATEWAY_HTTP_REDIRECT_LISTEN || '',
tlsCertFile: values.GENARRATIVE_PINGORA_GATEWAY_TLS_CERT_FILE || '',
hasTlsKeyFile: Boolean(
values.GENARRATIVE_PINGORA_GATEWAY_TLS_KEY_FILE,
),
hasTlsKeyFile: Boolean(values.GENARRATIVE_PINGORA_GATEWAY_TLS_KEY_FILE),
hasProbeToken: Boolean(values.GENARRATIVE_PINGORA_GATEWAY_PROBE_TOKEN),
accessLogFile:
values.GENARRATIVE_PINGORA_GATEWAY_ACCESS_LOG_FILE || '',
accessLogFile: values.GENARRATIVE_PINGORA_GATEWAY_ACCESS_LOG_FILE || '',
compressionAlgorithms:
values.GENARRATIVE_PINGORA_GATEWAY_COMPRESSION_ALGORITHMS || '',
trustXForwardedFor:
@@ -519,7 +513,9 @@ async function inspectPingoraSystemd(input) {
let status = 'OK';
if (catResult.code !== 0) {
diagnostics.push(`systemctl cat 失败: ${catResult.stderr || catResult.error}`);
diagnostics.push(
`systemctl cat 失败: ${catResult.stderr || catResult.error}`,
);
status = 'CRITICAL';
}
if (showResult.code !== 0) {
@@ -646,7 +642,9 @@ function parseSsOutput(stdout) {
}
const processText = parts.slice(5).join(' ');
const processNames = [
...new Set([...processText.matchAll(/"([^"]+)"/gu)].map((match) => match[1])),
...new Set(
[...processText.matchAll(/"([^"]+)"/gu)].map((match) => match[1]),
),
];
listeners.push({
localAddress: local,
@@ -681,7 +679,9 @@ function inspectPort(port, listeners, input) {
listener.processNames.some((name) => name.toLowerCase().includes('nginx')),
);
const hasPingora = listeners.some((listener) =>
listener.processNames.some((name) => name.toLowerCase().includes('pingora')),
listener.processNames.some((name) =>
name.toLowerCase().includes('pingora'),
),
);
const hasUnknownProcess =
listeners.length > 0 &&
@@ -702,7 +702,9 @@ function inspectPort(port, listeners, input) {
status = 'CRITICAL';
}
if (hasPingora) {
diagnostics.push(`${port} 已被 Pingora 监听,不能作为未切公网彩排状态。`);
diagnostics.push(
`${port} 已被 Pingora 监听,不能作为未切公网彩排状态。`,
);
status = 'CRITICAL';
}
}
@@ -785,10 +787,14 @@ async function inspectRealpathCanary(input, ports) {
'genarrative-pingora-realpath-canary.access.log',
'X-Genarrative-Nginx-Handoff pingora-realpath-canary',
];
const missing = requiredSnippets.filter((snippet) => !content.includes(snippet));
const missing = requiredSnippets.filter(
(snippet) => !content.includes(snippet),
);
templateLooksValid = missing.length === 0;
if (missing.length > 0) {
diagnostics.push(`realpath canary 配置缺少关键片段: ${missing.join(', ')}`);
diagnostics.push(
`realpath canary 配置缺少关键片段: ${missing.join(', ')}`,
);
status = 'CRITICAL';
}
}
+3 -4
View File
@@ -12,9 +12,7 @@ const STATUS_RANK = {
CRITICAL: 2,
};
const DEFAULT_PUBLIC_PATHS = [
'/',
];
const DEFAULT_PUBLIC_PATHS = ['/'];
const DEFAULT_SERVICES = [
'genarrative-api.service',
@@ -30,7 +28,8 @@ const PINGORA_DIRECT_SERVICES = [
'spacetimedb.service',
'genarrative-pingora-gateway.service',
];
const WORKER_SERVICE_PATTERN = 'genarrative-external-generation-worker@*.service';
const WORKER_SERVICE_PATTERN =
'genarrative-external-generation-worker@*.service';
const GATEWAY_MODES = new Set(['nginx', 'pingora-direct']);
function usage() {