From aa773062fe74aae8a5f196f83565d6561d4f2732 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 16 Oct 2026 22:28:35 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E7=A7=BB=E9=99=A4=20CI/Actions=20?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E8=AE=BE=E6=96=BD=EF=BC=88workflow=E3=80=81c?= =?UTF-8?q?i=20=E8=84=9A=E6=9C=AC=E3=80=81tests=20=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=EF=BC=89=EF=BC=8C=E5=AE=A1=E8=AE=A1=E8=84=9A=E6=9C=AC=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E5=9C=A8=20repro/=20=E4=BE=9B=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E5=BC=80=E5=8F=91=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/ci.yml | 58 -------- .../repro/admin-audit.mjs | 0 .../repro/protocol-audit.mjs | 0 scripts/ci.mjs | 131 ------------------ 4 files changed, 189 deletions(-) delete mode 100644 .gitea/workflows/ci.yml rename tests/server/admin.mjs => intern-log/repro/admin-audit.mjs (100%) rename tests/server/protocol.mjs => intern-log/repro/protocol-audit.mjs (100%) delete mode 100644 scripts/ci.mjs diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml deleted file mode 100644 index 62bb006..0000000 --- a/.gitea/workflows/ci.yml +++ /dev/null @@ -1,58 +0,0 @@ -# CI:每次推送自动运行完整测试套件(本地与云端同一入口:node scripts/ci.mjs) -# 网络适配(服务器在国内): -# 1) 不使用任何远程 action(actions/checkout 会被解析到 github.com 导致挂死),改用内置环境变量手动 clone -# 2) job 容器镜像走腾讯内网镜像源(runner 标签已指向 mirror.ccs.tencentyun.com) -# 3) npm/pnpm 走国内 registry -name: CI -on: - push: - pull_request: - -jobs: - test: - name: 测试套件(协议/用户管理/单测/双设备/实时链路/构建) - runs-on: ubuntu-latest - container: - image: mirror.ccs.tencentyun.com/library/node:22-bookworm - steps: - - name: 检出代码(内网闭环,不访问 github) - run: | - git config --global --add safe.directory "$GITHUB_WORKSPACE" - git clone --depth 1 "https://x-access-token:${GITHUB_TOKEN}@${GITHUB_SERVER_URL#https://}/root/daywork.git" . - git checkout "$GITHUB_SHA" - - - name: 安装 pnpm - run: npm install -g pnpm@11 --registry=https://registry.npmmirror.com - - - name: 安装依赖 - working-directory: intern-log - run: pnpm install --frozen-lockfile --registry=https://registry.npmmirror.com - - - name: 运行测试与构建 - env: - NODE_ENV: production - run: node scripts/ci.mjs - - audit: - name: 依赖漏洞扫描(软性检查) - runs-on: ubuntu-latest - container: - image: mirror.ccs.tencentyun.com/library/node:22-bookworm - continue-on-error: true - steps: - - name: 检出代码(内网闭环,不访问 github) - run: | - git config --global --add safe.directory "$GITHUB_WORKSPACE" - git clone --depth 1 "https://x-access-token:${GITHUB_TOKEN}@${GITHUB_SERVER_URL#https://}/root/daywork.git" . - git checkout "$GITHUB_SHA" - - - name: 安装 pnpm - run: npm install -g pnpm@11 --registry=https://registry.npmmirror.com - - - name: 安装依赖 - working-directory: intern-log - run: pnpm install --frozen-lockfile --registry=https://registry.npmmirror.com - - - name: pnpm audit(高危及以上) - working-directory: intern-log - run: pnpm audit --prod --audit-level=high --registry=https://registry.npmmirror.com diff --git a/tests/server/admin.mjs b/intern-log/repro/admin-audit.mjs similarity index 100% rename from tests/server/admin.mjs rename to intern-log/repro/admin-audit.mjs diff --git a/tests/server/protocol.mjs b/intern-log/repro/protocol-audit.mjs similarity index 100% rename from tests/server/protocol.mjs rename to intern-log/repro/protocol-audit.mjs diff --git a/scripts/ci.mjs b/scripts/ci.mjs deleted file mode 100644 index 9a8fea8..0000000 --- a/scripts/ci.mjs +++ /dev/null @@ -1,131 +0,0 @@ -// 统一 CI 编排:本地与 Gitea Actions 共用同一入口 -// 用法:node scripts/ci.mjs -// 步骤:起本地同步服务 → 协议审计 → 用户管理审计 → 单元测试 → 双设备回归 → 实时链路 → 网页构建 → 依赖审计(软性) -// 任何硬性步骤失败即整体失败(依赖审计默认软性,CI 里单独放开) -import { spawn, spawnSync } from 'node:child_process' -import fs from 'node:fs' -import os from 'node:os' -import path from 'node:path' -import { fileURLToPath } from 'node:url' - -const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') -const PORT = process.env.SYNC_PORT || '3780' -const BASE = 'http://127.0.0.1:' + PORT -const results = [] -let hardFailed = false -let softWarned = false - -function log(tag, line) { console.log('[' + tag + '] ' + line) } - -function runStep(name, cmd, args, opts = {}) { - log('RUN', name + ' (' + cmd + ' ' + args.join(' ') + ')') - const started = Date.now() - const r = spawnSync(cmd, args, { cwd: opts.cwd || ROOT, env: process.env, encoding: 'utf-8', shell: process.platform === 'win32' && cmd.endsWith('.cmd') }) - const out = ((r.stdout || '') + (r.stderr || '')).trim() - const tail = out.split('\n').slice(-opts.tailLines || -40) - for (const l of (opts.tail || out.split('\n').slice(-25))) if (l.trim()) console.log(' ' + l) - const dur = ((Date.now() - started) / 1000).toFixed(1) + 's' - const ok = r.status === 0 - results.push({ name, ok, dur }) - if (ok) log('OK ', name + ' (' + dur + ')') - else { - log('FAIL', name + ' (exit ' + r.status + ', ' + dur + ')') - if (opts.soft) { softWarned = true; log('WARN', name + ' 为软性检查,不阻断 CI') } - else hardFailed = true - } - return { ok, out } -} - -function findEsbuild() { - const candidates = [ - path.join(ROOT, 'intern-log', 'node_modules', 'esbuild', 'bin', 'esbuild') - ] - const pnpmDir = path.join(ROOT, 'intern-log', 'node_modules', '.pnpm') - try { - for (const d of fs.readdirSync(pnpmDir)) { - if (d.startsWith('esbuild@')) candidates.push(path.join(pnpmDir, d, 'node_modules', 'esbuild', 'bin', 'esbuild')) - } - } catch { /* 无 .pnpm 目录 */ } - for (const c of candidates) if (fs.existsSync(c)) return c - throw new Error('找不到 esbuild,请先在 intern-log 下执行 pnpm install') -} - -function runBundled(name, tsPath) { - const outfile = path.join(os.tmpdir(), 'daywork-' + name + '-' + Date.now() + '.cjs') - const esbuildBin = findEsbuild() - const b = runStep('bundle:' + name, process.execPath, [esbuildBin, tsPath, '--bundle', '--platform=node', '--format=cjs', '--outfile=' + outfile], { tail: [] }) - if (!b.ok) return - runStep('test:' + name, process.execPath, [outfile], {}) - try { fs.rmSync(outfile, { force: true }) } catch { /* 忽略 */ } -} - -async function waitForServer(deadlineMs) { - const started = Date.now() - while (Date.now() - started < deadlineMs) { - try { - const r = await fetch(BASE + '/api/health') - if (r.ok) return true - } catch { /* 未就绪 */ } - await new Promise(r => globalThis.setTimeout(r, 300)) - } - return false -} - -// ---------- 0) 清理历史测试数据,保证套件幂等 ---------- -fs.rmSync(path.join(ROOT, 'server', 'data'), { recursive: true, force: true }) - -// ---------- 1) 启动本地同步服务 ---------- -const serverProc = spawn(process.execPath, [path.join(ROOT, 'server', 'index.js')], { - cwd: ROOT, - env: { ...process.env, PORT }, - stdio: ['ignore', 'pipe', 'pipe'] -}) -let serverLog = '' -serverProc.stdout.on('data', d => { serverLog += d }) -serverProc.stderr.on('data', d => { serverLog += d }) - -const up = await waitForServer(15000) -if (!up) { - console.error('[FATAL] 本地同步服务 15 秒内未就绪') - console.error(serverLog.slice(-1500)) - process.exit(1) -} -log('OK', '同步服务已就绪 ' + BASE) - -try { - // ---------- 2) 服务端协议审计 ---------- - runStep('protocol-audit', process.execPath, [path.join(ROOT, 'tests', 'server', 'protocol.mjs')], {}) - // ---------- 3) 用户管理审计 ---------- - runStep('admin-audit', process.execPath, [path.join(ROOT, 'tests', 'server', 'admin.mjs')], {}) - // ---------- 4/5/6) 客户端引擎级测试 ---------- - runBundled('unit', 'intern-log/repro/unit-tests.ts') - runBundled('two-device', 'intern-log/repro/two-device.ts') - runBundled('realtime', 'intern-log/repro/engine-realtime.ts') - - // ---------- 7) 网页构建(编译即检查) ---------- - const isWin = process.platform === 'win32' - const pnpmCmd = isWin ? 'pnpm.cmd' : 'pnpm' - runStep('build:web', pnpmCmd, ['run', 'build'], { cwd: path.join(ROOT, 'intern-log') }) -} finally { - serverProc.kill() - try { fs.rmSync(path.join(ROOT, 'server', 'data'), { recursive: true, force: true }) } catch { /* 忽略 */ } -} - -// ---------- 8) 依赖漏洞审计(软性:高危及以上才告警,CI 可单独放开为硬性) ---------- -const isWin = process.platform === 'win32' -runStep('audit:deps', isWin ? 'pnpm.cmd' : 'pnpm', ['audit', '--prod', '--audit-level=high'], { - cwd: path.join(ROOT, 'intern-log'), - soft: true -}) - -// ---------- 汇总 ---------- -console.log('') -console.log('==================== CI 汇总 ====================') -for (const r of results) console.log((r.ok ? ' PASS ' : ' FAIL ') + r.name.padEnd(18) + r.dur) -console.log('================================================') -if (hardFailed) { - console.error('结果:存在失败步骤,CI 不通过') - process.exit(1) -} -if (softWarned) console.log('结果:全部硬性步骤通过(存在软性告警,见上方 WARN)') -else console.log('结果:全部通过')