chore: 移除 CI/Actions 相关设施(workflow、ci 脚本、tests 目录),审计脚本保留在 repro/ 供本地开发使用
This commit is contained in:
@@ -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
|
|
||||||
-131
@@ -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('结果:全部通过')
|
|
||||||
Reference in New Issue
Block a user