refactor(GOV-009): 彻底切换为仅源码部署
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtemp, mkdir, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
|
||||
import { verifySourceDeployment } from './verify-source-deployment.mjs';
|
||||
|
||||
const processNames = ['api', 'scheduler', 'worker'];
|
||||
|
||||
function systemdUnit(processName, { user = 'a-share' } = {}) {
|
||||
const displayName =
|
||||
processName === 'api' ? 'API' : `${processName[0].toUpperCase()}${processName.slice(1)}`;
|
||||
|
||||
return `[Unit]
|
||||
Description=A Share Analysis ${displayName}
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=${user}
|
||||
Group=a-share
|
||||
WorkingDirectory=/opt/a-share-analysis/current
|
||||
EnvironmentFile=/etc/a-share-analysis/${processName}.env
|
||||
ExecStartPre=/usr/bin/test -r /etc/a-share-analysis/${processName}.env
|
||||
ExecStart=/usr/bin/node /opt/a-share-analysis/current/server/dist/apps/${processName}/main.js
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
`;
|
||||
}
|
||||
|
||||
async function createSourceReleaseFixture({
|
||||
omit,
|
||||
apiUser = 'a-share',
|
||||
invalidNginx = false,
|
||||
} = {}) {
|
||||
const root = await mkdtemp(path.join(tmpdir(), 'a-share-source-release-'));
|
||||
const files = new Map([
|
||||
['web/dist/index.html', '<!doctype html><title>A Share Analysis</title>\n'],
|
||||
['server/dist/apps/api/main.js', 'export {};\n'],
|
||||
['server/dist/apps/scheduler/main.js', 'export {};\n'],
|
||||
['server/dist/apps/worker/main.js', 'export {};\n'],
|
||||
...processNames.map((processName) => [
|
||||
`deploy/systemd/a-share-${processName}.service.example`,
|
||||
systemdUnit(processName, { user: processName === 'api' ? apiUser : 'a-share' }),
|
||||
]),
|
||||
[
|
||||
'deploy/nginx/a-share-analysis.conf.example',
|
||||
invalidNginx
|
||||
? 'server { root /srv/invalid; }\n'
|
||||
: `server {
|
||||
root /opt/a-share-analysis/current/web/dist;
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
}
|
||||
}
|
||||
`,
|
||||
],
|
||||
]);
|
||||
|
||||
for (const [relativePath, content] of files) {
|
||||
if (relativePath === omit) continue;
|
||||
const absolutePath = path.join(root, relativePath);
|
||||
await mkdir(path.dirname(absolutePath), { recursive: true });
|
||||
await writeFile(absolutePath, content);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
test('完整源码发布夹具通过', async () => {
|
||||
const root = await createSourceReleaseFixture();
|
||||
|
||||
await assert.doesNotReject(() => verifySourceDeployment(root));
|
||||
});
|
||||
|
||||
test('任一 Docker 活动文件会阻断源码发布', async () => {
|
||||
const root = await createSourceReleaseFixture();
|
||||
await writeFile(path.join(root, 'Dockerfile'), 'FROM node:22\n');
|
||||
|
||||
await assert.rejects(() => verifySourceDeployment(root), /禁止存在 Docker 活动文件.*Dockerfile/);
|
||||
});
|
||||
|
||||
test('Server 构建入口缺失会阻断源码发布', async () => {
|
||||
const root = await createSourceReleaseFixture({ omit: 'server/dist/apps/worker/main.js' });
|
||||
|
||||
await assert.rejects(() => verifySourceDeployment(root), /Worker 构建入口不存在/);
|
||||
});
|
||||
|
||||
test('systemd 配置不是最小权限时会阻断源码发布', async () => {
|
||||
const root = await createSourceReleaseFixture({ apiUser: 'root' });
|
||||
|
||||
await assert.rejects(() => verifySourceDeployment(root), /API systemd 必须使用 User=a-share/);
|
||||
});
|
||||
|
||||
test('Nginx 未代理 API 或未指向 Web dist 时会阻断源码发布', async () => {
|
||||
const root = await createSourceReleaseFixture({ invalidNginx: true });
|
||||
|
||||
await assert.rejects(() => verifySourceDeployment(root), /Nginx/);
|
||||
});
|
||||
Reference in New Issue
Block a user