Files
Genarrative/scripts/check-production-health-patrol-env-check.mjs
T
kdletters a7711d2dc3 引入Pingora独立网关正式化链路
新增 pingora-gateway 独立二进制 crate,覆盖路由、静态资源、压缩、接流保护、TLS 直连和访问日志能力。

新增 Nginx canary、realpath canary、direct preflight、direct live、direct enable 和 rollback 脚本。

新增 Pingora 切流证据包、命令证据、manifest 验真、根目录总审计和 release readiness 聚合门禁。

完善 API release、Jenkins、systemd、health patrol、生产部署和发布包自包含校验。

更新 Pingora 试点文档、Nginx README 与 Hermes 共享记忆。
2026-06-17 20:16:51 +08:00

282 lines
8.3 KiB
JavaScript

#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
import {
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
const failures = [];
const tmpRoot = mkdtempSync(
path.join(tmpdir(), 'genarrative-health-patrol-env-check-'),
);
try {
main();
} finally {
rmSync(tmpRoot, { recursive: true, force: true });
}
if (failures.length > 0) {
console.error('[check:production-health-patrol-env] FAILED');
for (const failure of failures) {
console.error(`- ${failure}`);
}
process.exit(1);
}
console.log('[check:production-health-patrol-env] OK');
function main() {
assertAcceptsPingoraDirectLoopbackWithHost();
assertRejectsPingoraDirectLoopbackWithoutHost();
assertRejectsWrongGatewayMode();
assertAcceptsNginxWithEmptyPublicHost();
assertRejectsNginxWithStalePublicHost();
assertRejectsInvalidPublicHost();
assertRejectsInvalidEnvFilePaths();
assertRejectsInvalidBoolEnv();
assertExampleDocumentsDirectMode();
}
function assertAcceptsPingoraDirectLoopbackWithHost() {
const envFile = writeEnv('pingora-direct-ok.env', {
GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE: 'pingora-direct',
GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL: 'https://127.0.0.1',
GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST: 'genarrative.example',
});
const result = runCheck([
'--env-file',
envFile,
'--expected-gateway-mode',
'pingora-direct',
'--expected-public-base-url',
'https://127.0.0.1',
'--expected-public-host',
'genarrative.example',
]);
assertStatus(result, 0, 'Pingora direct loopback 配正式 Host 应通过。');
}
function assertRejectsPingoraDirectLoopbackWithoutHost() {
const envFile = writeEnv('pingora-direct-missing-host.env', {
GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE: 'pingora-direct',
GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL: 'https://127.0.0.1',
GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST: '',
});
const result = runCheck([
'--env-file',
envFile,
'--expected-gateway-mode',
'pingora-direct',
'--expected-public-base-url',
'https://127.0.0.1',
]);
assertStatus(
result,
1,
'Pingora direct loopback 缺 Host 必须失败。',
);
assertIncludes(
`${result.stdout}\n${result.stderr}`,
'Pingora direct 模式使用本机 public base URL 时必须配置 GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST',
'Pingora direct loopback 缺 Host 必须给出明确错误。',
);
}
function assertRejectsWrongGatewayMode() {
const envFile = writeEnv('wrong-mode.env', {
GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE: 'nginx',
GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL: 'https://127.0.0.1',
GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST: 'genarrative.example',
});
const result = runCheck([
'--env-file',
envFile,
'--expected-gateway-mode',
'pingora-direct',
]);
assertStatus(result, 1, 'gateway mode 与期望不一致必须失败。');
assertIncludes(
`${result.stdout}\n${result.stderr}`,
'GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE 应为 pingora-direct,实际 nginx',
'gateway mode mismatch 必须给出明确错误。',
);
}
function assertAcceptsNginxWithEmptyPublicHost() {
const envFile = writeEnv('nginx-empty-host.env', {
GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE: 'nginx',
GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL: 'http://127.0.0.1',
GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST: '',
});
const result = runCheck([
'--env-file',
envFile,
'--expected-gateway-mode',
'nginx',
'--expected-public-base-url',
'http://127.0.0.1',
'--require-empty-public-host',
]);
assertStatus(result, 0, 'Nginx 模式回退且 Host 为空应通过。');
}
function assertRejectsNginxWithStalePublicHost() {
const envFile = writeEnv('nginx-stale-host.env', {
GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE: 'nginx',
GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL: 'http://127.0.0.1',
GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST: 'genarrative.example',
});
const result = runCheck([
'--env-file',
envFile,
'--expected-gateway-mode',
'nginx',
'--require-empty-public-host',
]);
assertStatus(result, 1, 'Nginx 回退后残留 Host 覆盖必须失败。');
assertIncludes(
`${result.stdout}\n${result.stderr}`,
'GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST 应为空',
'Nginx 回退后残留 Host 覆盖必须给出明确错误。',
);
}
function assertRejectsInvalidPublicHost() {
const envFile = writeEnv('invalid-host.env', {
GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE: 'pingora-direct',
GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL: 'https://127.0.0.1',
GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST: 'https://genarrative.example',
});
const result = runCheck([
'--env-file',
envFile,
'--expected-gateway-mode',
'pingora-direct',
]);
assertStatus(result, 1, '非法 public Host 必须失败。');
assertIncludes(
`${result.stdout}\n${result.stderr}`,
'GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST 只能是 host 或 host:port',
'非法 public Host 必须给出明确错误。',
);
}
function assertRejectsInvalidEnvFilePaths() {
const relativePath = runCheck([
'--env-file',
'health-patrol.env',
'--expected-gateway-mode',
'nginx',
]);
assertStatus(relativePath, 1, 'health patrol env 复核必须拒绝相对 env 文件路径。');
assertIncludes(
`${relativePath.stdout}\n${relativePath.stderr}`,
'--env-file 必须是绝对路径',
'相对 env 文件路径必须给出明确错误。',
);
const rootPath = runCheck([
'--env-file',
'/',
'--expected-gateway-mode',
'nginx',
]);
assertStatus(rootPath, 1, 'health patrol env 复核必须拒绝文件系统根目录。');
assertIncludes(
`${rootPath.stdout}\n${rootPath.stderr}`,
'--env-file 不能是文件系统根目录',
'文件系统根目录 env 文件路径必须给出明确错误。',
);
}
function assertRejectsInvalidBoolEnv() {
const envFile = writeEnv('invalid-bool-env.env', {
GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE: 'nginx',
GENARRATIVE_HEALTH_PATROL_PUBLIC_BASE_URL: 'http://127.0.0.1',
GENARRATIVE_HEALTH_PATROL_PUBLIC_HOST: '',
});
const result = runCheck(
[
'--env-file',
envFile,
'--expected-gateway-mode',
'nginx',
],
{
GENARRATIVE_HEALTH_PATROL_REQUIRE_EMPTY_PUBLIC_HOST: 'enabled',
},
);
assertStatus(result, 1, 'health patrol env 复核必须拒绝非法布尔 env。');
assertIncludes(
`${result.stdout}\n${result.stderr}`,
'GENARRATIVE_HEALTH_PATROL_REQUIRE_EMPTY_PUBLIC_HOST 必须是布尔值',
'health patrol env 复核非法布尔 env 必须给出明确错误。',
);
}
function assertExampleDocumentsDirectMode() {
const example = readFileSync('deploy/env/health-patrol.env.example', 'utf8');
assertIncludes(
example,
'GENARRATIVE_HEALTH_PATROL_GATEWAY_MODE=nginx',
'health patrol env 示例必须保留默认 nginx 模式。',
);
assertIncludes(
example,
'Pingora 直连切换后改为 pingora-direct',
'health patrol env 示例必须说明 Pingora direct 切换口径。',
);
}
function writeEnv(name, values) {
const filePath = path.join(tmpRoot, name);
mkdirSync(path.dirname(filePath), { recursive: true });
const body = Object.entries(values)
.map(([key, value]) => `${key}=${value}`)
.join('\n');
writeFileSync(filePath, `${body}\n`, 'utf8');
return filePath;
}
function runCheck(args, extraEnv = {}) {
return spawnSync(
process.execPath,
['--', 'scripts/check-production-health-patrol-env.mjs', ...args],
{
cwd: process.cwd(),
encoding: 'utf8',
env: {
...process.env,
GENARRATIVE_HEALTH_PATROL_ENV_FILE: '',
GENARRATIVE_HEALTH_PATROL_EXPECTED_GATEWAY_MODE: '',
GENARRATIVE_HEALTH_PATROL_EXPECTED_PUBLIC_BASE_URL: '',
GENARRATIVE_HEALTH_PATROL_EXPECTED_PUBLIC_HOST: '',
GENARRATIVE_HEALTH_PATROL_REQUIRE_EMPTY_PUBLIC_HOST: '',
...extraEnv,
},
},
);
}
function assertStatus(result, expected, reason) {
const actual = result.status ?? 0;
if (actual !== expected) {
failures.push(
`${reason} 预期退出码 ${expected},实际 ${actual}。\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`,
);
}
}
function assertIncludes(content, needle, reason) {
if (!content.includes(needle)) {
failures.push(`${reason} 缺少: ${needle}`);
}
}