feat: strengthen backup and mobile workflows
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { spawn } from 'node:child_process'
|
||||
import { createWriteStream, mkdirSync, writeFileSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
const frontend = process.cwd()
|
||||
const root = path.resolve(frontend, '..')
|
||||
const project = process.env.DODO_E2E_PROJECT
|
||||
if (!project) throw new Error('DODO_E2E_PROJECT is required')
|
||||
const runtimeBase = path.join(frontend, 'playwright-runtime', project)
|
||||
const runtime = path.join(runtimeBase, `run-${new Date().toISOString().replace(/[:.]/g, '-')}-${process.pid}`)
|
||||
const attachments = path.join(runtime, 'attachments')
|
||||
const staging = path.join(runtime, 'backup-staging')
|
||||
mkdirSync(attachments, { recursive: true })
|
||||
mkdirSync(staging, { recursive: true })
|
||||
writeFileSync(path.join(runtimeBase, 'latest.json'), JSON.stringify({ runtime, database: path.join(runtime, 'dodo.sqlite3'), attachments, staging }, null, 2))
|
||||
writeFileSync(path.join(runtime, 'runtime.json'), JSON.stringify({ database: path.join(runtime, 'dodo.sqlite3'), attachments, staging }, null, 2))
|
||||
|
||||
const logs = {
|
||||
backend: createWriteStream(path.join(runtime, 'backend.log'), { flags: 'a' }),
|
||||
frontend: createWriteStream(path.join(runtime, 'frontend.log'), { flags: 'a' }),
|
||||
}
|
||||
const children = []
|
||||
function start(command, args, options, log) {
|
||||
const child = spawn(command, args, { ...options, stdio: ['ignore', 'pipe', 'pipe'] })
|
||||
child.stdout.pipe(log)
|
||||
child.stderr.pipe(log)
|
||||
children.push(child)
|
||||
return child
|
||||
}
|
||||
|
||||
start(path.join(root, '.venv/bin/uvicorn'), ['backend.main:app', '--host', '127.0.0.1', '--port', '8781'], {
|
||||
cwd: root,
|
||||
env: {
|
||||
...process.env,
|
||||
DODO_DATABASE_URL: `sqlite+aiosqlite:///${path.join(runtime, 'dodo.sqlite3')}`,
|
||||
DODO_AUTO_CREATE_SCHEMA: 'true',
|
||||
DODO_COOKIE_SECURE: 'false',
|
||||
DODO_ATTACHMENT_DIR: attachments,
|
||||
DODO_BACKUP_STAGING_DIR: staging,
|
||||
},
|
||||
}, logs.backend)
|
||||
start('pnpm', ['exec', 'vite', '--host', '127.0.0.1', '--port', '5173', '--strictPort'], { cwd: frontend, env: process.env }, logs.frontend)
|
||||
|
||||
let stopping = false
|
||||
function stop(signal = 'SIGTERM') {
|
||||
if (stopping) return
|
||||
stopping = true
|
||||
for (const child of children) if (!child.killed) child.kill(signal)
|
||||
setTimeout(() => { for (const child of children) if (!child.killed) child.kill('SIGKILL') }, 3000).unref()
|
||||
}
|
||||
process.on('SIGTERM', () => stop())
|
||||
process.on('SIGINT', () => stop())
|
||||
process.on('exit', () => stop())
|
||||
|
||||
await Promise.all(children.map(child => new Promise((resolve, reject) => {
|
||||
child.once('exit', (code, signal) => stopping ? resolve() : reject(new Error(`server exited code=${code} signal=${signal}`)))
|
||||
})))
|
||||
@@ -0,0 +1,15 @@
|
||||
import { spawn } from 'node:child_process'
|
||||
|
||||
const projects = ['mobile-390', 'mobile-375']
|
||||
for (const project of projects) {
|
||||
const code = await new Promise((resolve, reject) => {
|
||||
const child = spawn('pnpm', ['exec', 'playwright', 'test', '--project', project], {
|
||||
cwd: process.cwd(),
|
||||
env: { ...process.env, DODO_E2E_PROJECT: project },
|
||||
stdio: 'inherit',
|
||||
})
|
||||
child.once('error', reject)
|
||||
child.once('exit', value => resolve(value ?? 1))
|
||||
})
|
||||
if (code !== 0) process.exit(code)
|
||||
}
|
||||
Reference in New Issue
Block a user