ci: add NestJS Worker deploy + M-AI-01 integration tests to deploy workflow
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 43s

- Deploy zhixi-nest-worker.service after API restart
- Run test/run-integration-ci.sh with real MySQL/Redis/BullMQ
- Integration test failure blocks deployment (exit 1)
- timeout-minutes: 10 to prevent hung tests

Line: .gitea/workflows/deploy.yml:99-113

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-19 22:51:28 +08:00
parent 729df364b6
commit eef42d51ab
4 changed files with 37 additions and 6 deletions

View File

@ -92,6 +92,34 @@ jobs:
exit 1 exit 1
fi fi
- name: Deploy NestJS Worker
run: |
cd /opt/zhixi/backend
sudo cp deploy/zhixi-nest-worker.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl reset-failed zhixi-nest-worker 2>/dev/null || true
sudo systemctl restart zhixi-nest-worker
sleep 5
echo "[deploy] Worker status: $(sudo systemctl is-active zhixi-nest-worker)"
echo "[deploy] Worker startup log:"
sudo journalctl -u zhixi-nest-worker --no-pager -n 10
- name: Run M-AI-01 integration tests
run: |
cd /tmp/api-server
bash test/run-integration-ci.sh
env:
DATABASE_URL: mysql://zhixi_user:hKHQ+N0wBjJAiLukFu5OMEI8@127.0.0.1:3306/zhixi_prod
REDIS_HOST: 127.0.0.1
REDIS_PORT: '6379'
REDIS_PASSWORD: THTC2B5wXvhlMw/ZB6TYwB6MI9r1tO0F
JWT_SECRET: test-integration-secret
INTERNAL_API_KEY: test-key
CREDENTIAL_ENCRYPTION_KEY: '0123456789abcdef0123456789abcdef'
STORAGE_DRIVER: local
MYSQL_ROOT_PASSWORD: Zhixi@2026!Root_8C
timeout-minutes: 10
- name: Deploy RAG Worker - name: Deploy RAG Worker
run: | run: |
set -e set -e

View File

@ -107,7 +107,7 @@ function envForProcess(baseEnv: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
}; };
} }
export async function startAPI(mockUrl: string, dbUrl: string, redisHost: string, redisPort: string, redisPw: string): Promise<number> { export async function startAPI(mockUrl: string, dbUrl: string, redisHost: string, redisPort: string, redisPw: string, jwtSecret?: string): Promise<number> {
mockAIUrl = mockUrl; mockAIUrl = mockUrl;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const env = envForProcess(process.env); const env = envForProcess(process.env);
@ -115,6 +115,7 @@ export async function startAPI(mockUrl: string, dbUrl: string, redisHost: string
DATABASE_URL: dbUrl, DATABASE_URL: dbUrl,
REDIS_HOST: redisHost, REDIS_PORT: redisPort, REDIS_HOST: redisHost, REDIS_PORT: redisPort,
REDIS_PASSWORD: redisPw, REDIS_DB: '0', REDIS_PASSWORD: redisPw, REDIS_DB: '0',
JWT_SECRET: jwtSecret || process.env.JWT_SECRET || 'test-integration-secret',
}); });
apiProc = spawn('node', ['dist/src/main.js'], { env, stdio: ['ignore', 'pipe', 'pipe'] }); apiProc = spawn('node', ['dist/src/main.js'], { env, stdio: ['ignore', 'pipe', 'pipe'] });
@ -135,13 +136,14 @@ export async function startAPI(mockUrl: string, dbUrl: string, redisHost: string
}); });
} }
export async function startWorker(dbUrl: string, redisHost: string, redisPort: string, redisPw: string, instanceId: string): Promise<number> { export async function startWorker(dbUrl: string, redisHost: string, redisPort: string, redisPw: string, instanceId: string, jwtSecret?: string): Promise<number> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const env = envForProcess(process.env); const env = envForProcess(process.env);
Object.assign(env, { Object.assign(env, {
DATABASE_URL: dbUrl, REDIS_HOST: redisHost, REDIS_PORT: redisPort, DATABASE_URL: dbUrl, REDIS_HOST: redisHost, REDIS_PORT: redisPort,
REDIS_PASSWORD: redisPw, REDIS_DB: '0', REDIS_PASSWORD: redisPw, REDIS_DB: '0',
WORKER_INSTANCE_ID: instanceId, WORKER_INSTANCE_ID: instanceId,
JWT_SECRET: jwtSecret || process.env.JWT_SECRET || 'test-integration-secret',
}); });
workerProc = spawn('node', ['dist/src/worker.main.js'], { env, stdio: ['ignore', 'pipe', 'pipe'] }); workerProc = spawn('node', ['dist/src/worker.main.js'], { env, stdio: ['ignore', 'pipe', 'pipe'] });

View File

@ -50,9 +50,9 @@ beforeAll(async () => {
console.log(`[api-e2e] JWT: ${userToken.slice(0, 20)}...`); console.log(`[api-e2e] JWT: ${userToken.slice(0, 20)}...`);
// Start API + Worker // Start API + Worker
const apiPid = await startAPI(mockAI.url, DB_URL, REDIS_HOST, REDIS_PORT, REDIS_PW); const apiPid = await startAPI(mockAI.url, DB_URL, REDIS_HOST, REDIS_PORT, REDIS_PW, JWT_SECRET);
console.log(`[api-e2e] API pid=${apiPid}`); console.log(`[api-e2e] API pid=${apiPid}`);
const workerPid = await startWorker(DB_URL, REDIS_HOST, REDIS_PORT, REDIS_PW, `e2e-worker-${Date.now()}`); const workerPid = await startWorker(DB_URL, REDIS_HOST, REDIS_PORT, REDIS_PW, `e2e-worker-${Date.now()}`, JWT_SECRET);
console.log(`[api-e2e] Worker pid=${workerPid}`); console.log(`[api-e2e] Worker pid=${workerPid}`);
await new Promise(r => setTimeout(r, 3000)); await new Promise(r => setTimeout(r, 3000));
}, 60_000); }, 60_000);

View File

@ -43,11 +43,12 @@ beforeAll(async () => {
setRedisConfig(REDIS_HOST, REDIS_PORT, REDIS_PW || undefined); setRedisConfig(REDIS_HOST, REDIS_PORT, REDIS_PW || undefined);
// 4. Start API // 4. Start API
const apiPid = await startAPI(mockAI.url, DB_URL, REDIS_HOST, String(REDIS_PORT), REDIS_PW); const JWT_SEC = process.env.JWT_SECRET || 'test-integration-secret';
const apiPid = await startAPI(mockAI.url, DB_URL, REDIS_HOST, String(REDIS_PORT), REDIS_PW, JWT_SEC);
console.log(`[test] API started, pid=${apiPid}`); console.log(`[test] API started, pid=${apiPid}`);
// 5. Start Worker // 5. Start Worker
const workerPid = await startWorker(DB_URL, REDIS_HOST, String(REDIS_PORT), REDIS_PW, `test-worker-${Date.now()}`); const workerPid = await startWorker(DB_URL, REDIS_HOST, String(REDIS_PORT), REDIS_PW, `test-worker-${Date.now()}`, JWT_SEC);
console.log(`[test] Worker started, pid=${workerPid}`); console.log(`[test] Worker started, pid=${workerPid}`);
await new Promise(r => setTimeout(r, 3000)); await new Promise(r => setTimeout(r, 3000));